update epan/dissectors/pidl/drsuapi/drsuapi.idl from samba
[wireshark-sm.git] / ui / cli / tap-iostat.c
blob2ba79a2c0f424d6f0cb4de1c32bcfc1b0892e5d1
1 /* tap-iostat.c
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
9 */
11 #include "config.h"
13 #include <stdlib.h>
14 #include <string.h>
16 #include <epan/epan_dissect.h>
17 #include <epan/tap.h>
18 #include <epan/stat_tap_ui.h>
19 #include "globals.h"
20 #include <wsutil/ws_assert.h>
22 #define CALC_TYPE_FRAMES 0
23 #define CALC_TYPE_BYTES 1
24 #define CALC_TYPE_FRAMES_AND_BYTES 2
25 #define CALC_TYPE_COUNT 3
26 #define CALC_TYPE_SUM 4
27 #define CALC_TYPE_MIN 5
28 #define CALC_TYPE_MAX 6
29 #define CALC_TYPE_AVG 7
30 #define CALC_TYPE_LOAD 8
32 void register_tap_listener_iostat(void);
34 typedef struct {
35 const char *func_name;
36 int calc_type;
37 } calc_type_ent_t;
39 static calc_type_ent_t calc_type_table[] = {
40 { "FRAMES", CALC_TYPE_FRAMES },
41 { "BYTES", CALC_TYPE_BYTES },
42 { "FRAMES BYTES", CALC_TYPE_FRAMES_AND_BYTES },
43 { "COUNT", CALC_TYPE_COUNT },
44 { "SUM", CALC_TYPE_SUM },
45 { "MIN", CALC_TYPE_MIN },
46 { "MAX", CALC_TYPE_MAX },
47 { "AVG", CALC_TYPE_AVG },
48 { "LOAD", CALC_TYPE_LOAD },
49 { NULL, 0 }
52 typedef struct _io_stat_t {
53 uint64_t interval; /* The user-specified time interval (us) */
54 unsigned invl_prec; /* Decimal precision of the time interval (1=10s, 2=100s etc) */
55 unsigned int num_cols; /* The number of columns of stats in the table */
56 struct _io_stat_item_t *items; /* Each item is a single cell in the table */
57 time_t start_time; /* Time of first frame matching the filter */
58 /* The following are all per-column fixed information arrays */
59 const char **filters; /* 'io,stat' cmd strings (e.g., "AVG(smb.time)smb.time") */
60 uint64_t *max_vals; /* The max value sans the decimal or nsecs portion in each stat column */
61 uint32_t *max_frame; /* The max frame number displayed in each stat column */
62 int *hf_indexes;
63 int *calc_type; /* The statistic type */
64 } io_stat_t;
66 typedef struct _io_stat_item_t {
67 io_stat_t *parent;
68 struct _io_stat_item_t *next;
69 struct _io_stat_item_t *prev;
70 uint64_t start_time; /* Time since start of capture (us)*/
71 int colnum; /* Column number of this stat (0 to n) */
72 uint32_t frames;
73 uint32_t num; /* The sample size of a given statistic (only needed for AVG) */
74 union { /* The accumulated data for the calculation of that statistic */
75 uint64_t counter;
76 float float_counter;
77 double double_counter;
79 } io_stat_item_t;
81 #define NANOSECS_PER_SEC UINT64_C(1000000000)
83 static uint64_t last_relative_time;
85 static tap_packet_status
86 iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_, tap_flags_t flags _U_)
88 io_stat_t *parent;
89 io_stat_item_t *mit;
90 io_stat_item_t *it;
91 uint64_t relative_time, rt;
92 const nstime_t *new_time;
93 GPtrArray *gp;
94 unsigned i;
95 int ftype;
97 mit = (io_stat_item_t *) arg;
98 parent = mit->parent;
100 /* If this frame's relative time is negative, set its relative time to last_relative_time
101 rather than disincluding it from the calculations. */
102 if ((pinfo->rel_ts.secs >= 0) && (pinfo->rel_ts.nsecs >= 0)) {
103 relative_time = ((uint64_t)pinfo->rel_ts.secs * UINT64_C(1000000)) +
104 ((uint64_t)((pinfo->rel_ts.nsecs+500)/1000));
105 last_relative_time = relative_time;
106 } else {
107 relative_time = last_relative_time;
110 if (mit->parent->start_time == 0) {
111 mit->parent->start_time = pinfo->abs_ts.secs - pinfo->rel_ts.secs;
114 /* The prev item is always the last interval in which we saw packets. */
115 it = mit->prev;
117 /* If we have moved into a new interval (row), create a new io_stat_item_t struct for every interval
118 * between the last struct and this one. If an item was not found in a previous interval, an empty
119 * struct will be created for it. */
120 rt = relative_time;
121 while (rt >= it->start_time + parent->interval) {
122 it->next = g_new(io_stat_item_t, 1);
123 it->next->prev = it;
124 it->next->next = NULL;
125 it = it->next;
126 mit->prev = it;
128 it->start_time = it->prev->start_time + parent->interval;
129 it->frames = 0;
130 it->counter = 0; /* 64-bit, type-punning with double is fine */
131 it->num = 0;
132 it->colnum = it->prev->colnum;
135 /* Store info in the current structure */
136 it->frames++;
138 switch (parent->calc_type[it->colnum]) {
139 case CALC_TYPE_FRAMES:
140 case CALC_TYPE_BYTES:
141 case CALC_TYPE_FRAMES_AND_BYTES:
142 it->counter += pinfo->fd->pkt_len;
143 break;
144 case CALC_TYPE_COUNT:
145 gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
146 if (gp) {
147 it->counter += gp->len;
149 break;
150 case CALC_TYPE_SUM:
151 gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
152 if (gp) {
153 uint64_t val;
155 for (i=0; i<gp->len; i++) {
156 switch (proto_registrar_get_ftype(parent->hf_indexes[it->colnum])) {
157 case FT_UINT8:
158 case FT_UINT16:
159 case FT_UINT24:
160 case FT_UINT32:
161 it->counter += fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
162 break;
163 case FT_UINT40:
164 case FT_UINT48:
165 case FT_UINT56:
166 case FT_UINT64:
167 it->counter += fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
168 break;
169 case FT_INT8:
170 case FT_INT16:
171 case FT_INT24:
172 case FT_INT32:
173 it->counter += fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
174 break;
175 case FT_INT40:
176 case FT_INT48:
177 case FT_INT56:
178 case FT_INT64:
179 it->counter += (int64_t)fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
180 break;
181 case FT_FLOAT:
182 it->float_counter +=
183 (float)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
184 break;
185 case FT_DOUBLE:
186 it->double_counter += fvalue_get_floating(((field_info *)gp->pdata[i])->value);
187 break;
188 case FT_RELATIVE_TIME:
189 new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
190 val = ((uint64_t)new_time->secs * NANOSECS_PER_SEC) + (uint64_t)new_time->nsecs;
191 it->counter += val;
192 break;
193 default:
195 * "Can't happen"; see the checks
196 * in register_io_tap().
198 ws_assert_not_reached();
199 break;
203 break;
204 case CALC_TYPE_MIN:
205 gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
206 if (gp) {
207 uint64_t val;
208 float float_val;
209 double double_val;
211 ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
212 for (i=0; i<gp->len; i++) {
213 switch (ftype) {
214 case FT_UINT8:
215 case FT_UINT16:
216 case FT_UINT24:
217 case FT_UINT32:
218 val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
219 if ((it->frames == 1 && i == 0) || (val < it->counter)) {
220 it->counter = val;
222 break;
223 case FT_UINT40:
224 case FT_UINT48:
225 case FT_UINT56:
226 case FT_UINT64:
227 val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
228 if ((it->frames == 1 && i == 0) || (val < it->counter)) {
229 it->counter = val;
231 break;
232 case FT_INT8:
233 case FT_INT16:
234 case FT_INT24:
235 case FT_INT32:
236 val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
237 if ((it->frames == 1 && i == 0) || ((int32_t)val < (int32_t)it->counter)) {
238 it->counter = val;
240 break;
241 case FT_INT40:
242 case FT_INT48:
243 case FT_INT56:
244 case FT_INT64:
245 val = fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
246 if ((it->frames == 1 && i == 0) || ((int64_t)val < (int64_t)it->counter)) {
247 it->counter = val;
249 break;
250 case FT_FLOAT:
251 float_val = (float)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
252 if ((it->frames == 1 && i == 0) || (float_val < it->float_counter)) {
253 it->float_counter = float_val;
255 break;
256 case FT_DOUBLE:
257 double_val = fvalue_get_floating(((field_info *)gp->pdata[i])->value);
258 if ((it->frames == 1 && i == 0) || (double_val < it->double_counter)) {
259 it->double_counter = double_val;
261 break;
262 case FT_RELATIVE_TIME:
263 new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
264 val = ((uint64_t)new_time->secs * NANOSECS_PER_SEC) + (uint64_t)new_time->nsecs;
265 if ((it->frames == 1 && i == 0) || (val < it->counter)) {
266 it->counter = val;
268 break;
269 default:
271 * "Can't happen"; see the checks
272 * in register_io_tap().
274 ws_assert_not_reached();
275 break;
279 break;
280 case CALC_TYPE_MAX:
281 gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
282 if (gp) {
283 uint64_t val;
284 float float_val;
285 double double_val;
287 ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
288 for (i=0; i<gp->len; i++) {
289 switch (ftype) {
290 case FT_UINT8:
291 case FT_UINT16:
292 case FT_UINT24:
293 case FT_UINT32:
294 val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
295 if (val > it->counter)
296 it->counter = val;
297 break;
298 case FT_UINT40:
299 case FT_UINT48:
300 case FT_UINT56:
301 case FT_UINT64:
302 val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
303 if (val > it->counter)
304 it->counter = val;
305 break;
306 case FT_INT8:
307 case FT_INT16:
308 case FT_INT24:
309 case FT_INT32:
310 val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
311 if ((int32_t)val > (int32_t)it->counter)
312 it->counter = val;
313 break;
314 case FT_INT40:
315 case FT_INT48:
316 case FT_INT56:
317 case FT_INT64:
318 val = fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
319 if ((int64_t)val > (int64_t)it->counter)
320 it->counter = val;
321 break;
322 case FT_FLOAT:
323 float_val = (float)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
324 if (float_val > it->float_counter)
325 it->float_counter = float_val;
326 break;
327 case FT_DOUBLE:
328 double_val = fvalue_get_floating(((field_info *)gp->pdata[i])->value);
329 if (double_val > it->double_counter)
330 it->double_counter = double_val;
331 break;
332 case FT_RELATIVE_TIME:
333 new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
334 val = ((uint64_t)new_time->secs * NANOSECS_PER_SEC) + (uint64_t)new_time->nsecs;
335 if (val > it->counter)
336 it->counter = val;
337 break;
338 default:
340 * "Can't happen"; see the checks
341 * in register_io_tap().
343 ws_assert_not_reached();
344 break;
348 break;
349 case CALC_TYPE_AVG:
350 gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
351 if (gp) {
352 uint64_t val;
354 ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
355 for (i=0; i<gp->len; i++) {
356 it->num++;
357 switch (ftype) {
358 case FT_UINT8:
359 case FT_UINT16:
360 case FT_UINT24:
361 case FT_UINT32:
362 val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
363 it->counter += val;
364 break;
365 case FT_UINT40:
366 case FT_UINT48:
367 case FT_UINT56:
368 case FT_UINT64:
369 val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
370 it->counter += val;
371 break;
372 case FT_INT8:
373 case FT_INT16:
374 case FT_INT24:
375 case FT_INT32:
376 val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
377 it->counter += val;
378 break;
379 case FT_INT40:
380 case FT_INT48:
381 case FT_INT56:
382 case FT_INT64:
383 val = fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
384 it->counter += val;
385 break;
386 case FT_FLOAT:
387 it->float_counter += (float)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
388 break;
389 case FT_DOUBLE:
390 it->double_counter += fvalue_get_floating(((field_info *)gp->pdata[i])->value);
391 break;
392 case FT_RELATIVE_TIME:
393 new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
394 val = ((uint64_t)new_time->secs * NANOSECS_PER_SEC) + (uint64_t)new_time->nsecs;
395 it->counter += val;
396 break;
397 default:
399 * "Can't happen"; see the checks
400 * in register_io_tap().
402 ws_assert_not_reached();
403 break;
407 break;
408 case CALC_TYPE_LOAD:
409 gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
410 if (gp) {
411 ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
412 if (ftype != FT_RELATIVE_TIME) {
413 fprintf(stderr,
414 "\ntshark: LOAD() is only supported for relative-time fields such as smb.time\n");
415 exit(10);
417 for (i=0; i<gp->len; i++) {
418 uint64_t val;
419 int tival;
420 io_stat_item_t *pit;
422 new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
423 val = ((uint64_t)new_time->secs*UINT64_C(1000000)) + (uint64_t)(new_time->nsecs/1000);
424 tival = (int)(val % parent->interval);
425 it->counter += tival;
426 val -= tival;
427 pit = it->prev;
428 while (val > 0) {
429 if (val < (uint64_t)parent->interval) {
430 pit->counter += val;
431 break;
433 pit->counter += parent->interval;
434 val -= parent->interval;
435 pit = pit->prev;
439 break;
441 /* Store the highest value for this item in order to determine the width of each stat column.
442 * For real numbers we only need to know its magnitude (the value to the left of the decimal point
443 * so round it up before storing it as an integer in max_vals. For AVG of RELATIVE_TIME fields,
444 * calc the average, round it to the next second and store the seconds. For all other calc types
445 * of RELATIVE_TIME fields, store the counters without modification.
446 * fields. */
447 switch (parent->calc_type[it->colnum]) {
448 case CALC_TYPE_FRAMES:
449 case CALC_TYPE_FRAMES_AND_BYTES:
450 parent->max_frame[it->colnum] =
451 MAX(parent->max_frame[it->colnum], it->frames);
452 if (parent->calc_type[it->colnum] == CALC_TYPE_FRAMES_AND_BYTES)
453 parent->max_vals[it->colnum] =
454 MAX(parent->max_vals[it->colnum], it->counter);
455 break;
456 case CALC_TYPE_BYTES:
457 case CALC_TYPE_COUNT:
458 case CALC_TYPE_LOAD:
459 parent->max_vals[it->colnum] = MAX(parent->max_vals[it->colnum], it->counter);
460 break;
461 case CALC_TYPE_SUM:
462 case CALC_TYPE_MIN:
463 case CALC_TYPE_MAX:
464 ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
465 switch (ftype) {
466 case FT_FLOAT:
467 parent->max_vals[it->colnum] =
468 MAX(parent->max_vals[it->colnum], (uint64_t)(it->float_counter+0.5));
469 break;
470 case FT_DOUBLE:
471 parent->max_vals[it->colnum] =
472 MAX(parent->max_vals[it->colnum], (uint64_t)(it->double_counter+0.5));
473 break;
474 case FT_RELATIVE_TIME:
475 parent->max_vals[it->colnum] =
476 MAX(parent->max_vals[it->colnum], it->counter);
477 break;
478 default:
479 /* UINT16-64 and INT8-64 */
480 parent->max_vals[it->colnum] =
481 MAX(parent->max_vals[it->colnum], it->counter);
482 break;
484 break;
485 case CALC_TYPE_AVG:
486 if (it->num == 0) /* avoid division by zero */
487 break;
488 ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
489 switch (ftype) {
490 case FT_FLOAT:
491 parent->max_vals[it->colnum] =
492 MAX(parent->max_vals[it->colnum], (uint64_t)it->float_counter/it->num);
493 break;
494 case FT_DOUBLE:
495 parent->max_vals[it->colnum] =
496 MAX(parent->max_vals[it->colnum], (uint64_t)it->double_counter/it->num);
497 break;
498 case FT_RELATIVE_TIME:
499 parent->max_vals[it->colnum] =
500 MAX(parent->max_vals[it->colnum], ((it->counter/(uint64_t)it->num) + UINT64_C(500000000)) / NANOSECS_PER_SEC);
501 break;
502 default:
503 /* UINT16-64 and INT8-64 */
504 parent->max_vals[it->colnum] =
505 MAX(parent->max_vals[it->colnum], it->counter/it->num);
506 break;
509 return TAP_PACKET_REDRAW;
512 static unsigned int
513 magnitude (uint64_t val, unsigned int max_w)
515 unsigned int i, mag = 0;
517 for (i=0; i<max_w; i++) {
518 mag++;
519 if ((val /= 10) == 0)
520 break;
522 return(mag);
526 * Print the calc_type_table[] function label centered in the column header.
528 static void
529 printcenter (const char *label, int lenval, int numpad)
531 int lenlab = (int) strlen(label), len;
532 const char spaces[] = " ", *spaces_ptr;
534 len = (int) (strlen(spaces)) - (((lenval-lenlab) / 2) + numpad);
535 if (len > 0 && len < 6) {
536 spaces_ptr = &spaces[len];
537 if ((lenval-lenlab)%2 == 0) {
538 printf("%s%s%s|", spaces_ptr, label, spaces_ptr);
539 } else {
540 printf("%s%s%s|", spaces_ptr-1, label, spaces_ptr);
542 } else if (len > 0 && len <= 15) {
543 printf("%s|", label);
547 typedef struct {
548 int fr; /* Width of this FRAMES column sans padding and border chars */
549 int val; /* Width of this non-FRAMES column sans padding and border chars */
550 } column_width;
552 static void
553 iostat_draw(void *arg)
555 uint32_t num;
556 uint64_t interval, duration, t, invl_end, dv;
557 unsigned int i, j, k, num_cols, num_rows, dur_secs_orig, dur_nsecs_orig, dur_secs, dur_nsecs, dur_mag,
558 invl_mag, invl_prec, tabrow_w, borderlen, invl_col_w, numpad = 1, namelen, len_filt, type,
559 maxfltr_w, ftype;
560 unsigned int fr_mag; /* The magnitude of the max frame number in this column */
561 unsigned int val_mag; /* The magnitude of the max value in this column */
562 char *spaces, *spaces_s, *filler_s = NULL, **fmts, *fmt = NULL;
563 const char *filter;
564 static char dur_mag_s[3], invl_prec_s[3], fr_mag_s[3], val_mag_s[3], *invl_fmt, *full_fmt;
565 io_stat_item_t *mit, **stat_cols, *item, **item_in_column;
566 bool last_row = false;
567 io_stat_t *iot;
568 column_width *col_w;
569 struct tm *tm_time;
570 time_t the_time;
572 mit = (io_stat_item_t *)arg;
573 iot = mit->parent;
574 num_cols = iot->num_cols;
575 col_w = g_new(column_width, num_cols);
576 fmts = (char **)g_malloc(sizeof(char *) * num_cols);
577 duration = ((uint64_t)cfile.elapsed_time.secs * UINT64_C(1000000)) +
578 (uint64_t)((cfile.elapsed_time.nsecs + 500) / 1000);
580 /* Store the pointer to each stat column */
581 stat_cols = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols);
582 for (j=0; j<num_cols; j++)
583 stat_cols[j] = &iot->items[j];
585 /* The following prevents gross inaccuracies when the user specifies an interval that is greater
586 * than the capture duration. */
587 if (iot->interval > duration || iot->interval == UINT64_MAX) {
588 interval = duration;
589 iot->interval = UINT64_MAX;
590 } else {
591 interval = iot->interval;
594 /* Calc the capture duration's magnitude (dur_mag) */
595 dur_secs = (unsigned int)(duration/UINT64_C(1000000));
596 dur_secs_orig = dur_secs;
597 dur_nsecs = (unsigned int)(duration%UINT64_C(1000000));
598 dur_nsecs_orig = dur_nsecs;
599 dur_mag = magnitude((uint64_t)dur_secs, 5);
600 snprintf(dur_mag_s, 3, "%u", dur_mag);
602 /* Calc the interval's magnitude */
603 invl_mag = magnitude(interval/UINT64_C(1000000), 5);
605 /* Set or get the interval precision */
606 if (interval == duration) {
608 * An interval arg of 0 or an interval size exceeding the capture duration was specified.
609 * Set the decimal precision of duration based on its magnitude. */
610 if (dur_mag >= 2)
611 invl_prec = 1;
612 else if (dur_mag == 1)
613 invl_prec = 3;
614 else
615 invl_prec = 6;
617 borderlen = 30 + dur_mag + (invl_prec == 0 ? 0 : invl_prec+1);
618 } else {
619 invl_prec = iot->invl_prec;
620 borderlen = 25 + MAX(invl_mag,dur_mag) + (invl_prec == 0 ? 0 : invl_prec+1);
623 /* Round the duration according to invl_prec */
624 dv = 1000000;
625 for (i=0; i<invl_prec; i++)
626 dv /= 10;
627 if ((duration%dv) > 5*(dv/10)) {
628 duration += 5*(dv/10);
629 duration = (duration/dv) * dv;
630 dur_secs = (unsigned int)(duration/UINT64_C(1000000));
631 dur_nsecs = (unsigned int)(duration%UINT64_C(1000000));
633 * Recalc dur_mag in case rounding has increased its magnitude */
634 dur_mag = magnitude((uint64_t)dur_secs, 5);
636 if (iot->interval == UINT64_MAX)
637 interval = duration;
639 /* Calc the width of the time interval column (incl borders and padding). */
640 if (invl_prec == 0)
641 invl_col_w = (2*dur_mag) + 8;
642 else
643 invl_col_w = (2*dur_mag) + (2*invl_prec) + 10;
645 /* Update the width of the time interval column if date is shown */
646 switch (timestamp_get_type()) {
647 case TS_ABSOLUTE_WITH_YMD:
648 case TS_ABSOLUTE_WITH_YDOY:
649 case TS_UTC_WITH_YMD:
650 case TS_UTC_WITH_YDOY:
651 invl_col_w = MAX(invl_col_w, 23);
652 break;
654 default:
655 invl_col_w = MAX(invl_col_w, 12);
656 break;
659 borderlen = MAX(borderlen, invl_col_w);
661 /* Calc the total width of each row in the stats table and build the printf format string for each
662 * column based on its field type, width, and name length.
663 * NOTE: The magnitude of all types including float and double are stored in iot->max_vals which
664 * is an *integer*. */
665 tabrow_w = invl_col_w;
666 for (j=0; j<num_cols; j++) {
667 type = iot->calc_type[j];
668 if (type == CALC_TYPE_FRAMES_AND_BYTES) {
669 namelen = 5;
670 } else {
671 namelen = (unsigned int)strlen(calc_type_table[type].func_name);
673 if (type == CALC_TYPE_FRAMES
674 || type == CALC_TYPE_FRAMES_AND_BYTES) {
676 fr_mag = magnitude(iot->max_frame[j], 15);
677 fr_mag = MAX(6, fr_mag);
678 col_w[j].fr = fr_mag;
679 tabrow_w += col_w[j].fr + 3;
680 snprintf(fr_mag_s, 3, "%u", fr_mag);
682 if (type == CALC_TYPE_FRAMES) {
683 fmt = g_strconcat(" %", fr_mag_s, "u |", NULL);
684 } else {
685 /* CALC_TYPE_FRAMES_AND_BYTES
687 val_mag = magnitude(iot->max_vals[j], 15);
688 val_mag = MAX(5, val_mag);
689 col_w[j].val = val_mag;
690 tabrow_w += (col_w[j].val + 3);
691 snprintf(val_mag_s, 3, "%u", val_mag);
692 fmt = g_strconcat(" %", fr_mag_s, "u |", " %", val_mag_s, PRIu64 " |", NULL);
694 if (fmt)
695 fmts[j] = fmt;
696 continue;
698 switch (type) {
699 case CALC_TYPE_BYTES:
700 case CALC_TYPE_COUNT:
702 val_mag = magnitude(iot->max_vals[j], 15);
703 val_mag = MAX(5, val_mag);
704 col_w[j].val = val_mag;
705 snprintf(val_mag_s, 3, "%u", val_mag);
706 fmt = g_strconcat(" %", val_mag_s, PRIu64 " |", NULL);
707 break;
709 default:
710 ftype = proto_registrar_get_ftype(iot->hf_indexes[j]);
711 switch (ftype) {
712 case FT_FLOAT:
713 case FT_DOUBLE:
714 val_mag = magnitude(iot->max_vals[j], 15);
715 snprintf(val_mag_s, 3, "%u", val_mag);
716 fmt = g_strconcat(" %", val_mag_s, ".6f |", NULL);
717 col_w[j].val = val_mag + 7;
718 break;
719 case FT_RELATIVE_TIME:
720 /* Convert FT_RELATIVE_TIME field to seconds
721 * CALC_TYPE_LOAD was already converted in iostat_packet() ) */
722 if (type == CALC_TYPE_LOAD) {
723 iot->max_vals[j] /= interval;
724 } else if (type != CALC_TYPE_AVG) {
725 iot->max_vals[j] = (iot->max_vals[j] + UINT64_C(500000000)) / NANOSECS_PER_SEC;
727 val_mag = magnitude(iot->max_vals[j], 15);
728 snprintf(val_mag_s, 3, "%u", val_mag);
729 fmt = g_strconcat(" %", val_mag_s, "u.%06u |", NULL);
730 col_w[j].val = val_mag + 7;
731 break;
733 default:
734 val_mag = magnitude(iot->max_vals[j], 15);
735 val_mag = MAX(namelen, val_mag);
736 col_w[j].val = val_mag;
737 snprintf(val_mag_s, 3, "%u", val_mag);
739 switch (ftype) {
740 case FT_UINT8:
741 case FT_UINT16:
742 case FT_UINT24:
743 case FT_UINT32:
744 case FT_UINT64:
745 fmt = g_strconcat(" %", val_mag_s, PRIu64 " |", NULL);
746 break;
747 case FT_INT8:
748 case FT_INT16:
749 case FT_INT24:
750 case FT_INT32:
751 case FT_INT64:
752 fmt = g_strconcat(" %", val_mag_s, PRId64 " |", NULL);
753 break;
755 } /* End of ftype switch */
756 } /* End of calc_type switch */
757 tabrow_w += col_w[j].val + 3;
758 if (fmt)
759 fmts[j] = fmt;
760 } /* End of for loop (columns) */
762 borderlen = MAX(borderlen, tabrow_w);
764 /* Calc the max width of the list of filters. */
765 maxfltr_w = 0;
766 for (j=0; j<num_cols; j++) {
767 if (iot->filters[j]) {
768 k = (unsigned int) (strlen(iot->filters[j]) + 11);
769 maxfltr_w = MAX(maxfltr_w, k);
770 } else {
771 maxfltr_w = MAX(maxfltr_w, 26);
774 /* The stat table is not wrapped (by tshark) but filter is wrapped at the width of the stats table
775 * (which currently = borderlen); however, if the filter width exceeds the table width and the
776 * table width is less than 102 bytes, set borderlen to the lesser of the max filter width and 102.
777 * The filters will wrap at the lesser of borderlen-2 and the last space in the filter.
778 * NOTE: 102 is the typical size of a user window when the font is fixed width (e.g., COURIER 10).
779 * XXX: A pref could be added to change the max width from the default size of 102. */
780 if (maxfltr_w > borderlen && borderlen < 102)
781 borderlen = MIN(maxfltr_w, 102);
783 /* Prevent double right border by adding a space */
784 if (borderlen-tabrow_w == 1)
785 borderlen++;
787 /* Display the top border */
788 printf("\n");
789 for (i=0; i<borderlen; i++)
790 printf("=");
792 spaces = (char *)g_malloc(borderlen+1);
793 for (i=0; i<borderlen; i++)
794 spaces[i] = ' ';
795 spaces[borderlen] = '\0';
797 spaces_s = &spaces[16];
798 printf("\n| IO Statistics%s|\n", spaces_s);
799 spaces_s = &spaces[2];
800 printf("|%s|\n", spaces_s);
802 if (invl_prec == 0) {
803 invl_fmt = g_strconcat("%", dur_mag_s, "u", NULL);
804 full_fmt = g_strconcat("| Duration: ", invl_fmt, ".%6u secs%s|\n", NULL);
805 spaces_s = &spaces[25 + dur_mag];
806 printf(full_fmt, dur_secs_orig, dur_nsecs_orig, spaces_s);
807 g_free(full_fmt);
808 full_fmt = g_strconcat("| Interval: ", invl_fmt, " secs%s|\n", NULL);
809 spaces_s = &spaces[18 + dur_mag];
810 printf(full_fmt, (uint32_t)(interval/UINT64_C(1000000)), spaces_s);
811 } else {
812 snprintf(invl_prec_s, 3, "%u", invl_prec);
813 invl_fmt = g_strconcat("%", dur_mag_s, "u.%0", invl_prec_s, "u", NULL);
814 full_fmt = g_strconcat("| Duration: ", invl_fmt, " secs%s|\n", NULL);
815 spaces_s = &spaces[19 + dur_mag + invl_prec];
816 printf(full_fmt, dur_secs, dur_nsecs/(int)dv, spaces_s);
817 g_free(full_fmt);
819 full_fmt = g_strconcat("| Interval: ", invl_fmt, " secs%s|\n", NULL);
820 spaces_s = &spaces[19 + dur_mag + invl_prec];
821 printf(full_fmt, (uint32_t)(interval/UINT64_C(1000000)),
822 (uint32_t)((interval%UINT64_C(1000000))/dv), spaces_s);
824 g_free(full_fmt);
826 spaces_s = &spaces[2];
827 printf("|%s|\n", spaces_s);
829 /* Display the list of filters and their column numbers vertically */
830 printf("| Col");
831 for (j=0; j<num_cols; j++) {
832 printf((j == 0 ? "%2u: " : "| %2u: "), j+1);
833 if (!iot->filters[j]) {
835 * An empty (no filter) comma field was specified */
836 spaces_s = &spaces[16 + 10];
837 printf("Frames and bytes%s|\n", spaces_s);
838 } else {
839 filter = iot->filters[j];
840 len_filt = (unsigned int) strlen(filter);
842 /* If the width of the widest filter exceeds the width of the stat table, borderlen has
843 * been set to 102 bytes above and filters wider than 102 will wrap at 91 bytes. */
844 if (len_filt+11 <= borderlen) {
845 printf("%s", filter);
846 if (len_filt+11 <= borderlen) {
847 spaces_s = &spaces[len_filt + 10];
848 printf("%s", spaces_s);
850 printf("|\n");
851 } else {
852 char *sfilter1, *sfilter2;
853 const char *pos;
854 size_t len;
855 unsigned int next_start, max_w = borderlen-11;
857 do {
858 if (len_filt > max_w) {
859 sfilter1 = g_strndup(filter, (size_t) max_w);
861 * Find the pos of the last space in sfilter1. If a space is found, set
862 * sfilter2 to the string prior to that space and print it; otherwise, wrap
863 * the filter at max_w. */
864 pos = g_strrstr(sfilter1, " ");
865 if (pos) {
866 len = (size_t)(pos-sfilter1);
867 next_start = (unsigned int) len+1;
868 } else {
869 len = (size_t) strlen(sfilter1);
870 next_start = (unsigned int)len;
872 sfilter2 = g_strndup(sfilter1, len);
873 printf("%s%s|\n", sfilter2, &spaces[len+10]);
874 g_free(sfilter1);
875 g_free(sfilter2);
877 printf("| ");
878 filter = &filter[next_start];
879 len_filt = (unsigned int) strlen(filter);
880 } else {
881 printf("%s%s|\n", filter, &spaces[strlen(filter)+10]);
882 break;
884 } while (1);
889 printf("|-");
890 for (i=0; i<borderlen-3; i++) {
891 printf("-");
893 printf("|\n");
895 /* Display spaces above "Interval (s)" label */
896 spaces_s = &spaces[borderlen-(invl_col_w-2)];
897 printf("|%s|", spaces_s);
899 /* Display column number headers */
900 for (j=0; j<num_cols; j++) {
901 if (iot->calc_type[j] == CALC_TYPE_FRAMES_AND_BYTES)
902 spaces_s = &spaces[borderlen - (col_w[j].fr + col_w[j].val)] - 3;
903 else if (iot->calc_type[j] == CALC_TYPE_FRAMES)
904 spaces_s = &spaces[borderlen - col_w[j].fr];
905 else
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);
915 k = 11;
916 switch (timestamp_get_type()) {
917 case TS_ABSOLUTE:
918 printf("\n| Time ");
919 break;
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");
925 k = 16;
926 break;
927 case TS_RELATIVE:
928 case TS_NOT_SET:
929 printf("\n| Interval");
930 break;
931 default:
932 break;
935 spaces_s = &spaces[borderlen-(invl_col_w-k)];
936 printf("%s|", spaces_s);
938 /* Display the stat label in each column */
939 for (j=0; j<num_cols; j++) {
940 type = iot->calc_type[j];
941 if (type == CALC_TYPE_FRAMES) {
942 printcenter (calc_type_table[type].func_name, col_w[j].fr, numpad);
943 } else if (type == CALC_TYPE_FRAMES_AND_BYTES) {
944 printcenter ("Frames", col_w[j].fr, numpad);
945 printcenter ("Bytes", col_w[j].val, numpad);
946 } else {
947 printcenter (calc_type_table[type].func_name, col_w[j].val, numpad);
950 if (filler_s)
951 printf("%s|", filler_s);
952 printf("\n|-");
954 for (i=0; i<tabrow_w-3; i++)
955 printf("-");
956 printf("|");
958 if (tabrow_w < borderlen)
959 printf("%s|", &spaces[tabrow_w+1]);
961 printf("\n");
962 t = 0;
963 if (invl_prec == 0 && dur_mag == 1)
964 full_fmt = g_strconcat("| ", invl_fmt, " <> ", invl_fmt, " |", NULL);
965 else
966 full_fmt = g_strconcat("| ", invl_fmt, " <> ", invl_fmt, " |", NULL);
968 if (interval == 0 || duration == 0) {
969 num_rows = 0;
970 } else {
971 num_rows = (unsigned int)(duration/interval) + ((unsigned int)(duration%interval) > 0 ? 1 : 0);
974 /* Load item_in_column with the first item in each column */
975 item_in_column = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols);
976 for (j=0; j<num_cols; j++) {
977 item_in_column[j] = stat_cols[j];
980 /* Display the table values
982 * The outer loop is for time interval rows and the inner loop is for stat column items.*/
983 for (i=0; i<num_rows; i++) {
985 if (i == num_rows-1)
986 last_row = true;
988 /* Compute the interval for this row */
989 if (!last_row) {
990 invl_end = t + interval;
991 } else {
992 invl_end = duration;
995 /* Patch for Absolute Time */
996 /* XXX - has a Y2.038K problem with 32-bit time_t */
997 the_time = (time_t)(iot->start_time + (t/UINT64_C(1000000)));
999 /* Display the interval for this row */
1000 switch (timestamp_get_type()) {
1001 case TS_ABSOLUTE:
1002 tm_time = localtime(&the_time);
1003 if (tm_time != NULL) {
1004 printf("| %02d:%02d:%02d |",
1005 tm_time->tm_hour,
1006 tm_time->tm_min,
1007 tm_time->tm_sec);
1008 } else
1009 printf("| XX:XX:XX |");
1010 break;
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,
1018 tm_time->tm_mday,
1019 tm_time->tm_hour,
1020 tm_time->tm_min,
1021 tm_time->tm_sec);
1022 } else
1023 printf("| XXXX-XX-XX XX:XX:XX |");
1024 break;
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,
1032 tm_time->tm_hour,
1033 tm_time->tm_min,
1034 tm_time->tm_sec);
1035 } else
1036 printf("| XXXX/XXX XX:XX:XX |");
1037 break;
1039 case TS_UTC:
1040 tm_time = gmtime(&the_time);
1041 if (tm_time != NULL) {
1042 printf("| %02d:%02d:%02d |",
1043 tm_time->tm_hour,
1044 tm_time->tm_min,
1045 tm_time->tm_sec);
1046 } else
1047 printf("| XX:XX:XX |");
1048 break;
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,
1056 tm_time->tm_mday,
1057 tm_time->tm_hour,
1058 tm_time->tm_min,
1059 tm_time->tm_sec);
1060 } else
1061 printf("| XXXX-XX-XX XX:XX:XX |");
1062 break;
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,
1070 tm_time->tm_hour,
1071 tm_time->tm_min,
1072 tm_time->tm_sec);
1073 } else
1074 printf("| XXXX/XXX XX:XX:XX |");
1075 break;
1077 case TS_RELATIVE:
1078 case TS_NOT_SET:
1079 if (invl_prec == 0) {
1080 if (last_row) {
1081 int maxw;
1082 maxw = dur_mag >= 3 ? dur_mag+1 : 3;
1083 g_free(full_fmt);
1084 snprintf(dur_mag_s, 3, "%u", maxw);
1085 full_fmt = g_strconcat( dur_mag == 1 ? "| " : "| ",
1086 invl_fmt, " <> ", "%-",
1087 dur_mag_s, "s|", NULL);
1088 printf(full_fmt, (uint32_t)(t/UINT64_C(1000000)), "Dur");
1089 } else {
1090 printf(full_fmt, (uint32_t)(t/UINT64_C(1000000)),
1091 (uint32_t)(invl_end/UINT64_C(1000000)));
1093 } else {
1094 printf(full_fmt, (uint32_t)(t/UINT64_C(1000000)),
1095 (uint32_t)(t%UINT64_C(1000000) / dv),
1096 (uint32_t)(invl_end/UINT64_C(1000000)),
1097 (uint32_t)(invl_end%UINT64_C(1000000) / dv));
1099 break;
1100 /* case TS_DELTA:
1101 case TS_DELTA_DIS:
1102 case TS_EPOCH:
1103 are not implemented */
1104 default:
1105 break;
1108 /* Display stat values in each column for this row */
1109 for (j=0; j<num_cols; j++) {
1110 fmt = fmts[j];
1111 item = item_in_column[j];
1112 type = iot->calc_type[j];
1114 if (item) {
1115 switch (type) {
1116 case CALC_TYPE_FRAMES:
1117 printf(fmt, item->frames);
1118 break;
1119 case CALC_TYPE_BYTES:
1120 case CALC_TYPE_COUNT:
1121 printf(fmt, item->counter);
1122 break;
1123 case CALC_TYPE_FRAMES_AND_BYTES:
1124 printf(fmt, item->frames, item->counter);
1125 break;
1127 case CALC_TYPE_SUM:
1128 case CALC_TYPE_MIN:
1129 case CALC_TYPE_MAX:
1130 ftype = proto_registrar_get_ftype(iot->hf_indexes[j]);
1131 switch (ftype) {
1132 case FT_FLOAT:
1133 printf(fmt, item->float_counter);
1134 break;
1135 case FT_DOUBLE:
1136 printf(fmt, item->double_counter);
1137 break;
1138 case FT_RELATIVE_TIME:
1139 item->counter = (item->counter + UINT64_C(500)) / UINT64_C(1000);
1140 printf(fmt,
1141 (int)(item->counter/UINT64_C(1000000)),
1142 (int)(item->counter%UINT64_C(1000000)));
1143 break;
1144 default:
1145 printf(fmt, item->counter);
1146 break;
1148 break;
1150 case CALC_TYPE_AVG:
1151 num = item->num;
1152 if (num == 0)
1153 num = 1;
1154 ftype = proto_registrar_get_ftype(iot->hf_indexes[j]);
1155 switch (ftype) {
1156 case FT_FLOAT:
1157 printf(fmt, item->float_counter/num);
1158 break;
1159 case FT_DOUBLE:
1160 printf(fmt, item->double_counter/num);
1161 break;
1162 case FT_RELATIVE_TIME:
1163 item->counter = ((item->counter / (uint64_t)num) + UINT64_C(500)) / UINT64_C(1000);
1164 printf(fmt,
1165 (int)(item->counter/UINT64_C(1000000)),
1166 (int)(item->counter%UINT64_C(1000000)));
1167 break;
1168 default:
1169 printf(fmt, item->counter / (uint64_t)num);
1170 break;
1172 break;
1174 case CALC_TYPE_LOAD:
1175 ftype = proto_registrar_get_ftype(iot->hf_indexes[j]);
1176 switch (ftype) {
1177 case FT_RELATIVE_TIME:
1178 if (!last_row) {
1179 printf(fmt,
1180 (int) (item->counter/interval),
1181 (int)((item->counter%interval)*UINT64_C(1000000) / interval));
1182 } else {
1183 printf(fmt,
1184 (int) (item->counter/(invl_end-t)),
1185 (int)((item->counter%(invl_end-t))*UINT64_C(1000000) / (invl_end-t)));
1187 break;
1189 break;
1192 if (last_row) {
1193 g_free(fmt);
1194 } else {
1195 item_in_column[j] = item_in_column[j]->next;
1197 } else {
1198 printf(fmt, (uint64_t)0, (uint64_t)0);
1201 if (filler_s)
1202 printf("%s|", filler_s);
1203 printf("\n");
1204 t += interval;
1207 for (i=0; i<borderlen; i++) {
1208 printf("=");
1210 printf("\n");
1211 g_free(iot->items);
1212 g_free((gpointer)iot->filters);
1213 g_free(iot->max_vals);
1214 g_free(iot->max_frame);
1215 g_free(iot->hf_indexes);
1216 g_free(iot->calc_type);
1217 g_free(iot);
1218 g_free(col_w);
1219 g_free(invl_fmt);
1220 g_free(full_fmt);
1221 g_free(fmts);
1222 g_free(spaces);
1223 g_free(stat_cols);
1224 g_free(item_in_column);
1228 static void
1229 register_io_tap(io_stat_t *io, unsigned int i, const char *filter)
1231 GString *error_string;
1232 const char *flt;
1233 int j;
1234 size_t namelen;
1235 const char *p, *parenp;
1236 char *field;
1237 header_field_info *hfi;
1239 io->items[i].prev = &io->items[i];
1240 io->items[i].next = NULL;
1241 io->items[i].parent = io;
1242 io->items[i].start_time = 0;
1243 io->items[i].frames = 0;
1244 io->items[i].counter = 0;
1245 io->items[i].num = 0;
1247 io->filters[i] = filter;
1248 flt = filter;
1250 io->calc_type[i] = CALC_TYPE_FRAMES_AND_BYTES;
1251 field = NULL;
1252 hfi = NULL;
1253 for (j=0; calc_type_table[j].func_name; j++) {
1254 namelen = strlen(calc_type_table[j].func_name);
1255 if (filter && strncmp(filter, calc_type_table[j].func_name, namelen) == 0) {
1256 io->calc_type[i] = calc_type_table[j].calc_type;
1257 io->items[i].colnum = i;
1258 if (*(filter+namelen) == '(') {
1259 p = filter+namelen+1;
1260 parenp = strchr(p, ')');
1261 if (!parenp) {
1262 fprintf(stderr,
1263 "\ntshark: Closing parenthesis missing from calculated expression.\n");
1264 exit(10);
1267 if (io->calc_type[i] == CALC_TYPE_FRAMES || io->calc_type[i] == CALC_TYPE_BYTES) {
1268 if (parenp != p) {
1269 fprintf(stderr,
1270 "\ntshark: %s does not require or allow a field name within the parens.\n",
1271 calc_type_table[j].func_name);
1272 exit(10);
1274 } else {
1275 if (parenp == p) {
1276 /* bail out if a field name was not specified */
1277 fprintf(stderr, "\ntshark: You didn't specify a field name for %s(*).\n",
1278 calc_type_table[j].func_name);
1279 exit(10);
1283 field = (char *)g_malloc(parenp-p+1);
1284 memcpy(field, p, parenp-p);
1285 field[parenp-p] = '\0';
1286 flt = parenp + 1;
1287 if (io->calc_type[i] == CALC_TYPE_FRAMES || io->calc_type[i] == CALC_TYPE_BYTES)
1288 break;
1289 hfi = proto_registrar_get_byname(field);
1290 if (!hfi) {
1291 fprintf(stderr, "\ntshark: There is no field named '%s'.\n",
1292 field);
1293 g_free(field);
1294 exit(10);
1297 io->hf_indexes[i] = hfi->id;
1298 break;
1300 } else {
1301 if (io->calc_type[i] == CALC_TYPE_FRAMES || io->calc_type[i] == CALC_TYPE_BYTES)
1302 flt = "";
1303 io->items[i].colnum = i;
1306 if (hfi && !(io->calc_type[i] == CALC_TYPE_BYTES ||
1307 io->calc_type[i] == CALC_TYPE_FRAMES ||
1308 io->calc_type[i] == CALC_TYPE_FRAMES_AND_BYTES)) {
1309 /* check that the type is compatible */
1310 switch (hfi->type) {
1311 case FT_UINT8:
1312 case FT_UINT16:
1313 case FT_UINT24:
1314 case FT_UINT32:
1315 case FT_UINT64:
1316 case FT_INT8:
1317 case FT_INT16:
1318 case FT_INT24:
1319 case FT_INT32:
1320 case FT_INT64:
1321 /* these types support all calculations */
1322 break;
1323 case FT_FLOAT:
1324 case FT_DOUBLE:
1325 /* these types only support SUM, COUNT, MAX, MIN, AVG */
1326 switch (io->calc_type[i]) {
1327 case CALC_TYPE_SUM:
1328 case CALC_TYPE_COUNT:
1329 case CALC_TYPE_MAX:
1330 case CALC_TYPE_MIN:
1331 case CALC_TYPE_AVG:
1332 break;
1333 default:
1334 fprintf(stderr,
1335 "\ntshark: %s is a float field, so %s(*) calculations are not supported on it.",
1336 field,
1337 calc_type_table[j].func_name);
1338 exit(10);
1340 break;
1341 case FT_RELATIVE_TIME:
1342 /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */
1343 switch (io->calc_type[i]) {
1344 case CALC_TYPE_SUM:
1345 case CALC_TYPE_COUNT:
1346 case CALC_TYPE_MAX:
1347 case CALC_TYPE_MIN:
1348 case CALC_TYPE_AVG:
1349 case CALC_TYPE_LOAD:
1350 break;
1351 default:
1352 fprintf(stderr,
1353 "\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
1354 field,
1355 calc_type_table[j].func_name);
1356 exit(10);
1358 break;
1359 default:
1361 * XXX - support all operations on floating-point
1362 * numbers?
1364 if (io->calc_type[i] != CALC_TYPE_COUNT) {
1365 fprintf(stderr,
1366 "\ntshark: %s doesn't have integral values, so %s(*) "
1367 "calculations are not supported on it.\n",
1368 field,
1369 calc_type_table[j].func_name);
1370 exit(10);
1372 break;
1375 g_free(field);
1377 error_string = register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE, NULL,
1378 iostat_packet, i ? NULL : iostat_draw, NULL);
1379 if (error_string) {
1380 g_free(io->items);
1381 g_free(io);
1382 fprintf(stderr, "\ntshark: Couldn't register io,stat tap: %s\n",
1383 error_string->str);
1384 g_string_free(error_string, TRUE);
1385 exit(1);
1389 static void
1390 iostat_init(const char *opt_arg, void *userdata _U_)
1392 double interval_float;
1393 uint32_t idx = 0;
1394 unsigned int i;
1395 io_stat_t *io;
1396 const char *filters, *str, *pos;
1398 if ((*(opt_arg+(strlen(opt_arg)-1)) == ',') ||
1399 (sscanf(opt_arg, "io,stat,%lf%n", &interval_float, (int *)&idx) != 1) ||
1400 (idx < 8)) {
1401 fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1402 exit(1);
1405 filters = opt_arg+idx;
1406 if (*filters) {
1407 if (*filters != ',') {
1408 /* For locale's that use ',' instead of '.', the comma might
1409 * have been consumed during the floating point conversion. */
1410 --filters;
1411 if (*filters != ',') {
1412 fprintf(stderr, "\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1413 exit(1);
1416 } else
1417 filters = NULL;
1419 switch (timestamp_get_type()) {
1420 case TS_DELTA:
1421 case TS_DELTA_DIS:
1422 case TS_EPOCH:
1423 fprintf(stderr, "\ntshark: invalid -t operand. io,stat only supports -t <r|a|ad|adoy|u|ud|udoy>\n");
1424 exit(1);
1425 default:
1426 break;
1429 io = g_new(io_stat_t, 1);
1431 /* If interval is 0, calculate statistics over the whole file by setting the interval to
1432 * UINT64_MAX */
1433 if (interval_float == 0) {
1434 io->interval = UINT64_MAX;
1435 io->invl_prec = 0;
1436 } else {
1437 /* Set interval to the number of us rounded to the nearest integer */
1438 io->interval = (uint64_t)(interval_float * 1000000.0 + 0.5);
1440 * Determine what interval precision the user has specified */
1441 io->invl_prec = 6;
1442 for (i=10; i<10000000; i*=10) {
1443 if (io->interval%i > 0)
1444 break;
1445 io->invl_prec--;
1447 if (io->invl_prec == 0) {
1448 /* The precision is zero but if the user specified one of more zeros after the decimal point,
1449 they want that many decimal places shown in the table for all time intervals except
1450 response time values such as smb.time which always have 6 decimal places of precision.
1451 This feature is useful in cases where for example the duration is 9.1, you specify an
1452 interval of 1 and the last interval becomes "9 <> 9". If the interval is instead set to
1453 1.1, the last interval becomes
1454 last interval is rounded up to value that is greater than the duration. */
1455 const char *invl_start = opt_arg+8;
1456 char *intv_end;
1457 int invl_len;
1459 intv_end = g_strstr_len(invl_start, -1, ",");
1460 invl_len = (int)(intv_end - invl_start);
1461 invl_start = g_strstr_len(invl_start, invl_len, ".");
1463 if (invl_start != NULL) {
1464 invl_len = (int)(intv_end - invl_start - 1);
1465 if (invl_len)
1466 io->invl_prec = MIN(invl_len, 6);
1470 if (io->interval < 1) {
1471 fprintf(stderr,
1472 "\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
1473 exit(10);
1476 /* Find how many ',' separated filters we have */
1477 io->num_cols = 1;
1478 io->start_time = 0;
1480 if (filters && (*filters != '\0')) {
1481 /* Eliminate the first comma. */
1482 filters++;
1483 str = filters;
1484 while ((str = strchr(str, ','))) {
1485 io->num_cols++;
1486 str++;
1490 io->items = g_new(io_stat_item_t, io->num_cols);
1491 io->filters = (const char **)g_malloc(sizeof(char *) * io->num_cols);
1492 io->max_vals = g_new(uint64_t, io->num_cols);
1493 io->max_frame = g_new(uint32_t, io->num_cols);
1494 io->hf_indexes = g_new(int, io->num_cols);
1495 io->calc_type = g_new(int, io->num_cols);
1497 for (i=0; i<io->num_cols; i++) {
1498 io->max_vals[i] = 0;
1499 io->max_frame[i] = 0;
1502 /* Register a tap listener for each filter */
1503 if ((!filters) || (filters[0] == 0)) {
1504 register_io_tap(io, 0, NULL);
1505 } else {
1506 char *filter;
1507 i = 0;
1508 str = filters;
1509 do {
1510 pos = (char*) strchr(str, ',');
1511 if (pos == str) {
1512 register_io_tap(io, i, NULL);
1513 } else if (pos == NULL) {
1514 str = (const char*) g_strstrip((char*)str);
1515 filter = g_strdup(str);
1516 if (*filter)
1517 register_io_tap(io, i, filter);
1518 else
1519 register_io_tap(io, i, NULL);
1520 } else {
1521 filter = (char *)g_malloc((pos-str)+1);
1522 (void) g_strlcpy( filter, str, (size_t) ((pos-str)+1));
1523 filter = g_strstrip(filter);
1524 register_io_tap(io, i, (char *) filter);
1526 str = pos+1;
1527 i++;
1528 } while (pos);
1532 static stat_tap_ui iostat_ui = {
1533 REGISTER_STAT_GROUP_GENERIC,
1534 NULL,
1535 "io,stat",
1536 iostat_init,
1538 NULL
1541 void
1542 register_tap_listener_iostat(void)
1544 register_stat_tap_ui(&iostat_ui, NULL);