2 * iostat 2002 Ronnie Sahlberg
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include <epan/epan_dissect.h>
32 #include <epan/packet_info.h>
34 #include <epan/timestamp.h>
35 #include <epan/stat_cmd_args.h>
36 #include <epan/strutil.h>
39 #define CALC_TYPE_FRAMES 0
40 #define CALC_TYPE_BYTES 1
41 #define CALC_TYPE_FRAMES_AND_BYTES 2
42 #define CALC_TYPE_COUNT 3
43 #define CALC_TYPE_SUM 4
44 #define CALC_TYPE_MIN 5
45 #define CALC_TYPE_MAX 6
46 #define CALC_TYPE_AVG 7
47 #define CALC_TYPE_LOAD 8
49 void register_tap_listener_iostat(void);
52 const char *func_name
;
56 static calc_type_ent_t calc_type_table
[] = {
57 { "FRAMES", CALC_TYPE_FRAMES
},
58 { "BYTES", CALC_TYPE_BYTES
},
59 { "FRAMES BYTES", CALC_TYPE_FRAMES_AND_BYTES
},
60 { "COUNT", CALC_TYPE_COUNT
},
61 { "SUM", CALC_TYPE_SUM
},
62 { "MIN", CALC_TYPE_MIN
},
63 { "MAX", CALC_TYPE_MAX
},
64 { "AVG", CALC_TYPE_AVG
},
65 { "LOAD", CALC_TYPE_LOAD
},
69 typedef struct _io_stat_t
{
70 guint64 interval
; /* The user-specified time interval (us) */
71 guint invl_prec
; /* Decimal precision of the time interval (1=10s, 2=100s etc) */
72 int num_cols
; /* The number of columns of stats in the table */
73 struct _io_stat_item_t
*items
; /* Each item is a single cell in the table */
74 time_t start_time
; /* Time of first frame matching the filter */
75 const char **filters
; /* 'io,stat' cmd strings (e.g., "AVG(smb.time)smb.time") */
76 guint64
*max_vals
; /* The max value sans the decimal or nsecs portion in each stat column */
77 guint32
*max_frame
; /* The max frame number displayed in each stat column */
80 typedef struct _io_stat_item_t
{
82 struct _io_stat_item_t
*next
;
83 struct _io_stat_item_t
*prev
;
84 guint64 time
; /* Time since start of capture (us)*/
85 int calc_type
; /* The statistic type */
86 int colnum
; /* Column number of this stat (0 to n) */
89 guint32 num
; /* The sample size of a given statistic (only needed for AVG) */
90 guint64 counter
; /* The accumulated data for the calculation of that statistic */
92 gdouble double_counter
;
95 #define NANOSECS_PER_SEC 1000000000ULL
97 static guint64 last_relative_time
;
100 iostat_packet(void *arg
, packet_info
*pinfo
, epan_dissect_t
*edt
, const void *dummy _U_
)
105 guint64 relative_time
, rt
;
111 mit
= (io_stat_item_t
*) arg
;
112 parent
= mit
->parent
;
114 /* If this frame's relative time is negative, set its relative time to last_relative_time
115 rather than disincluding it from the calculations. */
116 if (pinfo
->rel_ts
.secs
>= 0) {
117 relative_time
= ((guint64
)pinfo
->rel_ts
.secs
* 1000000ULL) +
118 ((guint64
)((pinfo
->rel_ts
.nsecs
+500)/1000));
119 last_relative_time
= relative_time
;
121 relative_time
= last_relative_time
;
124 if (mit
->parent
->start_time
== 0) {
125 mit
->parent
->start_time
= pinfo
->fd
->abs_ts
.secs
- pinfo
->rel_ts
.secs
;
128 /* The prev item is always the last interval in which we saw packets. */
131 /* If we have moved into a new interval (row), create a new io_stat_item_t struct for every interval
132 * between the last struct and this one. If an item was not found in a previous interval, an empty
133 * struct will be created for it. */
135 while (rt
>= it
->time
+ parent
->interval
) {
136 it
->next
= (io_stat_item_t
*)g_malloc(sizeof(io_stat_item_t
));
138 it
->next
->next
= NULL
;
142 it
->time
= it
->prev
->time
+ parent
->interval
;
145 it
->float_counter
= 0;
146 it
->double_counter
= 0;
148 it
->calc_type
= it
->prev
->calc_type
;
149 it
->hf_index
= it
->prev
->hf_index
;
150 it
->colnum
= it
->prev
->colnum
;
153 /* Store info in the current structure */
156 switch(it
->calc_type
) {
157 case CALC_TYPE_FRAMES
:
158 case CALC_TYPE_BYTES
:
159 case CALC_TYPE_FRAMES_AND_BYTES
:
160 it
->counter
+= pinfo
->fd
->pkt_len
;
162 case CALC_TYPE_COUNT
:
163 gp
=proto_get_finfo_ptr_array(edt
->tree
, it
->hf_index
);
165 it
->counter
+= gp
->len
;
169 gp
=proto_get_finfo_ptr_array(edt
->tree
, it
->hf_index
);
173 for(i
=0;i
<gp
->len
;i
++){
174 switch(proto_registrar_get_ftype(it
->hf_index
)){
179 it
->counter
+= fvalue_get_uinteger(&((field_info
*)gp
->pdata
[i
])->value
);
182 it
->counter
+= fvalue_get_integer64(&((field_info
*)gp
->pdata
[i
])->value
);
188 it
->counter
+= fvalue_get_sinteger(&((field_info
*)gp
->pdata
[i
])->value
);
191 it
->counter
+= (gint64
)fvalue_get_integer64(&((field_info
*)gp
->pdata
[i
])->value
);
195 (gfloat
)fvalue_get_floating(&((field_info
*)gp
->pdata
[i
])->value
);
198 it
->double_counter
+= fvalue_get_floating(&((field_info
*)gp
->pdata
[i
])->value
);
200 case FT_RELATIVE_TIME
:
201 new_time
= (nstime_t
*)fvalue_get(&((field_info
*)gp
->pdata
[i
])->value
);
202 val
= ((guint64
)new_time
->secs
* NANOSECS_PER_SEC
) + (guint64
)new_time
->nsecs
;
207 * "Can't happen"; see the checks
208 * in register_io_tap().
210 g_assert_not_reached();
217 gp
=proto_get_finfo_ptr_array(edt
->tree
, it
->hf_index
);
223 ftype
=proto_registrar_get_ftype(it
->hf_index
);
224 for(i
=0;i
<gp
->len
;i
++){
230 val
= fvalue_get_uinteger(&((field_info
*)gp
->pdata
[i
])->value
);
231 if ((it
->frames
==1 && i
==0) || (val
< it
->counter
)) {
236 val
= fvalue_get_integer64(&((field_info
*)gp
->pdata
[i
])->value
);
237 if((it
->frames
==1 && i
==0) || (val
< it
->counter
)){
245 val
= fvalue_get_sinteger(&((field_info
*)gp
->pdata
[i
])->value
);
246 if((it
->frames
==1 && i
==0) || ((gint32
)val
< (gint32
)it
->counter
)) {
251 val
= fvalue_get_integer64(&((field_info
*)gp
->pdata
[i
])->value
);
252 if((it
->frames
==1 && i
==0) || ((gint64
)val
< (gint64
)it
->counter
)) {
257 float_val
=(gfloat
)fvalue_get_floating(&((field_info
*)gp
->pdata
[i
])->value
);
258 if((it
->frames
==1 && i
==0) || (float_val
< it
->float_counter
)) {
259 it
->float_counter
=float_val
;
263 double_val
=fvalue_get_floating(&((field_info
*)gp
->pdata
[i
])->value
);
264 if((it
->frames
==1 && i
==0) || (double_val
< it
->double_counter
)) {
265 it
->double_counter
=double_val
;
268 case FT_RELATIVE_TIME
:
269 new_time
= (nstime_t
*)fvalue_get(&((field_info
*)gp
->pdata
[i
])->value
);
270 val
= ((guint64
)new_time
->secs
* NANOSECS_PER_SEC
) + (guint64
)new_time
->nsecs
;
271 if((it
->frames
==1 && i
==0) || (val
< it
->counter
)) {
277 * "Can't happen"; see the checks
278 * in register_io_tap().
280 g_assert_not_reached();
287 gp
=proto_get_finfo_ptr_array(edt
->tree
, it
->hf_index
);
293 ftype
=proto_registrar_get_ftype(it
->hf_index
);
294 for(i
=0;i
<gp
->len
;i
++){
300 val
= fvalue_get_uinteger(&((field_info
*)gp
->pdata
[i
])->value
);
301 if(val
> it
->counter
)
305 val
= fvalue_get_integer64(&((field_info
*)gp
->pdata
[i
])->value
);
306 if(val
> it
->counter
)
313 val
= fvalue_get_sinteger(&((field_info
*)gp
->pdata
[i
])->value
);
314 if((gint32
)val
> (gint32
)it
->counter
)
318 val
= fvalue_get_integer64(&((field_info
*)gp
->pdata
[i
])->value
);
319 if ((gint64
)val
> (gint64
)it
->counter
)
323 float_val
= (gfloat
)fvalue_get_floating(&((field_info
*)gp
->pdata
[i
])->value
);
324 if(float_val
> it
->float_counter
)
325 it
->float_counter
=float_val
;
328 double_val
= fvalue_get_floating(&((field_info
*)gp
->pdata
[i
])->value
);
329 if(double_val
> it
->double_counter
)
330 it
->double_counter
=double_val
;
332 case FT_RELATIVE_TIME
:
333 new_time
= (nstime_t
*)fvalue_get(&((field_info
*)gp
->pdata
[i
])->value
);
334 val
= ((guint64
)new_time
->secs
* NANOSECS_PER_SEC
) + (guint64
)new_time
->nsecs
;
340 * "Can't happen"; see the checks
341 * in register_io_tap().
343 g_assert_not_reached();
350 gp
=proto_get_finfo_ptr_array(edt
->tree
, it
->hf_index
);
354 ftype
=proto_registrar_get_ftype(it
->hf_index
);
355 for(i
=0;i
<gp
->len
;i
++){
362 val
= fvalue_get_uinteger(&((field_info
*)gp
->pdata
[i
])->value
);
367 val
= fvalue_get_integer64(&((field_info
*)gp
->pdata
[i
])->value
);
374 val
= fvalue_get_sinteger(&((field_info
*)gp
->pdata
[i
])->value
);
378 it
->float_counter
+= (gfloat
)fvalue_get_floating(&((field_info
*)gp
->pdata
[i
])->value
);
381 it
->double_counter
+= fvalue_get_floating(&((field_info
*)gp
->pdata
[i
])->value
);
383 case FT_RELATIVE_TIME
:
384 new_time
= (nstime_t
*)fvalue_get(&((field_info
*)gp
->pdata
[i
])->value
);
385 val
= ((guint64
)new_time
->secs
* NANOSECS_PER_SEC
) + (guint64
)new_time
->nsecs
;
390 * "Can't happen"; see the checks
391 * in register_io_tap().
393 g_assert_not_reached();
400 gp
= proto_get_finfo_ptr_array(edt
->tree
, it
->hf_index
);
402 ftype
= proto_registrar_get_ftype(it
->hf_index
);
403 if (ftype
!= FT_RELATIVE_TIME
) {
405 "\ntshark: LOAD() is only supported for relative-time fields such as smb.time\n");
408 for(i
=0;i
<gp
->len
;i
++){
413 new_time
= (nstime_t
*)fvalue_get(&((field_info
*)gp
->pdata
[i
])->value
);
414 val
= ((guint64
)new_time
->secs
*1000000ULL) + (guint64
)(new_time
->nsecs
/1000);
415 tival
= (int)(val
% parent
->interval
);
416 it
->counter
+= tival
;
420 if (val
< (guint64
)parent
->interval
) {
424 pit
->counter
+= parent
->interval
;
425 val
-= parent
->interval
;
432 /* Store the highest value for this item in order to determine the width of each stat column.
433 * For real numbers we only need to know its magnitude (the value to the left of the decimal point
434 * so round it up before storing it as an integer in max_vals. For AVG of RELATIVE_TIME fields,
435 * calc the average, round it to the next second and store the seconds. For all other calc types
436 * of RELATIVE_TIME fields, store the counters without modification.
438 switch(it
->calc_type
) {
439 case CALC_TYPE_FRAMES
:
440 case CALC_TYPE_FRAMES_AND_BYTES
:
441 parent
->max_frame
[it
->colnum
] =
442 MAX(parent
->max_frame
[it
->colnum
], it
->frames
);
443 if (it
->calc_type
==CALC_TYPE_FRAMES_AND_BYTES
)
444 parent
->max_vals
[it
->colnum
] =
445 MAX(parent
->max_vals
[it
->colnum
], it
->counter
);
447 case CALC_TYPE_BYTES
:
448 case CALC_TYPE_COUNT
:
450 parent
->max_vals
[it
->colnum
] = MAX(parent
->max_vals
[it
->colnum
], it
->counter
);
455 ftype
=proto_registrar_get_ftype(it
->hf_index
);
458 parent
->max_vals
[it
->colnum
] =
459 MAX(parent
->max_vals
[it
->colnum
], (guint64
)(it
->float_counter
+0.5));
462 parent
->max_vals
[it
->colnum
] =
463 MAX(parent
->max_vals
[it
->colnum
],(guint64
)(it
->double_counter
+0.5));
465 case FT_RELATIVE_TIME
:
466 parent
->max_vals
[it
->colnum
] =
467 MAX(parent
->max_vals
[it
->colnum
], it
->counter
);
470 /* UINT16-64 and INT8-64 */
471 parent
->max_vals
[it
->colnum
] =
472 MAX(parent
->max_vals
[it
->colnum
], it
->counter
);
477 if (it
->num
==0) /* avoid division by zero */
479 ftype
=proto_registrar_get_ftype(it
->hf_index
);
482 parent
->max_vals
[it
->colnum
] =
483 MAX(parent
->max_vals
[it
->colnum
], (guint64
)it
->float_counter
/it
->num
);
486 parent
->max_vals
[it
->colnum
] =
487 MAX(parent
->max_vals
[it
->colnum
],(guint64
)it
->double_counter
/it
->num
);
489 case FT_RELATIVE_TIME
:
490 parent
->max_vals
[it
->colnum
] =
491 MAX(parent
->max_vals
[it
->colnum
], ((it
->counter
/(guint64
)it
->num
) + 500000000ULL) / NANOSECS_PER_SEC
);
494 /* UINT16-64 and INT8-64 */
495 parent
->max_vals
[it
->colnum
] =
496 MAX(parent
->max_vals
[it
->colnum
], it
->counter
/it
->num
);
504 magnitude (guint64 val
, int max_w
)
508 for (i
=0; i
<max_w
; i
++) {
517 * Print the calc_type_table[] function label centered in the column header.
520 printcenter (const char *label
, int lenval
, int numpad
)
522 int lenlab
= (int) strlen(label
), len
;
523 const char spaces
[]=" ", *spaces_ptr
;
525 len
= (int) (strlen(spaces
)) - (((lenval
-lenlab
) / 2) + numpad
);
526 if (len
> 0 && len
< 6) {
527 spaces_ptr
= &spaces
[len
];
528 if ((lenval
-lenlab
)%2==0) {
529 printf("%s%s%s|", spaces_ptr
, label
, spaces_ptr
);
531 printf("%s%s%s|", spaces_ptr
-1, label
, spaces_ptr
);
533 } else if (len
> 0 && len
<= 15) {
534 printf("%s|", label
);
539 int fr
; /* Width of this FRAMES column sans padding and border chars */
540 int val
; /* Width of this non-FRAMES column sans padding and border chars */
544 iostat_draw(void *arg
)
547 guint64 interval
, duration
, t
, invl_end
, dv
;
548 int i
, j
, k
, num_cols
, num_rows
, dur_secs_orig
, dur_nsecs_orig
, dur_secs
, dur_nsecs
, dur_mag
,
549 invl_mag
, invl_prec
, tabrow_w
, borderlen
, invl_col_w
, numpad
=1, namelen
, len_filt
, type
,
551 int fr_mag
; /* The magnitude of the max frame number in this column */
552 int val_mag
; /* The magnitude of the max value in this column */
553 char *spaces
, *spaces_s
, *filler_s
=NULL
, **fmts
, *fmt
=NULL
;
555 static gchar dur_mag_s
[3], invl_prec_s
[3], fr_mag_s
[3], val_mag_s
[3], *invl_fmt
, *full_fmt
;
556 io_stat_item_t
*mit
, **stat_cols
, *item
, **item_in_column
;
557 gboolean last_row
=FALSE
;
563 mit
= (io_stat_item_t
*)arg
;
565 num_cols
= iot
->num_cols
;
566 col_w
= (column_width
*)g_malloc(sizeof(column_width
) * num_cols
);
567 fmts
= (char **)g_malloc(sizeof(char *) * num_cols
);
568 duration
= ((guint64
)cfile
.elapsed_time
.secs
* 1000000ULL) +
569 (guint64
)((cfile
.elapsed_time
.nsecs
+ 500) / 1000);
571 /* Store the pointer to each stat column */
572 stat_cols
= (io_stat_item_t
**) g_malloc(sizeof(io_stat_item_t
*) * num_cols
);
573 for (j
=0; j
<num_cols
; j
++)
574 stat_cols
[j
] = &iot
->items
[j
];
576 /* The following prevents gross inaccuracies when the user specifies an interval that is greater
577 * than the capture duration. */
578 if (iot
->interval
> duration
|| iot
->interval
==G_MAXINT32
) {
580 iot
->interval
= G_MAXINT32
;
582 interval
= iot
->interval
;
585 /* Calc the capture duration's magnitude (dur_mag) */
586 dur_secs
= (int)(duration
/1000000ULL);
587 dur_secs_orig
= dur_secs
;
588 dur_nsecs
= (int)(duration
%1000000ULL);
589 dur_nsecs_orig
= dur_nsecs
;
590 dur_mag
= magnitude((guint64
)dur_secs
, 5);
591 g_snprintf(dur_mag_s
, 3, "%u", dur_mag
);
593 /* Calc the interval's magnitude */
594 invl_mag
= magnitude(interval
/1000000ULL, 5);
596 /* Set or get the interval precision */
597 if (interval
==duration
) {
599 * An interval arg of 0 or an interval size exceeding the capture duration was specified.
600 * Set the decimal precision of duration based on its magnitude. */
608 borderlen
= 30 + dur_mag
+ (invl_prec
==0 ? 0 : invl_prec
+1);
610 invl_prec
= iot
->invl_prec
;
611 borderlen
= 24 + invl_mag
+ (invl_prec
==0 ? 0 : invl_prec
+1);
614 /* Round the duration according to invl_prec */
616 for (i
=0; i
<invl_prec
; i
++)
618 if ((duration
%dv
) > 5*(dv
/10)) {
619 duration
+= 5*(dv
/10);
620 duration
= (duration
/dv
) * dv
;
621 dur_secs
= (int)(duration
/1000000ULL);
622 dur_nsecs
= (int)(duration
%1000000ULL);
624 * Recalc dur_mag in case rounding has increased its magnitude */
625 dur_mag
= magnitude((guint64
)dur_secs
, 5);
627 if (iot
->interval
==G_MAXINT32
)
630 /* Calc the width of the time interval column (incl borders and padding). */
632 invl_col_w
= (2*dur_mag
) + 8;
634 invl_col_w
= (2*dur_mag
) + (2*invl_prec
) + 10;
636 /* Update the width of the time interval column if date is shown */
637 switch (timestamp_get_type()) {
638 case TS_ABSOLUTE_WITH_YMD
:
639 case TS_ABSOLUTE_WITH_YDOY
:
640 case TS_UTC_WITH_YMD
:
641 case TS_UTC_WITH_YDOY
:
642 invl_col_w
= MAX(invl_col_w
, 23);
646 invl_col_w
= MAX(invl_col_w
, 12);
650 borderlen
= MAX(borderlen
, invl_col_w
);
652 /* Calc the total width of each row in the stats table and build the printf format string for each
653 * column based on its field type, width, and name length.
654 * NOTE: The magnitude of all types including float and double are stored in iot->max_vals which
655 * is an *integer*. */
656 tabrow_w
= invl_col_w
;
657 for (j
=0; j
<num_cols
; j
++) {
658 type
= iot
->items
[j
].calc_type
;
659 if (type
==CALC_TYPE_FRAMES_AND_BYTES
) {
662 namelen
= (int) strlen(calc_type_table
[type
].func_name
);
664 if(type
==CALC_TYPE_FRAMES
665 || type
==CALC_TYPE_FRAMES_AND_BYTES
) {
667 fr_mag
= magnitude(iot
->max_frame
[j
], 15);
668 fr_mag
= MAX(6, fr_mag
);
669 col_w
[j
].fr
= fr_mag
;
670 tabrow_w
+= col_w
[j
].fr
+ 3;
671 g_snprintf(fr_mag_s
, 3, "%u", fr_mag
);
673 if (type
==CALC_TYPE_FRAMES
) {
674 fmt
= g_strconcat(" %", fr_mag_s
, "u |", NULL
);
676 /* CALC_TYPE_FRAMES_AND_BYTES
678 val_mag
= magnitude(iot
->max_vals
[j
], 15);
679 val_mag
= MAX(5, val_mag
);
680 col_w
[j
].val
= val_mag
;
681 tabrow_w
+= (col_w
[j
].val
+ 3);
682 g_snprintf(val_mag_s
, 3, "%u", val_mag
);
683 fmt
= g_strconcat(" %", fr_mag_s
, "u |", " %", val_mag_s
, G_GINT64_MODIFIER
, "u |", NULL
);
690 case CALC_TYPE_BYTES
:
691 case CALC_TYPE_COUNT
:
693 val_mag
= magnitude(iot
->max_vals
[j
], 15);
694 val_mag
= MAX(5, val_mag
);
695 col_w
[j
].val
= val_mag
;
696 g_snprintf(val_mag_s
, 3, "%u", val_mag
);
697 fmt
= g_strconcat(" %", val_mag_s
, G_GINT64_MODIFIER
, "u |", NULL
);
701 ftype
= proto_registrar_get_ftype(stat_cols
[j
]->hf_index
);
705 val_mag
= magnitude(iot
->max_vals
[j
], 15);
706 g_snprintf(val_mag_s
, 3, "%u", val_mag
);
707 fmt
= g_strconcat(" %", val_mag_s
, ".6f |", NULL
);
708 col_w
[j
].val
= val_mag
+ 7;
710 case FT_RELATIVE_TIME
:
711 /* Convert FT_RELATIVE_TIME field to seconds
712 * CALC_TYPE_LOAD was already converted in iostat_packet() ) */
713 if (type
==CALC_TYPE_LOAD
) {
714 iot
->max_vals
[j
] /= interval
;
715 } else if (type
!= CALC_TYPE_AVG
) {
716 iot
->max_vals
[j
] = (iot
->max_vals
[j
] + 500000000ULL) / NANOSECS_PER_SEC
;
718 val_mag
= magnitude(iot
->max_vals
[j
], 15);
719 g_snprintf(val_mag_s
, 3, "%u", val_mag
);
720 fmt
= g_strconcat(" %", val_mag_s
, "u.%06u |", NULL
);
721 col_w
[j
].val
= val_mag
+ 7;
725 val_mag
= magnitude(iot
->max_vals
[j
], 15);
726 val_mag
= MAX(namelen
, val_mag
);
727 col_w
[j
].val
= val_mag
;
728 g_snprintf(val_mag_s
, 3, "%u", val_mag
);
736 fmt
= g_strconcat(" %", val_mag_s
, G_GINT64_MODIFIER
, "u |", NULL
);
743 fmt
= g_strconcat(" %", val_mag_s
, G_GINT64_MODIFIER
, "d |", NULL
);
746 } /* End of ftype switch */
747 } /* End of calc_type switch */
748 tabrow_w
+= col_w
[j
].val
+ 3;
751 } /* End of for loop (columns) */
753 borderlen
= MAX(borderlen
, tabrow_w
);
755 /* Calc the max width of the list of filters. */
757 for(j
=0; j
<num_cols
; j
++) {
758 if (iot
->filters
[j
]) {
759 k
= (int) (strlen(iot
->filters
[j
]) + 11);
760 maxfltr_w
= MAX(maxfltr_w
, k
);
762 maxfltr_w
= MAX(maxfltr_w
, 26);
765 /* The stat table is not wrapped (by tshark) but filter is wrapped at the width of the stats table
766 * (which currently = borderlen); however, if the filter width exceeds the table width and the
767 * table width is less than 102 bytes, set borderlen to the lesser of the max filter width and 102.
768 * The filters will wrap at the lesser of borderlen-2 and the last space in the filter.
769 * NOTE: 102 is the typical size of a user window when the font is fixed width (e.g., COURIER 10).
770 * XXX: A pref could be added to change the max width from the default size of 102. */
771 if (maxfltr_w
> borderlen
&& borderlen
< 102)
772 borderlen
= MIN(maxfltr_w
, 102);
774 /* Prevent double right border by adding a space */
775 if (borderlen
-tabrow_w
==1)
778 /* Display the top border */
780 for (i
=0; i
<borderlen
; i
++)
783 spaces
= (char*) g_malloc(borderlen
+1);
784 for (i
=0; i
<borderlen
; i
++)
786 spaces
[borderlen
] = '\0';
788 spaces_s
= &spaces
[16];
789 printf("\n| IO Statistics%s|\n", spaces_s
);
790 spaces_s
= &spaces
[2];
791 printf("|%s|\n", spaces_s
);
794 invl_fmt
= g_strconcat("%", dur_mag_s
, "u", NULL
);
795 full_fmt
= g_strconcat("| Duration: ", invl_fmt
, ".%6u secs%s|\n", NULL
);
796 spaces_s
= &spaces
[25 + dur_mag
];
797 printf(full_fmt
, dur_secs_orig
, dur_nsecs_orig
, spaces_s
);
799 full_fmt
= g_strconcat("| Interval: ", invl_fmt
, " secs%s|\n", NULL
);
800 spaces_s
= &spaces
[18 + dur_mag
];
801 printf(full_fmt
, (guint32
)(interval
/1000000ULL), spaces_s
);
803 g_snprintf(invl_prec_s
, 3, "%u", invl_prec
);
804 invl_fmt
= g_strconcat("%", dur_mag_s
, "u.%0", invl_prec_s
, "u", NULL
);
805 full_fmt
= g_strconcat("| Duration: ", invl_fmt
, " secs%s|\n", NULL
);
806 spaces_s
= &spaces
[19 + dur_mag
+ invl_prec
];
807 printf(full_fmt
, dur_secs
, dur_nsecs
/(int)dv
, spaces_s
);
810 full_fmt
= g_strconcat("| Interval: ", invl_fmt
, " secs%s|\n", NULL
);
811 spaces_s
= &spaces
[19 + dur_mag
+ invl_prec
];
812 printf(full_fmt
, (guint32
)(interval
/1000000ULL),
813 (guint32
)((interval
%1000000ULL)/dv
), spaces_s
);
817 spaces_s
= &spaces
[2];
818 printf("|%s|\n", spaces_s
);
820 /* Display the list of filters and their column numbers vertically */
822 for(j
=0; j
<num_cols
; j
++){
823 printf((j
==0 ? "%2u: " : "| %2u: "), j
+1);
824 if (!iot
->filters
[j
] || (iot
->filters
[j
]==0)) {
826 * An empty (no filter) comma field was specified */
827 spaces_s
= &spaces
[16 + 10];
828 printf("Frames and bytes%s|\n", spaces_s
);
830 filter
= iot
->filters
[j
];
831 len_filt
= (int) strlen(filter
);
833 /* If the width of the widest filter exceeds the width of the stat table, borderlen has
834 * been set to 102 bytes above and filters wider than 102 will wrap at 91 bytes. */
835 if (len_filt
+11 <= borderlen
) {
836 printf("%s", filter
);
837 if (len_filt
+11 <= borderlen
) {
838 spaces_s
= &spaces
[len_filt
+ 10];
839 printf("%s", spaces_s
);
843 gchar
*sfilter1
, *sfilter2
;
846 int next_start
, max_w
=borderlen
-11;
849 if (len_filt
> max_w
) {
850 sfilter1
= g_strndup( (gchar
*) filter
, (gsize
) max_w
);
852 * Find the pos of the last space in sfilter1. If a space is found, set
853 * sfilter2 to the string prior to that space and print it; otherwise, wrap
854 * the filter at max_w. */
855 pos
= g_strrstr(sfilter1
, " ");
857 len
= (gsize
)(pos
-sfilter1
);
858 next_start
= (int) len
+1;
860 len
= (gsize
) strlen(sfilter1
);
861 next_start
= (int)len
;
863 sfilter2
= g_strndup(sfilter1
, len
);
864 printf("%s%s|\n", sfilter2
, &spaces
[len
+10]);
869 filter
= &filter
[next_start
];
870 len_filt
= (int) strlen(filter
);
872 printf("%s%s|\n", filter
, &spaces
[((int)strlen(filter
))+10]);
881 for(i
=0;i
<borderlen
-3;i
++){
886 /* Display spaces above "Interval (s)" label */
887 spaces_s
= &spaces
[borderlen
-(invl_col_w
-2)];
888 printf("|%s|", spaces_s
);
890 /* Display column number headers */
891 for(j
=0; j
<num_cols
; j
++) {
893 if(item
->calc_type
==CALC_TYPE_FRAMES_AND_BYTES
)
894 spaces_s
= &spaces
[borderlen
- (col_w
[j
].fr
+ col_w
[j
].val
)] - 3;
895 else if (item
->calc_type
==CALC_TYPE_FRAMES
)
896 spaces_s
= &spaces
[borderlen
- col_w
[j
].fr
];
898 spaces_s
= &spaces
[borderlen
- col_w
[j
].val
];
900 printf("%-2u%s|", j
+1, spaces_s
);
902 if (tabrow_w
< borderlen
) {
903 filler_s
= &spaces
[tabrow_w
+1];
904 printf("%s|", filler_s
);
908 switch (timestamp_get_type()) {
912 case TS_ABSOLUTE_WITH_YMD
:
913 case TS_ABSOLUTE_WITH_YDOY
:
914 case TS_UTC_WITH_YMD
:
915 case TS_UTC_WITH_YDOY
:
916 printf("\n| Date and time");
921 printf("\n| Interval");
927 spaces_s
= &spaces
[borderlen
-(invl_col_w
-k
)];
928 printf("%s|", spaces_s
);
930 /* Display the stat label in each column */
931 for(j
=0; j
<num_cols
; j
++) {
932 type
= stat_cols
[j
]->calc_type
;
933 if(type
==CALC_TYPE_FRAMES
) {
934 printcenter (calc_type_table
[type
].func_name
, col_w
[j
].fr
, numpad
);
935 } else if (type
==CALC_TYPE_FRAMES_AND_BYTES
) {
936 printcenter ("Frames", col_w
[j
].fr
, numpad
);
937 printcenter ("Bytes", col_w
[j
].val
, numpad
);
939 printcenter (calc_type_table
[type
].func_name
, col_w
[j
].val
, numpad
);
943 printf("%s|", filler_s
);
946 for(i
=0; i
<tabrow_w
-3; i
++)
950 if (tabrow_w
< borderlen
)
951 printf("%s|", &spaces
[tabrow_w
+1]);
955 if (invl_prec
==0 && dur_mag
==1)
956 full_fmt
= g_strconcat("| ", invl_fmt
, " <> ", invl_fmt
, " |", NULL
);
958 full_fmt
= g_strconcat("| ", invl_fmt
, " <> ", invl_fmt
, " |", NULL
);
960 if (interval
== 0 || duration
== 0) {
963 num_rows
= (int)(duration
/interval
) + ((int)(duration
%interval
) > 0 ? 1 : 0);
966 /* Load item_in_column with the first item in each column */
967 item_in_column
= (io_stat_item_t
**) g_malloc(sizeof(io_stat_item_t
*) * num_cols
);
968 for (j
=0; j
<num_cols
; j
++) {
969 item_in_column
[j
] = stat_cols
[j
];
972 /* Display the table values
974 * The outer loop is for time interval rows and the inner loop is for stat column items.*/
975 for (i
=0; i
<num_rows
; i
++) {
980 /* Compute the interval for this row */
982 invl_end
= t
+ interval
;
987 /* Patch for Absolute Time */
988 /* XXX - has a Y2.038K problem with 32-bit time_t */
989 the_time
= (time_t)(iot
->start_time
+ (t
/1000000ULL));
991 /* Display the interval for this row */
992 switch (timestamp_get_type()) {
994 tm_time
= localtime(&the_time
);
995 printf("| %02d:%02d:%02d |",
1001 case TS_ABSOLUTE_WITH_YMD
:
1002 tm_time
= localtime(&the_time
);
1003 printf("| %04d-%02d-%02d %02d:%02d:%02d |",
1004 tm_time
->tm_year
+ 1900,
1005 tm_time
->tm_mon
+ 1,
1012 case TS_ABSOLUTE_WITH_YDOY
:
1013 tm_time
= localtime(&the_time
);
1014 printf("| %04d/%03d %02d:%02d:%02d |",
1015 tm_time
->tm_year
+ 1900,
1016 tm_time
->tm_yday
+ 1,
1023 tm_time
= gmtime(&the_time
);
1024 printf("| %02d:%02d:%02d |",
1030 case TS_UTC_WITH_YMD
:
1031 tm_time
= gmtime(&the_time
);
1032 printf("| %04d-%02d-%02d %02d:%02d:%02d |",
1033 tm_time
->tm_year
+ 1900,
1034 tm_time
->tm_mon
+ 1,
1041 case TS_UTC_WITH_YDOY
:
1042 tm_time
= gmtime(&the_time
);
1043 printf("| %04d/%03d %02d:%02d:%02d |",
1044 tm_time
->tm_year
+ 1900,
1045 tm_time
->tm_yday
+ 1,
1056 maxw
= dur_mag
>= 3 ? dur_mag
+1 : 3;
1058 g_snprintf(dur_mag_s
, 3, "%u", maxw
);
1059 full_fmt
= g_strconcat( dur_mag
==1 ? "| " : "| ",
1060 invl_fmt
, " <> ", "%-",
1061 dur_mag_s
, "s|", NULL
);
1062 printf(full_fmt
, (guint32
)(t
/1000000ULL), "Dur");
1064 printf(full_fmt
, (guint32
)(t
/1000000ULL),
1065 (guint32
)(invl_end
/1000000ULL));
1068 printf(full_fmt
, (guint32
)(t
/1000000ULL),
1069 (guint32
)(t
%1000000ULL / dv
),
1070 (guint32
)(invl_end
/1000000ULL),
1071 (guint32
)(invl_end
%1000000ULL / dv
));
1077 are not implemented */
1082 /* Display stat values in each column for this row */
1083 for (j
=0; j
<num_cols
; j
++) {
1085 item
= item_in_column
[j
];
1088 switch(item
->calc_type
) {
1089 case CALC_TYPE_FRAMES
:
1090 printf(fmt
, item
->frames
);
1092 case CALC_TYPE_BYTES
:
1093 case CALC_TYPE_COUNT
:
1094 printf(fmt
, item
->counter
);
1096 case CALC_TYPE_FRAMES_AND_BYTES
:
1097 printf(fmt
, item
->frames
, item
->counter
);
1103 ftype
= proto_registrar_get_ftype(stat_cols
[j
]->hf_index
);
1106 printf(fmt
, item
->float_counter
);
1109 printf(fmt
, item
->double_counter
);
1111 case FT_RELATIVE_TIME
:
1112 item
->counter
= (item
->counter
+ 500ULL) / 1000ULL;
1113 printf(fmt
, (int)(item
->counter
/1000000ULL), (int)(item
->counter
%1000000ULL));
1116 printf(fmt
, item
->counter
);
1125 ftype
= proto_registrar_get_ftype(stat_cols
[j
]->hf_index
);
1128 printf(fmt
, item
->float_counter
/num
);
1131 printf(fmt
, item
->double_counter
/num
);
1133 case FT_RELATIVE_TIME
:
1134 item
->counter
= ((item
->counter
/ (guint64
)num
) + 500ULL) / 1000ULL;
1136 (int)(item
->counter
/1000000ULL), (int)(item
->counter
%1000000ULL));
1139 printf(fmt
, item
->counter
/ (guint64
)num
);
1144 case CALC_TYPE_LOAD
:
1145 ftype
= proto_registrar_get_ftype(stat_cols
[j
]->hf_index
);
1147 case FT_RELATIVE_TIME
:
1150 (int) (item
->counter
/interval
),
1151 (int)((item
->counter
%interval
)*1000000ULL / interval
));
1154 (int) (item
->counter
/(invl_end
-t
)),
1155 (int)((item
->counter
%(invl_end
-t
))*1000000ULL / (invl_end
-t
)));
1166 item_in_column
[j
] = item_in_column
[j
]->next
;
1169 printf(fmt
, (guint64
)0, (guint64
)0);
1173 printf("%s|", filler_s
);
1178 for(i
=0;i
<borderlen
;i
++){
1183 g_free(iot
->max_vals
);
1184 g_free(iot
->max_frame
);
1192 g_free(item_in_column
);
1197 register_io_tap(io_stat_t
*io
, int i
, const char *filter
)
1199 GString
*error_string
;
1203 const char *p
, *parenp
;
1205 header_field_info
*hfi
;
1207 io
->items
[i
].prev
=&io
->items
[i
];
1208 io
->items
[i
].next
=NULL
;
1209 io
->items
[i
].parent
=io
;
1210 io
->items
[i
].time
=0;
1211 io
->items
[i
].calc_type
=CALC_TYPE_FRAMES_AND_BYTES
;
1212 io
->items
[i
].frames
=0;
1213 io
->items
[i
].counter
=0;
1216 io
->filters
[i
]=filter
;
1221 for(j
=0; calc_type_table
[j
].func_name
; j
++){
1222 namelen
=strlen(calc_type_table
[j
].func_name
);
1223 if(filter
&& strncmp(filter
, calc_type_table
[j
].func_name
, namelen
) == 0) {
1224 io
->items
[i
].calc_type
=calc_type_table
[j
].calc_type
;
1225 io
->items
[i
].colnum
= i
;
1226 if(*(filter
+namelen
)=='(') {
1228 parenp
=strchr(p
, ')');
1231 "\ntshark: Closing parenthesis missing from calculated expression.\n");
1235 if(io
->items
[i
].calc_type
==CALC_TYPE_FRAMES
|| io
->items
[i
].calc_type
==CALC_TYPE_BYTES
){
1238 "\ntshark: %s does not require or allow a field name within the parens.\n",
1239 calc_type_table
[j
].func_name
);
1244 /* bail out if a field name was not specified */
1245 fprintf(stderr
, "\ntshark: You didn't specify a field name for %s(*).\n",
1246 calc_type_table
[j
].func_name
);
1251 field
= (char *) g_malloc(parenp
-p
+1);
1252 memcpy(field
, p
, parenp
-p
);
1253 field
[parenp
-p
] = '\0';
1255 if (io
->items
[i
].calc_type
==CALC_TYPE_FRAMES
|| io
->items
[i
].calc_type
==CALC_TYPE_BYTES
)
1257 hfi
=proto_registrar_get_byname(field
);
1259 fprintf(stderr
, "\ntshark: There is no field named '%s'.\n",
1265 io
->items
[i
].hf_index
=hfi
->id
;
1269 if (io
->items
[i
].calc_type
==CALC_TYPE_FRAMES
|| io
->items
[i
].calc_type
==CALC_TYPE_BYTES
)
1271 io
->items
[i
].colnum
= i
;
1274 if(hfi
&& !(io
->items
[i
].calc_type
==CALC_TYPE_BYTES
||
1275 io
->items
[i
].calc_type
==CALC_TYPE_FRAMES
||
1276 io
->items
[i
].calc_type
==CALC_TYPE_FRAMES_AND_BYTES
)){
1277 /* check that the type is compatible */
1289 /* these types support all calculations */
1293 /* these types only support SUM, COUNT, MAX, MIN, AVG */
1294 switch(io
->items
[i
].calc_type
){
1296 case CALC_TYPE_COUNT
:
1303 "\ntshark: %s is a float field, so %s(*) calculations are not supported on it.",
1305 calc_type_table
[j
].func_name
);
1309 case FT_RELATIVE_TIME
:
1310 /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */
1311 switch(io
->items
[i
].calc_type
){
1313 case CALC_TYPE_COUNT
:
1317 case CALC_TYPE_LOAD
:
1321 "\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
1323 calc_type_table
[j
].func_name
);
1329 * XXX - support all operations on floating-point
1332 if(io
->items
[i
].calc_type
!=CALC_TYPE_COUNT
){
1334 "\ntshark: %s doesn't have integral values, so %s(*) "
1335 "calculations are not supported on it.\n",
1337 calc_type_table
[j
].func_name
);
1345 error_string
=register_tap_listener("frame", &io
->items
[i
], flt
, TL_REQUIRES_PROTO_TREE
, NULL
,
1346 iostat_packet
, i
?NULL
:iostat_draw
);
1350 fprintf(stderr
, "\ntshark: Couldn't register io,stat tap: %s\n",
1352 g_string_free(error_string
, TRUE
);
1358 iostat_init(const char *opt_arg
, void* userdata _U_
)
1360 gdouble interval_float
;
1364 const gchar
*filters
, *str
, *pos
;
1366 if ((*(opt_arg
+(strlen(opt_arg
)-1)) == ',') ||
1367 (sscanf(opt_arg
, "io,stat,%lf%n", &interval_float
, (int *)&idx
) != 1) ||
1369 fprintf(stderr
, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1373 filters
=opt_arg
+idx
;
1375 if (*filters
!= ',') {
1376 /* For locale's that use ',' instead of '.', the comma might
1377 * have been consumed during the floating point conversion. */
1379 if (*filters
!= ',') {
1380 fprintf(stderr
, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1387 switch (timestamp_get_type()) {
1391 fprintf(stderr
, "\ntshark: invalid -t operand. io,stat only supports -t <r|a|ad|adoy|u|ud|udoy>\n");
1397 io
= (io_stat_t
*) g_malloc(sizeof(io_stat_t
));
1399 /* If interval is 0, calculate statistics over the whole file by setting the interval to
1401 if (interval_float
==0) {
1402 io
->interval
= G_MAXINT32
;
1405 /* Set interval to the number of us rounded to the nearest integer */
1406 io
->interval
= (guint64
)(interval_float
* 1000000.0 + 0.5);
1408 * Determine what interval precision the user has specified */
1410 for (i
=10; i
<10000000; i
*=10) {
1411 if (io
->interval
%i
> 0)
1415 if (io
->invl_prec
==0) {
1416 /* The precision is zero but if the user specified one of more zeros after the decimal point,
1417 they want that many decimal places shown in the table for all time intervals except
1418 response time values such as smb.time which always have 6 decimal places of precision.
1419 This feature is useful in cases where for example the duration is 9.1, you specify an
1420 interval of 1 and the last interval becomes "9 <> 9". If the interval is instead set to
1421 1.1, the last interval becomes
1422 last interval is rounded up to value that is greater than the duration. */
1423 const gchar
*invl_start
= opt_arg
+8;
1427 intv_end
= g_strstr_len(invl_start
, -1, ",");
1428 invl_len
= (int)(intv_end
- invl_start
);
1429 invl_start
= g_strstr_len(invl_start
, invl_len
, ".");
1431 if (invl_start
!= NULL
) {
1432 invl_len
= (int)(intv_end
- invl_start
- 1);
1434 io
->invl_prec
= MIN(invl_len
, 6);
1438 if (io
->interval
< 1){
1440 "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
1444 /* Find how many ',' separated filters we have */
1448 if (filters
&& (*filters
!= '\0')) {
1449 /* Eliminate the first comma. */
1452 while((str
= strchr(str
, ','))) {
1458 io
->items
= (io_stat_item_t
*) g_malloc(sizeof(io_stat_item_t
) * io
->num_cols
);
1459 io
->filters
= (const char **)g_malloc(sizeof(char *) * io
->num_cols
);
1460 io
->max_vals
= (guint64
*) g_malloc(sizeof(guint64
) * io
->num_cols
);
1461 io
->max_frame
= (guint32
*) g_malloc(sizeof(guint32
) * io
->num_cols
);
1463 for (i
=0; i
<io
->num_cols
; i
++) {
1464 io
->max_vals
[i
] = 0;
1465 io
->max_frame
[i
] = 0;
1468 /* Register a tap listener for each filter */
1469 if((!filters
) || (filters
[0]==0)) {
1470 register_io_tap(io
, 0, NULL
);
1476 pos
= (gchar
*) strchr(str
, ',');
1478 register_io_tap(io
, i
, NULL
);
1479 } else if (pos
==NULL
) {
1480 str
= (const char*) g_strstrip((gchar
*)str
);
1481 filter
= g_strdup((gchar
*) str
);
1483 register_io_tap(io
, i
, filter
);
1485 register_io_tap(io
, i
, NULL
);
1487 filter
= (gchar
*)g_malloc((pos
-str
)+1);
1488 g_strlcpy( filter
, str
, (gsize
) ((pos
-str
)+1));
1489 filter
= g_strstrip(filter
);
1490 register_io_tap(io
, i
, (char *) filter
);
1499 register_tap_listener_iostat(void)
1501 register_stat_cmd_arg("io,stat,", iostat_init
, NULL
);
1505 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1510 * indent-tabs-mode: nil
1513 * vi: set shiftwidth=4 tabstop=4 expandtab:
1514 * :indentSize=4:tabSize=4:noTabs=true: