2 * iostat 2002 Ronnie Sahlberg
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include <epan/epan_dissect.h>
31 #include <epan/packet_info.h>
33 #include <epan/timestamp.h>
34 #include <epan/stat_cmd_args.h>
35 #include <epan/strutil.h>
38 #define CALC_TYPE_FRAMES 0
39 #define CALC_TYPE_BYTES 1
40 #define CALC_TYPE_FRAMES_AND_BYTES 2
41 #define CALC_TYPE_COUNT 3
42 #define CALC_TYPE_SUM 4
43 #define CALC_TYPE_MIN 5
44 #define CALC_TYPE_MAX 6
45 #define CALC_TYPE_AVG 7
46 #define CALC_TYPE_LOAD 8
49 const char *func_name;
53 static calc_type_ent_t calc_type_table[] = {
54 { "FRAMES", CALC_TYPE_FRAMES },
55 { "BYTES", CALC_TYPE_BYTES },
56 { "FRAMES BYTES", CALC_TYPE_FRAMES_AND_BYTES },
57 { "COUNT", CALC_TYPE_COUNT },
58 { "SUM", CALC_TYPE_SUM },
59 { "MIN", CALC_TYPE_MIN },
60 { "MAX", CALC_TYPE_MAX },
61 { "AVG", CALC_TYPE_AVG },
62 { "LOAD", CALC_TYPE_LOAD },
66 typedef struct _io_stat_t {
67 guint64 interval; /* The user-specified time interval (us) */
68 guint invl_prec; /* Decimal precision of the time interval (1=10s, 2=100s etc) */
69 guint32 num_cols; /* The number of columns of statistics in the table */
70 struct _io_stat_item_t *items; /* Each item is a single cell in the table */
71 time_t start_time; /* Time of first frame matching the filter */
72 const char **filters; /* 'io,stat' cmd strings (e.g., "AVG(smb.time)smb.time") */
73 guint64 *max_vals; /* The max value sans the decimal or nsecs portion in each stat column */
74 guint32 *max_frame; /* The max frame number displayed in each stat column */
77 typedef struct _io_stat_item_t {
79 struct _io_stat_item_t *next;
80 struct _io_stat_item_t *prev;
81 guint64 time; /* Time since start of capture (us)*/
82 int calc_type; /* The statistic type */
83 int colnum; /* Column number of this stat (0 to n) */
86 guint32 num; /* The sample size of a given statistic (only needed for AVG) */
87 guint64 counter; /* The accumulated data for the calculation of that statistic */
89 gdouble double_counter;
92 #define NANOSECS_PER_SEC 1000000000
95 iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
100 guint64 relative_time, rt;
106 mit = (io_stat_item_t *) arg;
107 parent = mit->parent;
108 relative_time = (guint64)((pinfo->fd->rel_ts.secs*1000000) + ((pinfo->fd->rel_ts.nsecs+500)/1000));
109 if (mit->parent->start_time == 0) {
110 mit->parent->start_time = pinfo->fd->abs_ts.secs - pinfo->fd->rel_ts.secs;
113 /* The prev item before the main one is always the last interval we saw packets for */
116 /* XXX for the time being, just ignore all frames that are in the past.
117 should be fixed in the future but hopefully it is uncommon */
118 if(relative_time < it->time){
122 /* If we have moved into a new interval (row), create a new io_stat_item_t struct for every interval
123 * between the last struct and this one. If an item was not found in a previous interval, an empty
124 * struct will be created for it. */
126 while (rt >= it->time + parent->interval) {
127 it->next = (io_stat_item_t *)g_malloc(sizeof(io_stat_item_t));
129 it->next->next = NULL;
133 it->time = it->prev->time + parent->interval;
136 it->float_counter = 0;
137 it->double_counter = 0;
139 it->calc_type = it->prev->calc_type;
140 it->hf_index = it->prev->hf_index;
141 it->colnum = it->prev->colnum;
144 /* Store info in the current structure */
147 switch(it->calc_type) {
148 case CALC_TYPE_FRAMES:
149 case CALC_TYPE_BYTES:
150 case CALC_TYPE_FRAMES_AND_BYTES:
151 it->counter += pinfo->fd->pkt_len;
153 case CALC_TYPE_COUNT:
154 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
156 it->counter += gp->len;
160 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
164 for(i=0;i<gp->len;i++){
165 switch(proto_registrar_get_ftype(it->hf_index)){
170 it->counter += fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
173 it->counter += fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
179 it->counter += fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
182 it->counter += (gint64)fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
186 (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
189 it->double_counter += fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
191 case FT_RELATIVE_TIME:
192 new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
193 val = (guint64)((new_time->secs * NANOSECS_PER_SEC) + new_time->nsecs);
198 * "Can't happen"; see the checks
199 * in register_io_tap().
201 g_assert_not_reached();
208 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
214 ftype=proto_registrar_get_ftype(it->hf_index);
215 for(i=0;i<gp->len;i++){
221 val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
222 if ((it->frames==1 && i==0) || (val < it->counter)) {
227 val = fvalue_get_integer64(&((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) || ((gint32)val < (gint32)it->counter)) {
242 val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
243 if((it->frames==1 && i==0) || ((gint64)val < (gint64)it->counter)) {
248 float_val=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
249 if((it->frames==1 && i==0) || (float_val < it->float_counter)) {
250 it->float_counter=float_val;
254 double_val=fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
255 if((it->frames==1 && i==0) || (double_val < it->double_counter)) {
256 it->double_counter=double_val;
259 case FT_RELATIVE_TIME:
260 new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
261 val = (guint64)new_time->secs * NANOSECS_PER_SEC + new_time->nsecs;
262 if((it->frames==1 && i==0) || (val < it->counter)) {
268 * "Can't happen"; see the checks
269 * in register_io_tap().
271 g_assert_not_reached();
278 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
284 ftype=proto_registrar_get_ftype(it->hf_index);
285 for(i=0;i<gp->len;i++){
291 val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
292 if(val > it->counter)
296 val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
297 if(val > it->counter)
304 val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
305 if((gint32)val > (gint32)it->counter)
309 val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
310 if ((gint64)val > (gint64)it->counter)
314 float_val = (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
315 if(float_val > it->float_counter)
316 it->float_counter=float_val;
319 double_val = fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
320 if(double_val > it->double_counter)
321 it->double_counter=double_val;
323 case FT_RELATIVE_TIME:
324 new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
325 val = (guint64)((new_time->secs * NANOSECS_PER_SEC) + new_time->nsecs);
331 * "Can't happen"; see the checks
332 * in register_io_tap().
334 g_assert_not_reached();
341 gp=proto_get_finfo_ptr_array(edt->tree, it->hf_index);
345 ftype=proto_registrar_get_ftype(it->hf_index);
346 for(i=0;i<gp->len;i++){
353 val = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
358 val = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
365 val = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
369 it->float_counter += (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
372 it->double_counter += fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
374 case FT_RELATIVE_TIME:
375 new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
376 val = (guint64)((new_time->secs * NANOSECS_PER_SEC) + new_time->nsecs);
381 * "Can't happen"; see the checks
382 * in register_io_tap().
384 g_assert_not_reached();
391 gp = proto_get_finfo_ptr_array(edt->tree, it->hf_index);
393 ftype = proto_registrar_get_ftype(it->hf_index);
394 if (ftype != FT_RELATIVE_TIME) {
396 "\ntshark: LOAD() is only supported for relative-time fields such as smb.time\n");
399 for(i=0;i<gp->len;i++){
404 new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
405 val = (guint64)((new_time->secs*1000000) + (new_time->nsecs/1000));
406 tival = (int)(val % parent->interval);
407 it->counter += tival;
411 if (val < (guint64)parent->interval) {
415 pit->counter += parent->interval;
416 val -= parent->interval;
423 /* Store the highest value for this item in order to determine the width of each stat column.
424 * For real numbers we only need to know its magnitude (the value to the left of the decimal point
425 * so round it up before storing it as an integer in max_vals. For AVG of RELATIVE_TIME fields,
426 * calc the average, round it to the next second and store the seconds. For all other calc types
427 * of RELATIVE_TIME fields, store the counters without modification.
429 switch(it->calc_type) {
430 case CALC_TYPE_FRAMES:
431 case CALC_TYPE_FRAMES_AND_BYTES:
432 parent->max_frame[it->colnum] =
433 MAX(parent->max_frame[it->colnum], it->frames);
434 if (it->calc_type==CALC_TYPE_FRAMES_AND_BYTES)
435 parent->max_vals[it->colnum] =
436 MAX(parent->max_vals[it->colnum], it->counter);
438 case CALC_TYPE_BYTES:
439 case CALC_TYPE_COUNT:
441 parent->max_vals[it->colnum] = MAX(parent->max_vals[it->colnum], it->counter);
446 ftype=proto_registrar_get_ftype(it->hf_index);
449 parent->max_vals[it->colnum] =
450 MAX(parent->max_vals[it->colnum], (guint64)(it->float_counter+0.5));
453 parent->max_vals[it->colnum] =
454 MAX(parent->max_vals[it->colnum],(guint64)(it->double_counter+0.5));
456 case FT_RELATIVE_TIME:
457 parent->max_vals[it->colnum] =
458 MAX(parent->max_vals[it->colnum], it->counter);
461 /* UINT16-64 and INT8-64 */
462 parent->max_vals[it->colnum] =
463 MAX(parent->max_vals[it->colnum], it->counter);
468 if (it->num==0) /* avoid division by zero */
470 ftype=proto_registrar_get_ftype(it->hf_index);
473 parent->max_vals[it->colnum] =
474 MAX(parent->max_vals[it->colnum], (guint64)it->float_counter/it->num);
477 parent->max_vals[it->colnum] =
478 MAX(parent->max_vals[it->colnum],(guint64)it->double_counter/it->num);
480 case FT_RELATIVE_TIME:
481 parent->max_vals[it->colnum] =
482 MAX(parent->max_vals[it->colnum], ((it->counter/it->num) + 500000000) / NANOSECS_PER_SEC);
485 /* UINT16-64 and INT8-64 */
486 parent->max_vals[it->colnum] =
487 MAX(parent->max_vals[it->colnum], it->counter/it->num);
495 magnitude (guint64 val, int max_w)
499 for (i=0; i<max_w; i++) {
508 * Print the calc_type_table[] function label centered in the column header.
511 printcenter (const char *label, int lenval, int numpad)
513 int lenlab = (int) strlen(label), len;
514 const char spaces[]=" ", *spaces_ptr;
516 len = (int) (strlen(spaces)) - (((lenval-lenlab) / 2) + numpad);
517 if (len > 0 && len < 6) {
518 spaces_ptr = &spaces[len];
519 if ((lenval-lenlab)%2==0) {
520 printf("%s%s%s|", spaces_ptr, label, spaces_ptr);
522 printf("%s%s%s|", spaces_ptr-1, label, spaces_ptr);
524 } else if (len > 0 && len <= 15) {
525 printf("%s|", label);
530 int fr; /* Width of this FRAMES column sans padding and border chars */
531 int val; /* Width of this non-FRAMES column sans padding and border chars */
535 iostat_draw(void *arg)
538 guint64 interval, duration, t, invl_end;
539 int i, j, k, num_cols, num_rows, dv, dur_secs, dur_mag, invl_mag, invl_prec, tabrow_w,
540 borderlen, invl_col_w, numpad=1, namelen, len_filt, type, maxfltr_w, ftype;
541 int fr_mag; /* The magnitude of the max frame number in this column */
542 int val_mag; /* The magnitude of the max value in this column */
543 char *spaces, *spaces_s, *filler_s=NULL, **fmts, *fmt=NULL;
545 static gchar dur_mag_s[3], invl_mag_s[3], invl_prec_s[3], fr_mag_s[3], val_mag_s[3], *invl_fmt, *full_fmt;
546 io_stat_item_t *mit, **stat_cols, *item, **item_in_column;
547 gboolean last_row=FALSE;
553 mit = (io_stat_item_t *)arg;
555 num_cols = iot->num_cols;
556 col_w = (column_width *)g_malloc(sizeof(column_width) * num_cols);
557 fmts = (char **)g_malloc(sizeof(char *) * num_cols);
558 duration = (guint64)((cfile.elapsed_time.secs*1000000) + ((cfile.elapsed_time.nsecs+500)/1000));
560 /* Store the pointer to each stat column */
561 stat_cols = (io_stat_item_t **) g_malloc(sizeof(io_stat_item_t *) * num_cols);
562 for (j=0; j<num_cols; j++)
563 stat_cols[j] = &iot->items[j];
565 /* The following prevents gross inaccuracies when the user specifies an interval that is greater
566 * than the capture duration. */
567 if (iot->interval > duration || iot->interval==G_MAXINT32) {
569 iot->interval = G_MAXINT32;
571 interval = iot->interval;
574 /* Calc the capture duration's magnitude (dur_mag) */
575 dur_secs = (int)duration/1000000;
576 dur_mag = magnitude((guint64)dur_secs, 5);
577 g_snprintf(dur_mag_s, 3, "%u", dur_mag);
579 /* Calc the interval's magnitude */
580 invl_mag = magnitude((guint64)interval/1000000, 5);
582 /* Set or get the interval precision */
583 if (interval==duration) {
585 * An interval arg of 0 or an interval size exceeding the capture duration was specified.
586 * Set the decimal precision of duration based on its magnitude. */
594 borderlen = 30 + dur_mag + (invl_prec==0 ? 0 : invl_prec+1);
596 invl_prec = iot->invl_prec;
597 borderlen = 24 + invl_mag + (invl_prec==0 ? 0 : invl_prec+1);
600 /* Round the duration according to invl_prec */
602 for (i=0; i<invl_prec; i++)
604 duration = duration + (5*(dv/10));
605 if (iot->interval==G_MAXINT32)
608 /* Recalc the dur_mag in case rounding has increased its magnitude */
609 dur_secs = (int)duration/1000000;
610 dur_mag = magnitude((guint64)dur_secs, 5);
612 /* Calc the width of the time interval column (incl borders and padding). */
614 invl_col_w = (2*dur_mag) + 8;
616 invl_col_w = (2*dur_mag) + (2*invl_prec) + 10;
618 /* Update the width of the time interval column for "-t ad" */
619 if (timestamp_get_type()==TS_ABSOLUTE_WITH_DATE)
620 invl_col_w = MAX(invl_col_w, 23);
622 invl_col_w = MAX(invl_col_w, 12);
624 borderlen = MAX(borderlen, invl_col_w);
626 /* Calc the total width of each row in the stats table and build the printf format string for each
627 * column based on its field type, width, and name length.
628 * NOTE: The magnitude of all types including float and double are stored in iot->max_vals which
629 * is an *integer*. */
630 tabrow_w = invl_col_w;
631 for (j=0; j<num_cols; j++) {
632 type = iot->items[j].calc_type;
633 if (type==CALC_TYPE_FRAMES_AND_BYTES) {
636 namelen = (int) strlen(calc_type_table[type].func_name);
638 if(type==CALC_TYPE_FRAMES
639 || type==CALC_TYPE_FRAMES_AND_BYTES) {
641 fr_mag = magnitude(iot->max_frame[j], 15);
642 fr_mag = MAX(6, fr_mag);
643 col_w[j].fr = fr_mag;
644 tabrow_w += col_w[j].fr + 3;
645 g_snprintf(fr_mag_s, 3, "%u", fr_mag);
647 if (type==CALC_TYPE_FRAMES) {
648 fmt = g_strconcat(" %", fr_mag_s, "u |", NULL);
650 /* CALC_TYPE_FRAMES_AND_BYTES
652 val_mag = magnitude(iot->max_vals[j], 15);
653 val_mag = MAX(5, val_mag);
654 col_w[j].val = val_mag;
655 tabrow_w += (col_w[j].val + 3);
656 g_snprintf(val_mag_s, 3, "%u", val_mag);
657 fmt = g_strconcat(" %", fr_mag_s, "u |", " %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
664 case CALC_TYPE_BYTES:
665 case CALC_TYPE_COUNT:
667 val_mag = magnitude(iot->max_vals[j], 15);
668 val_mag = MAX(5, val_mag);
669 col_w[j].val = val_mag;
670 g_snprintf(val_mag_s, 3, "%u", val_mag);
671 fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
675 ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
679 val_mag = magnitude(iot->max_vals[j], 15);
680 g_snprintf(val_mag_s, 3, "%u", val_mag);
681 fmt = g_strconcat(" %", val_mag_s, ".6f |", NULL);
682 col_w[j].val = val_mag + 7;
684 case FT_RELATIVE_TIME:
685 /* Convert FT_RELATIVE_TIME field to seconds
686 * CALC_TYPE_LOAD was already converted in iostat_packet() ) */
687 if (type==CALC_TYPE_LOAD) {
688 iot->max_vals[j] /= interval;
689 } else if (type != CALC_TYPE_AVG) {
690 iot->max_vals[j] = (iot->max_vals[j] + 500000000) / NANOSECS_PER_SEC;
692 val_mag = magnitude(iot->max_vals[j], 15);
693 g_snprintf(val_mag_s, 3, "%u", val_mag);
694 fmt = g_strconcat(" %", val_mag_s, "u.%06u |", NULL);
695 col_w[j].val = val_mag + 7;
699 val_mag = magnitude(iot->max_vals[j], 15);
700 val_mag = MAX(namelen, val_mag);
701 col_w[j].val = val_mag;
702 g_snprintf(val_mag_s, 3, "%u", val_mag);
710 fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "u |", NULL);
717 fmt = g_strconcat(" %", val_mag_s, G_GINT64_MODIFIER, "d |", NULL);
720 } /* End of ftype switch */
721 } /* End of calc_type switch */
722 tabrow_w += col_w[j].val + 3;
725 } /* End of for loop (columns) */
727 borderlen = MAX(borderlen, tabrow_w);
729 /* Calc the max width of the list of filters. */
731 for(j=0; j<num_cols; j++) {
732 if (iot->filters[j]) {
733 k = (int) (strlen(iot->filters[j]) + 11);
734 maxfltr_w = MAX(maxfltr_w, k);
736 maxfltr_w = MAX(maxfltr_w, 26);
739 /* The stat table is not wrapped (by tshark) but filter is wrapped at the width of the stats table
740 * (which currently = borderlen); however, if the filter width exceeds the table width and the
741 * table width is less than 102 bytes, set borderlen to the lesser of the max filter width and 102.
742 * The filters will wrap at the lesser of borderlen-2 and the last space in the filter.
743 * NOTE: 102 is the typical size of a user window when the font is fixed width (e.g., COURIER 10).
744 * XXX: A pref could be added to change the max width from the default size of 102. */
745 if (maxfltr_w > borderlen && borderlen < 102)
746 borderlen = MIN(maxfltr_w, 102);
748 /* Prevent double right border by adding a space */
749 if (borderlen-tabrow_w==1)
752 /* Display the top border */
754 for (i=0; i<borderlen; i++)
757 spaces = (char*) g_malloc(borderlen+1);
758 for (i=0; i<borderlen; i++)
760 spaces[borderlen] = '\0';
762 spaces_s = &spaces[16];
763 printf("\n| IO Statistics%s|\n", spaces_s);
764 spaces_s = &spaces[2];
765 printf("|%s|\n", spaces_s);
767 g_snprintf(invl_mag_s, 3, "%u", invl_mag);
769 g_snprintf(invl_prec_s, 3, "%u", invl_prec);
770 invl_fmt = g_strconcat("%", invl_mag_s, "u.%0", invl_prec_s, "u", NULL);
771 if (interval==duration) {
772 full_fmt = g_strconcat("| Interval size: ", invl_fmt, " secs (dur)%s", NULL);
773 spaces_s = &spaces[30+invl_mag+invl_prec];
775 full_fmt = g_strconcat("| Interval size: ", invl_fmt, " secs%s", NULL);
776 spaces_s = &spaces[24+invl_mag+invl_prec];
778 printf(full_fmt, (guint32)interval/1000000,
779 (guint32)((interval%1000000)/dv), spaces_s);
781 invl_fmt = g_strconcat("%", invl_mag_s, "u", NULL);
782 full_fmt = g_strconcat("| Interval size: ", invl_fmt, " secs%s", NULL);
783 spaces_s = &spaces[23 + invl_mag];
784 printf(full_fmt, (guint32)interval/1000000, spaces_s);
790 invl_fmt = g_strconcat("%", dur_mag_s, "u.%0", invl_prec_s, "u", NULL);
792 invl_fmt = g_strconcat("%", dur_mag_s, "u", NULL);
794 /* Display the list of filters and their column numbers vertically */
796 for(j=0; j<num_cols; j++){
797 printf((j==0 ? "%2u: " : "| %2u: "), j+1);
798 if (!iot->filters[j] || (iot->filters[j]==0)) {
800 * An empty (no filter) comma field was specified */
801 spaces_s = &spaces[16 + 10];
802 printf("Frames and bytes%s|\n", spaces_s);
804 filter = iot->filters[j];
805 len_filt = (int) strlen(filter);
807 /* If the width of the widest filter exceeds the width of the stat table, borderlen has
808 * been set to 102 bytes above and filters wider than 102 will wrap at 91 bytes. */
809 if (len_filt+11 <= borderlen) {
810 printf("%s", filter);
811 if (len_filt+11 <= borderlen) {
812 spaces_s = &spaces[len_filt + 10];
813 printf("%s", spaces_s);
817 gchar *sfilter1, *sfilter2;
820 int next_start, max_w=borderlen-11;
823 if (len_filt > max_w) {
824 sfilter1 = g_strndup( (gchar *) filter, (gsize) max_w);
826 * Find the pos of the last space in sfilter1. If a space is found, set
827 * sfilter2 to the string prior to that space and print it; otherwise, wrap
828 * the filter at max_w. */
829 pos = g_strrstr(sfilter1, " ");
831 len = (gsize)(pos-sfilter1);
832 next_start = (int) len+1;
834 len = (gsize) strlen(sfilter1);
835 next_start = (int)len;
837 sfilter2 = g_strndup(sfilter1, len);
838 printf("%s%s|\n", sfilter2, &spaces[len+10]);
843 filter = &filter[next_start];
844 len_filt = (int) strlen(filter);
846 printf("%s%s|\n", filter, &spaces[((int)strlen(filter))+10]);
855 for(i=0;i<borderlen-3;i++){
860 /* Display spaces above "Interval (s)" label */
861 spaces_s = &spaces[borderlen-(invl_col_w-2)];
862 printf("|%s|", spaces_s);
864 /* Display column number headers */
865 for(j=0; j<num_cols; j++) {
867 if(item->calc_type==CALC_TYPE_FRAMES_AND_BYTES)
868 spaces_s = &spaces[borderlen - (col_w[j].fr + col_w[j].val)] - 3;
869 else if (item->calc_type==CALC_TYPE_FRAMES)
870 spaces_s = &spaces[borderlen - col_w[j].fr];
872 spaces_s = &spaces[borderlen - col_w[j].val];
874 printf("%-2u%s|", j+1, spaces_s);
876 if (tabrow_w < borderlen) {
877 filler_s = &spaces[tabrow_w+1];
878 printf("%s|", filler_s);
882 switch (timestamp_get_type()) {
886 case TS_ABSOLUTE_WITH_DATE:
887 printf("\n| Date and time");
892 printf("\n| Interval");
898 spaces_s = &spaces[borderlen-(invl_col_w-k)];
899 printf("%s|", spaces_s);
901 /* Display the stat label in each column */
902 for(j=0; j<num_cols; j++) {
903 type = stat_cols[j]->calc_type;
904 if(type==CALC_TYPE_FRAMES) {
905 printcenter (calc_type_table[type].func_name, col_w[j].fr, numpad);
906 } else if (type==CALC_TYPE_FRAMES_AND_BYTES) {
907 printcenter ("Frames", col_w[j].fr, numpad);
908 printcenter ("Bytes", col_w[j].val, numpad);
910 printcenter (calc_type_table[type].func_name, col_w[j].val, numpad);
914 printf("%s|", filler_s);
917 for(i=0; i<tabrow_w-3; i++)
921 if (tabrow_w < borderlen)
922 printf("%s|", &spaces[tabrow_w+1]);
926 full_fmt = g_strconcat("| ", invl_fmt, " <> ", invl_fmt, " |", NULL);
927 num_rows = (int)(duration/interval) + (((duration%interval+500000)/1000000) > 0 ? 1 : 0);
929 /* Load item_in_column with the first item in each column */
930 item_in_column = (io_stat_item_t **) g_malloc(sizeof(io_stat_item_t) * num_cols);
931 for (j=0; j<num_cols; j++) {
932 item_in_column[j] = stat_cols[j];
935 /* Display the table values
937 * The outer loop is for time interval rows and the inner loop is for stat column items.*/
938 for (i=0; i<num_rows; i++) {
943 /* Compute the interval for this row */
945 invl_end = t + interval;
950 /* Patch for Absolute Time */
951 the_time=iot->start_time+(guint32)(t/1000000);
952 tm_time = localtime(&the_time);
954 /* Display the interval for this row */
955 switch (timestamp_get_type()) {
957 printf("| %02d:%02d:%02d |",
963 case TS_ABSOLUTE_WITH_DATE:
964 printf("| %04d-%02d-%02d %02d:%02d:%02d |",
965 tm_time->tm_year + 1900,
977 printf(full_fmt, (guint32)(t/1000000),
978 (guint32)(invl_end/1000000));
980 printf(full_fmt, (guint32)(t/1000000),
981 (guint32)(t%1000000) / dv,
982 (guint32) (invl_end/1000000),
983 (guint32)((invl_end%1000000) / dv));
990 case TS_UTC_WITH_DATE:
991 are not implemented */
996 /* Display stat values in each column for this row */
997 for (j=0; j<num_cols; j++) {
999 item = item_in_column[j];
1002 switch(item->calc_type) {
1003 case CALC_TYPE_FRAMES:
1004 printf(fmt, item->frames);
1006 case CALC_TYPE_BYTES:
1007 case CALC_TYPE_COUNT:
1008 printf(fmt, item->counter);
1010 case CALC_TYPE_FRAMES_AND_BYTES:
1011 printf(fmt, item->frames, item->counter);
1017 ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1020 printf(fmt, item->float_counter);
1023 printf(fmt, item->double_counter);
1025 case FT_RELATIVE_TIME:
1026 item->counter = (item->counter + 500) / 1000;
1027 printf(fmt, (int)(item->counter/1000000), (int)(item->counter%1000000));
1030 printf(fmt, item->counter);
1039 ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1042 printf(fmt, item->float_counter/num);
1045 printf(fmt, item->double_counter/num);
1047 case FT_RELATIVE_TIME:
1048 item->counter = ((item->counter/num) + 500) / 1000;
1050 (int)(item->counter/1000000), (int)(item->counter%1000000));
1053 printf(fmt, item->counter/num);
1058 case CALC_TYPE_LOAD:
1059 ftype = proto_registrar_get_ftype(stat_cols[j]->hf_index);
1061 case FT_RELATIVE_TIME:
1064 (int) (item->counter/interval),
1065 (int)((item->counter%interval)*1000000 / interval));
1068 (int) (item->counter/(invl_end-t)),
1069 (int)((item->counter%(invl_end-t))*1000000 / (invl_end-t)));
1080 item_in_column[j] = item_in_column[j]->next;
1083 printf(fmt, (guint64)0);
1087 printf("%s|", filler_s);
1092 for(i=0;i<borderlen;i++){
1097 g_free(iot->max_vals);
1098 g_free(iot->max_frame);
1106 g_free(item_in_column);
1111 register_io_tap(io_stat_t *io, int i, const char *filter)
1113 GString *error_string;
1117 const char *p, *parenp;
1119 header_field_info *hfi;
1121 io->items[i].prev=&io->items[i];
1122 io->items[i].next=NULL;
1123 io->items[i].parent=io;
1124 io->items[i].time=0;
1125 io->items[i].calc_type=CALC_TYPE_FRAMES_AND_BYTES;
1126 io->items[i].frames=0;
1127 io->items[i].counter=0;
1130 io->filters[i]=filter;
1135 for(j=0; calc_type_table[j].func_name; j++){
1136 namelen=strlen(calc_type_table[j].func_name);
1137 if(filter && strncmp(filter, calc_type_table[j].func_name, namelen) == 0) {
1138 io->items[i].calc_type=calc_type_table[j].calc_type;
1139 io->items[i].colnum = i;
1140 if(*(filter+namelen)=='(') {
1142 parenp=strchr(p, ')');
1145 "\ntshark: Closing parenthesis missing from calculated expression.\n");
1149 if(io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES){
1152 "\ntshark: %s does not require or allow a field name within the parens.\n",
1153 calc_type_table[j].func_name);
1158 /* bail out if a field name was not specified */
1159 fprintf(stderr, "\ntshark: You didn't specify a field name for %s(*).\n",
1160 calc_type_table[j].func_name);
1165 field = (char *) g_malloc(parenp-p+1);
1166 memcpy(field, p, parenp-p);
1167 field[parenp-p] = '\0';
1169 if (io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES)
1171 hfi=proto_registrar_get_byname(field);
1173 fprintf(stderr, "\ntshark: There is no field named '%s'.\n",
1179 io->items[i].hf_index=hfi->id;
1183 if (io->items[i].calc_type==CALC_TYPE_FRAMES || io->items[i].calc_type==CALC_TYPE_BYTES)
1185 io->items[i].colnum = i;
1188 if(hfi && !(io->items[i].calc_type==CALC_TYPE_BYTES ||
1189 io->items[i].calc_type==CALC_TYPE_FRAMES ||
1190 io->items[i].calc_type==CALC_TYPE_FRAMES_AND_BYTES)){
1191 /* check that the type is compatible */
1203 /* these types support all calculations */
1207 /* these types only support SUM, COUNT, MAX, MIN, AVG */
1208 switch(io->items[i].calc_type){
1210 case CALC_TYPE_COUNT:
1217 "\ntshark: %s is a float field, so %s(*) calculations are not supported on it.",
1219 calc_type_table[j].func_name);
1223 case FT_RELATIVE_TIME:
1224 /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */
1225 switch(io->items[i].calc_type){
1227 case CALC_TYPE_COUNT:
1231 case CALC_TYPE_LOAD:
1235 "\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
1237 calc_type_table[j].func_name);
1243 * XXX - support all operations on floating-point
1246 if(io->items[i].calc_type!=CALC_TYPE_COUNT){
1248 "\ntshark: %s doesn't have integral values, so %s(*) "
1249 "calculations are not supported on it.\n",
1251 calc_type_table[j].func_name);
1259 error_string=register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE, NULL,
1260 iostat_packet, i?NULL:iostat_draw);
1264 fprintf(stderr, "\ntshark: Couldn't register io,stat tap: %s\n",
1266 g_string_free(error_string, TRUE);
1272 iostat_init(const char *optarg, void* userdata _U_)
1274 gdouble interval_float;
1277 const gchar *filters, *str, *pos;
1279 if ((*(optarg+(strlen(optarg)-1)) == ',') ||
1280 (sscanf(optarg, "io,stat,%lf%n", &interval_float, (int *)&idx) != 1) ||
1282 fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1288 if (*filters != ',') {
1289 /* For locale's that use ',' instead of '.', the comma might
1290 * have been consumed during the floating point conversion. */
1292 if (*filters != ',') {
1293 fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1300 switch (timestamp_get_type()) {
1305 case TS_UTC_WITH_DATE:
1306 fprintf(stderr, "\ntshark: invalid -t operand. io,stat only supports -t <r|a|ad>\n");
1312 io = (io_stat_t *) g_malloc(sizeof(io_stat_t));
1314 /* If interval is 0, calculate statistics over the whole file by setting the interval to
1316 if (interval_float==0) {
1317 io->interval = G_MAXINT32;
1320 /* Set interval to the number of us rounded to the nearest integer */
1321 io->interval = (gint64)(interval_float*1000000.0+0.5);
1323 * Determine what interval precision the user has specified */
1325 for (i=10; i<10000000; i*=10) {
1326 if (io->interval%i > 0)
1331 if (io->interval < 1){
1333 "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
1337 /* Find how many ',' separated filters we have */
1341 if (filters && (*filters != '\0')) {
1343 while((str = strchr(str, ','))) {
1349 io->items = (io_stat_item_t *) g_malloc(sizeof(io_stat_item_t) * io->num_cols);
1350 io->filters = (const char **)g_malloc(sizeof(char *) * io->num_cols);
1351 io->max_vals = (guint64 *) g_malloc(sizeof(guint64) * io->num_cols);
1352 io->max_frame = (guint32 *) g_malloc(sizeof(guint32) * io->num_cols);
1354 for (i=0; i<io->num_cols; i++) {
1355 io->max_vals[i] = 0;
1356 io->max_frame[i] = 0;
1359 /* Register a tap listener for each filter */
1360 if((!filters) || (filters[0]==0)) {
1361 register_io_tap(io, 0, NULL);
1367 pos = (gchar*) strchr(str, ',');
1369 register_io_tap(io, i, NULL);
1370 } else if (pos==NULL) {
1371 str = (const char*) g_strstrip((gchar*)str);
1372 filter = g_strdup((gchar*) str);
1374 register_io_tap(io, i, filter);
1376 register_io_tap(io, i, NULL);
1378 filter = (gchar *)g_malloc((pos-str)+1);
1379 g_strlcpy( filter, str, (gsize) ((pos-str)+1));
1380 filter = g_strstrip(filter);
1381 register_io_tap(io, i, (char *) filter);
1390 register_tap_listener_iostat(void)
1392 register_stat_cmd_arg("io,stat,", iostat_init, NULL);