File: | ui/cli/tap-iostat.c |
Warning: | line 1721, column 23 Potential leak of memory pointed to by 'filter' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* tap-iostat.c | |||
2 | * iostat 2002 Ronnie Sahlberg | |||
3 | * | |||
4 | * Wireshark - Network traffic analyzer | |||
5 | * By Gerald Combs <[email protected]> | |||
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((gpointer)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_NOT_SET: | |||
763 | display_signed_time(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"), rel_time, invl_prec); | |||
764 | break; | |||
765 | case TS_DELTA: | |||
766 | case TS_DELTA_DIS: | |||
767 | case TS_EPOCH: | |||
768 | /* Can't happen - see iostat_init. */ | |||
769 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "ui/cli/tap-iostat.c", 769, __func__, "assertion \"not reached\" failed"); | |||
770 | break; | |||
771 | default: | |||
772 | break; | |||
773 | } | |||
774 | } | |||
775 | ||||
776 | static char* | |||
777 | iostat_get_item_value(const io_stat_t *iot, io_stat_item_t *item, const char *fmt, unsigned j, uint64_t interval) | |||
778 | { | |||
779 | uint32_t num; | |||
780 | unsigned type, ftype; | |||
781 | ||||
782 | type = iot->calc_type[j]; | |||
783 | ||||
784 | if (item) { | |||
785 | switch (type) { | |||
786 | case CALC_TYPE_FRAMES0: | |||
787 | return g_strdup_printf(fmt, item->frames); | |||
788 | case CALC_TYPE_BYTES1: | |||
789 | case CALC_TYPE_COUNT3: | |||
790 | return g_strdup_printf(fmt, item->counter); | |||
791 | case CALC_TYPE_FRAMES_AND_BYTES2: | |||
792 | return g_strdup_printf(fmt, item->frames, item->counter); | |||
793 | ||||
794 | case CALC_TYPE_SUM4: | |||
795 | case CALC_TYPE_MIN5: | |||
796 | case CALC_TYPE_MAX6: | |||
797 | ftype = proto_registrar_get_ftype(iot->hf_indexes[j]); | |||
798 | switch (ftype) { | |||
799 | case FT_FLOAT: | |||
800 | return g_strdup_printf(fmt, item->float_counter); | |||
801 | case FT_DOUBLE: | |||
802 | return g_strdup_printf(fmt, item->double_counter); | |||
803 | case FT_RELATIVE_TIME: | |||
804 | item->counter = (item->counter + UINT64_C(500)500UL) / UINT64_C(1000)1000UL; | |||
805 | return g_strdup_printf(fmt, | |||
806 | (int)(item->counter/UINT64_C(1000000)1000000UL), | |||
807 | (int)(item->counter%UINT64_C(1000000)1000000UL)); | |||
808 | default: | |||
809 | return g_strdup_printf(fmt, item->counter); | |||
810 | } | |||
811 | break; | |||
812 | ||||
813 | case CALC_TYPE_AVG7: | |||
814 | num = item->num; | |||
815 | if (num == 0) | |||
816 | num = 1; | |||
817 | ftype = proto_registrar_get_ftype(iot->hf_indexes[j]); | |||
818 | switch (ftype) { | |||
819 | case FT_FLOAT: | |||
820 | return g_strdup_printf(fmt, item->float_counter/num); | |||
821 | case FT_DOUBLE: | |||
822 | return g_strdup_printf(fmt, item->double_counter/num); | |||
823 | case FT_RELATIVE_TIME: | |||
824 | item->counter = ((item->counter / (uint64_t)num) + UINT64_C(500)500UL) / UINT64_C(1000)1000UL; | |||
825 | return g_strdup_printf(fmt, | |||
826 | (int)(item->counter/UINT64_C(1000000)1000000UL), | |||
827 | (int)(item->counter%UINT64_C(1000000)1000000UL)); | |||
828 | default: | |||
829 | return g_strdup_printf(fmt, item->counter / (uint64_t)num); | |||
830 | } | |||
831 | break; | |||
832 | ||||
833 | case CALC_TYPE_LOAD8: | |||
834 | ftype = proto_registrar_get_ftype(iot->hf_indexes[j]); | |||
835 | switch (ftype) { | |||
836 | case FT_RELATIVE_TIME: | |||
837 | return g_strdup_printf(fmt, | |||
838 | (int) (item->counter/interval), | |||
839 | (int)((item->counter%interval)*UINT64_C(1000000)1000000UL / interval)); | |||
840 | } | |||
841 | break; | |||
842 | } | |||
843 | } | |||
844 | return g_strdup_printf(fmt, (uint64_t)0, (uint64_t)0); | |||
845 | } | |||
846 | ||||
847 | /* Calc the total width of each row in the stats table and build the printf format string for each | |||
848 | * column based on its field type, width, and name length. | |||
849 | * NOTE: The magnitude of all types including float and double are stored in iot->max_vals which | |||
850 | * is an *integer*. */ | |||
851 | static unsigned | |||
852 | iostat_calc_cols_width_and_fmt(io_stat_t *iot, uint64_t interval, column_width* col_w, char**fmts) | |||
853 | { | |||
854 | unsigned tabrow_w, type, ftype, namelen; | |||
855 | unsigned fr_mag; /* The magnitude of the max frame number in this column */ | |||
856 | unsigned val_mag; /* The magnitude of the max value in this column */ | |||
857 | char *fmt = NULL((void*)0); | |||
858 | ||||
859 | tabrow_w = 0; | |||
860 | for (unsigned j=0; j < iot->num_cols; j++) { | |||
861 | type = iot->calc_type[j]; | |||
862 | if (type == CALC_TYPE_FRAMES_AND_BYTES2) { | |||
863 | namelen = 5; | |||
864 | } else { | |||
865 | namelen = (unsigned int)strlen(calc_type_table[type].func_name); | |||
866 | } | |||
867 | if (type == CALC_TYPE_FRAMES0 | |||
868 | || type == CALC_TYPE_FRAMES_AND_BYTES2) { | |||
869 | ||||
870 | fr_mag = magnitude(iot->max_frame[j], 15); | |||
871 | fr_mag = MAX(6, fr_mag)(((6) > (fr_mag)) ? (6) : (fr_mag)); | |||
872 | col_w[j].fr = fr_mag; | |||
873 | tabrow_w += col_w[j].fr + 3; | |||
874 | ||||
875 | if (type == CALC_TYPE_FRAMES0) { | |||
876 | fmt = g_strdup_printf("%%%uu", fr_mag); | |||
877 | } else { | |||
878 | /* CALC_TYPE_FRAMES_AND_BYTES | |||
879 | */ | |||
880 | val_mag = magnitude(iot->max_vals[j], 15); | |||
881 | val_mag = MAX(5, val_mag)(((5) > (val_mag)) ? (5) : (val_mag)); | |||
882 | col_w[j].val = val_mag; | |||
883 | tabrow_w += (col_w[j].val + 3); | |||
884 | fmt = g_strdup_printf("%%%uu | %%%u"PRIu64"l" "u", fr_mag, val_mag); | |||
885 | } | |||
886 | if (fmt) | |||
887 | fmts[j] = fmt; | |||
888 | continue; | |||
889 | } | |||
890 | switch (type) { | |||
891 | case CALC_TYPE_BYTES1: | |||
892 | case CALC_TYPE_COUNT3: | |||
893 | ||||
894 | val_mag = magnitude(iot->max_vals[j], 15); | |||
895 | val_mag = MAX(5, val_mag)(((5) > (val_mag)) ? (5) : (val_mag)); | |||
896 | col_w[j].val = val_mag; | |||
897 | fmt = g_strdup_printf("%%%u"PRIu64"l" "u", val_mag); | |||
898 | break; | |||
899 | ||||
900 | default: | |||
901 | ftype = proto_registrar_get_ftype(iot->hf_indexes[j]); | |||
902 | switch (ftype) { | |||
903 | case FT_FLOAT: | |||
904 | case FT_DOUBLE: | |||
905 | val_mag = magnitude(iot->max_vals[j], 15); | |||
906 | fmt = g_strdup_printf("%%%u.6f", val_mag); | |||
907 | col_w[j].val = val_mag + 7; | |||
908 | break; | |||
909 | case FT_RELATIVE_TIME: | |||
910 | /* Convert FT_RELATIVE_TIME field to seconds | |||
911 | * CALC_TYPE_LOAD was already converted in iostat_packet() ) */ | |||
912 | if (type == CALC_TYPE_LOAD8) { | |||
913 | iot->max_vals[j] /= interval; | |||
914 | } else if (type != CALC_TYPE_AVG7) { | |||
915 | iot->max_vals[j] = (iot->max_vals[j] + UINT64_C(500000000)500000000UL) / NANOSECS_PER_SEC1000000000UL; | |||
916 | } | |||
917 | val_mag = magnitude(iot->max_vals[j], 15); | |||
918 | fmt = g_strdup_printf("%%%uu.%%06u", val_mag); | |||
919 | col_w[j].val = val_mag + 7; | |||
920 | break; | |||
921 | ||||
922 | default: | |||
923 | val_mag = magnitude(iot->max_vals[j], 15); | |||
924 | val_mag = MAX(namelen, val_mag)(((namelen) > (val_mag)) ? (namelen) : (val_mag)); | |||
925 | col_w[j].val = val_mag; | |||
926 | ||||
927 | switch (ftype) { | |||
928 | case FT_UINT8: | |||
929 | case FT_UINT16: | |||
930 | case FT_UINT24: | |||
931 | case FT_UINT32: | |||
932 | case FT_UINT64: | |||
933 | fmt = g_strdup_printf("%%%u"PRIu64"l" "u", val_mag); | |||
934 | break; | |||
935 | case FT_INT8: | |||
936 | case FT_INT16: | |||
937 | case FT_INT24: | |||
938 | case FT_INT32: | |||
939 | case FT_INT64: | |||
940 | fmt = g_strdup_printf("%%%u"PRId64"l" "d", val_mag); | |||
941 | break; | |||
942 | } | |||
943 | } /* End of ftype switch */ | |||
944 | } /* End of calc_type switch */ | |||
945 | tabrow_w += col_w[j].val + 3; | |||
946 | if (fmt) | |||
947 | fmts[j] = fmt; | |||
948 | } /* End of for loop (columns) */ | |||
949 | ||||
950 | return tabrow_w; | |||
951 | } | |||
952 | ||||
953 | static void | |||
954 | iostat_draw_filters(unsigned borderlen, const io_stat_t *iot) | |||
955 | { | |||
956 | const char *filter; | |||
957 | size_t len_filt; | |||
958 | GString *filt_str; | |||
959 | ||||
960 | /* Display the list of filters and their column numbers vertically */ | |||
961 | for (unsigned j=0; j<iot->num_cols; j++) { | |||
962 | if (j == 0) { | |||
963 | filt_str = g_string_new("| Col "); | |||
964 | } else { | |||
965 | filt_str = g_string_new("| "); | |||
966 | }; | |||
967 | g_string_append_printf(filt_str, "%2u: ", j + 1); | |||
968 | if (!iot->filters[j]) { | |||
969 | /* An empty (no filter) comma field was specified */ | |||
970 | 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)); | |||
971 | } else { | |||
972 | filter = iot->filters[j]; | |||
973 | len_filt = strlen(filter); | |||
974 | /* borderlen has been adjusted to try to accommodate the widest | |||
975 | * filter, but only up to a limit (currently 102 bytes), and so | |||
976 | * filters wider than that must still wrap. */ | |||
977 | /* 11 is the length of "| Col XX: " plus the trailing "|" */ | |||
978 | size_t max_w = borderlen - 11; | |||
979 | ||||
980 | while (len_filt > max_w) { | |||
981 | const char *pos; | |||
982 | size_t len; | |||
983 | unsigned int next_start; | |||
984 | ||||
985 | /* Find the pos of the last space in filter up to max_w. If a | |||
986 | * space is found, copy up to that space; otherwise, wrap the | |||
987 | * filter at max_w. */ | |||
988 | pos = g_strrstr_len(filter, max_w, " "); | |||
989 | if (pos) { | |||
990 | len = (size_t)(pos-filter); | |||
991 | /* Skip the space when wrapping. */ | |||
992 | next_start = (unsigned int) len+1; | |||
993 | } else { | |||
994 | len = max_w; | |||
995 | next_start = (unsigned int)len; | |||
996 | } | |||
997 | g_string_append_len(filt_str, filter, len)g_string_append_len_inline (filt_str, filter, len); | |||
998 | g_string_append_printf(filt_str, "%*s", (int)(borderlen - filt_str->len), "|"); | |||
999 | ||||
1000 | puts(filt_str->str); | |||
1001 | 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))))); | |||
1002 | ||||
1003 | filt_str = g_string_new("| "); | |||
1004 | filter = &filter[next_start]; | |||
1005 | len_filt = strlen(filter); | |||
1006 | } | |||
1007 | ||||
1008 | 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)); | |||
1009 | } | |||
1010 | g_string_append_printf(filt_str, "%*s", (int)(borderlen - filt_str->len), "|"); | |||
1011 | puts(filt_str->str); | |||
1012 | 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))))); | |||
1013 | } | |||
1014 | } | |||
1015 | ||||
1016 | static void | |||
1017 | iostat_draw_header(unsigned borderlen, const io_stat_t *iot, const nstime_t *duration, const nstime_t *interval, ws_tsprec_e invl_prec) | |||
1018 | { | |||
1019 | unsigned i; | |||
1020 | char time_buf[NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z")]; | |||
1021 | ||||
1022 | /* Display the top border */ | |||
1023 | printf("\n"); | |||
1024 | for (i=0; i<borderlen; i++) | |||
1025 | printf("="); | |||
1026 | ||||
1027 | printf("\n|%-*s|\n", borderlen - 2, " IO Statistics"); | |||
1028 | printf("|%-*s|\n", borderlen - 2, ""); | |||
1029 | ||||
1030 | /* For some reason, we print the total duration in microsecond precision | |||
1031 | * here if the interval is in seconds precision, and use the interval | |||
1032 | * precision otherwise. | |||
1033 | */ | |||
1034 | ws_tsprec_e dur_prec = (invl_prec == WS_TSPREC_SEC) ? WS_TSPREC_USEC : invl_prec; | |||
1035 | nstime_t dur_rounded; | |||
1036 | nstime_rounded(&dur_rounded, duration, dur_prec); | |||
1037 | int dur_mag = magnitude(duration->secs, 5); | |||
1038 | int dur_w = dur_mag + (invl_prec == 0 ? 0 : invl_prec+1); | |||
1039 | ||||
1040 | GString *dur_str = g_string_new("| Duration: "); | |||
1041 | display_signed_time(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"), &dur_rounded, dur_prec); | |||
1042 | g_string_append_printf(dur_str, "%*s secs", dur_w, time_buf); | |||
1043 | g_string_append_printf(dur_str, "%*s", (int)(borderlen - dur_str->len), "|"); | |||
1044 | puts(dur_str->str); | |||
1045 | 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))))); | |||
1046 | ||||
1047 | GString *invl_str = g_string_new("| Interval: "); | |||
1048 | display_signed_time(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"), interval, invl_prec); | |||
1049 | g_string_append_printf(invl_str, "%*s secs", dur_w, time_buf); | |||
1050 | g_string_append_printf(invl_str, "%*s", (int)(borderlen - invl_str->len), "|"); | |||
1051 | puts(invl_str->str); | |||
1052 | 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))))); | |||
1053 | ||||
1054 | printf("|%-*s|\n", borderlen - 2, ""); | |||
1055 | ||||
1056 | iostat_draw_filters(borderlen, iot); | |||
1057 | ||||
1058 | printf("|-"); | |||
1059 | for (i=0; i<borderlen-3; i++) { | |||
1060 | printf("-"); | |||
1061 | } | |||
1062 | printf("|\n"); | |||
1063 | } | |||
1064 | ||||
1065 | static void | |||
1066 | iostat_draw_header_row(unsigned borderlen, const io_stat_t *iot, const column_width *col_w, unsigned invl_col_w, unsigned tabrow_w) | |||
1067 | { | |||
1068 | unsigned j, type, numpad = 1; | |||
1069 | char *filler_s = NULL((void*)0); | |||
1070 | ||||
1071 | /* Display spaces above "Interval (s)" label */ | |||
1072 | printf("|%*s", invl_col_w - 1, "|"); | |||
1073 | ||||
1074 | /* Display column number headers */ | |||
1075 | for (j=0; j < iot->num_cols; j++) { | |||
1076 | int padding; | |||
1077 | if (iot->calc_type[j] == CALC_TYPE_FRAMES_AND_BYTES2) | |||
1078 | padding = col_w[j].fr + col_w[j].val + 3; | |||
1079 | else if (iot->calc_type[j] == CALC_TYPE_FRAMES0) | |||
1080 | padding = col_w[j].fr; | |||
1081 | else | |||
1082 | padding = col_w[j].val; | |||
1083 | ||||
1084 | printf("%-2d%*s|", j+1, padding, ""); | |||
1085 | } | |||
1086 | if (tabrow_w < borderlen) { | |||
1087 | filler_s = g_strdup_printf("%*s", borderlen - tabrow_w, "|"); | |||
1088 | printf("%s", filler_s); | |||
1089 | } | |||
1090 | printf("\n"); | |||
1091 | ||||
1092 | GString *timestamp_str; | |||
1093 | switch (timestamp_get_type()) { | |||
1094 | case TS_ABSOLUTE: | |||
1095 | case TS_UTC: | |||
1096 | timestamp_str = g_string_new("| Time "); | |||
1097 | break; | |||
1098 | case TS_ABSOLUTE_WITH_YMD: | |||
1099 | case TS_ABSOLUTE_WITH_YDOY: | |||
1100 | case TS_UTC_WITH_YMD: | |||
1101 | case TS_UTC_WITH_YDOY: | |||
1102 | timestamp_str = g_string_new("| Date and time"); | |||
1103 | break; | |||
1104 | case TS_RELATIVE: | |||
1105 | case TS_NOT_SET: | |||
1106 | timestamp_str = g_string_new("| Interval"); | |||
1107 | break; | |||
1108 | default: | |||
1109 | timestamp_str = g_string_new(NULL((void*)0)); | |||
1110 | break; | |||
1111 | } | |||
1112 | ||||
1113 | printf("%s%*s", timestamp_str->str, (int)(invl_col_w - timestamp_str->len), "|"); | |||
1114 | 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))))); | |||
1115 | ||||
1116 | /* Display the stat label in each column */ | |||
1117 | for (j=0; j < iot->num_cols; j++) { | |||
1118 | type = iot->calc_type[j]; | |||
1119 | if (type == CALC_TYPE_FRAMES0) { | |||
1120 | printcenter (calc_type_table[type].func_name, col_w[j].fr, numpad); | |||
1121 | } else if (type == CALC_TYPE_FRAMES_AND_BYTES2) { | |||
1122 | printcenter ("Frames", col_w[j].fr, numpad); | |||
1123 | printcenter ("Bytes", col_w[j].val, numpad); | |||
1124 | } else { | |||
1125 | printcenter (calc_type_table[type].func_name, col_w[j].val, numpad); | |||
1126 | } | |||
1127 | } | |||
1128 | if (filler_s) { | |||
1129 | printf("%s", filler_s); | |||
1130 | } | |||
1131 | printf("\n|-"); | |||
1132 | ||||
1133 | for (j=0; j<tabrow_w-3; j++) | |||
1134 | printf("-"); | |||
1135 | printf("|"); | |||
1136 | ||||
1137 | if (filler_s) { | |||
1138 | printf("%s", filler_s); | |||
1139 | g_free(filler_s); | |||
1140 | } | |||
1141 | ||||
1142 | printf("\n"); | |||
1143 | } | |||
1144 | ||||
1145 | static void | |||
1146 | iostat_draw(void *arg) | |||
1147 | { | |||
1148 | uint64_t interval, duration, t, real_invl, dv; | |||
1149 | unsigned int i, j, k, num_cols, num_rows, dur_secs, dur_mag, | |||
1150 | invl_mag, invl_prec, tabrow_w, borderlen, invl_col_w, maxfltr_w; | |||
1151 | char **fmts, *fmt = NULL((void*)0); | |||
1152 | io_stat_item_t *mit, **stat_cols, *item, **item_in_column; | |||
1153 | bool_Bool last_row = false0; | |||
1154 | io_stat_t *iot; | |||
1155 | column_width *col_w; | |||
1156 | char time_buf[NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z")]; | |||
1157 | ||||
1158 | mit = (io_stat_item_t *)arg; | |||
1159 | iot = mit->parent; | |||
1160 | num_cols = iot->num_cols; | |||
1161 | col_w = g_new(column_width, num_cols)((column_width *) g_malloc_n ((num_cols), sizeof (column_width ))); | |||
1162 | fmts = g_new0(char *, num_cols)((char * *) g_malloc0_n ((num_cols), sizeof (char *))); | |||
1163 | duration = ((uint64_t)cfile.elapsed_time.secs * UINT64_C(1000000)1000000UL) + | |||
1164 | (uint64_t)((cfile.elapsed_time.nsecs + 500) / 1000); | |||
1165 | ||||
1166 | /* Store the pointer to each stat column */ | |||
1167 | stat_cols = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols); | |||
1168 | for (j=0; j<num_cols; j++) | |||
1169 | stat_cols[j] = &iot->items[j]; | |||
1170 | ||||
1171 | /* The following prevents gross inaccuracies when the user specifies an interval that is greater | |||
1172 | * than the capture duration. */ | |||
1173 | if (iot->interval > duration || iot->interval == UINT64_MAX(18446744073709551615UL)) { | |||
1174 | interval = duration; | |||
1175 | iot->interval = UINT64_MAX(18446744073709551615UL); | |||
1176 | } else { | |||
1177 | interval = iot->interval; | |||
1178 | } | |||
1179 | ||||
1180 | /* Calc the capture duration's magnitude (dur_mag) */ | |||
1181 | dur_secs = (unsigned int)(duration/UINT64_C(1000000)1000000UL); | |||
1182 | dur_mag = magnitude((uint64_t)dur_secs, 5); | |||
1183 | ||||
1184 | /* Calc the interval's magnitude */ | |||
1185 | invl_mag = magnitude(interval/UINT64_C(1000000)1000000UL, 5); | |||
1186 | ||||
1187 | /* Set or get the interval precision */ | |||
1188 | if (interval == duration) { | |||
1189 | /* | |||
1190 | * An interval arg of 0 or an interval size exceeding the capture duration was specified. | |||
1191 | * Set the decimal precision of duration based on its magnitude. */ | |||
1192 | if (dur_mag >= 2) | |||
1193 | invl_prec = 1; | |||
1194 | else if (dur_mag == 1) | |||
1195 | invl_prec = 3; | |||
1196 | else | |||
1197 | invl_prec = 6; | |||
1198 | ||||
1199 | borderlen = 30 + dur_mag + (invl_prec == 0 ? 0 : invl_prec+1); | |||
1200 | } else { | |||
1201 | invl_prec = iot->invl_prec; | |||
1202 | borderlen = 25 + MAX(invl_mag,dur_mag)(((invl_mag) > (dur_mag)) ? (invl_mag) : (dur_mag)) + (invl_prec == 0 ? 0 : invl_prec+1); | |||
1203 | } | |||
1204 | ||||
1205 | /* Round the duration according to invl_prec */ | |||
1206 | dv = 1000000; | |||
1207 | for (i=0; i<invl_prec; i++) | |||
1208 | dv /= 10; | |||
1209 | if ((duration%dv) > 5*(dv/10)) { | |||
1210 | duration += 5*(dv/10); | |||
1211 | duration = (duration/dv) * dv; | |||
1212 | dur_secs = (unsigned int)(duration/UINT64_C(1000000)1000000UL); | |||
1213 | /* | |||
1214 | * Recalc dur_mag in case rounding has increased its magnitude */ | |||
1215 | dur_mag = magnitude((uint64_t)dur_secs, 5); | |||
1216 | } | |||
1217 | if (iot->interval == UINT64_MAX(18446744073709551615UL)) | |||
1218 | interval = duration; | |||
1219 | ||||
1220 | /* This is the max width of a duration. */ | |||
1221 | int dur_w = dur_mag + (invl_prec == 0 ? 0 : invl_prec+1); | |||
1222 | /* Add a space on the side, and make sure the width is at least as wide | |||
1223 | * as "Dur". ("Dur" does not need a space between it and "|".) */ | |||
1224 | dur_w = MAX(dur_w + 1, 3)(((dur_w + 1) > (3)) ? (dur_w + 1) : (3)); | |||
1225 | ||||
1226 | /* Update the width of the time interval column if date is shown */ | |||
1227 | switch (timestamp_get_type()) { | |||
1228 | case TS_ABSOLUTE_WITH_YMD: | |||
1229 | case TS_ABSOLUTE_WITH_YDOY: | |||
1230 | case TS_UTC_WITH_YMD: | |||
1231 | case TS_UTC_WITH_YDOY: | |||
1232 | // We don't show more than 6 fractional digits (+Z) currently. | |||
1233 | // NSTIME_ISO8601_BUFSIZE is enough room for 9 frac digits + Z + '\0' | |||
1234 | // That's 4 extra characters, which leaves room for the "| |". | |||
1235 | invl_col_w = NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z") + invl_prec - 6; | |||
1236 | break; | |||
1237 | ||||
1238 | default: | |||
1239 | /* Calc the width of the time interval column (incl borders and padding, | |||
1240 | * which are "|" and " <" on each side.) */ | |||
1241 | invl_col_w = 2*(dur_w + 3); | |||
1242 | break; | |||
1243 | } | |||
1244 | ||||
1245 | /* Calculate the width and format string of all the other columns, and add | |||
1246 | * the total to the interval column width for the entire total. */ | |||
1247 | tabrow_w = invl_col_w + iostat_calc_cols_width_and_fmt(iot, interval, col_w, fmts); | |||
1248 | ||||
1249 | borderlen = MAX(borderlen, tabrow_w)(((borderlen) > (tabrow_w)) ? (borderlen) : (tabrow_w)); | |||
1250 | ||||
1251 | /* Calc the max width of the list of filters. */ | |||
1252 | maxfltr_w = 0; | |||
1253 | for (j=0; j<num_cols; j++) { | |||
1254 | if (iot->filters[j]) { | |||
1255 | k = (unsigned int) (strlen(iot->filters[j]) + 11); | |||
1256 | maxfltr_w = MAX(maxfltr_w, k)(((maxfltr_w) > (k)) ? (maxfltr_w) : (k)); | |||
1257 | } else { | |||
1258 | maxfltr_w = MAX(maxfltr_w, 26)(((maxfltr_w) > (26)) ? (maxfltr_w) : (26)); | |||
1259 | } | |||
1260 | } | |||
1261 | /* The stat table is not wrapped (by tshark) but filter is wrapped at the width of the stats table | |||
1262 | * (which currently = borderlen); however, if the filter width exceeds the table width and the | |||
1263 | * table width is less than 102 bytes, set borderlen to the lesser of the max filter width and 102. | |||
1264 | * The filters will wrap at the lesser of borderlen-2 and the last space in the filter. | |||
1265 | * NOTE: 102 is the typical size of a user window when the font is fixed width (e.g., COURIER 10). | |||
1266 | * XXX: A pref could be added to change the max width from the default size of 102. */ | |||
1267 | if (maxfltr_w > borderlen && borderlen < 102) | |||
1268 | borderlen = MIN(maxfltr_w, 102)(((maxfltr_w) < (102)) ? (maxfltr_w) : (102)); | |||
1269 | ||||
1270 | /* Prevent double right border by adding a space */ | |||
1271 | if (borderlen-tabrow_w == 1) | |||
1272 | borderlen++; | |||
1273 | ||||
1274 | nstime_t invl_time = NSTIME_INIT_SECS_USECS(interval/UINT64_C(1000000), interval%UINT64_C(1000000)){interval/1000000UL, interval%1000000UL*1000}; | |||
1275 | iostat_draw_header(borderlen, iot, &cfile.elapsed_time, &invl_time, invl_prec); | |||
1276 | ||||
1277 | iostat_draw_header_row(borderlen, iot, col_w, invl_col_w, tabrow_w); | |||
1278 | ||||
1279 | t = 0; | |||
1280 | ||||
1281 | if (interval == 0 || duration == 0) { | |||
1282 | num_rows = 0; | |||
1283 | } else { | |||
1284 | num_rows = (unsigned int)(duration/interval) + ((unsigned int)(duration%interval) > 0 ? 1 : 0); | |||
1285 | } | |||
1286 | ||||
1287 | /* Load item_in_column with the first item in each column */ | |||
1288 | item_in_column = (io_stat_item_t **)g_malloc(sizeof(io_stat_item_t *) * num_cols); | |||
1289 | for (j=0; j<num_cols; j++) { | |||
1290 | item_in_column[j] = stat_cols[j]; | |||
1291 | } | |||
1292 | ||||
1293 | /* Display the table values | |||
1294 | * | |||
1295 | * The outer loop is for time interval rows and the inner loop is for stat column items.*/ | |||
1296 | for (i=0; i<num_rows; i++) { | |||
1297 | ||||
1298 | if (i == num_rows-1) | |||
1299 | last_row = true1; | |||
1300 | ||||
1301 | /* Compute the interval for this row */ | |||
1302 | if (!last_row) { | |||
1303 | real_invl = interval; | |||
1304 | } else { | |||
1305 | real_invl = duration - t; | |||
1306 | } | |||
1307 | ||||
1308 | /* Patch for Absolute Time */ | |||
1309 | /* XXX - has a Y2.038K problem with 32-bit time_t */ | |||
1310 | nstime_t the_time = NSTIME_INIT_SECS_USECS(t / 1000000, t % 1000000){t / 1000000, t % 1000000*1000}; | |||
1311 | ||||
1312 | /* Display the interval for this row */ | |||
1313 | /* Get the string representing the start time of this interval. */ | |||
1314 | fill_start_time(iot, &the_time, invl_prec, time_buf); | |||
1315 | /* Now add the surrounding column information according to our | |||
1316 | * output format (currently, only text table is supported.) */ | |||
1317 | switch (timestamp_get_type()) { | |||
1318 | ||||
1319 | case TS_RELATIVE: | |||
1320 | case TS_NOT_SET: | |||
1321 | /* For relative times, we show both ends of the interval. */ | |||
1322 | printf("|%*s <", dur_w, time_buf); | |||
1323 | if (invl_prec == 0 && last_row) { | |||
1324 | g_strlcpy(time_buf, "Dur", NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z")); | |||
1325 | } else { | |||
1326 | nstime_add(&the_time, &invl_time)nstime_sum(&the_time, &the_time, &invl_time); | |||
1327 | display_signed_time(time_buf, NSTIME_ISO8601_BUFSIZEsizeof("YYYY-MM-DDTHH:MM:SS.123456789Z"), &the_time, invl_prec); | |||
1328 | } | |||
1329 | printf("> %-*s|", dur_w, time_buf); | |||
1330 | break; | |||
1331 | default: | |||
1332 | printf("| %-*s |", invl_col_w - 4, time_buf); | |||
1333 | break; | |||
1334 | } | |||
1335 | ||||
1336 | /* Display stat values in each column for this row */ | |||
1337 | for (j=0; j<num_cols; j++) { | |||
1338 | fmt = fmts[j]; | |||
1339 | item = item_in_column[j]; | |||
1340 | ||||
1341 | /* To try to optimize speed, we could copy the value string with | |||
1342 | * snprintf into a pre-allocated buffer with the maximum column | |||
1343 | * width, which we determined (though that's more error-prone.) | |||
1344 | */ | |||
1345 | char *value = iostat_get_item_value(iot, item, fmt, j, real_invl); | |||
1346 | printf(" %s |", value); | |||
1347 | g_free(value); | |||
1348 | ||||
1349 | if (item) | |||
1350 | item_in_column[j] = item_in_column[j]->next; | |||
1351 | } | |||
1352 | if (tabrow_w < borderlen) { | |||
1353 | printf("%*s", borderlen - tabrow_w, "|"); | |||
1354 | } | |||
1355 | printf("\n"); | |||
1356 | t += interval; | |||
1357 | ||||
1358 | } | |||
1359 | for (i=0; i<borderlen; i++) { | |||
1360 | printf("="); | |||
1361 | } | |||
1362 | printf("\n"); | |||
1363 | g_free(col_w); | |||
1364 | for (i=0; i<num_cols; ++i) { | |||
1365 | g_free(fmts[i]); | |||
1366 | } | |||
1367 | g_free(fmts); | |||
1368 | g_free(stat_cols); | |||
1369 | g_free(item_in_column); | |||
1370 | } | |||
1371 | ||||
1372 | /* A new capture file is being loaded (or the current one reloaded), | |||
1373 | * reset our statistics. | |||
1374 | */ | |||
1375 | static void | |||
1376 | iostat_reset(void *arg) | |||
1377 | { | |||
1378 | io_stat_item_t *mit = (io_stat_item_t *)arg; | |||
1379 | io_stat_t *io = mit->parent; | |||
1380 | ||||
1381 | nstime_set_unset(&io->start_time); | |||
1382 | io->last_relative_time = UINT64_C(0)0UL; | |||
1383 | for (unsigned int i=0; i<io->num_cols; i++) { | |||
1384 | iostat_item_reset(&io->items[i]); | |||
1385 | io->max_vals[i] = 0; | |||
1386 | io->max_frame[i] = 0; | |||
1387 | } | |||
1388 | } | |||
1389 | ||||
1390 | /* Our listener is being removed, free our memory. */ | |||
1391 | static void | |||
1392 | iostat_finish(void *arg) | |||
1393 | { | |||
1394 | io_stat_item_t *mit = (io_stat_item_t *)arg; | |||
1395 | io_stat_t *io = mit->parent; | |||
1396 | iostat_io_free(io); | |||
1397 | } | |||
1398 | ||||
1399 | /* | |||
1400 | * Register a new iostat tap for column number i. | |||
1401 | * The new tap's tapdata (see doc/README.tapping) is io->items[i], not io itself. | |||
1402 | * We only set the draw/reset/finish functions if i == 0 so everything is handled only once. | |||
1403 | */ | |||
1404 | static bool_Bool | |||
1405 | register_io_tap(io_stat_t *io, unsigned int i, const char *filter, GString *err) | |||
1406 | { | |||
1407 | GString *error_string; | |||
1408 | const char *flt; | |||
1409 | int j; | |||
1410 | size_t namelen; | |||
1411 | const char *p, *parenp; | |||
1412 | char *field; | |||
1413 | header_field_info *hfi; | |||
1414 | ||||
1415 | io->items[i].prev = &io->items[i]; | |||
1416 | io->items[i].next = NULL((void*)0); | |||
1417 | io->items[i].parent = io; | |||
1418 | io->items[i].start_time = 0; | |||
1419 | io->items[i].frames = 0; | |||
1420 | io->items[i].counter = 0; | |||
1421 | io->items[i].num = 0; | |||
1422 | ||||
1423 | io->filters[i] = filter; | |||
1424 | flt = filter; | |||
1425 | ||||
1426 | io->calc_type[i] = CALC_TYPE_FRAMES_AND_BYTES2; | |||
1427 | field = NULL((void*)0); | |||
1428 | hfi = NULL((void*)0); | |||
1429 | for (j=0; calc_type_table[j].func_name; j++) { | |||
1430 | namelen = strlen(calc_type_table[j].func_name); | |||
1431 | if (filter && strncmp(filter, calc_type_table[j].func_name, namelen) == 0) { | |||
1432 | io->calc_type[i] = calc_type_table[j].calc_type; | |||
1433 | io->items[i].colnum = i; | |||
1434 | if (*(filter+namelen) == '(') { | |||
1435 | p = filter+namelen+1; | |||
1436 | parenp = strchr(p, ')'); | |||
1437 | if (!parenp) { | |||
1438 | cmdarg_err("\ntshark: Closing parenthesis missing from calculated expression.\n"); | |||
1439 | return false0; | |||
1440 | } | |||
1441 | ||||
1442 | if (io->calc_type[i] == CALC_TYPE_FRAMES0 || io->calc_type[i] == CALC_TYPE_BYTES1) { | |||
1443 | if (parenp != p) { | |||
1444 | cmdarg_err("\ntshark: %s does not require or allow a field name within the parens.\n", | |||
1445 | calc_type_table[j].func_name); | |||
1446 | return false0; | |||
1447 | } | |||
1448 | } else { | |||
1449 | if (parenp == p) { | |||
1450 | /* bail out if a field name was not specified */ | |||
1451 | cmdarg_err("\ntshark: You didn't specify a field name for %s(*).\n", | |||
1452 | calc_type_table[j].func_name); | |||
1453 | return false0; | |||
1454 | } | |||
1455 | } | |||
1456 | ||||
1457 | field = (char *)g_malloc(parenp-p+1); | |||
1458 | memcpy(field, p, parenp-p); | |||
1459 | field[parenp-p] = '\0'; | |||
1460 | flt = parenp + 1; | |||
1461 | if (io->calc_type[i] == CALC_TYPE_FRAMES0 || io->calc_type[i] == CALC_TYPE_BYTES1) | |||
1462 | break; | |||
1463 | hfi = proto_registrar_get_byname(field); | |||
1464 | if (!hfi) { | |||
1465 | cmdarg_err("\ntshark: There is no field named '%s'.\n", field); | |||
1466 | g_free(field); | |||
1467 | return false0; | |||
1468 | } | |||
1469 | ||||
1470 | io->hf_indexes[i] = hfi->id; | |||
1471 | break; | |||
1472 | } | |||
1473 | } else { | |||
1474 | if (io->calc_type[i] == CALC_TYPE_FRAMES0 || io->calc_type[i] == CALC_TYPE_BYTES1) | |||
1475 | flt = ""; | |||
1476 | io->items[i].colnum = i; | |||
1477 | } | |||
1478 | } | |||
1479 | if (hfi && !(io->calc_type[i] == CALC_TYPE_BYTES1 || | |||
1480 | io->calc_type[i] == CALC_TYPE_FRAMES0 || | |||
1481 | io->calc_type[i] == CALC_TYPE_FRAMES_AND_BYTES2)) { | |||
1482 | /* check that the type is compatible */ | |||
1483 | switch (hfi->type) { | |||
1484 | case FT_UINT8: | |||
1485 | case FT_UINT16: | |||
1486 | case FT_UINT24: | |||
1487 | case FT_UINT32: | |||
1488 | case FT_UINT64: | |||
1489 | case FT_INT8: | |||
1490 | case FT_INT16: | |||
1491 | case FT_INT24: | |||
1492 | case FT_INT32: | |||
1493 | case FT_INT64: | |||
1494 | /* these types support all calculations */ | |||
1495 | break; | |||
1496 | case FT_FLOAT: | |||
1497 | case FT_DOUBLE: | |||
1498 | /* these types only support SUM, COUNT, MAX, MIN, AVG */ | |||
1499 | switch (io->calc_type[i]) { | |||
1500 | case CALC_TYPE_SUM4: | |||
1501 | case CALC_TYPE_COUNT3: | |||
1502 | case CALC_TYPE_MAX6: | |||
1503 | case CALC_TYPE_MIN5: | |||
1504 | case CALC_TYPE_AVG7: | |||
1505 | break; | |||
1506 | default: | |||
1507 | cmdarg_err("\ntshark: %s is a float field, so %s(*) calculations are not supported on it.", | |||
1508 | field, | |||
1509 | calc_type_table[j].func_name); | |||
1510 | return false0; | |||
1511 | } | |||
1512 | break; | |||
1513 | case FT_RELATIVE_TIME: | |||
1514 | /* this type only supports SUM, COUNT, MAX, MIN, AVG, LOAD */ | |||
1515 | switch (io->calc_type[i]) { | |||
1516 | case CALC_TYPE_SUM4: | |||
1517 | case CALC_TYPE_COUNT3: | |||
1518 | case CALC_TYPE_MAX6: | |||
1519 | case CALC_TYPE_MIN5: | |||
1520 | case CALC_TYPE_AVG7: | |||
1521 | case CALC_TYPE_LOAD8: | |||
1522 | break; | |||
1523 | default: | |||
1524 | cmdarg_err("\ntshark: %s is a relative-time field, so %s(*) calculations are not supported on it.", | |||
1525 | field, | |||
1526 | calc_type_table[j].func_name); | |||
1527 | return false0; | |||
1528 | } | |||
1529 | break; | |||
1530 | default: | |||
1531 | /* | |||
1532 | * XXX - support all operations on floating-point | |||
1533 | * numbers? | |||
1534 | */ | |||
1535 | if (io->calc_type[i] != CALC_TYPE_COUNT3) { | |||
1536 | cmdarg_err("\ntshark: %s doesn't have integral values, so %s(*) " | |||
1537 | "calculations are not supported on it.\n", | |||
1538 | field, | |||
1539 | calc_type_table[j].func_name); | |||
1540 | return false0; | |||
1541 | } | |||
1542 | break; | |||
1543 | } | |||
1544 | } | |||
1545 | g_free(field); | |||
1546 | ||||
1547 | error_string = register_tap_listener("frame", &io->items[i], flt, TL_REQUIRES_PROTO_TREE0x00000001, | |||
1548 | i ? NULL((void*)0) : iostat_reset, | |||
1549 | iostat_packet, | |||
1550 | i ? NULL((void*)0) : iostat_draw, | |||
1551 | i ? NULL((void*)0) : iostat_finish); | |||
1552 | if (error_string) { | |||
1553 | /* Accumulate errors about all the possible filters tried at the same | |||
1554 | * starting character. | |||
1555 | */ | |||
1556 | if (err->len) { | |||
1557 | g_string_append_c(err, '\n')g_string_append_c_inline (err, '\n'); | |||
1558 | } | |||
1559 | 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)); | |||
1560 | 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))))); | |||
1561 | return false0; | |||
1562 | } | |||
1563 | ||||
1564 | /* On success, clear old errors (from splitting on internal commas). */ | |||
1565 | g_string_truncate(err, 0)g_string_truncate_inline (err, 0); | |||
1566 | return true1; | |||
1567 | } | |||
1568 | ||||
1569 | static bool_Bool | |||
1570 | iostat_init(const char *opt_arg, void *userdata _U___attribute__((unused))) | |||
1571 | { | |||
1572 | double interval_float; | |||
1573 | uint32_t idx = 0; | |||
1574 | unsigned int i; | |||
1575 | io_stat_t *io; | |||
1576 | const char *filters, *str, *pos; | |||
1577 | ||||
1578 | io_decimal_point = localeconv()->decimal_point; | |||
1579 | ||||
1580 | /* XXX - Why can't the last character be a comma? Shouldn't it be | |||
1581 | * fine for the last filter to be empty? Even in the case of locales | |||
1582 | * that use ',' for the decimal separator, there shouldn't be any | |||
1583 | * difference between interpreting a terminating ',' as a decimal | |||
1584 | * point for the interval, and interpreting it as a separator followed | |||
1585 | * by an empty filter. | |||
1586 | */ | |||
1587 | if ((*(opt_arg+(strlen(opt_arg)-1)) == ',') || | |||
| ||||
1588 | (sscanf(opt_arg, "io,stat,%lf%n", &interval_float, (int *)&idx) != 1) || | |||
1589 | (idx < 8)) { | |||
1590 | cmdarg_err("\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n"); | |||
1591 | return false0; | |||
1592 | } | |||
1593 | ||||
1594 | filters = opt_arg+idx; | |||
1595 | if (*filters) { | |||
1596 | if (*filters != ',') { | |||
1597 | /* For locales that use ',' instead of '.', the comma might | |||
1598 | * have been consumed during the floating point conversion. */ | |||
1599 | --filters; | |||
1600 | if (*filters != ',') { | |||
1601 | cmdarg_err("\ntshark: invalid \"-z io,stat,<interval>[,<filter>][,<filter>]...\" argument\n"); | |||
1602 | return false0; | |||
1603 | } | |||
1604 | } | |||
1605 | } | |||
1606 | /* filters now either starts with ',' or '\0' */ | |||
1607 | ||||
1608 | switch (timestamp_get_type()) { | |||
1609 | case TS_DELTA: | |||
1610 | case TS_DELTA_DIS: | |||
1611 | case TS_EPOCH: | |||
1612 | cmdarg_err("\ntshark: invalid -t operand. io,stat only supports -t <r|a|ad|adoy|u|ud|udoy>\n"); | |||
1613 | return false0; | |||
1614 | default: | |||
1615 | break; | |||
1616 | } | |||
1617 | ||||
1618 | io = g_new0(io_stat_t, 1)((io_stat_t *) g_malloc0_n ((1), sizeof (io_stat_t))); | |||
1619 | io->last_relative_time = UINT64_C(0)0UL; | |||
1620 | ||||
1621 | /* If interval is 0, calculate statistics over the whole file by setting the interval to | |||
1622 | * UINT64_MAX */ | |||
1623 | if (interval_float == 0) { | |||
1624 | io->interval = UINT64_MAX(18446744073709551615UL); | |||
1625 | io->invl_prec = 0; | |||
1626 | } else { | |||
1627 | /* Set interval to the number of us rounded to the nearest integer */ | |||
1628 | io->interval = (uint64_t)(interval_float * 1000000.0 + 0.5); | |||
1629 | /* | |||
1630 | * Determine what interval precision the user has specified */ | |||
1631 | io->invl_prec = 6; | |||
1632 | for (i=10; i<10000000; i*=10) { | |||
1633 | if (io->interval%i > 0) | |||
1634 | break; | |||
1635 | io->invl_prec--; | |||
1636 | } | |||
1637 | if (io->invl_prec == 0) { | |||
1638 | /* The precision is zero but if the user specified one of more zeros after the decimal point, | |||
1639 | they want that many decimal places shown in the table for all time intervals except | |||
1640 | response time values such as smb.time which always have 6 decimal places of precision. | |||
1641 | This feature is useful in cases where for example the duration is 9.1, you specify an | |||
1642 | interval of 1 and the last interval becomes "9 <> 9". If the interval is instead set to | |||
1643 | 1.1, the last interval becomes | |||
1644 | last interval is rounded up to value that is greater than the duration. */ | |||
1645 | const char *invl_start = opt_arg+8; | |||
1646 | unsigned invl_len; | |||
1647 | ||||
1648 | invl_start = strpbrk(invl_start, ".,"); | |||
1649 | ||||
1650 | if (invl_start != NULL((void*)0) && *invl_start == '.') { | |||
1651 | invl_len = (unsigned)strcspn(invl_start + 1, ","); | |||
1652 | if (invl_len) | |||
1653 | io->invl_prec = MIN(invl_len, 6U)(((invl_len) < (6U)) ? (invl_len) : (6U)); | |||
1654 | } | |||
1655 | } | |||
1656 | } | |||
1657 | if (io->interval
| |||
1658 | cmdarg_err("\ntshark: \"-z\" interval must be >=0.000001 seconds or \"0\" for the entire capture duration.\n"); | |||
1659 | iostat_io_free(io); | |||
1660 | return false0; | |||
1661 | } | |||
1662 | ||||
1663 | /* Find how many ',' separated filters we have */ | |||
1664 | /* Filter can have internal commas, so this is only an upper bound on the | |||
1665 | * number of filters. In the display filter grammar, commas only appear | |||
1666 | * inside delimiters (quoted strings, slices, sets, and functions), so | |||
1667 | * splitting in the wrong place produces an invalid filter. That is, there | |||
1668 | * can be at most only one valid interpretation (but might be none). | |||
1669 | * | |||
1670 | * XXX - If the grammar changes to allow commas in other places, then there | |||
1671 | * is ambiguity. | |||
1672 | * | |||
1673 | * Perhaps ideally we'd verify the filters before doing allocation. | |||
1674 | */ | |||
1675 | io->num_cols = 1; | |||
1676 | nstime_set_unset(&io->start_time); | |||
1677 | ||||
1678 | if (*filters != '\0') { | |||
1679 | /* Eliminate the first comma. */ | |||
1680 | filters++; | |||
1681 | str = filters; | |||
1682 | while ((str = strchr(str, ','))) { | |||
1683 | io->num_cols++; | |||
1684 | str++; | |||
1685 | } | |||
1686 | } | |||
1687 | ||||
1688 | 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 ))); | |||
1689 | io->filters = (const char **)g_malloc(sizeof(char *) * io->num_cols); | |||
1690 | io->max_vals = g_new(uint64_t, io->num_cols)((uint64_t *) g_malloc_n ((io->num_cols), sizeof (uint64_t ))); | |||
1691 | io->max_frame = g_new(uint32_t, io->num_cols)((uint32_t *) g_malloc_n ((io->num_cols), sizeof (uint32_t ))); | |||
1692 | io->hf_indexes = g_new(int, io->num_cols)((int *) g_malloc_n ((io->num_cols), sizeof (int))); | |||
1693 | io->calc_type = g_new(int, io->num_cols)((int *) g_malloc_n ((io->num_cols), sizeof (int))); | |||
1694 | ||||
1695 | for (i=0; i
| |||
1696 | io->max_vals[i] = 0; | |||
1697 | io->max_frame[i] = 0; | |||
1698 | } | |||
1699 | ||||
1700 | bool_Bool success; | |||
1701 | GString *err = g_string_new(NULL((void*)0)); | |||
1702 | ||||
1703 | /* Register a tap listener for each filter */ | |||
1704 | if (filters[0] == '\0') { | |||
1705 | success = register_io_tap(io, 0, NULL((void*)0), err); | |||
1706 | } else { | |||
1707 | char *filter; | |||
1708 | i = 0; | |||
1709 | str = filters; | |||
1710 | pos = str; | |||
1711 | while ((pos = strchr(pos, ',')) != NULL((void*)0)) { | |||
1712 | if (pos == str) { | |||
1713 | /* Consecutive commas - an empty filter. */ | |||
1714 | filter = NULL((void*)0); | |||
1715 | } else { | |||
1716 | /* Likely a filter. */ | |||
1717 | filter = (char *)g_malloc((pos-str)+1); | |||
1718 | (void) g_strlcpy( filter, str, (size_t) ((pos-str)+1)); | |||
1719 | filter = g_strstrip(filter)g_strchomp (g_strchug (filter)); | |||
1720 | } | |||
1721 | success = register_io_tap(io, i, filter, err); | |||
| ||||
1722 | /* Advance to the next position to look for commas. */ | |||
1723 | pos++; | |||
1724 | if (success) { | |||
1725 | /* Also advance the filter start on success. */ | |||
1726 | str = pos; | |||
1727 | i++; | |||
1728 | } else { | |||
1729 | g_free(filter); | |||
1730 | } | |||
1731 | } | |||
1732 | /* No more commas, the rest of the string is the last filter. */ | |||
1733 | filter = g_strstrip(g_strdup(str))g_strchomp (g_strchug (g_strdup_inline (str))); | |||
1734 | if (*filter) { | |||
1735 | success = register_io_tap(io, i, filter, err); | |||
1736 | } else { | |||
1737 | success = register_io_tap(io, i, NULL((void*)0), err); | |||
1738 | } | |||
1739 | if (success) { | |||
1740 | i++; | |||
1741 | } | |||
1742 | io->num_cols = i; | |||
1743 | } | |||
1744 | ||||
1745 | if (!success) { | |||
1746 | cmdarg_err("\ntshark: Couldn't register io,stat tap: %s\n", | |||
1747 | err->str); | |||
1748 | 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))))); | |||
1749 | iostat_io_free(io); | |||
1750 | return false0; | |||
1751 | } | |||
1752 | 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))))); | |||
1753 | return true1; | |||
1754 | ||||
1755 | } | |||
1756 | ||||
1757 | static stat_tap_ui iostat_ui = { | |||
1758 | REGISTER_STAT_GROUP_GENERIC, | |||
1759 | NULL((void*)0), | |||
1760 | "io,stat", | |||
1761 | iostat_init, | |||
1762 | 0, | |||
1763 | NULL((void*)0) | |||
1764 | }; | |||
1765 | ||||
1766 | void | |||
1767 | register_tap_listener_iostat(void) | |||
1768 | { | |||
1769 | register_stat_tap_ui(&iostat_ui, NULL((void*)0)); | |||
1770 | } |