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*/
15 #include <epan/epan_dissect.h>
17 #include <epan/stat_tap_ui.h>
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
30 void register_tap_listener_iostat(void);
33 const char *func_name;
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 },
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 */
61 typedef struct _io_stat_item_t {
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) */
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 */
73 gdouble double_counter;
76 #define NANOSECS_PER_SEC G_GUINT64_CONSTANT(1000000000)
78 static guint64 last_relative_time;
81 iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
86 guint64 relative_time, rt;
92 mit = (io_stat_item_t *) arg;
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;
102 relative_time = last_relative_time;
105 if (mit->parent->start_time == 0) {
106 mit->parent->start_time = pinfo->abs_ts.secs - pinfo->rel_ts.secs;
109 /* The prev item is always the last interval in which we saw packets. */
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. */
116 while (rt >= it->start_time + parent->interval) {
117 it->next = (io_stat_item_t *)g_malloc(sizeof(io_stat_item_t));
119 it->next->next = NULL;
123 it->start_time = it->prev->start_time + parent->interval;
126 it->float_counter = 0;
127 it->double_counter = 0;
129 it->calc_type = it->prev->calc_type;
130 it->hf_index = it->prev->hf_index;
131 it->colnum = it->prev->colnum;
134 /* Store info in the current structure */
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;
143 case CALC_TYPE_COUNT:
144 gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
146 it->counter += gp->len;
150 gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
154 for (i=0; i<gp->len; i++) {
155 switch (proto_registrar_get_ftype(it->hf_index)) {
160 it->counter += fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
166 it->counter += fvalue_get_uinteger64(&((field_info *)gp->pdata[i])->value);
172 it->counter += fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
178 it->counter += (gint64)fvalue_get_sinteger64(&((field_info *)gp->pdata[i])->value);
182 (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
185 it->double_counter += fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
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;
194 * "Can't happen"; see the checks
195 * in register_io_tap().
197 g_assert_not_reached();
204 gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
210 ftype = proto_registrar_get_ftype(it->hf_index);
211 for (i=0; i<gp->len; i++) {
217 val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
218 if ((it->frames == 1 && i == 0) || (val < it->counter)) {
226 val = fvalue_get_uinteger64(&((field_info *)gp->pdata[i])->value);
227 if ((it->frames == 1 && i == 0) || (val < it->counter)) {
235 val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
236 if ((it->frames == 1 && i == 0) || ((gint32)val < (gint32)it->counter)) {
244 val = fvalue_get_sinteger64(&((field_info *)gp->pdata[i])->value);
245 if ((it->frames == 1 && i == 0) || ((gint64)val < (gint64)it->counter)) {
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;
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;
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)) {
270 * "Can't happen"; see the checks
271 * in register_io_tap().
273 g_assert_not_reached();
280 gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
286 ftype = proto_registrar_get_ftype(it->hf_index);
287 for (i=0; i<gp->len; i++) {
293 val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
294 if (val > it->counter)
301 val = fvalue_get_uinteger64(&((field_info *)gp->pdata[i])->value);
302 if (val > it->counter)
309 val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
310 if ((gint32)val > (gint32)it->counter)
317 val = fvalue_get_sinteger64(&((field_info *)gp->pdata[i])->value);
318 if ((gint64)val > (gint64)it->counter)
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;
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;
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)
339 * "Can't happen"; see the checks
340 * in register_io_tap().
342 g_assert_not_reached();
349 gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
353 ftype = proto_registrar_get_ftype(it->hf_index);
354 for (i=0; i<gp->len; i++) {
361 val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
368 val = fvalue_get_uinteger64(&((field_info *)gp->pdata[i])->value);
375 val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
382 val = fvalue_get_sinteger64(&((field_info *)gp->pdata[i])->value);
386 it->float_counter += (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
389 it->double_counter += fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
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;
398 * "Can't happen"; see the checks
399 * in register_io_tap().
401 g_assert_not_reached();
408 gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
410 ftype = proto_registrar_get_ftype(it->hf_index);
411 if (ftype != FT_RELATIVE_TIME) {
413 "\ntshark: LOAD() is only supported for relative-time fields such as smb.time\n");
416 for (i=0; i<gp->len; i++) {
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;
428 if (val < (guint64)parent->interval) {
432 pit->counter += parent->interval;
433 val -= parent->interval;
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.
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);
455 case CALC_TYPE_BYTES:
456 case CALC_TYPE_COUNT:
458 parent->max_vals[it->colnum] = MAX(parent->max_vals[it->colnum], it->counter);
463 ftype = proto_registrar_get_ftype(it->hf_index);
466 parent->max_vals[it->colnum] =
467 MAX(parent->max_vals[it->colnum], (guint64)(it->float_counter+0.5));
470 parent->max_vals[it->colnum] =
471 MAX(parent->max_vals[it->colnum], (guint64)(it->double_counter+0.5));
473 case FT_RELATIVE_TIME:
474 parent->max_vals[it->colnum] =
475 MAX(parent->max_vals[it->colnum], it->counter);
478 /* UINT16-64 and INT8-64 */
479 parent->max_vals[it->colnum] =
480 MAX(parent->max_vals[it->colnum], it->counter);
485 if (it->num == 0) /* avoid division by zero */
487 ftype = proto_registrar_get_ftype(it->hf_index);
490 parent->max_vals[it->colnum] =
491 MAX(parent->max_vals[it->colnum], (guint64)it->float_counter/it->num);
494 parent->max_vals[it->colnum] =
495 MAX(parent->max_vals[it->colnum], (guint64)it->double_counter/it->num);
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);
502 /* UINT16-64 and INT8-64 */
503 parent->max_vals[it->colnum] =
504 MAX(parent->max_vals[it->colnum], it->counter/it->num);
512 magnitude (guint64 val, int max_w)
516 for (i=0; i<max_w; i++) {
518 if ((val /= 10) == 0)
525 * Print the calc_type_table[] function label centered in the column header.
528 printcenter (const char *label, int lenval, int numpad)
530 int lenlab = (int) strlen(label), len;
531 const char spaces[] = " ", *spaces_ptr;
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);
539 printf("%s%s%s|", spaces_ptr-1, label, spaces_ptr);
541 } else if (len > 0 && len <= 15) {
542 printf("%s|", label);
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 */
552 iostat_draw(void *arg)
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,
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;
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;
571 mit = (io_stat_item_t *)arg;
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);
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];
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) {
588 iot->interval = G_MAXUINT64;
590 interval = iot->interval;
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);
601 /* Calc the interval's magnitude */
602 invl_mag = magnitude(interval/G_GUINT64_CONSTANT(1000000), 5);
604 /* Set or get the interval precision */
605 if (interval == duration) {
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. */
611 else if (dur_mag == 1)
616 borderlen = 30 + dur_mag + (invl_prec == 0 ? 0 : invl_prec+1);
618 invl_prec = iot->invl_prec;
619 borderlen = 25 + MAX(invl_mag,dur_mag) + (invl_prec == 0 ? 0 : invl_prec+1);
622 /* Round the duration according to invl_prec */
624 for (i=0; i<invl_prec; i++)
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));
632 * Recalc dur_mag in case rounding has increased its magnitude */
633 dur_mag = magnitude((guint64)dur_secs, 5);
635 if (iot->interval == G_MAXUINT64)
638 /* Calc the width of the time interval column (incl borders and padding). */
640 invl_col_w = (2*dur_mag) + 8;
642 invl_col_w = (2*dur_mag) + (2*invl_prec) + 10;
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);
654 invl_col_w = MAX(invl_col_w, 12);
658 borderlen = MAX(borderlen, invl_col_w);
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) {
670 namelen = (int) strlen(calc_type_table[type].func_name);
672 if (type == CALC_TYPE_FRAMES
673 || type == CALC_TYPE_FRAMES_AND_BYTES) {
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);
681 if (type == CALC_TYPE_FRAMES) {
682 fmt = g_strconcat(" %", fr_mag_s, "u |", NULL);
684 /* CALC_TYPE_FRAMES_AND_BYTES
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);
698 case CALC_TYPE_BYTES:
699 case CALC_TYPE_COUNT:
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);
709 ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
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;
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;
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;
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);
744 fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
751 fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "d |", NULL);
754 } /* End of ftype switch */
755 } /* End of calc_type switch */
756 tabrow_w += col_w[j].val + 3;
759 } /* End of for loop (columns) */
761 borderlen = MAX(borderlen, tabrow_w);
763 /* Calc the max width of the list of filters. */
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);
770 maxfltr_w = MAX(maxfltr_w, 26);
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);
782 /* Prevent double right border by adding a space */
783 if (borderlen-tabrow_w == 1)
786 /* Display the top border */
788 for (i=0; i<borderlen; i++)
791 spaces = (char *)g_malloc(borderlen+1);
792 for (i=0; i<borderlen; i++)
794 spaces[borderlen] = '\0';
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);
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);
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);
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);
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);
825 spaces_s = &spaces[2];
826 printf("|%s|\n", spaces_s);
828 /* Display the list of filters and their column numbers vertically */
830 for (j=0; j<num_cols; j++) {
831 printf((j == 0 ? "%2u: " : "| %2u: "), j+1);
832 if (!iot->filters[j]) {
834 * An empty (no filter) comma field was specified */
835 spaces_s = &spaces[16 + 10];
836 printf("Frames and bytes%s|\n", spaces_s);
838 filter = iot->filters[j];
839 len_filt = (int) strlen(filter);
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);
851 gchar *sfilter1, *sfilter2;
854 int next_start, max_w = borderlen-11;
857 if (len_filt > max_w) {
858 sfilter1 = g_strndup(filter, (gsize) max_w);
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, " ");
865 len = (gsize)(pos-sfilter1);
866 next_start = (int) len+1;
868 len = (gsize) strlen(sfilter1);
869 next_start = (int)len;
871 sfilter2 = g_strndup(sfilter1, len);
872 printf("%s%s|\n", sfilter2, &spaces[len+10]);
877 filter = &filter[next_start];
878 len_filt = (int) strlen(filter);
880 printf("%s%s|\n", filter, &spaces[((int)strlen(filter))+10]);
889 for (i=0; i<borderlen-3; i++) {
894 /* Display spaces above "Interval (s)" label */
895 spaces_s = &spaces[borderlen-(invl_col_w-2)];
896 printf("|%s|", spaces_s);
898 /* Display column number headers */
899 for (j=0; j<num_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];
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 = 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);
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 = (int)(duration/interval) + ((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/G_GUINT64_CONSTANT(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 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");
1090 printf(full_fmt, (guint32)(t/G_GUINT64_CONSTANT(1000000)),
1091 (guint32)(invl_end/G_GUINT64_CONSTANT(1000000)));
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));
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];
1114 switch (item->calc_type) {
1115 case CALC_TYPE_FRAMES:
1116 printf(fmt, item->frames);
1118 case CALC_TYPE_BYTES:
1119 case CALC_TYPE_COUNT:
1120 printf(fmt, item->counter);
1122 case CALC_TYPE_FRAMES_AND_BYTES:
1123 printf(fmt, item->frames, item->counter);
1129 ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1132 printf(fmt, item->float_counter);
1135 printf(fmt, item->double_counter);
1137 case FT_RELATIVE_TIME:
1138 item->counter = (item->counter + G_GUINT64_CONSTANT(500)) / G_GUINT64_CONSTANT(1000);
1140 (int)(item->counter/G_GUINT64_CONSTANT(1000000)),
1141 (int)(item->counter%G_GUINT64_CONSTANT(1000000)));
1144 printf(fmt, item->counter);
1153 ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1156 printf(fmt, item->float_counter/num);
1159 printf(fmt, item->double_counter/num);
1161 case FT_RELATIVE_TIME:
1162 item->counter = ((item->counter / (guint64)num) + G_GUINT64_CONSTANT(500)) / G_GUINT64_CONSTANT(1000);
1164 (int)(item->counter/G_GUINT64_CONSTANT(1000000)),
1165 (int)(item->counter%G_GUINT64_CONSTANT(1000000)));
1168 printf(fmt, item->counter / (guint64)num);
1173 case CALC_TYPE_LOAD:
1174 ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1176 case FT_RELATIVE_TIME:
1179 (int) (item->counter/interval),
1180 (int)((item->counter%interval)*G_GUINT64_CONSTANT(1000000) / interval));
1183 (int) (item->counter/(invl_end-t)),
1184 (int)((item->counter%(invl_end-t))*G_GUINT64_CONSTANT(1000000) / (invl_end-t)));
1194 item_in_column[j] = item_in_column[j]->next;
1197 printf(fmt, (guint64)0, (guint64)0);
1201 printf("%s|", filler_s);
1206 for (i=0; i<borderlen; i++) {
1211 g_free(iot->max_vals);
1212 g_free(iot->max_frame);
1220 g_free(item_in_column);
1225 register_io_tap(io_stat_t *io, int i, const char *filter)
1227 GString *error_string;
1231 const char *p, *parenp;
1233 header_field_info *hfi;
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;
1244 io->filters[i] = filter;
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, ')');
1259 "\ntshark: Closing parenthesis missing from calculated expression.\n");
1263 if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES) {
1266 "\ntshark: %s does not require or allow a field name within the parens.\n",
1267 calc_type_table[j].func_name);
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);
1279 field = (char *)g_malloc(parenp-p+1);
1280 memcpy(field, p, parenp-p);
1281 field[parenp-p] = '\0';
1283 if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES)
1285 hfi = proto_registrar_get_byname(field);
1287 fprintf(stderr, "\ntshark: There is no field named '%s'.\n",
1293 io->items[i].hf_index = hfi->id;
1297 if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES)
1299 io->items[i].colnum = i;
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) {
1317 /* these types support all calculations */
1321 /* these types only support SUM, COUNT, MAX, MIN, AVG */
1322 switch (io->items[i].calc_type) {
1324 case CALC_TYPE_COUNT:
1331 "\ntshark: %s is a float field, so %s(*) calculations are not supported on it.",
1333 calc_type_table[j].func_name);
1337 case FT_RELATIVE_TIME:
1338 /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */
1339 switch (io->items[i].calc_type) {
1341 case CALC_TYPE_COUNT:
1345 case CALC_TYPE_LOAD:
1349 "\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
1351 calc_type_table[j].func_name);
1357 * XXX - support all operations on floating-point
1360 if (io->items[i].calc_type != CALC_TYPE_COUNT) {
1362 "\ntshark: %s doesn't have integral values, so %s(*) "
1363 "calculations are not supported on it.\n",
1365 calc_type_table[j].func_name);
1373 error_string = register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE, NULL,
1374 iostat_packet, i ? NULL : iostat_draw);
1378 fprintf(stderr, "\ntshark: Couldn't register io,stat tap: %s\n",
1380 g_string_free(error_string, TRUE);
1386 iostat_init(const char *opt_arg, void *userdata _U_)
1388 gdouble interval_float;
1392 const gchar *filters, *str, *pos;
1394 if ((*(opt_arg+(strlen(opt_arg)-1)) == ',') ||
1395 (sscanf(opt_arg, "io,stat,%lf%n", &interval_float, (int *)&idx) != 1) ||
1397 fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1401 filters = opt_arg+idx;
1403 if (*filters != ',') {
1404 /* For locale's that use ',' instead of '.', the comma might
1405 * have been consumed during the floating point conversion. */
1407 if (*filters != ',') {
1408 fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1415 switch (timestamp_get_type()) {
1419 fprintf(stderr, "\ntshark: invalid -t operand. io,stat only supports -t <r|a|ad|adoy|u|ud|udoy>\n");
1425 io = (io_stat_t *)g_malloc(sizeof(io_stat_t));
1427 /* If interval is 0, calculate statistics over the whole file by setting the interval to
1429 if (interval_float == 0) {
1430 io->interval = G_MAXUINT64;
1433 /* Set interval to the number of us rounded to the nearest integer */
1434 io->interval = (guint64)(interval_float * 1000000.0 + 0.5);
1436 * Determine what interval precision the user has specified */
1438 for (i=10; i<10000000; i*=10) {
1439 if (io->interval%i > 0)
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;
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, ".");
1459 if (invl_start != NULL) {
1460 invl_len = (int)(intv_end - invl_start - 1);
1462 io->invl_prec = MIN(invl_len, 6);
1466 if (io->interval < 1) {
1468 "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
1472 /* Find how many ',' separated filters we have */
1476 if (filters && (*filters != '\0')) {
1477 /* Eliminate the first comma. */
1480 while ((str = strchr(str, ','))) {
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);
1491 for (i=0; i<io->num_cols; i++) {
1492 io->max_vals[i] = 0;
1493 io->max_frame[i] = 0;
1496 /* Register a tap listener for each filter */
1497 if ((!filters) || (filters[0] == 0)) {
1498 register_io_tap(io, 0, NULL);
1504 pos = (gchar*) strchr(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);
1511 register_io_tap(io, i, filter);
1513 register_io_tap(io, i, NULL);
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);
1526 static stat_tap_ui iostat_ui = {
1527 REGISTER_STAT_GROUP_GENERIC,
1536 register_tap_listener_iostat(void)
1538 register_stat_tap_ui(&iostat_ui, NULL);
1542 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1547 * indent-tabs-mode: nil
1550 * vi: set shiftwidth=4 tabstop=8 expandtab:
1551 * :indentSize=4:tabSize=8:noTabs=true: