summaryrefslogtreecommitdiffstats
path: root/ui/cli/tap-iostat.c
diff options
context:
space:
mode:
Diffstat (limited to 'ui/cli/tap-iostat.c')
-rw-r--r--ui/cli/tap-iostat.c1541
1 files changed, 1541 insertions, 0 deletions
diff --git a/ui/cli/tap-iostat.c b/ui/cli/tap-iostat.c
new file mode 100644
index 00000000..83810b23
--- /dev/null
+++ b/ui/cli/tap-iostat.c
@@ -0,0 +1,1541 @@
+/* tap-iostat.c
+ * iostat 2002 Ronnie Sahlberg
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <epan/epan_dissect.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include "globals.h"
+#include <wsutil/ws_assert.h>
+
+#define CALC_TYPE_FRAMES 0
+#define CALC_TYPE_BYTES 1
+#define CALC_TYPE_FRAMES_AND_BYTES 2
+#define CALC_TYPE_COUNT 3
+#define CALC_TYPE_SUM 4
+#define CALC_TYPE_MIN 5
+#define CALC_TYPE_MAX 6
+#define CALC_TYPE_AVG 7
+#define CALC_TYPE_LOAD 8
+
+void register_tap_listener_iostat(void);
+
+typedef struct {
+ const char *func_name;
+ int calc_type;
+} calc_type_ent_t;
+
+static calc_type_ent_t calc_type_table[] = {
+ { "FRAMES", CALC_TYPE_FRAMES },
+ { "BYTES", CALC_TYPE_BYTES },
+ { "FRAMES BYTES", CALC_TYPE_FRAMES_AND_BYTES },
+ { "COUNT", CALC_TYPE_COUNT },
+ { "SUM", CALC_TYPE_SUM },
+ { "MIN", CALC_TYPE_MIN },
+ { "MAX", CALC_TYPE_MAX },
+ { "AVG", CALC_TYPE_AVG },
+ { "LOAD", CALC_TYPE_LOAD },
+ { NULL, 0 }
+};
+
+typedef struct _io_stat_t {
+ guint64 interval; /* The user-specified time interval (us) */
+ guint invl_prec; /* Decimal precision of the time interval (1=10s, 2=100s etc) */
+ unsigned int num_cols; /* The number of columns of stats in the table */
+ struct _io_stat_item_t *items; /* Each item is a single cell in the table */
+ time_t start_time; /* Time of first frame matching the filter */
+ const char **filters; /* 'io,stat' cmd strings (e.g., "AVG(smb.time)smb.time") */
+ guint64 *max_vals; /* The max value sans the decimal or nsecs portion in each stat column */
+ guint32 *max_frame; /* The max frame number displayed in each stat column */
+} io_stat_t;
+
+typedef struct _io_stat_item_t {
+ io_stat_t *parent;
+ struct _io_stat_item_t *next;
+ struct _io_stat_item_t *prev;
+ guint64 start_time; /* Time since start of capture (us)*/
+ int calc_type; /* The statistic type */
+ int colnum; /* Column number of this stat (0 to n) */
+ int hf_index;
+ guint32 frames;
+ guint32 num; /* The sample size of a given statistic (only needed for AVG) */
+ guint64 counter; /* The accumulated data for the calculation of that statistic */
+ gfloat float_counter;
+ gdouble double_counter;
+} io_stat_item_t;
+
+#define NANOSECS_PER_SEC G_GUINT64_CONSTANT(1000000000)
+
+static guint64 last_relative_time;
+
+static tap_packet_status
+iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_, tap_flags_t flags _U_)
+{
+ io_stat_t *parent;
+ io_stat_item_t *mit;
+ io_stat_item_t *it;
+ guint64 relative_time, rt;
+ const nstime_t *new_time;
+ GPtrArray *gp;
+ guint i;
+ int ftype;
+
+ mit = (io_stat_item_t *) arg;
+ parent = mit->parent;
+
+ /* If this frame's relative time is negative, set its relative time to last_relative_time
+ rather than disincluding it from the calculations. */
+ if ((pinfo->rel_ts.secs >= 0) && (pinfo->rel_ts.nsecs >= 0)) {
+ relative_time = ((guint64)pinfo->rel_ts.secs * G_GUINT64_CONSTANT(1000000)) +
+ ((guint64)((pinfo->rel_ts.nsecs+500)/1000));
+ last_relative_time = relative_time;
+ } else {
+ relative_time = last_relative_time;
+ }
+
+ if (mit->parent->start_time == 0) {
+ mit->parent->start_time = pinfo->abs_ts.secs - pinfo->rel_ts.secs;
+ }
+
+ /* The prev item is always the last interval in which we saw packets. */
+ it = mit->prev;
+
+ /* If we have moved into a new interval (row), create a new io_stat_item_t struct for every interval
+ * between the last struct and this one. If an item was not found in a previous interval, an empty
+ * struct will be created for it. */
+ rt = relative_time;
+ while (rt >= it->start_time + parent->interval) {
+ it->next = g_new(io_stat_item_t, 1);
+ it->next->prev = it;
+ it->next->next = NULL;
+ it = it->next;
+ mit->prev = it;
+
+ it->start_time = it->prev->start_time + parent->interval;
+ it->frames = 0;
+ it->counter = 0;
+ it->float_counter = 0;
+ it->double_counter = 0;
+ it->num = 0;
+ it->calc_type = it->prev->calc_type;
+ it->hf_index = it->prev->hf_index;
+ it->colnum = it->prev->colnum;
+ }
+
+ /* Store info in the current structure */
+ it->frames++;
+
+ switch (it->calc_type) {
+ case CALC_TYPE_FRAMES:
+ case CALC_TYPE_BYTES:
+ case CALC_TYPE_FRAMES_AND_BYTES:
+ it->counter += pinfo->fd->pkt_len;
+ break;
+ case CALC_TYPE_COUNT:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ it->counter += gp->len;
+ }
+ break;
+ case CALC_TYPE_SUM:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ guint64 val;
+
+ for (i=0; i<gp->len; i++) {
+ switch (proto_registrar_get_ftype(it->hf_index)) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ it->counter += fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_UINT40:
+ case FT_UINT48:
+ case FT_UINT56:
+ case FT_UINT64:
+ it->counter += fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ it->counter += fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_INT40:
+ case FT_INT48:
+ case FT_INT56:
+ case FT_INT64:
+ it->counter += (gint64)fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_FLOAT:
+ it->float_counter +=
+ (gfloat)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_DOUBLE:
+ it->double_counter += fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_RELATIVE_TIME:
+ new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
+ val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
+ it->counter += val;
+ break;
+ default:
+ /*
+ * "Can't happen"; see the checks
+ * in register_io_tap().
+ */
+ ws_assert_not_reached();
+ break;
+ }
+ }
+ }
+ break;
+ case CALC_TYPE_MIN:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ guint64 val;
+ gfloat float_val;
+ gdouble double_val;
+
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ for (i=0; i<gp->len; i++) {
+ switch (ftype) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || (val < it->counter)) {
+ it->counter = val;
+ }
+ break;
+ case FT_UINT40:
+ case FT_UINT48:
+ case FT_UINT56:
+ case FT_UINT64:
+ val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || (val < it->counter)) {
+ it->counter = val;
+ }
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || ((gint32)val < (gint32)it->counter)) {
+ it->counter = val;
+ }
+ break;
+ case FT_INT40:
+ case FT_INT48:
+ case FT_INT56:
+ case FT_INT64:
+ val = fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || ((gint64)val < (gint64)it->counter)) {
+ it->counter = val;
+ }
+ break;
+ case FT_FLOAT:
+ float_val = (gfloat)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || (float_val < it->float_counter)) {
+ it->float_counter = float_val;
+ }
+ break;
+ case FT_DOUBLE:
+ double_val = fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ if ((it->frames == 1 && i == 0) || (double_val < it->double_counter)) {
+ it->double_counter = double_val;
+ }
+ break;
+ case FT_RELATIVE_TIME:
+ new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
+ val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
+ if ((it->frames == 1 && i == 0) || (val < it->counter)) {
+ it->counter = val;
+ }
+ break;
+ default:
+ /*
+ * "Can't happen"; see the checks
+ * in register_io_tap().
+ */
+ ws_assert_not_reached();
+ break;
+ }
+ }
+ }
+ break;
+ case CALC_TYPE_MAX:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ guint64 val;
+ gfloat float_val;
+ gdouble double_val;
+
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ for (i=0; i<gp->len; i++) {
+ switch (ftype) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
+ if (val > it->counter)
+ it->counter = val;
+ break;
+ case FT_UINT40:
+ case FT_UINT48:
+ case FT_UINT56:
+ case FT_UINT64:
+ val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
+ if (val > it->counter)
+ it->counter = val;
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
+ if ((gint32)val > (gint32)it->counter)
+ it->counter = val;
+ break;
+ case FT_INT40:
+ case FT_INT48:
+ case FT_INT56:
+ case FT_INT64:
+ val = fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
+ if ((gint64)val > (gint64)it->counter)
+ it->counter = val;
+ break;
+ case FT_FLOAT:
+ float_val = (gfloat)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ if (float_val > it->float_counter)
+ it->float_counter = float_val;
+ break;
+ case FT_DOUBLE:
+ double_val = fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ if (double_val > it->double_counter)
+ it->double_counter = double_val;
+ break;
+ case FT_RELATIVE_TIME:
+ new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
+ val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
+ if (val > it->counter)
+ it->counter = val;
+ break;
+ default:
+ /*
+ * "Can't happen"; see the checks
+ * in register_io_tap().
+ */
+ ws_assert_not_reached();
+ break;
+ }
+ }
+ }
+ break;
+ case CALC_TYPE_AVG:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ guint64 val;
+
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ for (i=0; i<gp->len; i++) {
+ it->num++;
+ switch (ftype) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
+ it->counter += val;
+ break;
+ case FT_UINT40:
+ case FT_UINT48:
+ case FT_UINT56:
+ case FT_UINT64:
+ val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
+ it->counter += val;
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
+ it->counter += val;
+ break;
+ case FT_INT40:
+ case FT_INT48:
+ case FT_INT56:
+ case FT_INT64:
+ val = fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
+ it->counter += val;
+ break;
+ case FT_FLOAT:
+ it->float_counter += (gfloat)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_DOUBLE:
+ it->double_counter += fvalue_get_floating(((field_info *)gp->pdata[i])->value);
+ break;
+ case FT_RELATIVE_TIME:
+ new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
+ val = ((guint64)new_time->secs * NANOSECS_PER_SEC) + (guint64)new_time->nsecs;
+ it->counter += val;
+ break;
+ default:
+ /*
+ * "Can't happen"; see the checks
+ * in register_io_tap().
+ */
+ ws_assert_not_reached();
+ break;
+ }
+ }
+ }
+ break;
+ case CALC_TYPE_LOAD:
+ gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
+ if (gp) {
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ if (ftype != FT_RELATIVE_TIME) {
+ fprintf(stderr,
+ "\ntshark: LOAD() is only supported for relative-time fields such as smb.time\n");
+ exit(10);
+ }
+ for (i=0; i<gp->len; i++) {
+ guint64 val;
+ int tival;
+ io_stat_item_t *pit;
+
+ new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
+ val = ((guint64)new_time->secs*G_GUINT64_CONSTANT(1000000)) + (guint64)(new_time->nsecs/1000);
+ tival = (int)(val % parent->interval);
+ it->counter += tival;
+ val -= tival;
+ pit = it->prev;
+ while (val > 0) {
+ if (val < (guint64)parent->interval) {
+ pit->counter += val;
+ break;
+ }
+ pit->counter += parent->interval;
+ val -= parent->interval;
+ pit = pit->prev;
+ }
+ }
+ }
+ break;
+ }
+ /* Store the highest value for this item in order to determine the width of each stat column.
+ * For real numbers we only need to know its magnitude (the value to the left of the decimal point
+ * so round it up before storing it as an integer in max_vals. For AVG of RELATIVE_TIME fields,
+ * calc the average, round it to the next second and store the seconds. For all other calc types
+ * of RELATIVE_TIME fields, store the counters without modification.
+ * fields. */
+ switch (it->calc_type) {
+ case CALC_TYPE_FRAMES:
+ case CALC_TYPE_FRAMES_AND_BYTES:
+ parent->max_frame[it->colnum] =
+ MAX(parent->max_frame[it->colnum], it->frames);
+ if (it->calc_type == CALC_TYPE_FRAMES_AND_BYTES)
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], it->counter);
+ break;
+ case CALC_TYPE_BYTES:
+ case CALC_TYPE_COUNT:
+ case CALC_TYPE_LOAD:
+ parent->max_vals[it->colnum] = MAX(parent->max_vals[it->colnum], it->counter);
+ break;
+ case CALC_TYPE_SUM:
+ case CALC_TYPE_MIN:
+ case CALC_TYPE_MAX:
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ switch (ftype) {
+ case FT_FLOAT:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], (guint64)(it->float_counter+0.5));
+ break;
+ case FT_DOUBLE:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], (guint64)(it->double_counter+0.5));
+ break;
+ case FT_RELATIVE_TIME:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], it->counter);
+ break;
+ default:
+ /* UINT16-64 and INT8-64 */
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], it->counter);
+ break;
+ }
+ break;
+ case CALC_TYPE_AVG:
+ if (it->num == 0) /* avoid division by zero */
+ break;
+ ftype = proto_registrar_get_ftype(it->hf_index);
+ switch (ftype) {
+ case FT_FLOAT:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], (guint64)it->float_counter/it->num);
+ break;
+ case FT_DOUBLE:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], (guint64)it->double_counter/it->num);
+ break;
+ case FT_RELATIVE_TIME:
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], ((it->counter/(guint64)it->num) + G_GUINT64_CONSTANT(500000000)) / NANOSECS_PER_SEC);
+ break;
+ default:
+ /* UINT16-64 and INT8-64 */
+ parent->max_vals[it->colnum] =
+ MAX(parent->max_vals[it->colnum], it->counter/it->num);
+ break;
+ }
+ }
+ return TAP_PACKET_REDRAW;
+}
+
+static unsigned int
+magnitude (guint64 val, unsigned int max_w)
+{
+ unsigned int i, mag = 0;
+
+ for (i=0; i<max_w; i++) {
+ mag++;
+ if ((val /= 10) == 0)
+ break;
+ }
+ return(mag);
+}
+
+/*
+* Print the calc_type_table[] function label centered in the column header.
+*/
+static void
+printcenter (const char *label, int lenval, int numpad)
+{
+ int lenlab = (int) strlen(label), len;
+ const char spaces[] = " ", *spaces_ptr;
+
+ len = (int) (strlen(spaces)) - (((lenval-lenlab) / 2) + numpad);
+ if (len > 0 && len < 6) {
+ spaces_ptr = &spaces[len];
+ if ((lenval-lenlab)%2 == 0) {
+ printf("%s%s%s|", spaces_ptr, label, spaces_ptr);
+ } else {
+ printf("%s%s%s|", spaces_ptr-1, label, spaces_ptr);
+ }
+ } else if (len > 0 && len <= 15) {
+ printf("%s|", label);
+ }
+}
+
+typedef struct {
+ int fr; /* Width of this FRAMES column sans padding and border chars */
+ int val; /* Width of this non-FRAMES column sans padding and border chars */
+} column_width;
+
+static void
+iostat_draw(void *arg)
+{
+ guint32 num;
+ guint64 interval, duration, t, invl_end, dv;
+ unsigned int i, j, k, num_cols, num_rows, dur_secs_orig, dur_nsecs_orig, dur_secs, dur_nsecs, dur_mag,
+ invl_mag, invl_prec, tabrow_w, borderlen, invl_col_w, numpad = 1, namelen, len_filt, type,
+ maxfltr_w, ftype;
+ unsigned int fr_mag; /* The magnitude of the max frame number in this column */
+ unsigned int val_mag; /* The magnitude of the max value in this column */
+ char *spaces, *spaces_s, *filler_s = NULL, **fmts, *fmt = NULL;
+ const char *filter;
+ static gchar dur_mag_s[3], invl_prec_s[3], fr_mag_s[3], val_mag_s[3], *invl_fmt, *full_fmt;
+ io_stat_item_t *mit, **stat_cols, *item, **item_in_column;
+ gboolean last_row = FALSE;
+ io_stat_t *iot;
+ column_width *col_w;
+ struct tm *tm_time;
+ time_t the_time;
+
+ mit = (io_stat_item_t *)arg;
+ iot = mit->parent;
+ num_cols = iot->num_cols;
+ col_w = g_new(column_width, num_cols);
+ fmts = (char **)g_malloc(sizeof(char *) * num_cols);
+ duration = ((guint64)cfile.elapsed_time.secs * G_GUINT64_CONSTANT(1000000)) +
+ (guint64)((cfile.elapsed_time.nsecs + 500) / 1000);
+
+ /* Store the pointer to each stat column */
+ stat_cols = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols);
+ for (j=0; j<num_cols; j++)
+ stat_cols[j] = &iot->items[j];
+
+ /* The following prevents gross inaccuracies when the user specifies an interval that is greater
+ * than the capture duration. */
+ if (iot->interval > duration || iot->interval == G_MAXUINT64) {
+ interval = duration;
+ iot->interval = G_MAXUINT64;
+ } else {
+ interval = iot->interval;
+ }
+
+ /* Calc the capture duration's magnitude (dur_mag) */
+ dur_secs = (unsigned int)(duration/G_GUINT64_CONSTANT(1000000));
+ dur_secs_orig = dur_secs;
+ dur_nsecs = (unsigned int)(duration%G_GUINT64_CONSTANT(1000000));
+ dur_nsecs_orig = dur_nsecs;
+ dur_mag = magnitude((guint64)dur_secs, 5);
+ snprintf(dur_mag_s, 3, "%u", dur_mag);
+
+ /* Calc the interval's magnitude */
+ invl_mag = magnitude(interval/G_GUINT64_CONSTANT(1000000), 5);
+
+ /* Set or get the interval precision */
+ if (interval == duration) {
+ /*
+ * An interval arg of 0 or an interval size exceeding the capture duration was specified.
+ * Set the decimal precision of duration based on its magnitude. */
+ if (dur_mag >= 2)
+ invl_prec = 1;
+ else if (dur_mag == 1)
+ invl_prec = 3;
+ else
+ invl_prec = 6;
+
+ borderlen = 30 + dur_mag + (invl_prec == 0 ? 0 : invl_prec+1);
+ } else {
+ invl_prec = iot->invl_prec;
+ borderlen = 25 + MAX(invl_mag,dur_mag) + (invl_prec == 0 ? 0 : invl_prec+1);
+ }
+
+ /* Round the duration according to invl_prec */
+ dv = 1000000;
+ for (i=0; i<invl_prec; i++)
+ dv /= 10;
+ if ((duration%dv) > 5*(dv/10)) {
+ duration += 5*(dv/10);
+ duration = (duration/dv) * dv;
+ dur_secs = (unsigned int)(duration/G_GUINT64_CONSTANT(1000000));
+ dur_nsecs = (unsigned int)(duration%G_GUINT64_CONSTANT(1000000));
+ /*
+ * Recalc dur_mag in case rounding has increased its magnitude */
+ dur_mag = magnitude((guint64)dur_secs, 5);
+ }
+ if (iot->interval == G_MAXUINT64)
+ interval = duration;
+
+ /* Calc the width of the time interval column (incl borders and padding). */
+ if (invl_prec == 0)
+ invl_col_w = (2*dur_mag) + 8;
+ else
+ invl_col_w = (2*dur_mag) + (2*invl_prec) + 10;
+
+ /* Update the width of the time interval column if date is shown */
+ switch (timestamp_get_type()) {
+ case TS_ABSOLUTE_WITH_YMD:
+ case TS_ABSOLUTE_WITH_YDOY:
+ case TS_UTC_WITH_YMD:
+ case TS_UTC_WITH_YDOY:
+ invl_col_w = MAX(invl_col_w, 23);
+ break;
+
+ default:
+ invl_col_w = MAX(invl_col_w, 12);
+ break;
+ }
+
+ borderlen = MAX(borderlen, invl_col_w);
+
+ /* Calc the total width of each row in the stats table and build the printf format string for each
+ * column based on its field type, width, and name length.
+ * NOTE: The magnitude of all types including float and double are stored in iot->max_vals which
+ * is an *integer*. */
+ tabrow_w = invl_col_w;
+ for (j=0; j<num_cols; j++) {
+ type = iot->items[j].calc_type;
+ if (type == CALC_TYPE_FRAMES_AND_BYTES) {
+ namelen = 5;
+ } else {
+ namelen = (unsigned int)strlen(calc_type_table[type].func_name);
+ }
+ if (type == CALC_TYPE_FRAMES
+ || type == CALC_TYPE_FRAMES_AND_BYTES) {
+
+ fr_mag = magnitude(iot->max_frame[j], 15);
+ fr_mag = MAX(6, fr_mag);
+ col_w[j].fr = fr_mag;
+ tabrow_w += col_w[j].fr + 3;
+ snprintf(fr_mag_s, 3, "%u", fr_mag);
+
+ if (type == CALC_TYPE_FRAMES) {
+ fmt = g_strconcat(" %", fr_mag_s, "u |", NULL);
+ } else {
+ /* CALC_TYPE_FRAMES_AND_BYTES
+ */
+ val_mag = magnitude(iot->max_vals[j], 15);
+ val_mag = MAX(5, val_mag);
+ col_w[j].val = val_mag;
+ tabrow_w += (col_w[j].val + 3);
+ snprintf(val_mag_s, 3, "%u", val_mag);
+ fmt = g_strconcat(" %", fr_mag_s, "u |", " %", val_mag_s, PRIu64 " |", NULL);
+ }
+ if (fmt)
+ fmts[j] = fmt;
+ continue;
+ }
+ switch (type) {
+ case CALC_TYPE_BYTES:
+ case CALC_TYPE_COUNT:
+
+ val_mag = magnitude(iot->max_vals[j], 15);
+ val_mag = MAX(5, val_mag);
+ col_w[j].val = val_mag;
+ snprintf(val_mag_s, 3, "%u", val_mag);
+ fmt = g_strconcat(" %", val_mag_s, PRIu64 " |", NULL);
+ break;
+
+ default:
+ ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
+ switch (ftype) {
+ case FT_FLOAT:
+ case FT_DOUBLE:
+ val_mag = magnitude(iot->max_vals[j], 15);
+ snprintf(val_mag_s, 3, "%u", val_mag);
+ fmt = g_strconcat(" %", val_mag_s, ".6f |", NULL);
+ col_w[j].val = val_mag + 7;
+ break;
+ case FT_RELATIVE_TIME:
+ /* Convert FT_RELATIVE_TIME field to seconds
+ * CALC_TYPE_LOAD was already converted in iostat_packet() ) */
+ if (type == CALC_TYPE_LOAD) {
+ iot->max_vals[j] /= interval;
+ } else if (type != CALC_TYPE_AVG) {
+ iot->max_vals[j] = (iot->max_vals[j] + G_GUINT64_CONSTANT(500000000)) / NANOSECS_PER_SEC;
+ }
+ val_mag = magnitude(iot->max_vals[j], 15);
+ snprintf(val_mag_s, 3, "%u", val_mag);
+ fmt = g_strconcat(" %", val_mag_s, "u.%06u |", NULL);
+ col_w[j].val = val_mag + 7;
+ break;
+
+ default:
+ val_mag = magnitude(iot->max_vals[j], 15);
+ val_mag = MAX(namelen, val_mag);
+ col_w[j].val = val_mag;
+ snprintf(val_mag_s, 3, "%u", val_mag);
+
+ switch (ftype) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_UINT64:
+ fmt = g_strconcat(" %", val_mag_s, PRIu64 " |", NULL);
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ case FT_INT64:
+ fmt = g_strconcat(" %", val_mag_s, PRId64 " |", NULL);
+ break;
+ }
+ } /* End of ftype switch */
+ } /* End of calc_type switch */
+ tabrow_w += col_w[j].val + 3;
+ if (fmt)
+ fmts[j] = fmt;
+ } /* End of for loop (columns) */
+
+ borderlen = MAX(borderlen, tabrow_w);
+
+ /* Calc the max width of the list of filters. */
+ maxfltr_w = 0;
+ for (j=0; j<num_cols; j++) {
+ if (iot->filters[j]) {
+ k = (unsigned int) (strlen(iot->filters[j]) + 11);
+ maxfltr_w = MAX(maxfltr_w, k);
+ } else {
+ maxfltr_w = MAX(maxfltr_w, 26);
+ }
+ }
+ /* The stat table is not wrapped (by tshark) but filter is wrapped at the width of the stats table
+ * (which currently = borderlen); however, if the filter width exceeds the table width and the
+ * table width is less than 102 bytes, set borderlen to the lesser of the max filter width and 102.
+ * The filters will wrap at the lesser of borderlen-2 and the last space in the filter.
+ * NOTE: 102 is the typical size of a user window when the font is fixed width (e.g., COURIER 10).
+ * XXX: A pref could be added to change the max width from the default size of 102. */
+ if (maxfltr_w > borderlen && borderlen < 102)
+ borderlen = MIN(maxfltr_w, 102);
+
+ /* Prevent double right border by adding a space */
+ if (borderlen-tabrow_w == 1)
+ borderlen++;
+
+ /* Display the top border */
+ printf("\n");
+ for (i=0; i<borderlen; i++)
+ printf("=");
+
+ spaces = (char *)g_malloc(borderlen+1);
+ for (i=0; i<borderlen; i++)
+ spaces[i] = ' ';
+ spaces[borderlen] = '\0';
+
+ spaces_s = &spaces[16];
+ printf("\n| IO Statistics%s|\n", spaces_s);
+ spaces_s = &spaces[2];
+ printf("|%s|\n", spaces_s);
+
+ if (invl_prec == 0) {
+ invl_fmt = g_strconcat("%", dur_mag_s, "u", NULL);
+ full_fmt = g_strconcat("| Duration: ", invl_fmt, ".%6u secs%s|\n", NULL);
+ spaces_s = &spaces[25 + dur_mag];
+ printf(full_fmt, dur_secs_orig, dur_nsecs_orig, spaces_s);
+ g_free(full_fmt);
+ full_fmt = g_strconcat("| Interval: ", invl_fmt, " secs%s|\n", NULL);
+ spaces_s = &spaces[18 + dur_mag];
+ printf(full_fmt, (guint32)(interval/G_GUINT64_CONSTANT(1000000)), spaces_s);
+ } else {
+ snprintf(invl_prec_s, 3, "%u", invl_prec);
+ invl_fmt = g_strconcat("%", dur_mag_s, "u.%0", invl_prec_s, "u", NULL);
+ full_fmt = g_strconcat("| Duration: ", invl_fmt, " secs%s|\n", NULL);
+ spaces_s = &spaces[19 + dur_mag + invl_prec];
+ printf(full_fmt, dur_secs, dur_nsecs/(int)dv, spaces_s);
+ g_free(full_fmt);
+
+ full_fmt = g_strconcat("| Interval: ", invl_fmt, " secs%s|\n", NULL);
+ spaces_s = &spaces[19 + dur_mag + invl_prec];
+ printf(full_fmt, (guint32)(interval/G_GUINT64_CONSTANT(1000000)),
+ (guint32)((interval%G_GUINT64_CONSTANT(1000000))/dv), spaces_s);
+ }
+ g_free(full_fmt);
+
+ spaces_s = &spaces[2];
+ printf("|%s|\n", spaces_s);
+
+ /* Display the list of filters and their column numbers vertically */
+ printf("| Col");
+ for (j=0; j<num_cols; j++) {
+ printf((j == 0 ? "%2u: " : "| %2u: "), j+1);
+ if (!iot->filters[j]) {
+ /*
+ * An empty (no filter) comma field was specified */
+ spaces_s = &spaces[16 + 10];
+ printf("Frames and bytes%s|\n", spaces_s);
+ } else {
+ filter = iot->filters[j];
+ len_filt = (unsigned int) strlen(filter);
+
+ /* If the width of the widest filter exceeds the width of the stat table, borderlen has
+ * been set to 102 bytes above and filters wider than 102 will wrap at 91 bytes. */
+ if (len_filt+11 <= borderlen) {
+ printf("%s", filter);
+ if (len_filt+11 <= borderlen) {
+ spaces_s = &spaces[len_filt + 10];
+ printf("%s", spaces_s);
+ }
+ printf("|\n");
+ } else {
+ gchar *sfilter1, *sfilter2;
+ const gchar *pos;
+ gsize len;
+ unsigned int next_start, max_w = borderlen-11;
+
+ do {
+ if (len_filt > max_w) {
+ sfilter1 = g_strndup(filter, (gsize) max_w);
+ /*
+ * Find the pos of the last space in sfilter1. If a space is found, set
+ * sfilter2 to the string prior to that space and print it; otherwise, wrap
+ * the filter at max_w. */
+ pos = g_strrstr(sfilter1, " ");
+ if (pos) {
+ len = (gsize)(pos-sfilter1);
+ next_start = (unsigned int) len+1;
+ } else {
+ len = (gsize) strlen(sfilter1);
+ next_start = (unsigned int)len;
+ }
+ sfilter2 = g_strndup(sfilter1, len);
+ printf("%s%s|\n", sfilter2, &spaces[len+10]);
+ g_free(sfilter1);
+ g_free(sfilter2);
+
+ printf("| ");
+ filter = &filter[next_start];
+ len_filt = (unsigned int) strlen(filter);
+ } else {
+ printf("%s%s|\n", filter, &spaces[strlen(filter)+10]);
+ break;
+ }
+ } while (1);
+ }
+ }
+ }
+
+ printf("|-");
+ for (i=0; i<borderlen-3; i++) {
+ printf("-");
+ }
+ printf("|\n");
+
+ /* Display spaces above "Interval (s)" label */
+ spaces_s = &spaces[borderlen-(invl_col_w-2)];
+ printf("|%s|", spaces_s);
+
+ /* Display column number headers */
+ for (j=0; j<num_cols; j++) {
+ item = stat_cols[j];
+ if (item->calc_type == CALC_TYPE_FRAMES_AND_BYTES)
+ spaces_s = &spaces[borderlen - (col_w[j].fr + col_w[j].val)] - 3;
+ else if (item->calc_type == CALC_TYPE_FRAMES)
+ spaces_s = &spaces[borderlen - col_w[j].fr];
+ else
+ spaces_s = &spaces[borderlen - col_w[j].val];
+
+ printf("%-2d%s|", j+1, spaces_s);
+ }
+ if (tabrow_w < borderlen) {
+ filler_s = &spaces[tabrow_w+1];
+ printf("%s|", filler_s);
+ }
+
+ k = 11;
+ switch (timestamp_get_type()) {
+ case TS_ABSOLUTE:
+ printf("\n| Time ");
+ break;
+ case TS_ABSOLUTE_WITH_YMD:
+ case TS_ABSOLUTE_WITH_YDOY:
+ case TS_UTC_WITH_YMD:
+ case TS_UTC_WITH_YDOY:
+ printf("\n| Date and time");
+ k = 16;
+ break;
+ case TS_RELATIVE:
+ case TS_NOT_SET:
+ printf("\n| Interval");
+ break;
+ default:
+ break;
+ }
+
+ spaces_s = &spaces[borderlen-(invl_col_w-k)];
+ printf("%s|", spaces_s);
+
+ /* Display the stat label in each column */
+ for (j=0; j<num_cols; j++) {
+ type = stat_cols[j]->calc_type;
+ if (type == CALC_TYPE_FRAMES) {
+ printcenter (calc_type_table[type].func_name, col_w[j].fr, numpad);
+ } else if (type == CALC_TYPE_FRAMES_AND_BYTES) {
+ printcenter ("Frames", col_w[j].fr, numpad);
+ printcenter ("Bytes", col_w[j].val, numpad);
+ } else {
+ printcenter (calc_type_table[type].func_name, col_w[j].val, numpad);
+ }
+ }
+ if (filler_s)
+ printf("%s|", filler_s);
+ printf("\n|-");
+
+ for (i=0; i<tabrow_w-3; i++)
+ printf("-");
+ printf("|");
+
+ if (tabrow_w < borderlen)
+ printf("%s|", &spaces[tabrow_w+1]);
+
+ printf("\n");
+ t = 0;
+ if (invl_prec == 0 && dur_mag == 1)
+ full_fmt = g_strconcat("| ", invl_fmt, " <> ", invl_fmt, " |", NULL);
+ else
+ full_fmt = g_strconcat("| ", invl_fmt, " <> ", invl_fmt, " |", NULL);
+
+ if (interval == 0 || duration == 0) {
+ num_rows = 0;
+ } else {
+ num_rows = (unsigned int)(duration/interval) + ((unsigned int)(duration%interval) > 0 ? 1 : 0);
+ }
+
+ /* Load item_in_column with the first item in each column */
+ item_in_column = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols);
+ for (j=0; j<num_cols; j++) {
+ item_in_column[j] = stat_cols[j];
+ }
+
+ /* Display the table values
+ *
+ * The outer loop is for time interval rows and the inner loop is for stat column items.*/
+ for (i=0; i<num_rows; i++) {
+
+ if (i == num_rows-1)
+ last_row = TRUE;
+
+ /* Compute the interval for this row */
+ if (!last_row) {
+ invl_end = t + interval;
+ } else {
+ invl_end = duration;
+ }
+
+ /* Patch for Absolute Time */
+ /* XXX - has a Y2.038K problem with 32-bit time_t */
+ the_time = (time_t)(iot->start_time + (t/G_GUINT64_CONSTANT(1000000)));
+
+ /* Display the interval for this row */
+ switch (timestamp_get_type()) {
+ case TS_ABSOLUTE:
+ tm_time = localtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %02d:%02d:%02d |",
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XX:XX:XX |");
+ break;
+
+ case TS_ABSOLUTE_WITH_YMD:
+ tm_time = localtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %04d-%02d-%02d %02d:%02d:%02d |",
+ tm_time->tm_year + 1900,
+ tm_time->tm_mon + 1,
+ tm_time->tm_mday,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XXXX-XX-XX XX:XX:XX |");
+ break;
+
+ case TS_ABSOLUTE_WITH_YDOY:
+ tm_time = localtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %04d/%03d %02d:%02d:%02d |",
+ tm_time->tm_year + 1900,
+ tm_time->tm_yday + 1,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XXXX/XXX XX:XX:XX |");
+ break;
+
+ case TS_UTC:
+ tm_time = gmtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %02d:%02d:%02d |",
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XX:XX:XX |");
+ break;
+
+ case TS_UTC_WITH_YMD:
+ tm_time = gmtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %04d-%02d-%02d %02d:%02d:%02d |",
+ tm_time->tm_year + 1900,
+ tm_time->tm_mon + 1,
+ tm_time->tm_mday,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XXXX-XX-XX XX:XX:XX |");
+ break;
+
+ case TS_UTC_WITH_YDOY:
+ tm_time = gmtime(&the_time);
+ if (tm_time != NULL) {
+ printf("| %04d/%03d %02d:%02d:%02d |",
+ tm_time->tm_year + 1900,
+ tm_time->tm_yday + 1,
+ tm_time->tm_hour,
+ tm_time->tm_min,
+ tm_time->tm_sec);
+ } else
+ printf("| XXXX/XXX XX:XX:XX |");
+ break;
+
+ case TS_RELATIVE:
+ case TS_NOT_SET:
+ if (invl_prec == 0) {
+ if (last_row) {
+ int maxw;
+ maxw = dur_mag >= 3 ? dur_mag+1 : 3;
+ g_free(full_fmt);
+ snprintf(dur_mag_s, 3, "%u", maxw);
+ full_fmt = g_strconcat( dur_mag == 1 ? "| " : "| ",
+ invl_fmt, " <> ", "%-",
+ dur_mag_s, "s|", NULL);
+ printf(full_fmt, (guint32)(t/G_GUINT64_CONSTANT(1000000)), "Dur");
+ } else {
+ printf(full_fmt, (guint32)(t/G_GUINT64_CONSTANT(1000000)),
+ (guint32)(invl_end/G_GUINT64_CONSTANT(1000000)));
+ }
+ } else {
+ printf(full_fmt, (guint32)(t/G_GUINT64_CONSTANT(1000000)),
+ (guint32)(t%G_GUINT64_CONSTANT(1000000) / dv),
+ (guint32)(invl_end/G_GUINT64_CONSTANT(1000000)),
+ (guint32)(invl_end%G_GUINT64_CONSTANT(1000000) / dv));
+ }
+ break;
+ /* case TS_DELTA:
+ case TS_DELTA_DIS:
+ case TS_EPOCH:
+ are not implemented */
+ default:
+ break;
+ }
+
+ /* Display stat values in each column for this row */
+ for (j=0; j<num_cols; j++) {
+ fmt = fmts[j];
+ item = item_in_column[j];
+
+ if (item) {
+ switch (item->calc_type) {
+ case CALC_TYPE_FRAMES:
+ printf(fmt, item->frames);
+ break;
+ case CALC_TYPE_BYTES:
+ case CALC_TYPE_COUNT:
+ printf(fmt, item->counter);
+ break;
+ case CALC_TYPE_FRAMES_AND_BYTES:
+ printf(fmt, item->frames, item->counter);
+ break;
+
+ case CALC_TYPE_SUM:
+ case CALC_TYPE_MIN:
+ case CALC_TYPE_MAX:
+ ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
+ switch (ftype) {
+ case FT_FLOAT:
+ printf(fmt, item->float_counter);
+ break;
+ case FT_DOUBLE:
+ printf(fmt, item->double_counter);
+ break;
+ case FT_RELATIVE_TIME:
+ item->counter = (item->counter + G_GUINT64_CONSTANT(500)) / G_GUINT64_CONSTANT(1000);
+ printf(fmt,
+ (int)(item->counter/G_GUINT64_CONSTANT(1000000)),
+ (int)(item->counter%G_GUINT64_CONSTANT(1000000)));
+ break;
+ default:
+ printf(fmt, item->counter);
+ break;
+ }
+ break;
+
+ case CALC_TYPE_AVG:
+ num = item->num;
+ if (num == 0)
+ num = 1;
+ ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
+ switch (ftype) {
+ case FT_FLOAT:
+ printf(fmt, item->float_counter/num);
+ break;
+ case FT_DOUBLE:
+ printf(fmt, item->double_counter/num);
+ break;
+ case FT_RELATIVE_TIME:
+ item->counter = ((item->counter / (guint64)num) + G_GUINT64_CONSTANT(500)) / G_GUINT64_CONSTANT(1000);
+ printf(fmt,
+ (int)(item->counter/G_GUINT64_CONSTANT(1000000)),
+ (int)(item->counter%G_GUINT64_CONSTANT(1000000)));
+ break;
+ default:
+ printf(fmt, item->counter / (guint64)num);
+ break;
+ }
+ break;
+
+ case CALC_TYPE_LOAD:
+ ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
+ switch (ftype) {
+ case FT_RELATIVE_TIME:
+ if (!last_row) {
+ printf(fmt,
+ (int) (item->counter/interval),
+ (int)((item->counter%interval)*G_GUINT64_CONSTANT(1000000) / interval));
+ } else {
+ printf(fmt,
+ (int) (item->counter/(invl_end-t)),
+ (int)((item->counter%(invl_end-t))*G_GUINT64_CONSTANT(1000000) / (invl_end-t)));
+ }
+ break;
+ }
+ break;
+ }
+
+ if (last_row) {
+ g_free(fmt);
+ } else {
+ item_in_column[j] = item_in_column[j]->next;
+ }
+ } else {
+ printf(fmt, (guint64)0, (guint64)0);
+ }
+ }
+ if (filler_s)
+ printf("%s|", filler_s);
+ printf("\n");
+ t += interval;
+
+ }
+ for (i=0; i<borderlen; i++) {
+ printf("=");
+ }
+ printf("\n");
+ g_free(iot->items);
+ g_free(iot->max_vals);
+ g_free(iot->max_frame);
+ g_free(iot);
+ g_free(col_w);
+ g_free(invl_fmt);
+ g_free(full_fmt);
+ g_free(fmts);
+ g_free(spaces);
+ g_free(stat_cols);
+ g_free(item_in_column);
+}
+
+
+static void
+register_io_tap(io_stat_t *io, unsigned int i, const char *filter)
+{
+ GString *error_string;
+ const char *flt;
+ int j;
+ size_t namelen;
+ const char *p, *parenp;
+ char *field;
+ header_field_info *hfi;
+
+ io->items[i].prev = &io->items[i];
+ io->items[i].next = NULL;
+ io->items[i].parent = io;
+ io->items[i].start_time = 0;
+ io->items[i].calc_type = CALC_TYPE_FRAMES_AND_BYTES;
+ io->items[i].frames = 0;
+ io->items[i].counter = 0;
+ io->items[i].num = 0;
+
+ io->filters[i] = filter;
+ flt = filter;
+
+ field = NULL;
+ hfi = NULL;
+ for (j=0; calc_type_table[j].func_name; j++) {
+ namelen = strlen(calc_type_table[j].func_name);
+ if (filter && strncmp(filter, calc_type_table[j].func_name, namelen) == 0) {
+ io->items[i].calc_type = calc_type_table[j].calc_type;
+ io->items[i].colnum = i;
+ if (*(filter+namelen) == '(') {
+ p = filter+namelen+1;
+ parenp = strchr(p, ')');
+ if (!parenp) {
+ fprintf(stderr,
+ "\ntshark: Closing parenthesis missing from calculated expression.\n");
+ exit(10);
+ }
+
+ if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES) {
+ if (parenp != p) {
+ fprintf(stderr,
+ "\ntshark: %s does not require or allow a field name within the parens.\n",
+ calc_type_table[j].func_name);
+ exit(10);
+ }
+ } else {
+ if (parenp == p) {
+ /* bail out if a field name was not specified */
+ fprintf(stderr, "\ntshark: You didn't specify a field name for %s(*).\n",
+ calc_type_table[j].func_name);
+ exit(10);
+ }
+ }
+
+ field = (char *)g_malloc(parenp-p+1);
+ memcpy(field, p, parenp-p);
+ field[parenp-p] = '\0';
+ flt = parenp + 1;
+ if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES)
+ break;
+ hfi = proto_registrar_get_byname(field);
+ if (!hfi) {
+ fprintf(stderr, "\ntshark: There is no field named '%s'.\n",
+ field);
+ g_free(field);
+ exit(10);
+ }
+
+ io->items[i].hf_index = hfi->id;
+ break;
+ }
+ } else {
+ if (io->items[i].calc_type == CALC_TYPE_FRAMES || io->items[i].calc_type == CALC_TYPE_BYTES)
+ flt = "";
+ io->items[i].colnum = i;
+ }
+ }
+ if (hfi && !(io->items[i].calc_type == CALC_TYPE_BYTES ||
+ io->items[i].calc_type == CALC_TYPE_FRAMES ||
+ io->items[i].calc_type == CALC_TYPE_FRAMES_AND_BYTES)) {
+ /* check that the type is compatible */
+ switch (hfi->type) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_UINT64:
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ case FT_INT64:
+ /* these types support all calculations */
+ break;
+ case FT_FLOAT:
+ case FT_DOUBLE:
+ /* these types only support SUM, COUNT, MAX, MIN, AVG */
+ switch (io->items[i].calc_type) {
+ case CALC_TYPE_SUM:
+ case CALC_TYPE_COUNT:
+ case CALC_TYPE_MAX:
+ case CALC_TYPE_MIN:
+ case CALC_TYPE_AVG:
+ break;
+ default:
+ fprintf(stderr,
+ "\ntshark: %s is a float field, so %s(*) calculations are not supported on it.",
+ field,
+ calc_type_table[j].func_name);
+ exit(10);
+ }
+ break;
+ case FT_RELATIVE_TIME:
+ /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */
+ switch (io->items[i].calc_type) {
+ case CALC_TYPE_SUM:
+ case CALC_TYPE_COUNT:
+ case CALC_TYPE_MAX:
+ case CALC_TYPE_MIN:
+ case CALC_TYPE_AVG:
+ case CALC_TYPE_LOAD:
+ break;
+ default:
+ fprintf(stderr,
+ "\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
+ field,
+ calc_type_table[j].func_name);
+ exit(10);
+ }
+ break;
+ default:
+ /*
+ * XXX - support all operations on floating-point
+ * numbers?
+ */
+ if (io->items[i].calc_type != CALC_TYPE_COUNT) {
+ fprintf(stderr,
+ "\ntshark: %s doesn't have integral values, so %s(*) "
+ "calculations are not supported on it.\n",
+ field,
+ calc_type_table[j].func_name);
+ exit(10);
+ }
+ break;
+ }
+ }
+ g_free(field);
+
+ error_string = register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE, NULL,
+ iostat_packet, i ? NULL : iostat_draw, NULL);
+ if (error_string) {
+ g_free(io->items);
+ g_free(io);
+ fprintf(stderr, "\ntshark: Couldn't register io,stat tap: %s\n",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+}
+
+static void
+iostat_init(const char *opt_arg, void *userdata _U_)
+{
+ gdouble interval_float;
+ guint32 idx = 0;
+ unsigned int i;
+ io_stat_t *io;
+ const gchar *filters, *str, *pos;
+
+ if ((*(opt_arg+(strlen(opt_arg)-1)) == ',') ||
+ (sscanf(opt_arg, "io,stat,%lf%n", &interval_float, (int *)&idx) != 1) ||
+ (idx < 8)) {
+ fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
+ exit(1);
+ }
+
+ filters = opt_arg+idx;
+ if (*filters) {
+ if (*filters != ',') {
+ /* For locale's that use ',' instead of '.', the comma might
+ * have been consumed during the floating point conversion. */
+ --filters;
+ if (*filters != ',') {
+ fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
+ exit(1);
+ }
+ }
+ } else
+ filters = NULL;
+
+ switch (timestamp_get_type()) {
+ case TS_DELTA:
+ case TS_DELTA_DIS:
+ case TS_EPOCH:
+ fprintf(stderr, "\ntshark: invalid -t operand. io,stat only supports -t <r|a|ad|adoy|u|ud|udoy>\n");
+ exit(1);
+ default:
+ break;
+ }
+
+ io = g_new(io_stat_t, 1);
+
+ /* If interval is 0, calculate statistics over the whole file by setting the interval to
+ * G_MAXUINT64 */
+ if (interval_float == 0) {
+ io->interval = G_MAXUINT64;
+ io->invl_prec = 0;
+ } else {
+ /* Set interval to the number of us rounded to the nearest integer */
+ io->interval = (guint64)(interval_float * 1000000.0 + 0.5);
+ /*
+ * Determine what interval precision the user has specified */
+ io->invl_prec = 6;
+ for (i=10; i<10000000; i*=10) {
+ if (io->interval%i > 0)
+ break;
+ io->invl_prec--;
+ }
+ if (io->invl_prec == 0) {
+ /* The precision is zero but if the user specified one of more zeros after the decimal point,
+ they want that many decimal places shown in the table for all time intervals except
+ response time values such as smb.time which always have 6 decimal places of precision.
+ This feature is useful in cases where for example the duration is 9.1, you specify an
+ interval of 1 and the last interval becomes "9 <> 9". If the interval is instead set to
+ 1.1, the last interval becomes
+ last interval is rounded up to value that is greater than the duration. */
+ const gchar *invl_start = opt_arg+8;
+ gchar *intv_end;
+ int invl_len;
+
+ intv_end = g_strstr_len(invl_start, -1, ",");
+ invl_len = (int)(intv_end - invl_start);
+ invl_start = g_strstr_len(invl_start, invl_len, ".");
+
+ if (invl_start != NULL) {
+ invl_len = (int)(intv_end - invl_start - 1);
+ if (invl_len)
+ io->invl_prec = MIN(invl_len, 6);
+ }
+ }
+ }
+ if (io->interval < 1) {
+ fprintf(stderr,
+ "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
+ exit(10);
+ }
+
+ /* Find how many ',' separated filters we have */
+ io->num_cols = 1;
+ io->start_time = 0;
+
+ if (filters && (*filters != '\0')) {
+ /* Eliminate the first comma. */
+ filters++;
+ str = filters;
+ while ((str = strchr(str, ','))) {
+ io->num_cols++;
+ str++;
+ }
+ }
+
+ io->items = g_new(io_stat_item_t, io->num_cols);
+ io->filters = (const char **)g_malloc(sizeof(char *) * io->num_cols);
+ io->max_vals = g_new(guint64, io->num_cols);
+ io->max_frame = g_new(guint32, io->num_cols);
+
+ for (i=0; i<io->num_cols; i++) {
+ io->max_vals[i] = 0;
+ io->max_frame[i] = 0;
+ }
+
+ /* Register a tap listener for each filter */
+ if ((!filters) || (filters[0] == 0)) {
+ register_io_tap(io, 0, NULL);
+ } else {
+ gchar *filter;
+ i = 0;
+ str = filters;
+ do {
+ pos = (gchar*) strchr(str, ',');
+ if (pos == str) {
+ register_io_tap(io, i, NULL);
+ } else if (pos == NULL) {
+ str = (const char*) g_strstrip((gchar*)str);
+ filter = g_strdup(str);
+ if (*filter)
+ register_io_tap(io, i, filter);
+ else
+ register_io_tap(io, i, NULL);
+ } else {
+ filter = (gchar *)g_malloc((pos-str)+1);
+ (void) g_strlcpy( filter, str, (gsize) ((pos-str)+1));
+ filter = g_strstrip(filter);
+ register_io_tap(io, i, (char *) filter);
+ }
+ str = pos+1;
+ i++;
+ } while (pos);
+ }
+}
+
+static stat_tap_ui iostat_ui = {
+ REGISTER_STAT_GROUP_GENERIC,
+ NULL,
+ "io,stat",
+ iostat_init,
+ 0,
+ NULL
+};
+
+void
+register_tap_listener_iostat(void)
+{
+ register_stat_tap_ui(&iostat_ui, NULL);
+}