### Bug Summary

File: | builds/wireshark/wireshark/ui/cli/tap-iostat.c
Warning: | line 1724, column 23 Potential leak of memory pointed to by 'filter'

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tap-iostat.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-21/lib/clang/21 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/wiretap -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2026-01-25-100340-3605-1 -x c /builds/wireshark/wireshark/ui/cli/tap-iostat.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /* tap-iostat.c
2 | * iostat 2002 Ronnie Sahlberg
3 | *
4 | * Wireshark - Network traffic analyzer
5 | * By Gerald Combs <gerald@wireshark.org>
6 | * Copyright 1998 Gerald Combs
7 | *
8 | * SPDX-License-Identifier: GPL-2.0-or-later
9 | */
10 |
11 | #include "config.h"
12 |
13 | #include <stdlib.h>
14 | #include <string.h>
15 | #include <locale.h>
16 |
17 | #include <epan/epan_dissect.h>
18 | #include <epan/tap.h>
19 | #include <epan/stat_tap_ui.h>
20 | #include "globals.h"
21 | #include <wsutil/ws_assert.h>
22 | #include <wsutil/time_util.h>
23 | #include <wsutil/to_str.h>
24 | #include <wsutil/cmdarg_err.h>
25 |
26 | #define CALC_TYPE_FRAMES0 0
27 | #define CALC_TYPE_BYTES1 1
28 | #define CALC_TYPE_FRAMES_AND_BYTES2 2
29 | #define CALC_TYPE_COUNT3 3
30 | #define CALC_TYPE_SUM4 4
31 | #define CALC_TYPE_MIN5 5
32 | #define CALC_TYPE_MAX6 6
33 | #define CALC_TYPE_AVG7 7
34 | #define CALC_TYPE_LOAD8 8
35 |
36 | void register_tap_listener_iostat(void);
37 |
38 | typedef struct {
39 | const char *func_name;
40 | int calc_type;
41 | } calc_type_ent_t;
42 |
43 | static calc_type_ent_t calc_type_table[] = {
44 | { "FRAMES", CALC_TYPE_FRAMES0 },
45 | { "BYTES", CALC_TYPE_BYTES1 },
46 | { "FRAMES BYTES", CALC_TYPE_FRAMES_AND_BYTES2 },
47 | { "COUNT", CALC_TYPE_COUNT3 },
48 | { "SUM", CALC_TYPE_SUM4 },
49 | { "MIN", CALC_TYPE_MIN5 },
50 | { "MAX", CALC_TYPE_MAX6 },
51 | { "AVG", CALC_TYPE_AVG7 },
52 | { "LOAD", CALC_TYPE_LOAD8 },
53 | { NULL((void*)0), 0 }
54 | };
55 |
56 | typedef struct _io_stat_t {
57 | uint64_t interval; /* The user-specified time interval (us) */
58 | unsigned invl_prec; /* Decimal precision of the time interval (1=10s, 2=100s etc) */
59 | unsigned int num_cols; /* The number of columns of stats in the table */
60 | struct _io_stat_item_t *items; /* Each item is a single cell in the table */
61 | nstime_t start_time; /* Time of first frame matching the filter */
62 | uint64_t last_relative_time;
63 | /* The following are all per-column fixed information arrays */
64 | const char **filters; /* 'io,stat' cmd strings (e.g., "AVG(smb.time)smb.time") */
65 | uint64_t *max_vals; /* The max value sans the decimal or nsecs portion in each stat column */
66 | uint32_t *max_frame; /* The max frame number displayed in each stat column */
67 | int *hf_indexes;
68 | int *calc_type; /* The statistic type */
69 | } io_stat_t;
70 |
71 | typedef struct _io_stat_item_t {
72 | io_stat_t *parent;
73 | struct _io_stat_item_t *next;
74 | struct _io_stat_item_t *prev;
75 | uint64_t start_time; /* Time since start of capture (us)*/
76 | int colnum; /* Column number of this stat (0 to n) */
77 | uint32_t frames;
78 | uint32_t num; /* The sample size of a given statistic (only needed for AVG) */
79 | union { /* The accumulated data for the calculation of that statistic */
80 | uint64_t counter;
81 | float float_counter;
82 | double double_counter;
83 | };
84 | } io_stat_item_t;
85 |
86 | static char *io_decimal_point;
87 |
88 | #define NANOSECS_PER_SEC1000000000UL UINT64_C(1000000000)1000000000UL
89 |
90 | /*
91 | * Reset an io_stat_item_t that's presumed to be one of io->items[].
92 | * Set its stats to 0 and remove any other items in its linked list.
93 | */
94 | static void
95 | iostat_item_reset(io_stat_item_t *mit)
96 | {
97 | io_stat_item_t *p, *cur;
98 |
99 | mit->start_time = 0;
100 | mit->frames = 0;
101 | mit->num = 0;
102 | mit->counter = 0;
103 |
104 | /* Free up the linked list in both directions. Reset mit->prev
105 | * to point back at mit to match the initialization in register_io_tap().
106 | * The list appears to be circular (XXX: is this intentional?)
107 | * so it's important to clear the transitive pointer back to the item
108 | * being freed, even if we'd think that next item is about to be freed anyway.
109 | */
110 | cur = mit->prev;
111 | mit->prev = mit;
112 | while (cur != NULL((void*)0) && cur != mit && cur != cur->prev) {
113 | p = cur->prev;
114 | p->next = NULL((void*)0);
115 | g_free(cur);
116 | cur = p;
117 | }
118 | cur = mit->next;
119 | mit->next = NULL((void*)0);
120 | while (cur != NULL((void*)0) && cur != mit && cur != cur->next) {
121 | p = cur->next;
122 | p->prev = NULL((void*)0);
123 | g_free(cur);
124 | cur = p;
125 | }
126 | }
127 |
128 | /*
129 | * Free an io_stat_t and all the memory it allocated.
130 | * Assumes that the pointers in an incompletely created io_stat_t are null
131 | * if they haven't been allocated yet.
132 | */
133 | static void
134 | iostat_io_free(io_stat_t *io)
135 | {
136 | for (unsigned int i = 0; i < io->num_cols; i++) {
137 | g_free((char*)io->filters[i]);
138 | iostat_item_reset(&io->items[i]);
139 | }
140 | g_free(io->items);
141 | g_free((void *)io->filters);
142 | g_free(io->max_vals);
143 | g_free(io->max_frame);
144 | g_free(io->hf_indexes);
145 | g_free(io->calc_type);
146 | g_free(io);
147 | }
148 |
149 | /* Tap function: collect statistics of interest from the current packet. */
150 | static tap_packet_status
151 | iostat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U___attribute__((unused)), tap_flags_t flags _U___attribute__((unused)))
152 | {
153 | io_stat_t *parent;
154 | io_stat_item_t *mit;
155 | io_stat_item_t *it;
156 | uint64_t relative_time, rt;
157 | const nstime_t *new_time;
158 | GPtrArray *gp;
159 | unsigned i;
160 | int ftype;
161 |
162 | mit = (io_stat_item_t *) arg;
163 | parent = mit->parent;
164 |
165 | /* If this frame's relative time is negative, set its relative time to last_relative_time
166 | rather than disincluding it from the calculations. */
167 | if ((pinfo->rel_ts.secs >= 0) && (pinfo->rel_ts.nsecs >= 0)) {
168 | relative_time = ((uint64_t)pinfo->rel_ts.secs * UINT64_C(1000000)1000000UL) +
169 | ((uint64_t)((pinfo->rel_ts.nsecs+500)/1000));
170 | parent->last_relative_time = relative_time;
171 | } else {
172 | relative_time = parent->last_relative_time;
173 | }
174 |
175 | if (nstime_is_unset(&mit->parent->start_time)) {
176 | nstime_delta(&mit->parent->start_time, &pinfo->abs_ts, &pinfo->rel_ts);
177 | }
178 |
179 | /* The prev item is always the last interval in which we saw packets. */
180 | it = mit->prev;
181 |
182 | /* If we have moved into a new interval (row), create a new io_stat_item_t struct for every interval
183 | * between the last struct and this one. If an item was not found in a previous interval, an empty
184 | * struct will be created for it. */
185 | rt = relative_time;
186 | while (rt >= it->start_time + parent->interval) {
187 | it->next = g_new(io_stat_item_t, 1)((io_stat_item_t *) g_malloc_n ((1), sizeof (io_stat_item_t)) );
188 | it->next->prev = it;
189 | it->next->next = NULL((void*)0);
190 | it = it->next;
191 | mit->prev = it;
192 |
193 | it->start_time = it->prev->start_time + parent->interval;
194 | it->frames = 0;
195 | it->counter = 0; /* 64-bit, type-punning with double is fine */
196 | it->num = 0;
197 | it->colnum = it->prev->colnum;
198 | }
199 |
200 | /* Store info in the current structure */
201 | it->frames++;
202 |
203 | switch (parent->calc_type[it->colnum]) {
204 | case CALC_TYPE_FRAMES0:
205 | case CALC_TYPE_BYTES1:
206 | case CALC_TYPE_FRAMES_AND_BYTES2:
207 | it->counter += pinfo->fd->pkt_len;
208 | break;
209 | case CALC_TYPE_COUNT3:
210 | gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
211 | if (gp) {
212 | it->counter += gp->len;
213 | }
214 | break;
215 | case CALC_TYPE_SUM4:
216 | gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
217 | if (gp) {
218 | uint64_t val;
219 |
220 | for (i=0; i<gp->len; i++) {
221 | switch (proto_registrar_get_ftype(parent->hf_indexes[it->colnum])) {
222 | case FT_UINT8:
223 | case FT_UINT16:
224 | case FT_UINT24:
225 | case FT_UINT32:
226 | it->counter += fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
227 | break;
228 | case FT_UINT40:
229 | case FT_UINT48:
230 | case FT_UINT56:
231 | case FT_UINT64:
232 | it->counter += fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
233 | break;
234 | case FT_INT8:
235 | case FT_INT16:
236 | case FT_INT24:
237 | case FT_INT32:
238 | it->counter += fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
239 | break;
240 | case FT_INT40:
241 | case FT_INT48:
242 | case FT_INT56:
243 | case FT_INT64:
244 | it->counter += (int64_t)fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
245 | break;
246 | case FT_FLOAT:
247 | it->float_counter +=
248 | (float)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
249 | break;
250 | case FT_DOUBLE:
251 | it->double_counter += fvalue_get_floating(((field_info *)gp->pdata[i])->value);
252 | break;
253 | case FT_RELATIVE_TIME:
254 | new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
255 | val = ((uint64_t)new_time->secs * NANOSECS_PER_SEC1000000000UL) + (uint64_t)new_time->nsecs;
256 | it->counter += val;
257 | break;
258 | default:
259 | /*
260 | * "Can't happen"; see the checks
261 | * in register_io_tap().
262 | */
263 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "ui/cli/tap-iostat.c", 263, __func__, "assertion \"not reached\" failed");
264 | break;
265 | }
266 | }
267 | }
268 | break;
269 | case CALC_TYPE_MIN5:
270 | gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
271 | if (gp) {
272 | uint64_t val;
273 | float float_val;
274 | double double_val;
275 |
276 | ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
277 | for (i=0; i<gp->len; i++) {
278 | switch (ftype) {
279 | case FT_UINT8:
280 | case FT_UINT16:
281 | case FT_UINT24:
282 | case FT_UINT32:
283 | val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
284 | if ((it->frames == 1 && i == 0) || (val < it->counter)) {
285 | it->counter = val;
286 | }
287 | break;
288 | case FT_UINT40:
289 | case FT_UINT48:
290 | case FT_UINT56:
291 | case FT_UINT64:
292 | val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
293 | if ((it->frames == 1 && i == 0) || (val < it->counter)) {
294 | it->counter = val;
295 | }
296 | break;
297 | case FT_INT8:
298 | case FT_INT16:
299 | case FT_INT24:
300 | case FT_INT32:
301 | val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
302 | if ((it->frames == 1 && i == 0) || ((int32_t)val < (int32_t)it->counter)) {
303 | it->counter = val;
304 | }
305 | break;
306 | case FT_INT40:
307 | case FT_INT48:
308 | case FT_INT56:
309 | case FT_INT64:
310 | val = fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
311 | if ((it->frames == 1 && i == 0) || ((int64_t)val < (int64_t)it->counter)) {
312 | it->counter = val;
313 | }
314 | break;
315 | case FT_FLOAT:
316 | float_val = (float)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
317 | if ((it->frames == 1 && i == 0) || (float_val < it->float_counter)) {
318 | it->float_counter = float_val;
319 | }
320 | break;
321 | case FT_DOUBLE:
322 | double_val = fvalue_get_floating(((field_info *)gp->pdata[i])->value);
323 | if ((it->frames == 1 && i == 0) || (double_val < it->double_counter)) {
324 | it->double_counter = double_val;
325 | }
326 | break;
327 | case FT_RELATIVE_TIME:
328 | new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
329 | val = ((uint64_t)new_time->secs * NANOSECS_PER_SEC1000000000UL) + (uint64_t)new_time->nsecs;
330 | if ((it->frames == 1 && i == 0) || (val < it->counter)) {
331 | it->counter = val;
332 | }
333 | break;
334 | default:
335 | /*
336 | * "Can't happen"; see the checks
337 | * in register_io_tap().
338 | */
339 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "ui/cli/tap-iostat.c", 339, __func__, "assertion \"not reached\" failed");
340 | break;
341 | }
342 | }
343 | }
344 | break;
345 | case CALC_TYPE_MAX6:
346 | gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
347 | if (gp) {
348 | uint64_t val;
349 | float float_val;
350 | double double_val;
351 |
352 | ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
353 | for (i=0; i<gp->len; i++) {
354 | switch (ftype) {
355 | case FT_UINT8:
356 | case FT_UINT16:
357 | case FT_UINT24:
358 | case FT_UINT32:
359 | val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
360 | if (val > it->counter)
361 | it->counter = val;
362 | break;
363 | case FT_UINT40:
364 | case FT_UINT48:
365 | case FT_UINT56:
366 | case FT_UINT64:
367 | val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
368 | if (val > it->counter)
369 | it->counter = val;
370 | break;
371 | case FT_INT8:
372 | case FT_INT16:
373 | case FT_INT24:
374 | case FT_INT32:
375 | val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
376 | if ((int32_t)val > (int32_t)it->counter)
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 | if ((int64_t)val > (int64_t)it->counter)
385 | it->counter = val;
386 | break;
387 | case FT_FLOAT:
388 | float_val = (float)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
389 | if (float_val > it->float_counter)
390 | it->float_counter = float_val;
391 | break;
392 | case FT_DOUBLE:
393 | double_val = fvalue_get_floating(((field_info *)gp->pdata[i])->value);
394 | if (double_val > it->double_counter)
395 | it->double_counter = double_val;
396 | break;
397 | case FT_RELATIVE_TIME:
398 | new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
399 | val = ((uint64_t)new_time->secs * NANOSECS_PER_SEC1000000000UL) + (uint64_t)new_time->nsecs;
400 | if (val > it->counter)
401 | it->counter = val;
402 | break;
403 | default:
404 | /*
405 | * "Can't happen"; see the checks
406 | * in register_io_tap().
407 | */
408 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "ui/cli/tap-iostat.c", 408, __func__, "assertion \"not reached\" failed");
409 | break;
410 | }
411 | }
412 | }
413 | break;
414 | case CALC_TYPE_AVG7:
415 | gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
416 | if (gp) {
417 | uint64_t val;
418 |
419 | ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
420 | for (i=0; i<gp->len; i++) {
421 | it->num++;
422 | switch (ftype) {
423 | case FT_UINT8:
424 | case FT_UINT16:
425 | case FT_UINT24:
426 | case FT_UINT32:
427 | val = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value);
428 | it->counter += val;
429 | break;
430 | case FT_UINT40:
431 | case FT_UINT48:
432 | case FT_UINT56:
433 | case FT_UINT64:
434 | val = fvalue_get_uinteger64(((field_info *)gp->pdata[i])->value);
435 | it->counter += val;
436 | break;
437 | case FT_INT8:
438 | case FT_INT16:
439 | case FT_INT24:
440 | case FT_INT32:
441 | val = fvalue_get_sinteger(((field_info *)gp->pdata[i])->value);
442 | it->counter += val;
443 | break;
444 | case FT_INT40:
445 | case FT_INT48:
446 | case FT_INT56:
447 | case FT_INT64:
448 | val = fvalue_get_sinteger64(((field_info *)gp->pdata[i])->value);
449 | it->counter += val;
450 | break;
451 | case FT_FLOAT:
452 | it->float_counter += (float)fvalue_get_floating(((field_info *)gp->pdata[i])->value);
453 | break;
454 | case FT_DOUBLE:
455 | it->double_counter += fvalue_get_floating(((field_info *)gp->pdata[i])->value);
456 | break;
457 | case FT_RELATIVE_TIME:
458 | new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
459 | val = ((uint64_t)new_time->secs * NANOSECS_PER_SEC1000000000UL) + (uint64_t)new_time->nsecs;
460 | it->counter += val;
461 | break;
462 | default:
463 | /*
464 | * "Can't happen"; see the checks
465 | * in register_io_tap().
466 | */
467 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "ui/cli/tap-iostat.c", 467, __func__, "assertion \"not reached\" failed");
468 | break;
469 | }
470 | }
471 | }
472 | break;
473 | case CALC_TYPE_LOAD8:
474 | gp = proto_get_finfo_ptr_array(edt->tree, parent->hf_indexes[it->colnum]);
475 | if (gp) {
476 | ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
477 | if (ftype != FT_RELATIVE_TIME) {
478 | cmdarg_err("\ntshark: LOAD() is only supported for relative-time fields such as smb.time\n");
479 | return TAP_PACKET_FAILED;
480 | }
481 | for (i=0; i<gp->len; i++) {
482 | uint64_t val;
483 | int tival;
484 | io_stat_item_t *pit;
485 |
486 | new_time = fvalue_get_time(((field_info *)gp->pdata[i])->value);
487 | val = ((uint64_t)new_time->secs*UINT64_C(1000000)1000000UL) + (uint64_t)(new_time->nsecs/1000);
488 | tival = (int)(val % parent->interval);
489 | it->counter += tival;
490 | val -= tival;
491 | pit = it->prev;
492 | while (val > 0) {
493 | if (val < (uint64_t)parent->interval) {
494 | pit->counter += val;
495 | break;
496 | }
497 | pit->counter += parent->interval;
498 | val -= parent->interval;
499 | pit = pit->prev;
500 | }
501 | }
502 | }
503 | break;
504 | }
505 | /* Store the highest value for this item in order to determine the width of each stat column.
506 | * For real numbers we only need to know its magnitude (the value to the left of the decimal point
507 | * so round it up before storing it as an integer in max_vals. For AVG of RELATIVE_TIME fields,
508 | * calc the average, round it to the next second and store the seconds. For all other calc types
509 | * of RELATIVE_TIME fields, store the counters without modification.
510 | * fields. */
511 | switch (parent->calc_type[it->colnum]) {
512 | case CALC_TYPE_FRAMES0:
513 | case CALC_TYPE_FRAMES_AND_BYTES2:
514 | parent->max_frame[it->colnum] =
515 | MAX(parent->max_frame[it->colnum], it->frames)(((parent->max_frame[it->colnum]) > (it->frames)) ? (parent->max_frame[it->colnum]) : (it->frames));
516 | if (parent->calc_type[it->colnum] == CALC_TYPE_FRAMES_AND_BYTES2)
517 | parent->max_vals[it->colnum] =
518 | MAX(parent->max_vals[it->colnum], it->counter)(((parent->max_vals[it->colnum]) > (it->counter)) ? (parent->max_vals[it->colnum]) : (it->counter));
519 | break;
520 | case CALC_TYPE_BYTES1:
521 | case CALC_TYPE_COUNT3:
522 | case CALC_TYPE_LOAD8:
523 | parent->max_vals[it->colnum] = MAX(parent->max_vals[it->colnum], it->counter)(((parent->max_vals[it->colnum]) > (it->counter)) ? (parent->max_vals[it->colnum]) : (it->counter));
524 | break;
525 | case CALC_TYPE_SUM4:
526 | case CALC_TYPE_MIN5:
527 | case CALC_TYPE_MAX6:
528 | ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
529 | switch (ftype) {
530 | case FT_FLOAT:
531 | parent->max_vals[it->colnum] =
532 | MAX(parent->max_vals[it->colnum], (uint64_t)(it->float_counter+0.5))(((parent->max_vals[it->colnum]) > ((uint64_t)(it-> float_counter+0.5))) ? (parent->max_vals[it->colnum]) : ((uint64_t)(it->float_counter+0.5)));
533 | break;
534 | case FT_DOUBLE:
535 | parent->max_vals[it->colnum] =
536 | MAX(parent->max_vals[it->colnum], (uint64_t)(it->double_counter+0.5))(((parent->max_vals[it->colnum]) > ((uint64_t)(it-> double_counter+0.5))) ? (parent->max_vals[it->colnum]) : ((uint64_t)(it->double_counter+0.5)));
537 | break;
538 | case FT_RELATIVE_TIME:
539 | parent->max_vals[it->colnum] =
540 | MAX(parent->max_vals[it->colnum], it->counter)(((parent->max_vals[it->colnum]) > (it->counter)) ? (parent->max_vals[it->colnum]) : (it->counter));
541 | break;
542 | default:
543 | /* UINT16-64 and INT8-64 */
544 | parent->max_vals[it->colnum] =
545 | MAX(parent->max_vals[it->colnum], it->counter)(((parent->max_vals[it->colnum]) > (it->counter)) ? (parent->max_vals[it->colnum]) : (it->counter));
546 | break;
547 | }
548 | break;
549 | case CALC_TYPE_AVG7:
550 | if (it->num == 0) /* avoid division by zero */
551 | break;
552 | ftype = proto_registrar_get_ftype(parent->hf_indexes[it->colnum]);
553 | switch (ftype) {
554 | case FT_FLOAT:
555 | parent->max_vals[it->colnum] =
556 | MAX(parent->max_vals[it->colnum], (uint64_t)it->float_counter/it->num)(((parent->max_vals[it->colnum]) > ((uint64_t)it-> float_counter/it->num)) ? (parent->max_vals[it->colnum ]) : ((uint64_t)it->float_counter/it->num));
557 | break;
558 | case FT_DOUBLE:
559 | parent->max_vals[it->colnum] =
560 | MAX(parent->max_vals[it->colnum], (uint64_t)it->double_counter/it->num)(((parent->max_vals[it->colnum]) > ((uint64_t)it-> double_counter/it->num)) ? (parent->max_vals[it->colnum ]) : ((uint64_t)it->double_counter/it->num));
561 | break;
562 | case FT_RELATIVE_TIME:
563 | parent->max_vals[it->colnum] =
564 | MAX(parent->max_vals[it->colnum], ((it->counter/(uint64_t)it->num) + UINT64_C(500000000)) / NANOSECS_PER_SEC)(((parent->max_vals[it->colnum]) > (((it->counter /(uint64_t)it->num) + 500000000UL) / 1000000000UL)) ? (parent ->max_vals[it->colnum]) : (((it->counter/(uint64_t)it ->num) + 500000000UL) / 1000000000UL));
565 | break;
566 | default:
567 | /* UINT16-64 and INT8-64 */
568 | parent->max_vals[it->colnum] =
569 | MAX(parent->max_vals[it->colnum], it->counter/it->num)(((parent->max_vals[it->colnum]) > (it->counter/it ->num)) ? (parent->max_vals[it->colnum]) : (it->counter /it->num));
570 | break;
571 | }
572 | }
573 | return TAP_PACKET_REDRAW;
574 | }
575 |
576 | static unsigned int
577 | magnitude (uint64_t val, unsigned int max_w)
578 | {
579 | unsigned int i, mag = 0;
580 |
581 | for (i=0; i<max_w; i++) {
582 | mag++;
583 | if ((val /= 10) == 0)
584 | break;
585 | }
586 | return(mag);
587 | }
588 |
589 | /*
590 | * Print the calc_type_table[] function label centered in the column header.
591 | */
592 | static void
593 | printcenter (const char *label, int lenval, int numpad)
594 | {
595 | int lenlab = (int) strlen(label), len;
596 | const char spaces[] = " ", *spaces_ptr;
597 |
598 | len = (int) (strlen(spaces)) - (((lenval-lenlab) / 2) + numpad);
599 | if (len > 0 && len < 6) {
600 | spaces_ptr = &spaces[len];
601 | if ((lenval-lenlab)%2 == 0) {
602 | printf("%s%s%s|", spaces_ptr, label, spaces_ptr);
603 | } else {
604 | printf("%s%s%s|", spaces_ptr-1, label, spaces_ptr);
605 | }
606 | } else if (len > 0 && len <= 15) {
607 | printf("%s|", label);
608 | }
609 | }
610 |
611 | typedef struct {
612 | int fr; /* Width of this FRAMES column sans padding and border chars */
613 | int val; /* Width of this non-FRAMES column sans padding and border chars */
614 | } column_width;
615 |
616 | static void
617 | fill_abs_time(const nstime_t* the_time, char *time_buf, char *decimal_point, unsigned invl_prec, bool_Bool local)
618 | {
619 | struct tm tm, *tmp;
620 | char *ptr;
621 | size_t remaining = NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z");
622 | int num_bytes;
623 |
624 | if (local) {
625 | tmp = ws_localtime_r(&the_time->secs, &tm);
626 | } else {
627 | tmp = ws_gmtime_r(&the_time->secs, &tm);
628 | }
629 |
630 | if (tmp == NULL((void*)0)) {
631 | snprintf(time_buf, remaining, "XX:XX:XX");
632 | return;
633 | }
634 |
635 | ptr = time_buf;
636 | num_bytes = snprintf(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"),
637 | "%02d:%02d:%02d",
638 | tmp->tm_hour,
639 | tmp->tm_min,
640 | tmp->tm_sec);
641 | if (num_bytes < 0) {
642 | // snprintf failed
643 | snprintf(time_buf, remaining, "XX:XX:XX");
644 | return;
645 | }
646 | ptr += num_bytes;
647 | remaining -= num_bytes;
648 | if (invl_prec != 0) {
649 | num_bytes = format_fractional_part_nsecs(ptr, remaining,
650 | (uint32_t)the_time->nsecs, decimal_point, invl_prec);
651 | ptr += num_bytes;
652 | remaining -= num_bytes;
653 | }
654 |
655 | if (!local) {
656 | if (remaining == 1 && num_bytes > 0) {
657 | /*
658 | * If we copied a fractional part but there's only room
659 | * for the terminating '\0', replace the last digit of
660 | * the fractional part with the "Z". (Remaining is at
661 | * least 1, otherwise we would have returned above.)
662 | */
663 | ptr--;
664 | remaining++;
665 | }
666 | (void)g_strlcpy(ptr, "Z", remaining);
667 | }
668 | return;
669 | }
670 |
671 | static void
672 | fill_abs_ydoy_time(const nstime_t* the_time, char *time_buf, char *decimal_point, unsigned invl_prec, bool_Bool local)
673 | {
674 | struct tm tm, *tmp;
675 | char *ptr;
676 | size_t remaining = NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z");
677 | int num_bytes;
678 |
679 | if (local) {
680 | tmp = ws_localtime_r(&the_time->secs, &tm);
681 | } else {
682 | tmp = ws_gmtime_r(&the_time->secs, &tm);
683 | }
684 |
685 | if (tmp == NULL((void*)0)) {
686 | snprintf(time_buf, remaining, "XXXX/XXX XX:XX:XX");
687 | return;
688 | }
689 |
690 | ptr = time_buf;
691 | num_bytes = snprintf(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"),
692 | "%04d/%03d %02d:%02d:%02d",
693 | tmp->tm_year + 1900,
694 | tmp->tm_yday + 1,
695 | tmp->tm_hour,
696 | tmp->tm_min,
697 | tmp->tm_sec);
698 | if (num_bytes < 0) {
699 | // snprintf failed
700 | snprintf(time_buf, remaining, "XXXX/XXX XX:XX:XX");
701 | return;
702 | }
703 | ptr += num_bytes;
704 | remaining -= num_bytes;
705 | if (invl_prec != 0) {
706 | num_bytes = format_fractional_part_nsecs(ptr, remaining,
707 | (uint32_t)the_time->nsecs, decimal_point, invl_prec);
708 | ptr += num_bytes;
709 | remaining -= num_bytes;
710 | }
711 |
712 | if (!local) {
713 | if (remaining == 1 && num_bytes > 0) {
714 | /*
715 | * If we copied a fractional part but there's only room
716 | * for the terminating '\0', replace the last digit of
717 | * the fractional part with the "Z". (Remaining is at
718 | * least 1, otherwise we would have returned above.)
719 | */
720 | ptr--;
721 | remaining++;
722 | }
723 | (void)g_strlcpy(ptr, "Z", remaining);
724 | }
725 | return;
726 | }
727 |
728 | static void
729 | fill_start_time(const io_stat_t *iot, const nstime_t *rel_time, ws_tsprec_e invl_prec, char *time_buf)
730 | {
731 | nstime_t abs_time;
732 | nstime_sum(&abs_time, rel_time, &iot->start_time);
733 |
734 | switch (timestamp_get_type()) {
735 | case TS_ABSOLUTE:
736 | fill_abs_time(&abs_time, time_buf, io_decimal_point, invl_prec, true1);
737 | break;
738 |
739 | case TS_ABSOLUTE_WITH_YMD:
740 | format_nstime_as_iso8601(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"), &abs_time,
741 | io_decimal_point, true1, invl_prec);
742 | break;
743 |
744 | case TS_ABSOLUTE_WITH_YDOY:
745 | fill_abs_ydoy_time(&abs_time, time_buf, io_decimal_point, invl_prec, true1);
746 | break;
747 |
748 | case TS_UTC:
749 | fill_abs_time(&abs_time, time_buf, io_decimal_point, invl_prec, false0);
750 | break;
751 |
752 | case TS_UTC_WITH_YMD:
753 | format_nstime_as_iso8601(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"), &abs_time,
754 | io_decimal_point, false0, invl_prec);
755 | break;
756 |
757 | case TS_UTC_WITH_YDOY:
758 | fill_abs_ydoy_time(&abs_time, time_buf, io_decimal_point, invl_prec, false0);
759 | break;
760 |
761 | case TS_RELATIVE:
762 | case TS_RELATIVE_CAP:
763 | case TS_NOT_SET:
764 | display_signed_time(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"), rel_time, invl_prec);
765 | break;
766 | case TS_DELTA:
767 | case TS_DELTA_DIS:
768 | case TS_EPOCH:
769 | /* Can't happen - see iostat_init. */
770 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "ui/cli/tap-iostat.c", 770, __func__, "assertion \"not reached\" failed");
771 | break;
772 | default:
773 | break;
774 | }
775 | }
776 |
777 | static char*
778 | iostat_get_item_value(const io_stat_t *iot, io_stat_item_t *item, const char *fmt, unsigned j, uint64_t interval)
779 | {
780 | uint32_t num;
781 | unsigned type, ftype;
782 |
783 | type = iot->calc_type[j];
784 |
785 | if (item) {
786 | switch (type) {
787 | case CALC_TYPE_FRAMES0:
788 | return g_strdup_printf(fmt, item->frames);
789 | case CALC_TYPE_BYTES1:
790 | case CALC_TYPE_COUNT3:
791 | return g_strdup_printf(fmt, item->counter);
792 | case CALC_TYPE_FRAMES_AND_BYTES2:
793 | return g_strdup_printf(fmt, item->frames, item->counter);
794 |
795 | case CALC_TYPE_SUM4:
796 | case CALC_TYPE_MIN5:
797 | case CALC_TYPE_MAX6:
798 | ftype = proto_registrar_get_ftype(iot->hf_indexes[j]);
799 | switch (ftype) {
800 | case FT_FLOAT:
801 | return g_strdup_printf(fmt, item->float_counter);
802 | case FT_DOUBLE:
803 | return g_strdup_printf(fmt, item->double_counter);
804 | case FT_RELATIVE_TIME:
805 | item->counter = (item->counter + UINT64_C(500)500UL) / UINT64_C(1000)1000UL;
806 | return g_strdup_printf(fmt,
807 | (int)(item->counter/UINT64_C(1000000)1000000UL),
808 | (int)(item->counter%UINT64_C(1000000)1000000UL));
809 | default:
810 | return g_strdup_printf(fmt, item->counter);
811 | }
812 | break;
813 |
814 | case CALC_TYPE_AVG7:
815 | num = item->num;
816 | if (num == 0)
817 | num = 1;
818 | ftype = proto_registrar_get_ftype(iot->hf_indexes[j]);
819 | switch (ftype) {
820 | case FT_FLOAT:
821 | return g_strdup_printf(fmt, item->float_counter/num);
822 | case FT_DOUBLE:
823 | return g_strdup_printf(fmt, item->double_counter/num);
824 | case FT_RELATIVE_TIME:
825 | item->counter = ((item->counter / (uint64_t)num) + UINT64_C(500)500UL) / UINT64_C(1000)1000UL;
826 | return g_strdup_printf(fmt,
827 | (int)(item->counter/UINT64_C(1000000)1000000UL),
828 | (int)(item->counter%UINT64_C(1000000)1000000UL));
829 | default:
830 | return g_strdup_printf(fmt, item->counter / (uint64_t)num);
831 | }
832 | break;
833 |
834 | case CALC_TYPE_LOAD8:
835 | ftype = proto_registrar_get_ftype(iot->hf_indexes[j]);
836 | switch (ftype) {
837 | case FT_RELATIVE_TIME:
838 | return g_strdup_printf(fmt,
839 | (int) (item->counter/interval),
840 | (int)((item->counter%interval)*UINT64_C(1000000)1000000UL / interval));
841 | }
842 | break;
843 | }
844 | }
845 | return g_strdup_printf(fmt, (uint64_t)0, (uint64_t)0);
846 | }
847 |
848 | /* Calc the total width of each row in the stats table and build the printf format string for each
849 | * column based on its field type, width, and name length.
850 | * NOTE: The magnitude of all types including float and double are stored in iot->max_vals which
851 | * is an *integer*. */
852 | static unsigned
853 | iostat_calc_cols_width_and_fmt(io_stat_t *iot, uint64_t interval, column_width* col_w, char**fmts)
854 | {
855 | unsigned tabrow_w, type, ftype, namelen;
856 | unsigned fr_mag; /* The magnitude of the max frame number in this column */
857 | unsigned val_mag; /* The magnitude of the max value in this column */
858 | char *fmt = NULL((void*)0);
859 |
860 | tabrow_w = 0;
861 | for (unsigned j=0; j < iot->num_cols; j++) {
862 | type = iot->calc_type[j];
863 | if (type == CALC_TYPE_FRAMES_AND_BYTES2) {
864 | namelen = 5;
865 | } else {
866 | namelen = (unsigned int)strlen(calc_type_table[type].func_name);
867 | }
868 | if (type == CALC_TYPE_FRAMES0
869 | || type == CALC_TYPE_FRAMES_AND_BYTES2) {
870 |
871 | fr_mag = magnitude(iot->max_frame[j], 15);
872 | fr_mag = MAX(6, fr_mag)(((6) > (fr_mag)) ? (6) : (fr_mag));
873 | col_w[j].fr = fr_mag;
874 | tabrow_w += col_w[j].fr + 3;
875 |
876 | if (type == CALC_TYPE_FRAMES0) {
877 | fmt = g_strdup_printf("%%%uu", fr_mag);
878 | } else {
879 | /* CALC_TYPE_FRAMES_AND_BYTES
880 | */
881 | val_mag = magnitude(iot->max_vals[j], 15);
882 | val_mag = MAX(5, val_mag)(((5) > (val_mag)) ? (5) : (val_mag));
883 | col_w[j].val = val_mag;
884 | tabrow_w += (col_w[j].val + 3);
885 | fmt = g_strdup_printf("%%%uu | %%%u"PRIu64"l" "u", fr_mag, val_mag);
886 | }
887 | if (fmt)
888 | fmts[j] = fmt;
889 | continue;
890 | }
891 | switch (type) {
892 | case CALC_TYPE_BYTES1:
893 | case CALC_TYPE_COUNT3:
894 |
895 | val_mag = magnitude(iot->max_vals[j], 15);
896 | val_mag = MAX(5, val_mag)(((5) > (val_mag)) ? (5) : (val_mag));
897 | col_w[j].val = val_mag;
898 | fmt = g_strdup_printf("%%%u"PRIu64"l" "u", val_mag);
899 | break;
900 |
901 | default:
902 | ftype = proto_registrar_get_ftype(iot->hf_indexes[j]);
903 | switch (ftype) {
904 | case FT_FLOAT:
905 | case FT_DOUBLE:
906 | val_mag = magnitude(iot->max_vals[j], 15);
907 | fmt = g_strdup_printf("%%%u.6f", val_mag);
908 | col_w[j].val = val_mag + 7;
909 | break;
910 | case FT_RELATIVE_TIME:
911 | /* Convert FT_RELATIVE_TIME field to seconds
912 | * CALC_TYPE_LOAD was already converted in iostat_packet() ) */
913 | if (type == CALC_TYPE_LOAD8) {
914 | iot->max_vals[j] /= interval;
915 | } else if (type != CALC_TYPE_AVG7) {
916 | iot->max_vals[j] = (iot->max_vals[j] + UINT64_C(500000000)500000000UL) / NANOSECS_PER_SEC1000000000UL;
917 | }
918 | val_mag = magnitude(iot->max_vals[j], 15);
919 | fmt = g_strdup_printf("%%%uu.%%06u", val_mag);
920 | col_w[j].val = val_mag + 7;
921 | break;
922 |
923 | default:
924 | val_mag = magnitude(iot->max_vals[j], 15);
925 | val_mag = MAX(namelen, val_mag)(((namelen) > (val_mag)) ? (namelen) : (val_mag));
926 | col_w[j].val = val_mag;
927 |
928 | switch (ftype) {
929 | case FT_UINT8:
930 | case FT_UINT16:
931 | case FT_UINT24:
932 | case FT_UINT32:
933 | case FT_UINT64:
934 | fmt = g_strdup_printf("%%%u"PRIu64"l" "u", val_mag);
935 | break;
936 | case FT_INT8:
937 | case FT_INT16:
938 | case FT_INT24:
939 | case FT_INT32:
940 | case FT_INT64:
941 | fmt = g_strdup_printf("%%%u"PRId64"l" "d", val_mag);
942 | break;
943 | }
944 | } /* End of ftype switch */
945 | } /* End of calc_type switch */
946 | tabrow_w += col_w[j].val + 3;
947 | if (fmt)
948 | fmts[j] = fmt;
949 | } /* End of for loop (columns) */
950 |
951 | return tabrow_w;
952 | }
953 |
954 | static void
955 | iostat_draw_filters(unsigned borderlen, const io_stat_t *iot)
956 | {
957 | const char *filter;
958 | size_t len_filt;
959 | GString *filt_str;
960 |
961 | /* Display the list of filters and their column numbers vertically */
962 | for (unsigned j=0; j<iot->num_cols; j++) {
963 | if (j == 0) {
964 | filt_str = g_string_new("| Col ");
965 | } else {
966 | filt_str = g_string_new("| ");
967 | };
968 | g_string_append_printf(filt_str, "%2u: ", j + 1);
969 | if (!iot->filters[j]) {
970 | /* An empty (no filter) comma field was specified */
971 | g_string_append(filt_str, "Frames and bytes")(__builtin_constant_p ("Frames and bytes") ? __extension__ ({ const char * const __val = ("Frames and bytes"); g_string_append_len_inline (filt_str, __val, (__val != ((void*)0)) ? (gssize) strlen (( (__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline (filt_str, "Frames and bytes", (gssize) -1));
972 | } else {
973 | filter = iot->filters[j];
974 | len_filt = strlen(filter);
975 | /* borderlen has been adjusted to try to accommodate the widest
976 | * filter, but only up to a limit (currently 102 bytes), and so
977 | * filters wider than that must still wrap. */
978 | /* 11 is the length of "| Col XX: " plus the trailing "|" */
979 | size_t max_w = borderlen - 11;
980 |
981 | while (len_filt > max_w) {
982 | const char *pos;
983 | size_t len;
984 | unsigned int next_start;
985 |
986 | /* Find the pos of the last space in filter up to max_w. If a
987 | * space is found, copy up to that space; otherwise, wrap the
988 | * filter at max_w. */
989 | pos = g_strrstr_len(filter, max_w, " ");
990 | if (pos) {
991 | len = (size_t)(pos-filter);
992 | /* Skip the space when wrapping. */
993 | next_start = (unsigned int) len+1;
994 | } else {
995 | len = max_w;
996 | next_start = (unsigned int)len;
997 | }
998 | g_string_append_len(filt_str, filter, len)g_string_append_len_inline (filt_str, filter, len);
999 | g_string_append_printf(filt_str, "%*s", (int)(borderlen - filt_str->len), "|");
1000 |
1001 | puts(filt_str->str);
1002 | g_string_free(filt_str, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) ( (filt_str), ((!(0)))) : g_string_free_and_steal (filt_str)) : (g_string_free) ((filt_str), ((!(0)))));
1003 |
1004 | filt_str = g_string_new("| ");
1005 | filter = &filter[next_start];
1006 | len_filt = strlen(filter);
1007 | }
1008 |
1009 | g_string_append(filt_str, filter)(__builtin_constant_p (filter) ? __extension__ ({ const char * const __val = (filter); g_string_append_len_inline (filt_str , __val, (__val != ((void*)0)) ? (gssize) strlen (((__val) + ! (__val))) : (gssize) -1); }) : g_string_append_len_inline (filt_str , filter, (gssize) -1));
1010 | }
1011 | g_string_append_printf(filt_str, "%*s", (int)(borderlen - filt_str->len), "|");
1012 | puts(filt_str->str);
1013 | g_string_free(filt_str, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) ( (filt_str), ((!(0)))) : g_string_free_and_steal (filt_str)) : (g_string_free) ((filt_str), ((!(0)))));
1014 | }
1015 | }
1016 |
1017 | static void
1018 | iostat_draw_header(unsigned borderlen, const io_stat_t *iot, const nstime_t *duration, const nstime_t *interval, ws_tsprec_e invl_prec)
1019 | {
1020 | unsigned i;
1021 | char time_buf[NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z")];
1022 |
1023 | /* Display the top border */
1024 | printf("\n");
1025 | for (i=0; i<borderlen; i++)
1026 | printf("=");
1027 |
1028 | printf("\n|%-*s|\n", borderlen - 2, " IO Statistics");
1029 | printf("|%-*s|\n", borderlen - 2, "");
1030 |
1031 | /* For some reason, we print the total duration in microsecond precision
1032 | * here if the interval is in seconds precision, and use the interval
1033 | * precision otherwise.
1034 | */
1035 | ws_tsprec_e dur_prec = (invl_prec == WS_TSPREC_SEC) ? WS_TSPREC_USEC : invl_prec;
1036 | nstime_t dur_rounded;
1037 | nstime_rounded(&dur_rounded, duration, dur_prec);
1038 | int dur_mag = magnitude(duration->secs, 5);
1039 | int dur_w = dur_mag + (invl_prec == 0 ? 0 : invl_prec+1);
1040 |
1041 | GString *dur_str = g_string_new("| Duration: ");
1042 | display_signed_time(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"), &dur_rounded, dur_prec);
1043 | g_string_append_printf(dur_str, "%*s secs", dur_w, time_buf);
1044 | g_string_append_printf(dur_str, "%*s", (int)(borderlen - dur_str->len), "|");
1045 | puts(dur_str->str);
1046 | g_string_free(dur_str, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) ( (dur_str), ((!(0)))) : g_string_free_and_steal (dur_str)) : ( g_string_free) ((dur_str), ((!(0)))));
1047 |
1048 | GString *invl_str = g_string_new("| Interval: ");
1049 | display_signed_time(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"), interval, invl_prec);
1050 | g_string_append_printf(invl_str, "%*s secs", dur_w, time_buf);
1051 | g_string_append_printf(invl_str, "%*s", (int)(borderlen - invl_str->len), "|");
1052 | puts(invl_str->str);
1053 | g_string_free(invl_str, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) ( (invl_str), ((!(0)))) : g_string_free_and_steal (invl_str)) : (g_string_free) ((invl_str), ((!(0)))));
1054 |
1055 | printf("|%-*s|\n", borderlen - 2, "");
1056 |
1057 | iostat_draw_filters(borderlen, iot);
1058 |
1059 | printf("|-");
1060 | for (i=0; i<borderlen-3; i++) {
1061 | printf("-");
1062 | }
1063 | printf("|\n");
1064 | }
1065 |
1066 | static void
1067 | iostat_draw_header_row(unsigned borderlen, const io_stat_t *iot, const column_width *col_w, unsigned invl_col_w, unsigned tabrow_w)
1068 | {
1069 | unsigned j, type, numpad = 1;
1070 | char *filler_s = NULL((void*)0);
1071 |
1072 | /* Display spaces above "Interval (s)" label */
1073 | printf("|%*s", invl_col_w - 1, "|");
1074 |
1075 | /* Display column number headers */
1076 | for (j=0; j < iot->num_cols; j++) {
1077 | int padding;
1078 | if (iot->calc_type[j] == CALC_TYPE_FRAMES_AND_BYTES2)
1079 | padding = col_w[j].fr + col_w[j].val + 3;
1080 | else if (iot->calc_type[j] == CALC_TYPE_FRAMES0)
1081 | padding = col_w[j].fr;
1082 | else
1083 | padding = col_w[j].val;
1084 |
1085 | printf("%-2d%*s|", j+1, padding, "");
1086 | }
1087 | if (tabrow_w < borderlen) {
1088 | filler_s = g_strdup_printf("%*s", borderlen - tabrow_w, "|");
1089 | printf("%s", filler_s);
1090 | }
1091 | printf("\n");
1092 |
1093 | GString *timestamp_str;
1094 | switch (timestamp_get_type()) {
1095 | case TS_ABSOLUTE:
1096 | case TS_UTC:
1097 | timestamp_str = g_string_new("| Time ");
1098 | break;
1099 | case TS_ABSOLUTE_WITH_YMD:
1100 | case TS_ABSOLUTE_WITH_YDOY:
1101 | case TS_UTC_WITH_YMD:
1102 | case TS_UTC_WITH_YDOY:
1103 | timestamp_str = g_string_new("| Date and time");
1104 | break;
1105 | case TS_RELATIVE:
1106 | case TS_RELATIVE_CAP:
1107 | case TS_NOT_SET:
1108 | timestamp_str = g_string_new("| Interval");
1109 | break;
1110 | default:
1111 | timestamp_str = g_string_new(NULL((void*)0));
1112 | break;
1113 | }
1114 |
1115 | printf("%s%*s", timestamp_str->str, (int)(invl_col_w - timestamp_str->len), "|");
1116 | g_string_free(timestamp_str, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) ( (timestamp_str), ((!(0)))) : g_string_free_and_steal (timestamp_str )) : (g_string_free) ((timestamp_str), ((!(0)))));
1117 |
1118 | /* Display the stat label in each column */
1119 | for (j=0; j < iot->num_cols; j++) {
1120 | type = iot->calc_type[j];
1121 | if (type == CALC_TYPE_FRAMES0) {
1122 | printcenter (calc_type_table[type].func_name, col_w[j].fr, numpad);
1123 | } else if (type == CALC_TYPE_FRAMES_AND_BYTES2) {
1124 | printcenter ("Frames", col_w[j].fr, numpad);
1125 | printcenter ("Bytes", col_w[j].val, numpad);
1126 | } else {
1127 | printcenter (calc_type_table[type].func_name, col_w[j].val, numpad);
1128 | }
1129 | }
1130 | if (filler_s) {
1131 | printf("%s", filler_s);
1132 | }
1133 | printf("\n|-");
1134 |
1135 | for (j=0; j<tabrow_w-3; j++)
1136 | printf("-");
1137 | printf("|");
1138 |
1139 | if (filler_s) {
1140 | printf("%s", filler_s);
1141 | g_free(filler_s);
1142 | }
1143 |
1144 | printf("\n");
1145 | }
1146 |
1147 | static void
1148 | iostat_draw(void *arg)
1149 | {
1150 | uint64_t interval, duration, t, real_invl, dv;
1151 | unsigned int i, j, k, num_cols, num_rows, dur_secs, dur_mag,
1152 | invl_mag, invl_prec, tabrow_w, borderlen, invl_col_w, maxfltr_w;
1153 | char **fmts, *fmt = NULL((void*)0);
1154 | io_stat_item_t *mit, **stat_cols, *item, **item_in_column;
1155 | bool_Bool last_row = false0;
1156 | io_stat_t *iot;
1157 | column_width *col_w;
1158 | char time_buf[NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z")];
1159 |
1160 | mit = (io_stat_item_t *)arg;
1161 | iot = mit->parent;
1162 | num_cols = iot->num_cols;
1163 | col_w = g_new(column_width, num_cols)((column_width *) g_malloc_n ((num_cols), sizeof (column_width )));
1164 | fmts = g_new0(char *, num_cols)((char * *) g_malloc0_n ((num_cols), sizeof (char *)));
1165 | duration = ((uint64_t)cfile.elapsed_time.secs * UINT64_C(1000000)1000000UL) +
1166 | (uint64_t)((cfile.elapsed_time.nsecs + 500) / 1000);
1167 |
1168 | /* Store the pointer to each stat column */
1169 | stat_cols = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols);
1170 | for (j=0; j<num_cols; j++)
1171 | stat_cols[j] = &iot->items[j];
1172 |
1173 | /* The following prevents gross inaccuracies when the user specifies an interval that is greater
1174 | * than the capture duration. */
1175 | if (iot->interval > duration || iot->interval == UINT64_MAX(18446744073709551615UL)) {
1176 | interval = duration;
1177 | iot->interval = UINT64_MAX(18446744073709551615UL);
1178 | } else {
1179 | interval = iot->interval;
1180 | }
1181 |
1182 | /* Calc the capture duration's magnitude (dur_mag) */
1183 | dur_secs = (unsigned int)(duration/UINT64_C(1000000)1000000UL);
1184 | dur_mag = magnitude((uint64_t)dur_secs, 5);
1185 |
1186 | /* Calc the interval's magnitude */
1187 | invl_mag = magnitude(interval/UINT64_C(1000000)1000000UL, 5);
1188 |
1189 | /* Set or get the interval precision */
1190 | if (interval == duration) {
1191 | /*
1192 | * An interval arg of 0 or an interval size exceeding the capture duration was specified.
1193 | * Set the decimal precision of duration based on its magnitude. */
1194 | if (dur_mag >= 2)
1195 | invl_prec = 1;
1196 | else if (dur_mag == 1)
1197 | invl_prec = 3;
1198 | else
1199 | invl_prec = 6;
1200 |
1201 | borderlen = 30 + dur_mag + (invl_prec == 0 ? 0 : invl_prec+1);
1202 | } else {
1203 | invl_prec = iot->invl_prec;
1204 | borderlen = 25 + MAX(invl_mag,dur_mag)(((invl_mag) > (dur_mag)) ? (invl_mag) : (dur_mag)) + (invl_prec == 0 ? 0 : invl_prec+1);
1205 | }
1206 |
1207 | /* Round the duration according to invl_prec */
1208 | dv = 1000000;
1209 | for (i=0; i<invl_prec; i++)
1210 | dv /= 10;
1211 | if ((duration%dv) > 5*(dv/10)) {
1212 | duration += 5*(dv/10);
1213 | duration = (duration/dv) * dv;
1214 | dur_secs = (unsigned int)(duration/UINT64_C(1000000)1000000UL);
1215 | /*
1216 | * Recalc dur_mag in case rounding has increased its magnitude */
1217 | dur_mag = magnitude((uint64_t)dur_secs, 5);
1218 | }
1219 | if (iot->interval == UINT64_MAX(18446744073709551615UL))
1220 | interval = duration;
1221 |
1222 | /* This is the max width of a duration. */
1223 | int dur_w = dur_mag + (invl_prec == 0 ? 0 : invl_prec+1);
1224 | /* Add a space on the side, and make sure the width is at least as wide
1225 | * as "Dur". ("Dur" does not need a space between it and "|".) */
1226 | dur_w = MAX(dur_w + 1, 3)(((dur_w + 1) > (3)) ? (dur_w + 1) : (3));
1227 |
1228 | /* Update the width of the time interval column if date is shown */
1229 | switch (timestamp_get_type()) {
1230 | case TS_ABSOLUTE_WITH_YMD:
1231 | case TS_ABSOLUTE_WITH_YDOY:
1232 | case TS_UTC_WITH_YMD:
1233 | case TS_UTC_WITH_YDOY:
1234 | // We don't show more than 6 fractional digits (+Z) currently.
1235 | // NSTIME_ISO8601_BUFSIZE is enough room for 9 frac digits + Z + '\0'
1236 | // That's 4 extra characters, which leaves room for the "| |".
1237 | invl_col_w = NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z") + invl_prec - 6;
1238 | break;
1239 |
1240 | default:
1241 | /* Calc the width of the time interval column (incl borders and padding,
1242 | * which are "|" and " <" on each side.) */
1243 | invl_col_w = 2*(dur_w + 3);
1244 | break;
1245 | }
1246 |
1247 | /* Calculate the width and format string of all the other columns, and add
1248 | * the total to the interval column width for the entire total. */
1249 | tabrow_w = invl_col_w + iostat_calc_cols_width_and_fmt(iot, interval, col_w, fmts);
1250 |
1251 | borderlen = MAX(borderlen, tabrow_w)(((borderlen) > (tabrow_w)) ? (borderlen) : (tabrow_w));
1252 |
1253 | /* Calc the max width of the list of filters. */
1254 | maxfltr_w = 0;
1255 | for (j=0; j<num_cols; j++) {
1256 | if (iot->filters[j]) {
1257 | k = (unsigned int) (strlen(iot->filters[j]) + 11);
1258 | maxfltr_w = MAX(maxfltr_w, k)(((maxfltr_w) > (k)) ? (maxfltr_w) : (k));
1259 | } else {
1260 | maxfltr_w = MAX(maxfltr_w, 26)(((maxfltr_w) > (26)) ? (maxfltr_w) : (26));
1261 | }
1262 | }
1263 | /* The stat table is not wrapped (by tshark) but filter is wrapped at the width of the stats table
1264 | * (which currently = borderlen); however, if the filter width exceeds the table width and the
1265 | * table width is less than 102 bytes, set borderlen to the lesser of the max filter width and 102.
1266 | * The filters will wrap at the lesser of borderlen-2 and the last space in the filter.
1267 | * NOTE: 102 is the typical size of a user window when the font is fixed width (e.g., COURIER 10).
1268 | * XXX: A pref could be added to change the max width from the default size of 102. */
1269 | if (maxfltr_w > borderlen && borderlen < 102)
1270 | borderlen = MIN(maxfltr_w, 102)(((maxfltr_w) < (102)) ? (maxfltr_w) : (102));
1271 |
1272 | /* Prevent double right border by adding a space */
1273 | if (borderlen-tabrow_w == 1)
1274 | borderlen++;
1275 |
1276 | nstime_t invl_time = NSTIME_INIT_SECS_USECS(interval/UINT64_C(1000000), interval%UINT64_C(1000000)){(interval/1000000UL) + ((interval%1000000UL) / 1000000), ((interval %1000000UL) % 1000000) * 1000};
1277 | iostat_draw_header(borderlen, iot, &cfile.elapsed_time, &invl_time, invl_prec);
1278 |
1279 | iostat_draw_header_row(borderlen, iot, col_w, invl_col_w, tabrow_w);
1280 |
1281 | t = 0;
1282 |
1283 | if (interval == 0 || duration == 0) {
1284 | num_rows = 0;
1285 | } else {
1286 | num_rows = (unsigned int)(duration/interval) + ((unsigned int)(duration%interval) > 0 ? 1 : 0);
1287 | }
1288 |
1289 | /* Load item_in_column with the first item in each column */
1290 | item_in_column = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols);
1291 | for (j=0; j<num_cols; j++) {
1292 | item_in_column[j] = stat_cols[j];
1293 | }
1294 |
1295 | /* Display the table values
1296 | *
1297 | * The outer loop is for time interval rows and the inner loop is for stat column items.*/
1298 | for (i=0; i<num_rows; i++) {
1299 |
1300 | if (i == num_rows-1)
1301 | last_row = true1;
1302 |
1303 | /* Compute the interval for this row */
1304 | if (!last_row) {
1305 | real_invl = interval;
1306 | } else {
1307 | real_invl = duration - t;
1308 | }
1309 |
1310 | /* Patch for Absolute Time */
1311 | /* XXX - has a Y2.038K problem with 32-bit time_t */
1312 | nstime_t the_time = NSTIME_INIT_SECS_USECS(t / 1000000, t % 1000000){(t / 1000000) + ((t % 1000000) / 1000000), ((t % 1000000) % 1000000 ) * 1000};
1313 |
1314 | /* Display the interval for this row */
1315 | /* Get the string representing the start time of this interval. */
1316 | fill_start_time(iot, &the_time, invl_prec, time_buf);
1317 | /* Now add the surrounding column information according to our
1318 | * output format (currently, only text table is supported.) */
1319 | switch (timestamp_get_type()) {
1320 |
1321 | case TS_RELATIVE:
1322 | case TS_RELATIVE_CAP:
1323 | case TS_NOT_SET:
1324 | /* For relative times, we show both ends of the interval. */
1325 | printf("|%*s <", dur_w, time_buf);
1326 | if (invl_prec == 0 && last_row) {
1327 | g_strlcpy(time_buf, "Dur", NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"));
1328 | } else {
1329 | nstime_add(&the_time, &invl_time)nstime_sum(&the_time, &the_time, &invl_time);
1330 | display_signed_time(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"), &the_time, invl_prec);
1331 | }
1332 | printf("> %-*s|", dur_w, time_buf);
1333 | break;
1334 | default:
1335 | printf("| %-*s |", invl_col_w - 4, time_buf);
1336 | break;
1337 | }
1338 |
1339 | /* Display stat values in each column for this row */
1340 | for (j=0; j<num_cols; j++) {
1341 | fmt = fmts[j];
1342 | item = item_in_column[j];
1343 |
1344 | /* To try to optimize speed, we could copy the value string with
1345 | * snprintf into a pre-allocated buffer with the maximum column
1346 | * width, which we determined (though that's more error-prone.)
1347 | */
1348 | char *value = iostat_get_item_value(iot, item, fmt, j, real_invl);
1349 | printf(" %s |", value);
1350 | g_free(value);
1351 |
1352 | if (item)
1353 | item_in_column[j] = item_in_column[j]->next;
1354 | }
1355 | if (tabrow_w < borderlen) {
1356 | printf("%*s", borderlen - tabrow_w, "|");
1357 | }
1358 | printf("\n");
1359 | t += interval;
1360 |
1361 | }
1362 | for (i=0; i<borderlen; i++) {
1363 | printf("=");
1364 | }
1365 | printf("\n");
1366 | g_free(col_w);
1367 | for (i=0; i<num_cols; ++i) {
1368 | g_free(fmts[i]);
1369 | }
1370 | g_free(fmts);
1371 | g_free(stat_cols);
1372 | g_free(item_in_column);
1373 | }
1374 |
1375 | /* A new capture file is being loaded (or the current one reloaded),
1376 | * reset our statistics.
1377 | */
1378 | static void
1379 | iostat_reset(void *arg)
1380 | {
1381 | io_stat_item_t *mit = (io_stat_item_t *)arg;
1382 | io_stat_t *io = mit->parent;
1383 |
1384 | nstime_set_unset(&io->start_time);
1385 | io->last_relative_time = UINT64_C(0)0UL;
1386 | for (unsigned int i=0; i<io->num_cols; i++) {
1387 | iostat_item_reset(&io->items[i]);
1388 | io->max_vals[i] = 0;
1389 | io->max_frame[i] = 0;
1390 | }
1391 | }
1392 |
1393 | /* Our listener is being removed, free our memory. */
1394 | static void
1395 | iostat_finish(void *arg)
1396 | {
1397 | io_stat_item_t *mit = (io_stat_item_t *)arg;
1398 | io_stat_t *io = mit->parent;
1399 | iostat_io_free(io);
1400 | }
1401 |
1402 | /*
1403 | * Register a new iostat tap for column number i.
1404 | * The new tap's tapdata (see doc/README.tapping) is io->items[i], not io itself.
1405 | * We only set the draw/reset/finish functions if i == 0 so everything is handled only once.
1406 | */
1407 | static bool_Bool
1408 | register_io_tap(io_stat_t *io, unsigned int i, const char *filter, GString *err)
1409 | {
1410 | GString *error_string;
1411 | const char *flt;
1412 | int j;
1413 | size_t namelen;
1414 | const char *p, *parenp;
1415 | char *field;
1416 | header_field_info *hfi;
1417 |
1418 | io->items[i].prev = &io->items[i];
1419 | io->items[i].next = NULL((void*)0);
1420 | io->items[i].parent = io;
1421 | io->items[i].start_time = 0;
1422 | io->items[i].frames = 0;
1423 | io->items[i].counter = 0;
1424 | io->items[i].num = 0;
1425 |
1426 | io->filters[i] = filter;
1427 | flt = filter;
1428 |
1429 | io->calc_type[i] = CALC_TYPE_FRAMES_AND_BYTES2;
1430 | field = NULL((void*)0);
1431 | hfi = NULL((void*)0);
1432 | for (j=0; calc_type_table[j].func_name; j++) {
1433 | namelen = strlen(calc_type_table[j].func_name);
1434 | if (filter && strncmp(filter, calc_type_table[j].func_name, namelen) == 0) {
1435 | io->calc_type[i] = calc_type_table[j].calc_type;
1436 | io->items[i].colnum = i;
1437 | if (*(filter+namelen) == '(') {
1438 | p = filter+namelen+1;
1439 | parenp = strchr(p, ')');
1440 | if (!parenp) {
1441 | cmdarg_err("\ntshark: Closing parenthesis missing from calculated expression.\n");
1442 | return false0;
1443 | }
1444 |
1445 | if (io->calc_type[i] == CALC_TYPE_FRAMES0 || io->calc_type[i] == CALC_TYPE_BYTES1) {
1446 | if (parenp != p) {
1447 | cmdarg_err("\ntshark: %s does not require or allow a field name within the parens.\n",
1448 | calc_type_table[j].func_name);
1449 | return false0;
1450 | }
1451 | } else {
1452 | if (parenp == p) {
1453 | /* bail out if a field name was not specified */
1454 | cmdarg_err("\ntshark: You didn't specify a field name for %s(*).\n",
1455 | calc_type_table[j].func_name);
1456 | return false0;
1457 | }
1458 | }
1459 |
1460 | field = (char *)g_malloc(parenp-p+1);
1461 | memcpy(field, p, parenp-p);
1462 | field[parenp-p] = '\0';
1463 | flt = parenp + 1;
1464 | if (io->calc_type[i] == CALC_TYPE_FRAMES0 || io->calc_type[i] == CALC_TYPE_BYTES1)
1465 | break;
1466 | hfi = proto_registrar_get_byname(field);
1467 | if (!hfi) {
1468 | cmdarg_err("\ntshark: There is no field named '%s'.\n", field);
1469 | g_free(field);
1470 | return false0;
1471 | }
1472 |
1473 | io->hf_indexes[i] = hfi->id;
1474 | break;
1475 | }
1476 | } else {
1477 | if (io->calc_type[i] == CALC_TYPE_FRAMES0 || io->calc_type[i] == CALC_TYPE_BYTES1)
1478 | flt = "";
1479 | io->items[i].colnum = i;
1480 | }
1481 | }
1482 | if (hfi && !(io->calc_type[i] == CALC_TYPE_BYTES1 ||
1483 | io->calc_type[i] == CALC_TYPE_FRAMES0 ||
1484 | io->calc_type[i] == CALC_TYPE_FRAMES_AND_BYTES2)) {
1485 | /* check that the type is compatible */
1486 | switch (hfi->type) {
1487 | case FT_UINT8:
1488 | case FT_UINT16:
1489 | case FT_UINT24:
1490 | case FT_UINT32:
1491 | case FT_UINT64:
1492 | case FT_INT8:
1493 | case FT_INT16:
1494 | case FT_INT24:
1495 | case FT_INT32:
1496 | case FT_INT64:
1497 | /* these types support all calculations */
1498 | break;
1499 | case FT_FLOAT:
1500 | case FT_DOUBLE:
1501 | /* these types only support SUM, COUNT, MAX, MIN, AVG */
1502 | switch (io->calc_type[i]) {
1503 | case CALC_TYPE_SUM4:
1504 | case CALC_TYPE_COUNT3:
1505 | case CALC_TYPE_MAX6:
1506 | case CALC_TYPE_MIN5:
1507 | case CALC_TYPE_AVG7:
1508 | break;
1509 | default:
1510 | cmdarg_err("\ntshark: %s is a float field, so %s(*) calculations are not supported on it.",
1511 | field,
1512 | calc_type_table[j].func_name);
1513 | return false0;
1514 | }
1515 | break;
1516 | case FT_RELATIVE_TIME:
1517 | /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */
1518 | switch (io->calc_type[i]) {
1519 | case CALC_TYPE_SUM4:
1520 | case CALC_TYPE_COUNT3:
1521 | case CALC_TYPE_MAX6:
1522 | case CALC_TYPE_MIN5:
1523 | case CALC_TYPE_AVG7:
1524 | case CALC_TYPE_LOAD8:
1525 | break;
1526 | default:
1527 | cmdarg_err("\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.",
1528 | field,
1529 | calc_type_table[j].func_name);
1530 | return false0;
1531 | }
1532 | break;
1533 | default:
1534 | /*
1535 | * XXX - support all operations on floating-point
1536 | * numbers?
1537 | */
1538 | if (io->calc_type[i] != CALC_TYPE_COUNT3) {
1539 | cmdarg_err("\ntshark: %s doesn't have integral values, so %s(*) "
1540 | "calculations are not supported on it.\n",
1541 | field,
1542 | calc_type_table[j].func_name);
1543 | return false0;
1544 | }
1545 | break;
1546 | }
1547 | }
1548 | g_free(field);
1549 |
1550 | error_string = register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE0x00000001,
1551 | i ? NULL((void*)0) : iostat_reset,
1552 | iostat_packet,
1553 | i ? NULL((void*)0) : iostat_draw,
1554 | i ? NULL((void*)0) : iostat_finish);
1555 | if (error_string) {
1556 | /* Accumulate errors about all the possible filters tried at the same
1557 | * starting character.
1558 | */
1559 | if (err->len) {
1560 | g_string_append_c(err, '\n')g_string_append_c_inline (err, '\n');
1561 | }
1562 | g_string_append(err, error_string->str)(__builtin_constant_p (error_string->str) ? __extension__ ( { const char * const __val = (error_string->str); g_string_append_len_inline (err, __val, (__val != ((void*)0)) ? (gssize) strlen (((__val ) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline (err, error_string->str, (gssize) -1));
1563 | g_string_free(error_string, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) ( (error_string), ((!(0)))) : g_string_free_and_steal (error_string )) : (g_string_free) ((error_string), ((!(0)))));
1564 | return false0;
1565 | }
1566 |
1567 | /* On success, clear old errors (from splitting on internal commas). */
1568 | g_string_truncate(err, 0)g_string_truncate_inline (err, 0);
1569 | return true1;
1570 | }
1571 |
1572 | static bool_Bool
1573 | iostat_init(const char *opt_arg, void *userdata _U___attribute__((unused)))
1574 | {
1575 | double interval_float;
1576 | uint32_t idx = 0;
1577 | unsigned int i;
1578 | io_stat_t *io;
1579 | const char *filters, *str, *pos;
1580 |
1581 | io_decimal_point = localeconv()->decimal_point;
1582 |
1583 | /* XXX - Why can't the last character be a comma? Shouldn't it be
1584 | * fine for the last filter to be empty? Even in the case of locales
1585 | * that use ',' for the decimal separator, there shouldn't be any
1586 | * difference between interpreting a terminating ',' as a decimal
1587 | * point for the interval, and interpreting it as a separator followed
1588 | * by an empty filter.
1589 | */
1590 | if ((*(opt_arg+(strlen(opt_arg)-1)) == ',') ||
1 | Assuming the condition is false | →
4 | ← | Taking false branch | →
1591 | (sscanf(opt_arg, "io,stat,%lf%n", &interval_float, (int *)&idx) != 1) ||
2 | ← | Assuming the condition is false | →
1592 | (idx < 8)) {
3 | ← | Assuming 'idx' is >= 8 | →
1593 | cmdarg_err("\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1594 | return false0;
1595 | }
1596 |
1597 | filters = opt_arg+idx;
1598 | if (*filters) {
5 | ← | Assuming the condition is true | →
6 | ← | Taking true branch | →
1599 | if (*filters != ',') {
7 | ← | Assuming the condition is false | →
8 | ← | Taking false branch | →
1600 | /* For locales that use ',' instead of '.', the comma might
1601 | * have been consumed during the floating point conversion. */
1602 | --filters;
1603 | if (*filters != ',') {
1604 | cmdarg_err("\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n");
1605 | return false0;
1606 | }
1607 | }
1608 | }
1609 | /* filters now either starts with ',' or '\0' */
1610 |
1611 | switch (timestamp_get_type()) {
9 | ← | Control jumps to the 'default' case at line 1617 | →
1612 | case TS_DELTA:
1613 | case TS_DELTA_DIS:
1614 | case TS_EPOCH:
1615 | cmdarg_err("\ntshark: invalid -t operand. io,stat only supports -t <r|a|ad|adoy|u|ud|udoy>\n");
1616 | return false0;
1617 | default:
1618 | break;
10 | ← | Execution continues on line 1621 | →
1619 | }
1620 |
1621 | io = g_new0(io_stat_t, 1)((io_stat_t *) g_malloc0_n ((1), sizeof (io_stat_t)));
1622 | io->last_relative_time = UINT64_C(0)0UL;
1623 |
1624 | /* If interval is 0, calculate statistics over the whole file by setting the interval to
1625 | * UINT64_MAX */
1626 | if (interval_float == 0) {
11 | ← | Assuming 'interval_float' is equal to 0 | →
12 | ← | Taking true branch | →
1627 | io->interval = UINT64_MAX(18446744073709551615UL);
1628 | io->invl_prec = 0;
1629 | } else {
1630 | /* Set interval to the number of us rounded to the nearest integer */
1631 | io->interval = (uint64_t)(interval_float * 1000000.0 + 0.5);
1632 | /*
1633 | * Determine what interval precision the user has specified */
1634 | io->invl_prec = 6;
1635 | for (i=10; i<10000000; i*=10) {
1636 | if (io->interval%i > 0)
1637 | break;
1638 | io->invl_prec--;
1639 | }
1640 | if (io->invl_prec == 0) {
1641 | /* The precision is zero but if the user specified one of more zeros after the decimal point,
1642 | they want that many decimal places shown in the table for all time intervals except
1643 | response time values such as smb.time which always have 6 decimal places of precision.
1644 | This feature is useful in cases where for example the duration is 9.1, you specify an
1645 | interval of 1 and the last interval becomes "9 <> 9". If the interval is instead set to
1646 | 1.1, the last interval becomes
1647 | last interval is rounded up to value that is greater than the duration. */
1648 | const char *invl_start = opt_arg+8;
1649 | unsigned invl_len;
1650 |
1651 | invl_start = strpbrk(invl_start, ".,");
1652 |
1653 | if (invl_start != NULL((void*)0) && *invl_start == '.') {
1654 | invl_len = (unsigned)strcspn(invl_start + 1, ",");
1655 | if (invl_len)
1656 | io->invl_prec = MIN(invl_len, 6U)(((invl_len) < (6U)) ? (invl_len) : (6U));
1657 | }
1658 | }
1659 | }
1660 | if (io->interval
12.1 | Field 'interval' is >= 1
< 1) {
13 | ← | Taking false branch | →
1661 | cmdarg_err("\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n");
1662 | iostat_io_free(io);
1663 | return false0;
1664 | }
1665 |
1666 | /* Find how many ',' separated filters we have */
1667 | /* Filter can have internal commas, so this is only an upper bound on the
1668 | * number of filters. In the display filter grammar, commas only appear
1669 | * inside delimiters (quoted strings, slices, sets, and functions), so
1670 | * splitting in the wrong place produces an invalid filter. That is, there
1671 | * can be at most only one valid interpretation (but might be none).
1672 | *
1673 | * XXX - If the grammar changes to allow commas in other places, then there
1674 | * is ambiguity.
1675 | *
1676 | * Perhaps ideally we'd verify the filters before doing allocation.
1677 | */
1678 | io->num_cols = 1;
1679 | nstime_set_unset(&io->start_time);
1680 |
1681 | if (*filters != '\0') {
14 | ← | Assuming the condition is false | →
15 | ← | Taking false branch | →
1682 | /* Eliminate the first comma. */
1683 | filters++;
1684 | str = filters;
1685 | while ((str = strchr(str, ','))) {
1686 | io->num_cols++;
1687 | str++;
1688 | }
1689 | }
1690 |
1691 | io->items = g_new0(io_stat_item_t, io->num_cols)((io_stat_item_t *) g_malloc0_n ((io->num_cols), sizeof (io_stat_item_t )));
1692 | io->filters = (const char **)g_malloc(sizeof(char *) * io->num_cols);
1693 | io->max_vals = g_new(uint64_t, io->num_cols)((uint64_t *) g_malloc_n ((io->num_cols), sizeof (uint64_t )));
1694 | io->max_frame = g_new(uint32_t, io->num_cols)((uint32_t *) g_malloc_n ((io->num_cols), sizeof (uint32_t )));
1695 | io->hf_indexes = g_new(int, io->num_cols)((int *) g_malloc_n ((io->num_cols), sizeof (int)));
1696 | io->calc_type = g_new(int, io->num_cols)((int *) g_malloc_n ((io->num_cols), sizeof (int)));
1697 |
1698 | for (i=0; i
15.1 | 'i' is < field 'num_cols'
<io->num_cols; i++) {
16 | ← | Loop condition is true. Entering loop body | →
17 | ← | Assuming 'i' is >= field 'num_cols' | →
18 | ← | Loop condition is false. Execution continues on line 1703 | →
1699 | io->max_vals[i] = 0;
1700 | io->max_frame[i] = 0;
1701 | }
1702 |
1703 | bool_Bool success;
1704 | GString *err = g_string_new(NULL((void*)0));
1705 |
1706 | /* Register a tap listener for each filter */
1707 | if (filters[0] == '\0') {
19 | ← | Assuming the condition is false | →
20 | ← | Taking false branch | →
1708 | success = register_io_tap(io, 0, NULL((void*)0), err);
1709 | } else {
1710 | char *filter;
1711 | i = 0;
1712 | str = filters;
1713 | pos = str;
1714 | while ((pos = strchr(pos, ',')) != NULL((void*)0)) {
21 | ← | Assuming the condition is true | →
22 | ← | Loop condition is true. Entering loop body | →
1715 | if (pos == str) {
23 | ← | Assuming 'pos' is not equal to 'str' | →
24 | ← | Taking false branch | →
1716 | /* Consecutive commas - an empty filter. */
1717 | filter = NULL((void*)0);
1718 | } else {
1719 | /* Likely a filter. */
1720 | filter = (char *)g_malloc((pos-str)+1);
25 | ← | Memory is allocated | →
1721 | (void) g_strlcpy( filter, str, (size_t) ((pos-str)+1));
1722 | filter = g_strstrip(filter)g_strchomp (g_strchug (filter));
1723 | }
1724 | success = register_io_tap(io, i, filter, err);
26 | ← | Potential leak of memory pointed to by 'filter'
1725 | /* Advance to the next position to look for commas. */
1726 | pos++;
1727 | if (success) {
1728 | /* Also advance the filter start on success. */
1729 | str = pos;
1730 | i++;
1731 | } else {
1732 | g_free(filter);
1733 | }
1734 | }
1735 | /* No more commas, the rest of the string is the last filter. */
1736 | filter = g_strstrip(g_strdup(str))g_strchomp (g_strchug (g_strdup_inline (str)));
1737 | if (*filter) {
1738 | success = register_io_tap(io, i, filter, err);
1739 | } else {
1740 | success = register_io_tap(io, i, NULL((void*)0), err);
1741 | }
1742 | if (success) {
1743 | i++;
1744 | }
1745 | io->num_cols = i;
1746 | }
1747 |
1748 | if (!success) {
1749 | cmdarg_err("\ntshark: Couldn't register io,stat tap: %s\n",
1750 | err->str);
1751 | g_string_free(err, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) ( (err), ((!(0)))) : g_string_free_and_steal (err)) : (g_string_free ) ((err), ((!(0)))));
1752 | iostat_io_free(io);
1753 | return false0;
1754 | }
1755 | g_string_free(err, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) ( (err), ((!(0)))) : g_string_free_and_steal (err)) : (g_string_free ) ((err), ((!(0)))));
1756 | return true1;
1757 |
1758 | }
1759 |
1760 | static stat_tap_ui iostat_ui = {
1761 | REGISTER_STAT_GROUP_GENERIC,
1762 | NULL((void*)0),
1763 | "io,stat",
1764 | iostat_init,
1765 | 0,
1766 | NULL((void*)0)
1767 | };
1768 |
1769 | void
1770 | register_tap_listener_iostat(void)
1771 | {
1772 | register_stat_tap_ui(&iostat_ui, NULL((void*)0));
1773 | }