2 * iostat 2002 Ronnie Sahlberg
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include <epan/epan_dissect.h>
18 #include <epan/stat_tap_ui.h>
20 #include <wsutil/ws_assert.h>
22 #define CALC_TYPE_FRAMES 0
23 #define CALC_TYPE_BYTES 1
24 #define CALC_TYPE_FRAMES_AND_BYTES 2
25 #define CALC_TYPE_COUNT 3
26 #define CALC_TYPE_SUM 4
27 #define CALC_TYPE_MIN 5
28 #define CALC_TYPE_MAX 6
29 #define CALC_TYPE_AVG 7
30 #define CALC_TYPE_LOAD 8
32 void register_tap_listener_iostat(void);
35 const char *func_name
;
39 static calc_type_ent_t calc_type_table
[] = {
40 { "FRAMES", CALC_TYPE_FRAMES
},
41 { "BYTES", CALC_TYPE_BYTES
},
42 { "FRAMES BYTES", CALC_TYPE_FRAMES_AND_BYTES
},
43 { "COUNT", CALC_TYPE_COUNT
},
44 { "SUM", CALC_TYPE_SUM
},
45 { "MIN", CALC_TYPE_MIN
},
46 { "MAX", CALC_TYPE_MAX
},
47 { "AVG", CALC_TYPE_AVG
},
48 { "LOAD", CALC_TYPE_LOAD
},
52 typedef struct _io_stat_t
{
53 uint64_t interval
; /* The user-specified time interval (us) */
54 unsigned invl_prec
; /* Decimal precision of the time interval (1=10s, 2=100s etc) */
55 unsigned int num_cols
; /* The number of columns of stats in the table */
56 struct _io_stat_item_t
*items
; /* Each item is a single cell in the table */
57 time_t start_time
; /* Time of first frame matching the filter */
58 /* The following are all per-column fixed information arrays */
59 const char **filters
; /* 'io,stat' cmd strings (e.g., "AVG(smb.time)smb.time") */
60 uint64_t *max_vals
; /* The max value sans the decimal or nsecs portion in each stat column */
61 uint32_t *max_frame
; /* The max frame number displayed in each stat column */
63 int *calc_type
; /* The statistic type */
66 typedef struct _io_stat_item_t
{
68 struct _io_stat_item_t
*next
;
69 struct _io_stat_item_t
*prev
;
70 uint64_t start_time
; /* Time since start of capture (us)*/
71 int colnum
; /* Column number of this stat (0 to n) */
73 uint32_t num
; /* The sample size of a given statistic (only needed for AVG) */
74 union { /* The accumulated data for the calculation of that statistic */
77 double double_counter
;
81 #define NANOSECS_PER_SEC UINT64_C(1000000000)
83 static uint64_t last_relative_time
;
85 static tap_packet_status
86 iostat_packet(void *arg
, packet_info
*pinfo
, epan_dissect_t
*edt
, const void *dummy _U_
, tap_flags_t flags _U_
)
91 uint64_t relative_time
, rt
;
92 const nstime_t
*new_time
;
97 mit
= (io_stat_item_t
*) arg
;
100 /* If this frame's relative time is negative, set its relative time to last_relative_time
101 rather than disincluding it from the calculations. */
102 if ((pinfo
->rel_ts
.secs
>= 0) && (pinfo
->rel_ts
.nsecs
>= 0)) {
103 relative_time
= ((uint64_t)pinfo
->rel_ts
.secs
* UINT64_C(1000000)) +
104 ((uint64_t)((pinfo
->rel_ts
.nsecs
+500)/1000));
105 last_relative_time
= relative_time
;
107 relative_time
= last_relative_time
;
110 if (mit
->parent
->start_time
== 0) {
111 mit
->parent
->start_time
= pinfo
->abs_ts
.secs
- pinfo
->rel_ts
.secs
;
114 /* The prev item is always the last interval in which we saw packets. */
117 /* If we have moved into a new interval (row), create a new io_stat_item_t struct for every interval
118 * between the last struct and this one. If an item was not found in a previous interval, an empty
119 * struct will be created for it. */
121 while (rt
>= it
->start_time
+ parent
->interval
) {
122 it
->next
= g_new(io_stat_item_t
, 1);
124 it
->next
->next
= NULL
;
128 it
->start_time
= it
->prev
->start_time
+ parent
->interval
;
130 it
->counter
= 0; /* 64-bit, type-punning with double is fine */
132 it
->colnum
= it
->prev
->colnum
;
135 /* Store info in the current structure */
138 switch (parent
->calc_type
[it
->colnum
]) {
139 case CALC_TYPE_FRAMES
:
140 case CALC_TYPE_BYTES
:
141 case CALC_TYPE_FRAMES_AND_BYTES
:
142 it
->counter
+= pinfo
->fd
->pkt_len
;
144 case CALC_TYPE_COUNT
:
145 gp
= proto_get_finfo_ptr_array(edt
->tree
, parent
->hf_indexes
[it
->colnum
]);
147 it
->counter
+= gp
->len
;
151 gp
= proto_get_finfo_ptr_array(edt
->tree
, parent
->hf_indexes
[it
->colnum
]);
155 for (i
=0; i
<gp
->len
; i
++) {
156 switch (proto_registrar_get_ftype(parent
->hf_indexes
[it
->colnum
])) {
161 it
->counter
+= fvalue_get_uinteger(((field_info
*)gp
->pdata
[i
])->value
);
167 it
->counter
+= fvalue_get_uinteger64(((field_info
*)gp
->pdata
[i
])->value
);
173 it
->counter
+= fvalue_get_sinteger(((field_info
*)gp
->pdata
[i
])->value
);
179 it
->counter
+= (int64_t)fvalue_get_sinteger64(((field_info
*)gp
->pdata
[i
])->value
);
183 (float)fvalue_get_floating(((field_info
*)gp
->pdata
[i
])->value
);
186 it
->double_counter
+= fvalue_get_floating(((field_info
*)gp
->pdata
[i
])->value
);
188 case FT_RELATIVE_TIME
:
189 new_time
= fvalue_get_time(((field_info
*)gp
->pdata
[i
])->value
);
190 val
= ((uint64_t)new_time
->secs
* NANOSECS_PER_SEC
) + (uint64_t)new_time
->nsecs
;
195 * "Can't happen"; see the checks
196 * in register_io_tap().
198 ws_assert_not_reached();
205 gp
= proto_get_finfo_ptr_array(edt
->tree
, parent
->hf_indexes
[it
->colnum
]);
211 ftype
= proto_registrar_get_ftype(parent
->hf_indexes
[it
->colnum
]);
212 for (i
=0; i
<gp
->len
; i
++) {
218 val
= fvalue_get_uinteger(((field_info
*)gp
->pdata
[i
])->value
);
219 if ((it
->frames
== 1 && i
== 0) || (val
< it
->counter
)) {
227 val
= fvalue_get_uinteger64(((field_info
*)gp
->pdata
[i
])->value
);
228 if ((it
->frames
== 1 && i
== 0) || (val
< it
->counter
)) {
236 val
= fvalue_get_sinteger(((field_info
*)gp
->pdata
[i
])->value
);
237 if ((it
->frames
== 1 && i
== 0) || ((int32_t)val
< (int32_t)it
->counter
)) {
245 val
= fvalue_get_sinteger64(((field_info
*)gp
->pdata
[i
])->value
);
246 if ((it
->frames
== 1 && i
== 0) || ((int64_t)val
< (int64_t)it
->counter
)) {
251 float_val
= (float)fvalue_get_floating(((field_info
*)gp
->pdata
[i
])->value
);
252 if ((it
->frames
== 1 && i
== 0) || (float_val
< it
->float_counter
)) {
253 it
->float_counter
= float_val
;
257 double_val
= fvalue_get_floating(((field_info
*)gp
->pdata
[i
])->value
);
258 if ((it
->frames
== 1 && i
== 0) || (double_val
< it
->double_counter
)) {
259 it
->double_counter
= double_val
;
262 case FT_RELATIVE_TIME
:
263 new_time
= fvalue_get_time(((field_info
*)gp
->pdata
[i
])->value
);
264 val
= ((uint64_t)new_time
->secs
* NANOSECS_PER_SEC
) + (uint64_t)new_time
->nsecs
;
265 if ((it
->frames
== 1 && i
== 0) || (val
< it
->counter
)) {
271 * "Can't happen"; see the checks
272 * in register_io_tap().
274 ws_assert_not_reached();
281 gp
= proto_get_finfo_ptr_array(edt
->tree
, parent
->hf_indexes
[it
->colnum
]);
287 ftype
= proto_registrar_get_ftype(parent
->hf_indexes
[it
->colnum
]);
288 for (i
=0; i
<gp
->len
; i
++) {
294 val
= fvalue_get_uinteger(((field_info
*)gp
->pdata
[i
])->value
);
295 if (val
> it
->counter
)
302 val
= fvalue_get_uinteger64(((field_info
*)gp
->pdata
[i
])->value
);
303 if (val
> it
->counter
)
310 val
= fvalue_get_sinteger(((field_info
*)gp
->pdata
[i
])->value
);
311 if ((int32_t)val
> (int32_t)it
->counter
)
318 val
= fvalue_get_sinteger64(((field_info
*)gp
->pdata
[i
])->value
);
319 if ((int64_t)val
> (int64_t)it
->counter
)
323 float_val
= (float)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
= fvalue_get_time(((field_info
*)gp
->pdata
[i
])->value
);
334 val
= ((uint64_t)new_time
->secs
* NANOSECS_PER_SEC
) + (uint64_t)new_time
->nsecs
;
335 if (val
> it
->counter
)
340 * "Can't happen"; see the checks
341 * in register_io_tap().
343 ws_assert_not_reached();
350 gp
= proto_get_finfo_ptr_array(edt
->tree
, parent
->hf_indexes
[it
->colnum
]);
354 ftype
= proto_registrar_get_ftype(parent
->hf_indexes
[it
->colnum
]);
355 for (i
=0; i
<gp
->len
; i
++) {
362 val
= fvalue_get_uinteger(((field_info
*)gp
->pdata
[i
])->value
);
369 val
= fvalue_get_uinteger64(((field_info
*)gp
->pdata
[i
])->value
);
376 val
= fvalue_get_sinteger(((field_info
*)gp
->pdata
[i
])->value
);
383 val
= fvalue_get_sinteger64(((field_info
*)gp
->pdata
[i
])->value
);
387 it
->float_counter
+= (float)fvalue_get_floating(((field_info
*)gp
->pdata
[i
])->value
);
390 it
->double_counter
+= fvalue_get_floating(((field_info
*)gp
->pdata
[i
])->value
);
392 case FT_RELATIVE_TIME
:
393 new_time
= fvalue_get_time(((field_info
*)gp
->pdata
[i
])->value
);
394 val
= ((uint64_t)new_time
->secs
* NANOSECS_PER_SEC
) + (uint64_t)new_time
->nsecs
;
399 * "Can't happen"; see the checks
400 * in register_io_tap().
402 ws_assert_not_reached();
409 gp
= proto_get_finfo_ptr_array(edt
->tree
, parent
->hf_indexes
[it
->colnum
]);
411 ftype
= proto_registrar_get_ftype(parent
->hf_indexes
[it
->colnum
]);
412 if (ftype
!= FT_RELATIVE_TIME
) {
414 "\ntshark: LOAD() is only supported for relative-time fields such as smb.time\n");
417 for (i
=0; i
<gp
->len
; i
++) {
422 new_time
= fvalue_get_time(((field_info
*)gp
->pdata
[i
])->value
);
423 val
= ((uint64_t)new_time
->secs
*UINT64_C(1000000)) + (uint64_t)(new_time
->nsecs
/1000);
424 tival
= (int)(val
% parent
->interval
);
425 it
->counter
+= tival
;
429 if (val
< (uint64_t)parent
->interval
) {
433 pit
->counter
+= parent
->interval
;
434 val
-= parent
->interval
;
441 /* Store the highest value for this item in order to determine the width of each stat column.
442 * For real numbers we only need to know its magnitude (the value to the left of the decimal point
443 * so round it up before storing it as an integer in max_vals. For AVG of RELATIVE_TIME fields,
444 * calc the average, round it to the next second and store the seconds. For all other calc types
445 * of RELATIVE_TIME fields, store the counters without modification.
447 switch (parent
->calc_type
[it
->colnum
]) {
448 case CALC_TYPE_FRAMES
:
449 case CALC_TYPE_FRAMES_AND_BYTES
:
450 parent
->max_frame
[it
->colnum
] =
451 MAX(parent
->max_frame
[it
->colnum
], it
->frames
);
452 if (parent
->calc_type
[it
->colnum
] == CALC_TYPE_FRAMES_AND_BYTES
)
453 parent
->max_vals
[it
->colnum
] =
454 MAX(parent
->max_vals
[it
->colnum
], it
->counter
);
456 case CALC_TYPE_BYTES
:
457 case CALC_TYPE_COUNT
:
459 parent
->max_vals
[it
->colnum
] = MAX(parent
->max_vals
[it
->colnum
], it
->counter
);
464 ftype
= proto_registrar_get_ftype(parent
->hf_indexes
[it
->colnum
]);
467 parent
->max_vals
[it
->colnum
] =
468 MAX(parent
->max_vals
[it
->colnum
], (uint64_t)(it
->float_counter
+0.5));
471 parent
->max_vals
[it
->colnum
] =
472 MAX(parent
->max_vals
[it
->colnum
], (uint64_t)(it
->double_counter
+0.5));
474 case FT_RELATIVE_TIME
:
475 parent
->max_vals
[it
->colnum
] =
476 MAX(parent
->max_vals
[it
->colnum
], it
->counter
);
479 /* UINT16-64 and INT8-64 */
480 parent
->max_vals
[it
->colnum
] =
481 MAX(parent
->max_vals
[it
->colnum
], it
->counter
);
486 if (it
->num
== 0) /* avoid division by zero */
488 ftype
= proto_registrar_get_ftype(parent
->hf_indexes
[it
->colnum
]);
491 parent
->max_vals
[it
->colnum
] =
492 MAX(parent
->max_vals
[it
->colnum
], (uint64_t)it
->float_counter
/it
->num
);
495 parent
->max_vals
[it
->colnum
] =
496 MAX(parent
->max_vals
[it
->colnum
], (uint64_t)it
->double_counter
/it
->num
);
498 case FT_RELATIVE_TIME
:
499 parent
->max_vals
[it
->colnum
] =
500 MAX(parent
->max_vals
[it
->colnum
], ((it
->counter
/(uint64_t)it
->num
) + UINT64_C(500000000)) / NANOSECS_PER_SEC
);
503 /* UINT16-64 and INT8-64 */
504 parent
->max_vals
[it
->colnum
] =
505 MAX(parent
->max_vals
[it
->colnum
], it
->counter
/it
->num
);
509 return TAP_PACKET_REDRAW
;
513 magnitude (uint64_t val
, unsigned int max_w
)
515 unsigned int i
, mag
= 0;
517 for (i
=0; i
<max_w
; i
++) {
519 if ((val
/= 10) == 0)
526 * Print the calc_type_table[] function label centered in the column header.
529 printcenter (const char *label
, int lenval
, int numpad
)
531 int lenlab
= (int) strlen(label
), len
;
532 const char spaces
[] = " ", *spaces_ptr
;
534 len
= (int) (strlen(spaces
)) - (((lenval
-lenlab
) / 2) + numpad
);
535 if (len
> 0 && len
< 6) {
536 spaces_ptr
= &spaces
[len
];
537 if ((lenval
-lenlab
)%2 == 0) {
538 printf("%s%s%s|", spaces_ptr
, label
, spaces_ptr
);
540 printf("%s%s%s|", spaces_ptr
-1, label
, spaces_ptr
);
542 } else if (len
> 0 && len
<= 15) {
543 printf("%s|", label
);
548 int fr
; /* Width of this FRAMES column sans padding and border chars */
549 int val
; /* Width of this non-FRAMES column sans padding and border chars */
553 iostat_draw(void *arg
)
556 uint64_t interval
, duration
, t
, invl_end
, dv
;
557 unsigned int i
, j
, k
, num_cols
, num_rows
, dur_secs_orig
, dur_nsecs_orig
, dur_secs
, dur_nsecs
, dur_mag
,
558 invl_mag
, invl_prec
, tabrow_w
, borderlen
, invl_col_w
, numpad
= 1, namelen
, len_filt
, type
,
560 unsigned int fr_mag
; /* The magnitude of the max frame number in this column */
561 unsigned int val_mag
; /* The magnitude of the max value in this column */
562 char *spaces
, *spaces_s
, *filler_s
= NULL
, **fmts
, *fmt
= NULL
;
564 static char dur_mag_s
[3], invl_prec_s
[3], fr_mag_s
[3], val_mag_s
[3], *invl_fmt
, *full_fmt
;
565 io_stat_item_t
*mit
, **stat_cols
, *item
, **item_in_column
;
566 bool last_row
= false;
572 mit
= (io_stat_item_t
*)arg
;
574 num_cols
= iot
->num_cols
;
575 col_w
= g_new(column_width
, num_cols
);
576 fmts
= (char **)g_malloc(sizeof(char *) * num_cols
);
577 duration
= ((uint64_t)cfile
.elapsed_time
.secs
* UINT64_C(1000000)) +
578 (uint64_t)((cfile
.elapsed_time
.nsecs
+ 500) / 1000);
580 /* Store the pointer to each stat column */
581 stat_cols
= (io_stat_item_t
**)g_malloc(sizeof(io_stat_item_t
*) * num_cols
);
582 for (j
=0; j
<num_cols
; j
++)
583 stat_cols
[j
] = &iot
->items
[j
];
585 /* The following prevents gross inaccuracies when the user specifies an interval that is greater
586 * than the capture duration. */
587 if (iot
->interval
> duration
|| iot
->interval
== UINT64_MAX
) {
589 iot
->interval
= UINT64_MAX
;
591 interval
= iot
->interval
;
594 /* Calc the capture duration's magnitude (dur_mag) */
595 dur_secs
= (unsigned int)(duration
/UINT64_C(1000000));
596 dur_secs_orig
= dur_secs
;
597 dur_nsecs
= (unsigned int)(duration
%UINT64_C(1000000));
598 dur_nsecs_orig
= dur_nsecs
;
599 dur_mag
= magnitude((uint64_t)dur_secs
, 5);
600 snprintf(dur_mag_s
, 3, "%u", dur_mag
);
602 /* Calc the interval's magnitude */
603 invl_mag
= magnitude(interval
/UINT64_C(1000000), 5);
605 /* Set or get the interval precision */
606 if (interval
== duration
) {
608 * An interval arg of 0 or an interval size exceeding the capture duration was specified.
609 * Set the decimal precision of duration based on its magnitude. */
612 else if (dur_mag
== 1)
617 borderlen
= 30 + dur_mag
+ (invl_prec
== 0 ? 0 : invl_prec
+1);
619 invl_prec
= iot
->invl_prec
;
620 borderlen
= 25 + MAX(invl_mag
,dur_mag
) + (invl_prec
== 0 ? 0 : invl_prec
+1);
623 /* Round the duration according to invl_prec */
625 for (i
=0; i
<invl_prec
; i
++)
627 if ((duration
%dv
) > 5*(dv
/10)) {
628 duration
+= 5*(dv
/10);
629 duration
= (duration
/dv
) * dv
;
630 dur_secs
= (unsigned int)(duration
/UINT64_C(1000000));
631 dur_nsecs
= (unsigned int)(duration
%UINT64_C(1000000));
633 * Recalc dur_mag in case rounding has increased its magnitude */
634 dur_mag
= magnitude((uint64_t)dur_secs
, 5);
636 if (iot
->interval
== UINT64_MAX
)
639 /* Calc the width of the time interval column (incl borders and padding). */
641 invl_col_w
= (2*dur_mag
) + 8;
643 invl_col_w
= (2*dur_mag
) + (2*invl_prec
) + 10;
645 /* Update the width of the time interval column if date is shown */
646 switch (timestamp_get_type()) {
647 case TS_ABSOLUTE_WITH_YMD
:
648 case TS_ABSOLUTE_WITH_YDOY
:
649 case TS_UTC_WITH_YMD
:
650 case TS_UTC_WITH_YDOY
:
651 invl_col_w
= MAX(invl_col_w
, 23);
655 invl_col_w
= MAX(invl_col_w
, 12);
659 borderlen
= MAX(borderlen
, invl_col_w
);
661 /* Calc the total width of each row in the stats table and build the printf format string for each
662 * column based on its field type, width, and name length.
663 * NOTE: The magnitude of all types including float and double are stored in iot->max_vals which
664 * is an *integer*. */
665 tabrow_w
= invl_col_w
;
666 for (j
=0; j
<num_cols
; j
++) {
667 type
= iot
->calc_type
[j
];
668 if (type
== CALC_TYPE_FRAMES_AND_BYTES
) {
671 namelen
= (unsigned int)strlen(calc_type_table
[type
].func_name
);
673 if (type
== CALC_TYPE_FRAMES
674 || type
== CALC_TYPE_FRAMES_AND_BYTES
) {
676 fr_mag
= magnitude(iot
->max_frame
[j
], 15);
677 fr_mag
= MAX(6, fr_mag
);
678 col_w
[j
].fr
= fr_mag
;
679 tabrow_w
+= col_w
[j
].fr
+ 3;
680 snprintf(fr_mag_s
, 3, "%u", fr_mag
);
682 if (type
== CALC_TYPE_FRAMES
) {
683 fmt
= g_strconcat(" %", fr_mag_s
, "u |", NULL
);
685 /* CALC_TYPE_FRAMES_AND_BYTES
687 val_mag
= magnitude(iot
->max_vals
[j
], 15);
688 val_mag
= MAX(5, val_mag
);
689 col_w
[j
].val
= val_mag
;
690 tabrow_w
+= (col_w
[j
].val
+ 3);
691 snprintf(val_mag_s
, 3, "%u", val_mag
);
692 fmt
= g_strconcat(" %", fr_mag_s
, "u |", " %", val_mag_s
, PRIu64
" |", NULL
);
699 case CALC_TYPE_BYTES
:
700 case CALC_TYPE_COUNT
:
702 val_mag
= magnitude(iot
->max_vals
[j
], 15);
703 val_mag
= MAX(5, val_mag
);
704 col_w
[j
].val
= val_mag
;
705 snprintf(val_mag_s
, 3, "%u", val_mag
);
706 fmt
= g_strconcat(" %", val_mag_s
, PRIu64
" |", NULL
);
710 ftype
= proto_registrar_get_ftype(iot
->hf_indexes
[j
]);
714 val_mag
= magnitude(iot
->max_vals
[j
], 15);
715 snprintf(val_mag_s
, 3, "%u", val_mag
);
716 fmt
= g_strconcat(" %", val_mag_s
, ".6f |", NULL
);
717 col_w
[j
].val
= val_mag
+ 7;
719 case FT_RELATIVE_TIME
:
720 /* Convert FT_RELATIVE_TIME field to seconds
721 * CALC_TYPE_LOAD was already converted in iostat_packet() ) */
722 if (type
== CALC_TYPE_LOAD
) {
723 iot
->max_vals
[j
] /= interval
;
724 } else if (type
!= CALC_TYPE_AVG
) {
725 iot
->max_vals
[j
] = (iot
->max_vals
[j
] + UINT64_C(500000000)) / NANOSECS_PER_SEC
;
727 val_mag
= magnitude(iot
->max_vals
[j
], 15);
728 snprintf(val_mag_s
, 3, "%u", val_mag
);
729 fmt
= g_strconcat(" %", val_mag_s
, "u.%06u |", NULL
);
730 col_w
[j
].val
= val_mag
+ 7;
734 val_mag
= magnitude(iot
->max_vals
[j
], 15);
735 val_mag
= MAX(namelen
, val_mag
);
736 col_w
[j
].val
= val_mag
;
737 snprintf(val_mag_s
, 3, "%u", val_mag
);
745 fmt
= g_strconcat(" %", val_mag_s
, PRIu64
" |", NULL
);
752 fmt
= g_strconcat(" %", val_mag_s
, PRId64
" |", NULL
);
755 } /* End of ftype switch */
756 } /* End of calc_type switch */
757 tabrow_w
+= col_w
[j
].val
+ 3;
760 } /* End of for loop (columns) */
762 borderlen
= MAX(borderlen
, tabrow_w
);
764 /* Calc the max width of the list of filters. */
766 for (j
=0; j
<num_cols
; j
++) {
767 if (iot
->filters
[j
]) {
768 k
= (unsigned int) (strlen(iot
->filters
[j
]) + 11);
769 maxfltr_w
= MAX(maxfltr_w
, k
);
771 maxfltr_w
= MAX(maxfltr_w
, 26);
774 /* The stat table is not wrapped (by tshark) but filter is wrapped at the width of the stats table
775 * (which currently = borderlen); however, if the filter width exceeds the table width and the
776 * table width is less than 102 bytes, set borderlen to the lesser of the max filter width and 102.
777 * The filters will wrap at the lesser of borderlen-2 and the last space in the filter.
778 * NOTE: 102 is the typical size of a user window when the font is fixed width (e.g., COURIER 10).
779 * XXX: A pref could be added to change the max width from the default size of 102. */
780 if (maxfltr_w
> borderlen
&& borderlen
< 102)
781 borderlen
= MIN(maxfltr_w
, 102);
783 /* Prevent double right border by adding a space */
784 if (borderlen
-tabrow_w
== 1)
787 /* Display the top border */
789 for (i
=0; i
<borderlen
; i
++)
792 spaces
= (char *)g_malloc(borderlen
+1);
793 for (i
=0; i
<borderlen
; i
++)
795 spaces
[borderlen
] = '\0';
797 spaces_s
= &spaces
[16];
798 printf("\n| IO Statistics%s|\n", spaces_s
);
799 spaces_s
= &spaces
[2];
800 printf("|%s|\n", spaces_s
);
802 if (invl_prec
== 0) {
803 invl_fmt
= g_strconcat("%", dur_mag_s
, "u", NULL
);
804 full_fmt
= g_strconcat("| Duration: ", invl_fmt
, ".%6u secs%s|\n", NULL
);
805 spaces_s
= &spaces
[25 + dur_mag
];
806 printf(full_fmt
, dur_secs_orig
, dur_nsecs_orig
, spaces_s
);
808 full_fmt
= g_strconcat("| Interval: ", invl_fmt
, " secs%s|\n", NULL
);
809 spaces_s
= &spaces
[18 + dur_mag
];
810 printf(full_fmt
, (uint32_t)(interval
/UINT64_C(1000000)), spaces_s
);
812 snprintf(invl_prec_s
, 3, "%u", invl_prec
);
813 invl_fmt
= g_strconcat("%", dur_mag_s
, "u.%0", invl_prec_s
, "u", NULL
);
814 full_fmt
= g_strconcat("| Duration: ", invl_fmt
, " secs%s|\n", NULL
);
815 spaces_s
= &spaces
[19 + dur_mag
+ invl_prec
];
816 printf(full_fmt
, dur_secs
, dur_nsecs
/(int)dv
, spaces_s
);
819 full_fmt
= g_strconcat("| Interval: ", invl_fmt
, " secs%s|\n", NULL
);
820 spaces_s
= &spaces
[19 + dur_mag
+ invl_prec
];
821 printf(full_fmt
, (uint32_t)(interval
/UINT64_C(1000000)),
822 (uint32_t)((interval
%UINT64_C(1000000))/dv
), spaces_s
);
826 spaces_s
= &spaces
[2];
827 printf("|%s|\n", spaces_s
);
829 /* Display the list of filters and their column numbers vertically */
831 for (j
=0; j
<num_cols
; j
++) {
832 printf((j
== 0 ? "%2u: " : "| %2u: "), j
+1);
833 if (!iot
->filters
[j
]) {
835 * An empty (no filter) comma field was specified */
836 spaces_s
= &spaces
[16 + 10];
837 printf("Frames and bytes%s|\n", spaces_s
);
839 filter
= iot
->filters
[j
];
840 len_filt
= (unsigned int) strlen(filter
);
842 /* If the width of the widest filter exceeds the width of the stat table, borderlen has
843 * been set to 102 bytes above and filters wider than 102 will wrap at 91 bytes. */
844 if (len_filt
+11 <= borderlen
) {
845 printf("%s", filter
);
846 if (len_filt
+11 <= borderlen
) {
847 spaces_s
= &spaces
[len_filt
+ 10];
848 printf("%s", spaces_s
);
852 char *sfilter1
, *sfilter2
;
855 unsigned int next_start
, max_w
= borderlen
-11;
858 if (len_filt
> max_w
) {
859 sfilter1
= g_strndup(filter
, (size_t) max_w
);
861 * Find the pos of the last space in sfilter1. If a space is found, set
862 * sfilter2 to the string prior to that space and print it; otherwise, wrap
863 * the filter at max_w. */
864 pos
= g_strrstr(sfilter1
, " ");
866 len
= (size_t)(pos
-sfilter1
);
867 next_start
= (unsigned int) len
+1;
869 len
= (size_t) strlen(sfilter1
);
870 next_start
= (unsigned int)len
;
872 sfilter2
= g_strndup(sfilter1
, len
);
873 printf("%s%s|\n", sfilter2
, &spaces
[len
+10]);
878 filter
= &filter
[next_start
];
879 len_filt
= (unsigned int) strlen(filter
);
881 printf("%s%s|\n", filter
, &spaces
[strlen(filter
)+10]);
890 for (i
=0; i
<borderlen
-3; i
++) {
895 /* Display spaces above "Interval (s)" label */
896 spaces_s
= &spaces
[borderlen
-(invl_col_w
-2)];
897 printf("|%s|", spaces_s
);
899 /* Display column number headers */
900 for (j
=0; j
<num_cols
; j
++) {
901 if (iot
->calc_type
[j
] == CALC_TYPE_FRAMES_AND_BYTES
)
902 spaces_s
= &spaces
[borderlen
- (col_w
[j
].fr
+ col_w
[j
].val
)] - 3;
903 else if (iot
->calc_type
[j
] == CALC_TYPE_FRAMES
)
904 spaces_s
= &spaces
[borderlen
- col_w
[j
].fr
];
906 spaces_s
= &spaces
[borderlen
- col_w
[j
].val
];
908 printf("%-2d%s|", j
+1, spaces_s
);
910 if (tabrow_w
< borderlen
) {
911 filler_s
= &spaces
[tabrow_w
+1];
912 printf("%s|", filler_s
);
916 switch (timestamp_get_type()) {
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");
929 printf("\n| Interval");
935 spaces_s
= &spaces
[borderlen
-(invl_col_w
-k
)];
936 printf("%s|", spaces_s
);
938 /* Display the stat label in each column */
939 for (j
=0; j
<num_cols
; j
++) {
940 type
= iot
->calc_type
[j
];
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
);
947 printcenter (calc_type_table
[type
].func_name
, col_w
[j
].val
, numpad
);
951 printf("%s|", filler_s
);
954 for (i
=0; i
<tabrow_w
-3; i
++)
958 if (tabrow_w
< borderlen
)
959 printf("%s|", &spaces
[tabrow_w
+1]);
963 if (invl_prec
== 0 && dur_mag
== 1)
964 full_fmt
= g_strconcat("| ", invl_fmt
, " <> ", invl_fmt
, " |", NULL
);
966 full_fmt
= g_strconcat("| ", invl_fmt
, " <> ", invl_fmt
, " |", NULL
);
968 if (interval
== 0 || duration
== 0) {
971 num_rows
= (unsigned int)(duration
/interval
) + ((unsigned int)(duration
%interval
) > 0 ? 1 : 0);
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
];
980 /* Display the table values
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
++) {
988 /* Compute the interval for this row */
990 invl_end
= t
+ interval
;
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
/UINT64_C(1000000)));
999 /* Display the interval for this row */
1000 switch (timestamp_get_type()) {
1002 tm_time
= localtime(&the_time
);
1003 if (tm_time
!= NULL
) {
1004 printf("| %02d:%02d:%02d |",
1009 printf("| XX:XX:XX |");
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,
1023 printf("| XXXX-XX-XX XX:XX:XX |");
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,
1036 printf("| XXXX/XXX XX:XX:XX |");
1040 tm_time
= gmtime(&the_time
);
1041 if (tm_time
!= NULL
) {
1042 printf("| %02d:%02d:%02d |",
1047 printf("| XX:XX:XX |");
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,
1061 printf("| XXXX-XX-XX XX:XX:XX |");
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,
1074 printf("| XXXX/XXX XX:XX:XX |");
1079 if (invl_prec
== 0) {
1082 maxw
= dur_mag
>= 3 ? dur_mag
+1 : 3;
1084 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
, (uint32_t)(t
/UINT64_C(1000000)), "Dur");
1090 printf(full_fmt
, (uint32_t)(t
/UINT64_C(1000000)),
1091 (uint32_t)(invl_end
/UINT64_C(1000000)));
1094 printf(full_fmt
, (uint32_t)(t
/UINT64_C(1000000)),
1095 (uint32_t)(t
%UINT64_C(1000000) / dv
),
1096 (uint32_t)(invl_end
/UINT64_C(1000000)),
1097 (uint32_t)(invl_end
%UINT64_C(1000000) / dv
));
1103 are not implemented */
1108 /* Display stat values in each column for this row */
1109 for (j
=0; j
<num_cols
; j
++) {
1111 item
= item_in_column
[j
];
1112 type
= iot
->calc_type
[j
];
1116 case CALC_TYPE_FRAMES
:
1117 printf(fmt
, item
->frames
);
1119 case CALC_TYPE_BYTES
:
1120 case CALC_TYPE_COUNT
:
1121 printf(fmt
, item
->counter
);
1123 case CALC_TYPE_FRAMES_AND_BYTES
:
1124 printf(fmt
, item
->frames
, item
->counter
);
1130 ftype
= proto_registrar_get_ftype(iot
->hf_indexes
[j
]);
1133 printf(fmt
, item
->float_counter
);
1136 printf(fmt
, item
->double_counter
);
1138 case FT_RELATIVE_TIME
:
1139 item
->counter
= (item
->counter
+ UINT64_C(500)) / UINT64_C(1000);
1141 (int)(item
->counter
/UINT64_C(1000000)),
1142 (int)(item
->counter
%UINT64_C(1000000)));
1145 printf(fmt
, item
->counter
);
1154 ftype
= proto_registrar_get_ftype(iot
->hf_indexes
[j
]);
1157 printf(fmt
, item
->float_counter
/num
);
1160 printf(fmt
, item
->double_counter
/num
);
1162 case FT_RELATIVE_TIME
:
1163 item
->counter
= ((item
->counter
/ (uint64_t)num
) + UINT64_C(500)) / UINT64_C(1000);
1165 (int)(item
->counter
/UINT64_C(1000000)),
1166 (int)(item
->counter
%UINT64_C(1000000)));
1169 printf(fmt
, item
->counter
/ (uint64_t)num
);
1174 case CALC_TYPE_LOAD
:
1175 ftype
= proto_registrar_get_ftype(iot
->hf_indexes
[j
]);
1177 case FT_RELATIVE_TIME
:
1180 (int) (item
->counter
/interval
),
1181 (int)((item
->counter
%interval
)*UINT64_C(1000000) / interval
));
1184 (int) (item
->counter
/(invl_end
-t
)),
1185 (int)((item
->counter
%(invl_end
-t
))*UINT64_C(1000000) / (invl_end
-t
)));
1195 item_in_column
[j
] = item_in_column
[j
]->next
;
1198 printf(fmt
, (uint64_t)0, (uint64_t)0);
1202 printf("%s|", filler_s
);
1207 for (i
=0; i
<borderlen
; i
++) {
1212 g_free((gpointer
)iot
->filters
);
1213 g_free(iot
->max_vals
);
1214 g_free(iot
->max_frame
);
1215 g_free(iot
->hf_indexes
);
1216 g_free(iot
->calc_type
);
1224 g_free(item_in_column
);
1229 register_io_tap(io_stat_t
*io
, unsigned int i
, const char *filter
)
1231 GString
*error_string
;
1235 const char *p
, *parenp
;
1237 header_field_info
*hfi
;
1239 io
->items
[i
].prev
= &io
->items
[i
];
1240 io
->items
[i
].next
= NULL
;
1241 io
->items
[i
].parent
= io
;
1242 io
->items
[i
].start_time
= 0;
1243 io
->items
[i
].frames
= 0;
1244 io
->items
[i
].counter
= 0;
1245 io
->items
[i
].num
= 0;
1247 io
->filters
[i
] = filter
;
1250 io
->calc_type
[i
] = CALC_TYPE_FRAMES_AND_BYTES
;
1253 for (j
=0; calc_type_table
[j
].func_name
; j
++) {
1254 namelen
= strlen(calc_type_table
[j
].func_name
);
1255 if (filter
&& strncmp(filter
, calc_type_table
[j
].func_name
, namelen
) == 0) {
1256 io
->calc_type
[i
] = calc_type_table
[j
].calc_type
;
1257 io
->items
[i
].colnum
= i
;
1258 if (*(filter
+namelen
) == '(') {
1259 p
= filter
+namelen
+1;
1260 parenp
= strchr(p
, ')');
1263 "\ntshark: Closing parenthesis missing from calculated expression.\n");
1267 if (io
->calc_type
[i
] == CALC_TYPE_FRAMES
|| io
->calc_type
[i
] == CALC_TYPE_BYTES
) {
1270 "\ntshark: %s does not require or allow a field name within the parens.\n",
1271 calc_type_table
[j
].func_name
);
1276 /* bail out if a field name was not specified */
1277 fprintf(stderr
, "\ntshark: You didn't specify a field name for %s(*).\n",
1278 calc_type_table
[j
].func_name
);
1283 field
= (char *)g_malloc(parenp
-p
+1);
1284 memcpy(field
, p
, parenp
-p
);
1285 field
[parenp
-p
] = '\0';
1287 if (io
->calc_type
[i
] == CALC_TYPE_FRAMES
|| io
->calc_type
[i
] == CALC_TYPE_BYTES
)
1289 hfi
= proto_registrar_get_byname(field
);
1291 fprintf(stderr
, "\ntshark: There is no field named '%s'.\n",
1297 io
->hf_indexes
[i
] = hfi
->id
;
1301 if (io
->calc_type
[i
] == CALC_TYPE_FRAMES
|| io
->calc_type
[i
] == CALC_TYPE_BYTES
)
1303 io
->items
[i
].colnum
= i
;
1306 if (hfi
&& !(io
->calc_type
[i
] == CALC_TYPE_BYTES
||
1307 io
->calc_type
[i
] == CALC_TYPE_FRAMES
||
1308 io
->calc_type
[i
] == CALC_TYPE_FRAMES_AND_BYTES
)) {
1309 /* check that the type is compatible */
1310 switch (hfi
->type
) {
1321 /* these types support all calculations */
1325 /* these types only support SUM, COUNT, MAX, MIN, AVG */
1326 switch (io
->calc_type
[i
]) {
1328 case CALC_TYPE_COUNT
:
1335 "\ntshark: %s is a float field, so %s(*) calculations are not supported on it.",
1337 calc_type_table
[j
].func_name
);
1341 case FT_RELATIVE_TIME
:
1342 /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */
1343 switch (io
->calc_type
[i
]) {
1345 case CALC_TYPE_COUNT
:
1349 case CALC_TYPE_LOAD
:
1353 "\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
1355 calc_type_table
[j
].func_name
);
1361 * XXX - support all operations on floating-point
1364 if (io
->calc_type
[i
] != CALC_TYPE_COUNT
) {
1366 "\ntshark: %s doesn't have integral values, so %s(*) "
1367 "calculations are not supported on it.\n",
1369 calc_type_table
[j
].func_name
);
1377 error_string
= register_tap_listener("frame", &io
->items
[i
], flt
, TL_REQUIRES_PROTO_TREE
, NULL
,
1378 iostat_packet
, i
? NULL
: iostat_draw
, NULL
);
1382 fprintf(stderr
, "\ntshark: Couldn't register io,stat tap: %s\n",
1384 g_string_free(error_string
, TRUE
);
1390 iostat_init(const char *opt_arg
, void *userdata _U_
)
1392 double interval_float
;
1396 const char *filters
, *str
, *pos
;
1398 if ((*(opt_arg
+(strlen(opt_arg
)-1)) == ',') ||
1399 (sscanf(opt_arg
, "io,stat,%lf%n", &interval_float
, (int *)&idx
) != 1) ||
1401 fprintf(stderr
, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1405 filters
= opt_arg
+idx
;
1407 if (*filters
!= ',') {
1408 /* For locale's that use ',' instead of '.', the comma might
1409 * have been consumed during the floating point conversion. */
1411 if (*filters
!= ',') {
1412 fprintf(stderr
, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1419 switch (timestamp_get_type()) {
1423 fprintf(stderr
, "\ntshark: invalid -t operand. io,stat only supports -t <r|a|ad|adoy|u|ud|udoy>\n");
1429 io
= g_new(io_stat_t
, 1);
1431 /* If interval is 0, calculate statistics over the whole file by setting the interval to
1433 if (interval_float
== 0) {
1434 io
->interval
= UINT64_MAX
;
1437 /* Set interval to the number of us rounded to the nearest integer */
1438 io
->interval
= (uint64_t)(interval_float
* 1000000.0 + 0.5);
1440 * Determine what interval precision the user has specified */
1442 for (i
=10; i
<10000000; i
*=10) {
1443 if (io
->interval
%i
> 0)
1447 if (io
->invl_prec
== 0) {
1448 /* The precision is zero but if the user specified one of more zeros after the decimal point,
1449 they want that many decimal places shown in the table for all time intervals except
1450 response time values such as smb.time which always have 6 decimal places of precision.
1451 This feature is useful in cases where for example the duration is 9.1, you specify an
1452 interval of 1 and the last interval becomes "9 <> 9". If the interval is instead set to
1453 1.1, the last interval becomes
1454 last interval is rounded up to value that is greater than the duration. */
1455 const char *invl_start
= opt_arg
+8;
1459 intv_end
= g_strstr_len(invl_start
, -1, ",");
1460 invl_len
= (int)(intv_end
- invl_start
);
1461 invl_start
= g_strstr_len(invl_start
, invl_len
, ".");
1463 if (invl_start
!= NULL
) {
1464 invl_len
= (int)(intv_end
- invl_start
- 1);
1466 io
->invl_prec
= MIN(invl_len
, 6);
1470 if (io
->interval
< 1) {
1472 "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
1476 /* Find how many ',' separated filters we have */
1480 if (filters
&& (*filters
!= '\0')) {
1481 /* Eliminate the first comma. */
1484 while ((str
= strchr(str
, ','))) {
1490 io
->items
= g_new(io_stat_item_t
, io
->num_cols
);
1491 io
->filters
= (const char **)g_malloc(sizeof(char *) * io
->num_cols
);
1492 io
->max_vals
= g_new(uint64_t, io
->num_cols
);
1493 io
->max_frame
= g_new(uint32_t, io
->num_cols
);
1494 io
->hf_indexes
= g_new(int, io
->num_cols
);
1495 io
->calc_type
= g_new(int, io
->num_cols
);
1497 for (i
=0; i
<io
->num_cols
; i
++) {
1498 io
->max_vals
[i
] = 0;
1499 io
->max_frame
[i
] = 0;
1502 /* Register a tap listener for each filter */
1503 if ((!filters
) || (filters
[0] == 0)) {
1504 register_io_tap(io
, 0, NULL
);
1510 pos
= (char*) strchr(str
, ',');
1512 register_io_tap(io
, i
, NULL
);
1513 } else if (pos
== NULL
) {
1514 str
= (const char*) g_strstrip((char*)str
);
1515 filter
= g_strdup(str
);
1517 register_io_tap(io
, i
, filter
);
1519 register_io_tap(io
, i
, NULL
);
1521 filter
= (char *)g_malloc((pos
-str
)+1);
1522 (void) g_strlcpy( filter
, str
, (size_t) ((pos
-str
)+1));
1523 filter
= g_strstrip(filter
);
1524 register_io_tap(io
, i
, (char *) filter
);
1532 static stat_tap_ui iostat_ui
= {
1533 REGISTER_STAT_GROUP_GENERIC
,
1542 register_tap_listener_iostat(void)
1544 register_stat_tap_ui(&iostat_ui
, NULL
);