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 stats_tree.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 -fhalf-no-semantic-interposition -mframe-pointer=all -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 -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-17/lib/clang/17 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/epan -isystem /builds/wireshark/wireshark/build/epan -isystem /usr/include/libxml2 -isystem /usr/include/lua5.4 -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -D epan_EXPORTS -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/wiretap -internal-isystem /usr/lib/llvm-17/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../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-error=stringop-overflow= -Wno-error=deprecated-declarations -Wno-format-truncation -Wno-pointer-sign -std=gnu11 -fdebug-compilation-dir=/builds/wireshark/wireshark/build -ferror-limit 19 -fvisibility=hidden -fwrapv -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2024-06-26-100254-3154-1 -x c /builds/wireshark/wireshark/epan/stats_tree.c
1 | /* stats_tree.c |
2 | * API for a counter tree for Wireshark |
3 | * 2004, Luis E. G. Ontanon |
4 | * |
5 | * Wireshark - Network traffic analyzer |
6 | * By Gerald Combs <[emailprotected]> |
7 | * Copyright 1998 Gerald Combs |
8 | * |
9 | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | */ |
11 | |
12 | /* stats_tree modifications by Deon van der Westhuysen, November 2013 |
13 | * support for |
14 | * - sorting by column, |
15 | * - calculation of average values |
16 | * - calculation of burst rate |
17 | * - export to text, CSV or XML file |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include <glib.h> |
23 | |
24 | #include <stdlib.h> |
25 | |
26 | #include <epan/stats_tree_priv.h> |
27 | #include <epan/prefs.h> |
28 | #include <math.h> |
29 | #include <string.h> |
30 | |
31 | #include "strutil.h" |
32 | #include "stats_tree.h" |
33 | #include <wsutil/ws_assert.h> |
34 | |
35 | enum _stat_tree_columns { |
36 | COL_NAME, |
37 | COL_COUNT, |
38 | COL_AVERAGE, |
39 | COL_MIN, |
40 | COL_MAX, |
41 | COL_RATE, |
42 | COL_PERCENT, |
43 | COL_BURSTRATE, |
44 | COL_BURSTTIME, |
45 | N_COLUMNS |
46 | }; |
47 | |
48 | /* used to contain the registered stat trees */ |
49 | static GHashTable *registry; |
50 | |
51 | /* a text representation of a node |
52 | if buffer is NULL returns a newly allocated string */ |
53 | extern char* |
54 | stats_tree_node_to_str(const stat_node *node, char *buffer, unsigned len) |
55 | { |
56 | if (buffer) { |
57 | snprintf(buffer,len,"%s: %i",node->name, node->counter); |
58 | return buffer; |
59 | } else { |
60 | return ws_strdup_printf("%s: %i",node->name, node->counter)wmem_strdup_printf(((void*)0), "%s: %i",node->name, node-> counter); |
61 | } |
62 | } |
63 | |
64 | extern unsigned |
65 | // NOLINTNEXTLINE(misc-no-recursion) |
66 | stats_tree_branch_max_namelen(const stat_node *node, unsigned indent) |
67 | { |
68 | stat_node *child; |
69 | unsigned maxlen = 0; |
70 | unsigned len; |
71 | |
72 | indent = indent > INDENT_MAX32 ? INDENT_MAX32 : indent; |
73 | |
74 | if (node->children) { |
75 | for (child = node->children; child; child = child->next ) { |
76 | // Recursion is limited by proto.c checks |
77 | len = stats_tree_branch_max_namelen(child,indent+1); |
78 | maxlen = len > maxlen ? len : maxlen; |
79 | } |
80 | } |
81 | |
82 | if (node->st_flags&ST_FLG_ROOTCHILD0x20000000) { |
83 | char *display_name = stats_tree_get_displayname(node->name); |
84 | len = (unsigned) strlen(display_name) + indent; |
85 | g_free(display_name); |
86 | } |
87 | else { |
88 | len = (unsigned) strlen(node->name) + indent; |
89 | } |
90 | maxlen = len > maxlen ? len : maxlen; |
91 | |
92 | return maxlen; |
93 | } |
94 | |
95 | /* frees the resources allocated by a stat_tree node */ |
96 | static void |
97 | // NOLINTNEXTLINE(misc-no-recursion) |
98 | free_stat_node(stat_node *node) |
99 | { |
100 | stat_node *child; |
101 | stat_node *next; |
102 | burst_bucket *bucket; |
103 | |
104 | if (node->children) { |
105 | for (child = node->children; child; child = next ) { |
106 | /* child->next will be gone after free_stat_node, so cache it here */ |
107 | next = child->next; |
108 | // Recursion is limited by proto.c checks |
109 | free_stat_node(child); |
110 | } |
111 | } |
112 | |
113 | if (node->hash) g_hash_table_destroy(node->hash); |
114 | |
115 | while (node->bh) { |
116 | bucket = node->bh; |
117 | node->bh = bucket->next; |
118 | g_free(bucket); |
119 | } |
120 | |
121 | g_free(node->rng); |
122 | g_free(node->name); |
123 | g_free(node); |
124 | } |
125 | |
126 | /* destroys the whole tree instance */ |
127 | extern void |
128 | stats_tree_free(stats_tree *st) |
129 | { |
130 | stat_node *child; |
131 | stat_node *next; |
132 | |
133 | if (!st) return; |
134 | |
135 | g_free(st->filter); |
136 | g_hash_table_destroy(st->names); |
137 | g_ptr_array_free(st->parents,TRUE(!(0))); |
138 | g_free(st->display_name); |
139 | |
140 | for (child = st->root.children; child; child = next ) { |
141 | /* child->next will be gone after free_stat_node, so cache it here */ |
142 | next = child->next; |
143 | free_stat_node(child); |
144 | } |
145 | |
146 | if (st->cfg->free_tree_pr) |
147 | st->cfg->free_tree_pr(st); |
148 | |
149 | if (st->cfg->cleanup) |
150 | st->cfg->cleanup(st); |
151 | |
152 | g_free(st); |
153 | } |
154 | |
155 | |
156 | /* reset a node to its original state */ |
157 | static void |
158 | // NOLINTNEXTLINE(misc-no-recursion) |
159 | reset_stat_node(stat_node *node) |
160 | { |
161 | stat_node *child; |
162 | burst_bucket *bucket; |
163 | |
164 | node->counter = 0; |
165 | switch (node->datatype) |
166 | { |
167 | case STAT_DT_INT: |
168 | node->total.int_total = 0; |
169 | node->minvalue.int_min = INT_MAX2147483647; |
170 | node->maxvalue.int_max = INT_MIN(-2147483647 -1); |
171 | break; |
172 | case STAT_DT_FLOAT: |
173 | node->total.float_total = 0; |
174 | node->minvalue.float_min = FLT_MAX3.40282347e+38F; |
175 | node->maxvalue.float_max = FLT_MIN1.17549435e-38F; |
176 | break; |
177 | } |
178 | node->st_flags = 0; |
179 | |
180 | while (node->bh) { |
181 | bucket = node->bh; |
182 | node->bh = bucket->next; |
183 | g_free(bucket); |
184 | } |
185 | node->bh = g_new0(burst_bucket, 1)((burst_bucket *) g_malloc0_n ((1), sizeof (burst_bucket))); |
186 | node->bt = node->bh; |
187 | node->bcount = 0; |
188 | node->max_burst = 0; |
189 | node->burst_time = -1.0; |
190 | |
191 | if (node->children) { |
192 | for (child = node->children; child; child = child->next ) |
193 | // Recursion is limited by proto.c checks |
194 | reset_stat_node(child); |
195 | } |
196 | } |
197 | |
198 | /* reset the whole stats_tree */ |
199 | extern void |
200 | stats_tree_reset(void *p) |
201 | { |
202 | stats_tree *st = (stats_tree *)p; |
203 | |
204 | st->start = -1.0; |
205 | st->elapsed = 0.0; |
206 | st->now = - 1.0; |
207 | |
208 | reset_stat_node(&st->root); |
209 | } |
210 | |
211 | extern void |
212 | stats_tree_reinit(void *p) |
213 | { |
214 | stats_tree *st = (stats_tree *)p; |
215 | stat_node *child; |
216 | stat_node *next; |
217 | |
218 | for (child = st->root.children; child; child = next) { |
219 | /* child->next will be gone after free_stat_node, so cache it here */ |
220 | next = child->next; |
221 | free_stat_node(child); |
222 | } |
223 | |
224 | st->root.children = NULL((void*)0); |
225 | st->root.counter = 0; |
226 | switch (st->root.datatype) |
227 | { |
228 | case STAT_DT_INT: |
229 | st->root.total.int_total = 0; |
230 | st->root.minvalue.int_min = INT_MAX2147483647; |
231 | st->root.maxvalue.int_max = INT_MIN(-2147483647 -1); |
232 | break; |
233 | case STAT_DT_FLOAT: |
234 | st->root.total.float_total = 0; |
235 | st->root.minvalue.float_min = FLT_MAX3.40282347e+38F; |
236 | st->root.maxvalue.float_max = FLT_MIN1.17549435e-38F; |
237 | break; |
238 | } |
239 | st->root.st_flags = 0; |
240 | |
241 | st->root.bh = g_new0(burst_bucket, 1)((burst_bucket *) g_malloc0_n ((1), sizeof (burst_bucket))); |
242 | st->root.bt = st->root.bh; |
243 | st->root.bcount = 0; |
244 | st->root.max_burst = 0; |
245 | st->root.burst_time = -1.0; |
246 | |
247 | /* No more stat_nodes left in tree - clean out hash, array */ |
248 | g_hash_table_remove_all(st->names); |
249 | if (st->parents->len>1) { |
250 | g_ptr_array_remove_range(st->parents, 1, st->parents->len-1); |
251 | } |
252 | |
253 | /* Do not update st_flags for the tree (sorting) - leave as was */ |
254 | st->num_columns = N_COLUMNS; |
255 | g_free(st->display_name); |
256 | st->display_name = stats_tree_get_displayname(st->cfg->path); |
257 | |
258 | if (st->cfg->init) { |
259 | st->cfg->init(st); |
260 | } |
261 | } |
262 | |
263 | static void |
264 | stats_tree_free_configuration(void *p) |
265 | { |
266 | stats_tree_cfg* cfg = (stats_tree_cfg*)p; |
267 | g_free(cfg->tapname); |
268 | g_free(cfg->abbr); |
269 | g_free(cfg->path); |
270 | g_free(cfg->title); |
271 | g_free(cfg->first_column_name); |
272 | g_free(cfg); |
273 | } |
274 | |
275 | /* register a new stats_tree */ |
276 | extern stats_tree_cfg * |
277 | stats_tree_register(const char *tapname, const char *abbr, const char *path, |
278 | unsigned flags, |
279 | stat_tree_packet_cb packet, stat_tree_init_cb init, |
280 | stat_tree_cleanup_cb cleanup) |
281 | { |
282 | stats_tree_cfg *cfg = g_new0(stats_tree_cfg, 1)((stats_tree_cfg *) g_malloc0_n ((1), sizeof (stats_tree_cfg) )); |
283 | |
284 | /* at the very least the abbrev and the packet function should be given */ |
285 | ws_assert( tapname && abbr && packet )do { if ((1) && !(tapname && abbr && packet )) ws_log_fatal_full("", LOG_LEVEL_ERROR, "epan/stats_tree.c" , 285, __func__, "assertion failed: %s", "tapname && abbr && packet" ); } while (0); |
286 | |
287 | cfg->tapname = g_strdup(tapname); |
288 | cfg->abbr = g_strdup(abbr); |
289 | cfg->path = path ? g_strdup(path) : g_strdup(abbr); |
290 | cfg->stat_group = REGISTER_PACKET_STAT_GROUP_UNSORTED; |
291 | |
292 | GString *title_str = g_string_new(""); |
293 | char **split = g_strsplit(path, STATS_TREE_MENU_SEPARATOR"//", 0); |
294 | const char *sep = ""; |
295 | for (size_t idx = 0; split[idx]; idx++) { |
296 | g_string_append_printf(title_str, "%s%s", sep, g_strstrip(split[idx])g_strchomp (g_strchug (split[idx]))); |
297 | sep = " / "; |
298 | } |
299 | g_strfreev(split); |
300 | cfg->title = g_string_free(title_str, false0); |
301 | |
302 | cfg->packet = packet; |
303 | cfg->init = init; |
304 | cfg->cleanup = cleanup; |
305 | |
306 | cfg->flags = flags&~ST_FLG_MASK(0x10000000|0x20000000|0x01000000| 0x00400000|0x00800000|0x000F0000 ); |
307 | cfg->st_flags = flags&ST_FLG_MASK(0x10000000|0x20000000|0x01000000| 0x00400000|0x00800000|0x000F0000 ); |
308 | |
309 | if (!registry) registry = g_hash_table_new_full(g_str_hash,g_str_equal,NULL((void*)0),stats_tree_free_configuration); |
310 | |
311 | g_hash_table_insert(registry,cfg->abbr,cfg); |
312 | |
313 | return cfg; |
314 | } |
315 | |
316 | /* register a new stat_tree with default group REGISTER_PACKET_STAT_GROUP_UNSORTED from a plugin */ |
317 | extern stats_tree_cfg * |
318 | stats_tree_register_plugin(const char *tapname, const char *abbr, const char *path, |
319 | unsigned flags, |
320 | stat_tree_packet_cb packet, stat_tree_init_cb init, |
321 | stat_tree_cleanup_cb cleanup) |
322 | { |
323 | stats_tree_cfg *cfg = stats_tree_register(tapname, abbr, path, |
324 | flags, packet, init, cleanup); |
325 | cfg->plugin = true1; |
326 | |
327 | return cfg; |
328 | } |
329 | |
330 | extern void |
331 | stats_tree_set_group(stats_tree_cfg *st_config, register_stat_group_t stat_group) { |
332 | if (st_config) { |
333 | st_config->stat_group = stat_group; |
334 | } |
335 | } |
336 | |
337 | extern void |
338 | stats_tree_set_first_column_name(stats_tree_cfg *st_config, const char *column_name) { |
339 | if (st_config) { |
340 | st_config->first_column_name = g_strdup(column_name); |
341 | } |
342 | } |
343 | |
344 | extern stats_tree* |
345 | stats_tree_new(stats_tree_cfg *cfg, tree_pres *pr, const char *filter) |
346 | { |
347 | stats_tree *st = g_new0(stats_tree, 1)((stats_tree *) g_malloc0_n ((1), sizeof (stats_tree))); |
348 | |
349 | st->cfg = cfg; |
350 | st->pr = pr; |
351 | |
352 | st->names = g_hash_table_new(g_str_hash,g_str_equal); |
353 | st->parents = g_ptr_array_new(); |
354 | st->filter = g_strdup(filter); |
355 | |
356 | st->start = -1.0; |
357 | st->elapsed = 0.0; |
358 | |
359 | switch (st->root.datatype) |
360 | { |
361 | case STAT_DT_INT: |
362 | st->root.minvalue.int_min = INT_MAX2147483647; |
363 | st->root.maxvalue.int_max = INT_MIN(-2147483647 -1); |
364 | break; |
365 | case STAT_DT_FLOAT: |
366 | st->root.minvalue.float_min = FLT_MAX3.40282347e+38F; |
367 | st->root.maxvalue.float_max = FLT_MIN1.17549435e-38F; |
368 | break; |
369 | } |
370 | |
371 | st->root.bh = g_new0(burst_bucket, 1)((burst_bucket *) g_malloc0_n ((1), sizeof (burst_bucket))); |
372 | st->root.bt = st->root.bh; |
373 | st->root.burst_time = -1.0; |
374 | |
375 | st->root.name = stats_tree_get_displayname(cfg->path); |
376 | st->root.st = st; |
377 | |
378 | st->st_flags = st->cfg->st_flags; |
379 | |
380 | if (!(st->st_flags&ST_FLG_SRTCOL_MASK0x000F0000)) { |
381 | /* No default sort specified - use preferences */ |
382 | st->st_flags |= prefs.st_sort_defcolflag<<ST_FLG_SRTCOL_SHIFT16; |
383 | if (prefs.st_sort_defdescending) { |
384 | st->st_flags |= ST_FLG_SORT_DESC0x00800000; |
385 | } |
386 | } |
387 | st->num_columns = N_COLUMNS; |
388 | st->display_name = stats_tree_get_displayname(st->cfg->path); |
389 | |
390 | g_ptr_array_add(st->parents,&st->root); |
391 | |
392 | return st; |
393 | } |
394 | |
395 | /* will be the tap packet cb */ |
396 | extern tap_packet_status |
397 | stats_tree_packet(void *p, packet_info *pinfo, epan_dissect_t *edt, const void *pri, tap_flags_t flags) |
398 | { |
399 | stats_tree *st = (stats_tree *)p; |
400 | |
401 | st->now = nstime_to_msec(&pinfo->rel_ts); |
402 | if (st->start < 0.0) st->start = st->now; |
403 | |
404 | st->elapsed = st->now - st->start; |
405 | |
406 | if (st->cfg->packet) |
407 | return st->cfg->packet(st,pinfo,edt,pri, flags); |
408 | else |
409 | return TAP_PACKET_DONT_REDRAW; |
410 | } |
411 | |
412 | extern stats_tree_cfg* |
413 | stats_tree_get_cfg_by_abbr(const char *abbr) |
414 | { |
415 | if (!abbr) return NULL((void*)0); |
416 | return (stats_tree_cfg *)g_hash_table_lookup(registry,abbr); |
417 | } |
418 | |
419 | static int |
420 | compare_stat_menu_item(gconstpointer stat_a, gconstpointer stat_b) |
421 | { |
422 | const stats_tree_cfg* stat_cfg_a = (const stats_tree_cfg*)stat_a; |
423 | const stats_tree_cfg* stat_cfg_b = (const stats_tree_cfg*)stat_b; |
424 | |
425 | return strcmp(stat_cfg_a->path, stat_cfg_b->path); |
426 | } |
427 | |
428 | extern GList* |
429 | stats_tree_get_cfg_list(void) |
430 | { |
431 | GList* registry_list = g_hash_table_get_values(registry); |
432 | /* Now sort the list so they can show up in the |
433 | menu alphabetically */ |
434 | return g_list_sort(registry_list, compare_stat_menu_item); |
435 | |
436 | } |
437 | |
438 | struct _stats_tree_pres_cbs { |
439 | void (*setup_node_pr)(stat_node*); |
440 | void (*free_tree_pr)(stats_tree*); |
441 | }; |
442 | |
443 | static void |
444 | setup_tree_presentation(void *k _U___attribute__((unused)), void *v, void *p) |
445 | { |
446 | stats_tree_cfg *cfg = (stats_tree_cfg *)v; |
447 | struct _stats_tree_pres_cbs *d = (struct _stats_tree_pres_cbs *)p; |
448 | |
449 | cfg->setup_node_pr = d->setup_node_pr; |
450 | cfg->free_tree_pr = d->free_tree_pr; |
451 | |
452 | } |
453 | |
454 | extern void |
455 | stats_tree_presentation(void (*registry_iterator)(void *,void *,void *), |
456 | void (*setup_node_pr)(stat_node*), |
457 | void (*free_tree_pr)(stats_tree*), |
458 | void *data) |
459 | { |
460 | static struct _stats_tree_pres_cbs d; |
461 | |
462 | d.setup_node_pr = setup_node_pr; |
463 | d.free_tree_pr = free_tree_pr; |
464 | |
465 | if (registry) g_hash_table_foreach(registry,setup_tree_presentation,&d); |
466 | |
467 | if (registry_iterator && registry) |
468 | g_hash_table_foreach(registry,registry_iterator,data); |
469 | |
470 | } |
471 | |
472 | |
473 | /* creates a stat_tree node |
474 | * name: the name of the stats_tree node |
475 | * parent_name: the name of the ALREADY REGISTERED parent |
476 | * with_hash: whether or not it should keep a hash with its children names |
477 | * as_named_node: whether or not it has to be registered in the root namespace |
478 | */ |
479 | static stat_node* |
480 | new_stat_node(stats_tree *st, const char *name, int parent_id, stat_node_datatype datatype, |
481 | bool_Bool with_hash, bool_Bool as_parent_node) |
482 | { |
483 | |
484 | stat_node *node = g_new0(stat_node, 1)((stat_node *) g_malloc0_n ((1), sizeof (stat_node))); |
485 | stat_node *last_chld = NULL((void*)0); |
486 | |
487 | node->datatype = datatype; |
488 | switch (datatype) |
| 24 | | Control jumps to 'case STAT_DT_INT:' at line 490 | |
|
489 | { |
490 | case STAT_DT_INT: |
491 | node->minvalue.int_min = INT_MAX2147483647; |
492 | node->maxvalue.int_max = INT_MIN(-2147483647 -1); |
493 | break; |
494 | case STAT_DT_FLOAT: |
495 | node->minvalue.float_min = FLT_MAX3.40282347e+38F; |
496 | node->maxvalue.float_max = FLT_MIN1.17549435e-38F; |
497 | break; |
498 | } |
499 | node->st_flags = parent_id?0:ST_FLG_ROOTCHILD0x20000000; |
| 25 | | Assuming 'parent_id' is 0 | |
|
| |
500 | |
501 | node->bh = g_new0(burst_bucket, 1)((burst_bucket *) g_malloc0_n ((1), sizeof (burst_bucket))); |
| 27 | | Null pointer value stored to field 'next' | |
|
502 | node->bt = node->bh; |
503 | node->burst_time = -1.0; |
504 | |
505 | node->name = g_strdup(name); |
506 | node->st = st; |
507 | node->hash = with_hash ? g_hash_table_new(g_str_hash,g_str_equal) : NULL((void*)0); |
| |
508 | |
509 | if (as_parent_node28.1 | 'as_parent_node' is false | ) { |
| |
510 | g_hash_table_insert(st->names, |
511 | node->name, |
512 | node); |
513 | |
514 | g_ptr_array_add(st->parents,node); |
515 | |
516 | node->id = st->parents->len - 1; |
517 | } else { |
518 | node->id = -1; |
519 | } |
520 | |
521 | if (parent_id >= 0 && parent_id29.2 | 'parent_id' is < field 'len' | < (int) st->parents->len ) { |
| |
522 | node->parent = (stat_node *)g_ptr_array_index(st->parents,parent_id)((st->parents)->pdata)[parent_id]; |
523 | } else { |
524 | /* ??? should we set the parent to be root ??? */ |
525 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "epan/stats_tree.c", 525 , __func__, "assertion \"not reached\" failed"); |
526 | } |
527 | |
528 | if (node->parent->children) { |
| 31 | | Assuming field 'children' is null | |
|
| |
529 | /* insert as last child */ |
530 | |
531 | for (last_chld = node->parent->children; |
532 | last_chld->next; |
533 | last_chld = last_chld->next ) ; |
534 | |
535 | last_chld->next = node; |
536 | |
537 | } else { |
538 | /* insert as first child */ |
539 | node->parent->children = node; |
540 | } |
541 | |
542 | if(node->parent->hash) { |
| 33 | | Assuming field 'hash' is null | |
|
| |
543 | g_hash_table_replace(node->parent->hash,node->name,node); |
544 | } |
545 | |
546 | if (st->cfg->setup_node_pr) { |
| 35 | | Assuming field 'setup_node_pr' is null | |
|
| |
547 | st->cfg->setup_node_pr(node); |
548 | } else { |
549 | node->pr = NULL((void*)0); |
550 | } |
551 | |
552 | return node; |
553 | } |
554 | /***/ |
555 | |
556 | extern int |
557 | stats_tree_create_node(stats_tree *st, const char *name, int parent_id, stat_node_datatype datatype, bool_Bool with_hash) |
558 | { |
559 | stat_node *node = new_stat_node(st,name,parent_id,datatype,with_hash,true1); |
560 | |
561 | if (node) |
562 | return node->id; |
563 | else |
564 | return 0; |
565 | } |
566 | |
567 | /* XXX: should this be a macro? */ |
568 | extern int |
569 | stats_tree_create_node_by_pname(stats_tree *st, const char *name, |
570 | const char *parent_name, stat_node_datatype datatype, bool_Bool with_children) |
571 | { |
572 | return stats_tree_create_node(st,name,stats_tree_parent_id_by_name(st,parent_name),datatype,with_children); |
573 | } |
574 | |
575 | /* Internal function to update the burst calculation data - add entry to bucket */ |
576 | static void |
577 | update_burst_calc(stat_node *node, int value) |
578 | { |
579 | double current_bucket; |
580 | double burstwin; |
581 | |
582 | burst_bucket *bn; |
583 | |
584 | if (!prefs.st_enable_burstinfo39.1 | Field 'st_enable_burstinfo' is true | ) { |
| 2 | | Assuming field 'st_enable_burstinfo' is true | |
|
| |
| |
585 | return; |
586 | } |
587 | |
588 | /* NB thebucket list should always contain at least one node - even if it is */ |
589 | /* the dummy created at init time. Head and tail should never be NULL! */ |
590 | current_bucket = floor(node->st->now/prefs.st_burst_resolution); |
591 | burstwin = prefs.st_burst_windowlen/prefs.st_burst_resolution; |
592 | if (current_bucket>node->bt->bucket_no) { |
| 4 | | Assuming 'current_bucket' is <= field 'bucket_no' | |
|
| |
| 41 | | Assuming 'current_bucket' is <= field 'bucket_no' | |
|
| |
593 | /* Must add a new bucket at the burst list tail */ |
594 | bn = g_new0(burst_bucket, 1)((burst_bucket *) g_malloc0_n ((1), sizeof (burst_bucket))); |
595 | bn->count = value; |
596 | bn->bucket_no = current_bucket; |
597 | bn->start_time = node->st->now; |
598 | bn->prev = node->bt; |
599 | node->bt->next = bn; |
600 | node->bt = bn; |
601 | /* And add value to the current burst count for node */ |
602 | node->bcount += value; |
603 | /* Check if bucket list head is now too old and must be removed */ |
604 | while (current_bucket>=(node->bh->bucket_no+burstwin)) { |
605 | /* off with its head! */ |
606 | bn = node->bh; |
607 | node->bh = bn->next; |
608 | node->bh->prev = NULL((void*)0); |
609 | node->bcount -= bn->count; |
610 | g_free(bn); |
611 | } |
612 | } |
613 | else if (current_bucket<node->bh->bucket_no) { |
| 6 | | Assuming 'current_bucket' is >= field 'bucket_no' | |
|
| |
| 43 | | Assuming 'current_bucket' is >= field 'bucket_no' | |
|
| |
614 | /* Packet must be added at head of burst list - check if not too old */ |
615 | if ((current_bucket+burstwin)>node->bt->bucket_no) { |
616 | /* packet still within the window */ |
617 | bn = g_new0(burst_bucket, 1)((burst_bucket *) g_malloc0_n ((1), sizeof (burst_bucket))); |
618 | bn->count = value; |
619 | bn->bucket_no = current_bucket; |
620 | bn->start_time = node->st->now; |
621 | bn->next = node->bh; |
622 | node->bh->prev = bn; |
623 | node->bh = bn; |
624 | /* And add value to the current burst count for node */ |
625 | node->bcount += value; |
626 | } |
627 | } |
628 | else |
629 | { |
630 | /* Somewhere in the middle... */ |
631 | burst_bucket *search = node->bt; |
632 | while (current_bucket<search->bucket_no) { |
| 8 | | Loop condition is false. Execution continues on line 635 | |
|
| 45 | | Loop condition is false. Execution continues on line 635 | |
|
633 | search = search->prev; |
634 | } |
635 | if (current_bucket==search->bucket_no) { |
| 9 | | Assuming 'current_bucket' is not equal to field 'bucket_no' | |
|
| |
| 46 | | Assuming 'current_bucket' is not equal to field 'bucket_no' | |
|
| |
636 | /* found existing bucket, increase value */ |
637 | search->count += value; |
638 | if (search->start_time>node->st->now) { |
639 | search->start_time = node->st->now; |
640 | } |
641 | } |
642 | else { |
643 | /* must add a new bucket after bn. */ |
644 | bn = g_new0(burst_bucket, 1)((burst_bucket *) g_malloc0_n ((1), sizeof (burst_bucket))); |
645 | bn->count = value; |
646 | bn->bucket_no = current_bucket; |
647 | bn->start_time = node->st->now; |
648 | bn->prev = search; |
649 | bn->next = search->next; |
| 48 | | Null pointer value stored to field 'next' | |
|
650 | search->next = bn; |
651 | bn->next->prev = bn; |
| 49 | | Access to field 'prev' results in a dereference of a null pointer (loaded from field 'next') |
|
652 | } |
653 | node->bcount += value; |
654 | } |
655 | if (node->bcount>node->max_burst) { |
| 11 | | Assuming field 'bcount' is <= field 'max_burst' | |
|
| |
656 | /* new record burst */ |
657 | node->max_burst = node->bcount; |
658 | node->burst_time = node->bh->start_time; |
659 | } |
660 | } |
661 | |
662 | /* |
663 | * Increases by delta the counter of the node whose name is given |
664 | * if the node does not exist yet it's created (with counter=1) |
665 | * using parent_name as parent node. |
666 | * with_hash=true to indicate that the created node will have a parent |
667 | */ |
668 | int |
669 | stats_tree_manip_node_int(manip_node_mode mode, stats_tree *st, const char *name, |
670 | int parent_id, bool_Bool with_hash, int value) |
671 | { |
672 | stat_node *node = NULL((void*)0); |
673 | stat_node *parent = NULL((void*)0); |
674 | |
675 | ws_assert( parent_id >= 0 && parent_id < (int) st->parents->len )do { if ((1) && !(parent_id >= 0 && parent_id < (int) st->parents->len)) ws_log_fatal_full("", LOG_LEVEL_ERROR , "epan/stats_tree.c", 675, __func__, "assertion failed: %s", "parent_id >= 0 && parent_id < (int) st->parents->len" ); } while (0); |
| 15 | | Assuming 'parent_id' is >= 0 | |
|
| 16 | | Assuming 'parent_id' is < field 'len' | |
|
| |
| 18 | | Loop condition is false. Exiting loop | |
|
676 | |
677 | parent = (stat_node *)g_ptr_array_index(st->parents,parent_id)((st->parents)->pdata)[parent_id]; |
678 | |
679 | if( parent->hash ) { |
| 19 | | Assuming field 'hash' is null | |
|
| |
680 | node = (stat_node *)g_hash_table_lookup(parent->hash,name); |
681 | } else { |
682 | node = (stat_node *)g_hash_table_lookup(st->names,name); |
683 | } |
684 | |
685 | if ( node == NULL((void*)0) ) |
| 21 | | Assuming 'node' is equal to NULL | |
|
| |
686 | node = new_stat_node(st,name,parent_id,STAT_DT_INT,with_hash,with_hash); |
| 23 | | Calling 'new_stat_node' | |
|
| 37 | | Returning from 'new_stat_node' | |
|
687 | |
688 | switch (mode) { |
| 38 | | Control jumps to 'case MN_INCREASE:' at line 689 | |
|
689 | case MN_INCREASE: |
690 | node->counter += value; |
691 | update_burst_calc(node, value); |
| 39 | | Calling 'update_burst_calc' | |
|
692 | break; |
693 | case MN_SET: |
694 | node->counter = value; |
695 | break; |
696 | case MN_AVERAGE: |
697 | node->counter++; |
698 | update_burst_calc(node, 1); |
699 | /* fall through */ /*to average code */ |
700 | case MN_AVERAGE_NOTICK: |
701 | node->total.int_total += value; |
702 | if (node->minvalue.int_min > value) { |
703 | node->minvalue.int_min = value; |
704 | } |
705 | if (node->maxvalue.int_max < value) { |
706 | node->maxvalue.int_max = value; |
707 | } |
708 | node->st_flags |= ST_FLG_AVERAGE0x10000000; |
709 | break; |
710 | case MN_SET_FLAGS: |
711 | node->st_flags |= value; |
712 | break; |
713 | case MN_CLEAR_FLAGS: |
714 | node->st_flags &= ~value; |
715 | break; |
716 | } |
717 | |
718 | if (node) |
719 | return node->id; |
720 | else |
721 | return -1; |
722 | } |
723 | |
724 | /* |
725 | * Increases by delta the counter of the node whose name is given |
726 | * if the node does not exist yet it's created (with counter=1) |
727 | * using parent_name as parent node. |
728 | * with_hash=true to indicate that the created node will have a parent |
729 | */ |
730 | int |
731 | stats_tree_manip_node_float(manip_node_mode mode, stats_tree *st, const char *name, |
732 | int parent_id, bool_Bool with_hash, float value) |
733 | { |
734 | stat_node *node = NULL((void*)0); |
735 | stat_node *parent = NULL((void*)0); |
736 | |
737 | ws_assert(parent_id >= 0 && parent_id < (int)st->parents->len)do { if ((1) && !(parent_id >= 0 && parent_id < (int)st->parents->len)) ws_log_fatal_full("", LOG_LEVEL_ERROR , "epan/stats_tree.c", 737, __func__, "assertion failed: %s", "parent_id >= 0 && parent_id < (int)st->parents->len" ); } while (0); |
738 | |
739 | parent = (stat_node *)g_ptr_array_index(st->parents, parent_id)((st->parents)->pdata)[parent_id]; |
740 | |
741 | if (parent->hash) { |
742 | node = (stat_node *)g_hash_table_lookup(parent->hash, name); |
743 | } |
744 | else { |
745 | node = (stat_node *)g_hash_table_lookup(st->names, name); |
746 | } |
747 | |
748 | if (node == NULL((void*)0)) |
749 | node = new_stat_node(st, name, parent_id, STAT_DT_FLOAT, with_hash, with_hash); |
750 | |
751 | switch (mode) { |
752 | case MN_AVERAGE: |
753 | node->counter++; |
754 | update_burst_calc(node, 1); |
755 | /* fall through */ /*to average code */ |
756 | case MN_AVERAGE_NOTICK: |
757 | node->total.float_total += value; |
758 | if (node->minvalue.float_min > value) { |
759 | node->minvalue.float_min = value; |
760 | } |
761 | if (node->maxvalue.float_max < value) { |
762 | node->maxvalue.float_max = value; |
763 | } |
764 | node->st_flags |= ST_FLG_AVERAGE0x10000000; |
765 | break; |
766 | default: |
767 | //only average is currently supported |
768 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "epan/stats_tree.c", 768 , __func__, "assertion \"not reached\" failed"); |
769 | break; |
770 | } |
771 | |
772 | if (node) |
773 | return node->id; |
774 | else |
775 | return -1; |
776 | } |
777 | |
778 | extern char* |
779 | stats_tree_get_abbr(const char *opt_arg) |
780 | { |
781 | unsigned i; |
782 | |
783 | /* XXX: this fails when tshark is given any options |
784 | after the -z */ |
785 | ws_assert(opt_arg != NULL)do { if ((1) && !(opt_arg != ((void*)0))) ws_log_fatal_full ("", LOG_LEVEL_ERROR, "epan/stats_tree.c", 785, __func__, "assertion failed: %s" , "opt_arg != ((void*)0)"); } while (0); |
786 | |
787 | for (i=0; opt_arg[i] && opt_arg[i] != ','; i++); |
788 | |
789 | if (opt_arg[i] == ',') { |
790 | return g_strndup(opt_arg,i); |
791 | } else { |
792 | return NULL((void*)0); |
793 | } |
794 | } |
795 | |
796 | |
797 | /* |
798 | * This function accepts an input string which should define a long integer range. |
799 | * The normal result is a struct containing the floor and ceil value of this |
800 | * range. |
801 | * |
802 | * It is allowed to define a range string in the following ways : |
803 | * |
804 | * "0-10" -> { 0, 10 } |
805 | * "-0" -> { INT_MIN, 0 } |
806 | * "0-" -> { 0, INT_MAX } |
807 | * "-" -> { INT_MIN, INT_MAX } |
808 | * |
809 | * Note that this function is robust to buggy input string. If in some cases it |
810 | * returns NULL, it but may also return a pair with undefined values. |
811 | * |
812 | */ |
813 | static range_pair_t* |
814 | get_range(char *rngstr) |
815 | { |
816 | char **split; |
817 | range_pair_t *rng; |
818 | |
819 | split = g_strsplit((char*)rngstr,"-",2); |
820 | |
821 | /* empty string */ |
822 | if (split[0] == NULL((void*)0)) { |
823 | g_strfreev(split); |
824 | return NULL((void*)0); |
825 | } |
826 | |
827 | rng = g_new(range_pair_t, 1)((range_pair_t *) g_malloc_n ((1), sizeof (range_pair_t))); |
828 | |
829 | if (split[1] == NULL((void*)0)) { |
830 | /* means we have a non empty string with no delimiter |
831 | * so it must be a single number */ |
832 | rng->floor = (int)strtol(split[0],NULL((void*)0),10); |
833 | rng->ceil = rng->floor; |
834 | } else { |
835 | /* string == "X-?" */ |
836 | if (*(split[0]) != '\0') { |
837 | rng->floor = (int)strtol(split[0],NULL((void*)0),10); |
838 | } else { |
839 | /* string == "-?" */ |
840 | rng->floor = INT_MIN(-2147483647 -1); |
841 | } |
842 | |
843 | /* string != "?-" */ |
844 | if (*(split[1]) != '\0') { |
845 | rng->ceil = (int)strtol(split[1],NULL((void*)0),10); |
846 | } else { |
847 | /* string == "?-" */ |
848 | rng->ceil = INT_MAX2147483647; |
849 | } |
850 | } |
851 | g_strfreev(split); |
852 | |
853 | return rng; |
854 | } |
855 | |
856 | |
857 | extern int |
858 | stats_tree_create_range_node(stats_tree *st, const char *name, int parent_id, ...) |
859 | { |
860 | va_list list; |
861 | char *curr_range; |
862 | stat_node *rng_root = new_stat_node(st, name, parent_id, STAT_DT_INT, false0, true1); |
863 | stat_node *range_node = NULL((void*)0); |
864 | |
865 | va_start( list, parent_id )__builtin_va_start(list, parent_id); |
866 | while (( curr_range = va_arg(list, char*)__builtin_va_arg(list, char*) )) { |
867 | range_node = new_stat_node(st, curr_range, rng_root->id, STAT_DT_INT, false0, false0); |
868 | range_node->rng = get_range(curr_range); |
869 | } |
870 | va_end( list )__builtin_va_end(list); |
871 | |
872 | return rng_root->id; |
873 | } |
874 | |
875 | extern int |
876 | stats_tree_create_range_node_string(stats_tree *st, const char *name, |
877 | int parent_id, int num_str_ranges, |
878 | char** str_ranges) |
879 | { |
880 | int i; |
881 | stat_node *rng_root = new_stat_node(st, name, parent_id, STAT_DT_INT, false0, true1); |
882 | stat_node *range_node = NULL((void*)0); |
883 | |
884 | for (i = 0; i < num_str_ranges - 1; i++) { |
885 | range_node = new_stat_node(st, str_ranges[i], rng_root->id, STAT_DT_INT, false0, false0); |
886 | range_node->rng = get_range(str_ranges[i]); |
887 | } |
888 | range_node = new_stat_node(st, str_ranges[i], rng_root->id, STAT_DT_INT, false0, false0); |
889 | range_node->rng = get_range(str_ranges[i]); |
890 | if (range_node->rng->floor == range_node->rng->ceil) { |
891 | range_node->rng->ceil = INT_MAX2147483647; |
892 | } |
893 | |
894 | return rng_root->id; |
895 | } |
896 | |
897 | /****/ |
898 | extern int |
899 | stats_tree_parent_id_by_name(stats_tree *st, const char *parent_name) |
900 | { |
901 | stat_node *node = (stat_node *)g_hash_table_lookup(st->names,parent_name); |
902 | |
903 | if (node) |
904 | return node->id; |
905 | else |
906 | return 0; /* XXX: this is the root should we return -1 instead?*/ |
907 | } |
908 | |
909 | |
910 | extern int |
911 | stats_tree_range_node_with_pname(stats_tree *st, const char *name, |
912 | const char *parent_name, ...) |
913 | { |
914 | va_list list; |
915 | char *curr_range; |
916 | stat_node *range_node = NULL((void*)0); |
917 | int parent_id = stats_tree_parent_id_by_name(st,parent_name); |
918 | stat_node *rng_root = new_stat_node(st, name, parent_id, STAT_DT_INT, false0, true1); |
919 | |
920 | va_start( list, parent_name )__builtin_va_start(list, parent_name); |
921 | while (( curr_range = va_arg(list, char*)__builtin_va_arg(list, char*) )) { |
922 | range_node = new_stat_node(st, curr_range, rng_root->id, STAT_DT_INT, false0, false0); |
923 | range_node->rng = get_range(curr_range); |
924 | } |
925 | va_end( list )__builtin_va_end(list); |
926 | |
927 | return rng_root->id; |
928 | } |
929 | |
930 | |
931 | extern int |
932 | stats_tree_tick_range(stats_tree *st, const char *name, int parent_id, |
933 | int value_in_range) |
934 | { |
935 | |
936 | stat_node *node = NULL((void*)0); |
937 | stat_node *parent = NULL((void*)0); |
938 | stat_node *child = NULL((void*)0); |
939 | int stat_floor, stat_ceil; |
940 | |
941 | if (parent_id >= 0 && parent_id < (int) st->parents->len) { |
942 | parent = (stat_node *)g_ptr_array_index(st->parents,parent_id)((st->parents)->pdata)[parent_id]; |
943 | } else { |
944 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "epan/stats_tree.c", 944 , __func__, "assertion \"not reached\" failed"); |
945 | } |
946 | |
947 | if( parent->hash ) { |
948 | node = (stat_node *)g_hash_table_lookup(parent->hash,name); |
949 | } else { |
950 | node = (stat_node *)g_hash_table_lookup(st->names,name); |
951 | } |
952 | |
953 | if ( node == NULL((void*)0) ) |
954 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "epan/stats_tree.c", 954 , __func__, "assertion \"not reached\" failed"); |
955 | |
956 | /* update stats for container node. counter should already be ticked so we only update total and min/max */ |
957 | node->total.int_total += value_in_range; |
958 | if (node->minvalue.int_min > value_in_range) { |
959 | node->minvalue.int_min = value_in_range; |
960 | } |
961 | if (node->maxvalue.int_max < value_in_range) { |
962 | node->maxvalue.int_max = value_in_range; |
963 | } |
964 | node->st_flags |= ST_FLG_AVERAGE0x10000000; |
965 | |
966 | for ( child = node->children; child; child = child->next) { |
967 | stat_floor = child->rng->floor; |
968 | stat_ceil = child->rng->ceil; |
969 | |
970 | if ( value_in_range >= stat_floor && value_in_range <= stat_ceil ) { |
971 | child->counter++; |
972 | child->total.int_total += value_in_range; |
973 | if (child->minvalue.int_min > value_in_range) { |
974 | child->minvalue.int_min = value_in_range; |
975 | } |
976 | if (child->maxvalue.int_max < value_in_range) { |
977 | child->maxvalue.int_max = value_in_range; |
978 | } |
979 | child->st_flags |= ST_FLG_AVERAGE0x10000000; |
980 | update_burst_calc(child, 1); |
981 | return node->id; |
982 | } |
983 | } |
984 | |
985 | return node->id; |
986 | } |
987 | |
988 | extern int |
989 | stats_tree_create_pivot(stats_tree *st, const char *name, int parent_id) |
990 | { |
991 | stat_node *node = new_stat_node(st,name,parent_id,STAT_DT_INT,true1,true1); |
992 | |
993 | if (node) |
994 | return node->id; |
995 | else |
996 | return 0; |
997 | } |
998 | |
999 | extern int |
1000 | stats_tree_create_pivot_by_pname(stats_tree *st, const char *name, |
1001 | const char *parent_name) |
1002 | { |
1003 | int parent_id = stats_tree_parent_id_by_name(st,parent_name); |
1004 | stat_node *node; |
1005 | |
1006 | node = new_stat_node(st,name,parent_id,STAT_DT_INT,true1,true1); |
1007 | |
1008 | if (node) |
1009 | return node->id; |
1010 | else |
1011 | return 0; |
1012 | } |
1013 | |
1014 | extern int |
1015 | stats_tree_tick_pivot(stats_tree *st, int pivot_id, const char *pivot_value) |
1016 | { |
1017 | stat_node *parent = (stat_node *)g_ptr_array_index(st->parents,pivot_id)((st->parents)->pdata)[pivot_id]; |
1018 | |
1019 | parent->counter++; |
1020 | update_burst_calc(parent, 1); |
| 1 | Calling 'update_burst_calc' | |
|
| 13 | | Returning from 'update_burst_calc' | |
|
1021 | stats_tree_manip_node_int( MN_INCREASE, st, pivot_value, pivot_id, false0, 1); |
| 14 | | Calling 'stats_tree_manip_node_int' | |
|
1022 | |
1023 | return pivot_id; |
1024 | } |
1025 | |
1026 | extern char* |
1027 | stats_tree_get_displayname (char* fullname) |
1028 | { |
1029 | char *buf = g_strdup(fullname); |
1030 | char *sep; |
1031 | |
1032 | if (prefs.st_sort_showfullname) { |
1033 | return buf; /* unmodified */ |
1034 | } |
1035 | |
1036 | sep = buf; |
1037 | while ((sep = strchr(sep,'/')) != NULL((void*)0)) { |
1038 | if (*(++sep)=='/') { /* escaped slash - two slash characters after each other */ |
1039 | memmove(sep,sep+1,strlen(sep)); |
1040 | } |
1041 | else { |
1042 | /* we got a new path separator */ |
1043 | memmove(buf,sep,strlen(sep)+1); |
1044 | sep = buf; |
1045 | } |
1046 | } |
1047 | |
1048 | return buf; |
1049 | } |
1050 | |
1051 | extern int |
1052 | stats_tree_get_default_sort_col (stats_tree *st) |
1053 | { |
1054 | switch ((st->st_flags&ST_FLG_SRTCOL_MASK0x000F0000)>>ST_FLG_SRTCOL_SHIFT16) { |
1055 | case ST_SORT_COL_NAME1: |
1056 | return COL_NAME; |
1057 | case ST_SORT_COL_COUNT2: |
1058 | return COL_COUNT; |
1059 | case ST_SORT_COL_AVG3: |
1060 | return COL_AVERAGE; |
1061 | case ST_SORT_COL_MIN4: |
1062 | return COL_MIN; |
1063 | case ST_SORT_COL_MAX5: |
1064 | return COL_MAX; |
1065 | case ST_SORT_COL_BURSTRATE6: |
1066 | return COL_BURSTRATE; |
1067 | } |
1068 | return COL_COUNT; /* nothing specific set */ |
1069 | } |
1070 | |
1071 | extern bool_Bool |
1072 | stats_tree_is_default_sort_DESC (stats_tree *st) |
1073 | { |
1074 | return st->st_flags&ST_FLG_SORT_DESC0x00800000; |
1075 | } |
1076 | |
1077 | extern const char* |
1078 | stats_tree_get_column_name (stats_tree_cfg *st_config, int col_index) |
1079 | { |
1080 | switch (col_index) { |
1081 | case COL_NAME: |
1082 | if (st_config->first_column_name) { |
1083 | return st_config->first_column_name; |
1084 | } |
1085 | return "Topic / Item"; |
1086 | case COL_COUNT: |
1087 | return "Count"; |
1088 | case COL_AVERAGE: |
1089 | return "Average"; |
1090 | case COL_MIN: |
1091 | return "Min Val"; |
1092 | case COL_MAX: |
1093 | return "Max Val"; |
1094 | case COL_RATE: |
1095 | return "Rate (ms)"; |
1096 | case COL_PERCENT: |
1097 | return "Percent"; |
1098 | case COL_BURSTRATE: |
1099 | return prefs.st_burst_showcount ? "Burst Count" : "Burst Rate"; |
1100 | case COL_BURSTTIME: |
1101 | return "Burst Start"; |
1102 | default: |
1103 | return "(Unknown)"; |
1104 | } |
1105 | } |
1106 | |
1107 | extern int |
1108 | stats_tree_get_column_size (int col_index) |
1109 | { |
1110 | if (col_index==COL_NAME) { |
1111 | return 36; /* but caller should really call stats_tree_branch_max_namelen() */ |
1112 | } |
1113 | if (col_index<N_COLUMNS) { |
1114 | return 12; /* all numerical values are this size */ |
1115 | } |
1116 | return 0; /* invalid column */ |
1117 | } |
1118 | |
1119 | extern char** |
1120 | stats_tree_get_values_from_node (const stat_node* node) |
1121 | { |
1122 | char **values = (char**) g_malloc0(sizeof(char*)*(node->st->num_columns)); |
1123 | |
1124 | values[COL_NAME] = (node->st_flags&ST_FLG_ROOTCHILD0x20000000)?stats_tree_get_displayname(node->name):g_strdup(node->name); |
1125 | values[COL_COUNT] = ws_strdup_printf("%u",node->counter)wmem_strdup_printf(((void*)0), "%u",node->counter); |
1126 | if (((node->st_flags&ST_FLG_AVERAGE0x10000000) || node->rng)) { |
1127 | if (node->counter) { |
1128 | switch (node->datatype) |
1129 | { |
1130 | case STAT_DT_INT: |
1131 | values[COL_AVERAGE] = ws_strdup_printf("%.2f", ((float)node->total.int_total) / node->counter)wmem_strdup_printf(((void*)0), "%.2f", ((float)node->total .int_total) / node->counter); |
1132 | break; |
1133 | case STAT_DT_FLOAT: |
1134 | values[COL_AVERAGE] = ws_strdup_printf("%.2f", node->total.float_total / node->counter)wmem_strdup_printf(((void*)0), "%.2f", node->total.float_total / node->counter); |
1135 | break; |
1136 | } |
1137 | } else { |
1138 | values[COL_AVERAGE] = g_strdup("-"); |
1139 | } |
1140 | } else { |
1141 | values[COL_AVERAGE] = g_strdup(""); |
1142 | } |
1143 | |
1144 | if (((node->st_flags&ST_FLG_AVERAGE0x10000000) || node->rng)) { |
1145 | if (node->counter) { |
1146 | switch (node->datatype) |
1147 | { |
1148 | case STAT_DT_INT: |
1149 | values[COL_MIN] = ws_strdup_printf("%d", node->minvalue.int_min)wmem_strdup_printf(((void*)0), "%d", node->minvalue.int_min ); |
1150 | break; |
1151 | case STAT_DT_FLOAT: |
1152 | values[COL_MIN] = ws_strdup_printf("%f", node->minvalue.float_min)wmem_strdup_printf(((void*)0), "%f", node->minvalue.float_min ); |
1153 | break; |
1154 | } |
1155 | } |
1156 | else { |
1157 | values[COL_MIN] = g_strdup("-"); |
1158 | } |
1159 | } |
1160 | else { |
1161 | values[COL_MIN] = g_strdup(""); |
1162 | } |
1163 | |
1164 | if (((node->st_flags&ST_FLG_AVERAGE0x10000000) || node->rng)) { |
1165 | if (node->counter) { |
1166 | switch (node->datatype) |
1167 | { |
1168 | case STAT_DT_INT: |
1169 | values[COL_MAX] = ws_strdup_printf("%d", node->maxvalue.int_max)wmem_strdup_printf(((void*)0), "%d", node->maxvalue.int_max ); |
1170 | break; |
1171 | case STAT_DT_FLOAT: |
1172 | values[COL_MAX] = ws_strdup_printf("%f", node->maxvalue.float_max)wmem_strdup_printf(((void*)0), "%f", node->maxvalue.float_max ); |
1173 | break; |
1174 | } |
1175 | } |
1176 | else { |
1177 | values[COL_MAX] = g_strdup("-"); |
1178 | } |
1179 | } |
1180 | else { |
1181 | values[COL_MAX] = g_strdup(""); |
1182 | } |
1183 | |
1184 | values[COL_RATE] = (node->st->elapsed)?ws_strdup_printf("%.4f",((float)node->counter)/node->st->elapsed)wmem_strdup_printf(((void*)0), "%.4f",((float)node->counter )/node->st->elapsed):g_strdup(""); |
1185 | values[COL_PERCENT] = ((node->parent)&&(node->parent->counter))? |
1186 | ws_strdup_printf("%.2f%%",(node->counter*100.0)/node->parent->counter)wmem_strdup_printf(((void*)0), "%.2f%%",(node->counter*100.0 )/node->parent->counter): |
1187 | (node->parent==&(node->st->root)?g_strdup("100%"):g_strdup("")); |
1188 | if (node->st->num_columns>COL_BURSTTIME) { |
1189 | values[COL_BURSTRATE] = (!prefs.st_enable_burstinfo)?g_strdup(""): |
1190 | (node->max_burst?(prefs.st_burst_showcount? |
1191 | ws_strdup_printf("%d",node->max_burst)wmem_strdup_printf(((void*)0), "%d",node->max_burst): |
1192 | ws_strdup_printf("%.4f",((double)node->max_burst)/prefs.st_burst_windowlen)wmem_strdup_printf(((void*)0), "%.4f",((double)node->max_burst )/prefs.st_burst_windowlen)): |
1193 | g_strdup("-")); |
1194 | values[COL_BURSTTIME] = (!prefs.st_enable_burstinfo)?g_strdup(""): |
1195 | (node->max_burst?ws_strdup_printf("%.3f",(node->burst_time/1000.0))wmem_strdup_printf(((void*)0), "%.3f",(node->burst_time/1000.0 )):g_strdup("-")); |
1196 | } |
1197 | return values; |
1198 | } |
1199 | |
1200 | extern int |
1201 | stats_tree_sort_compare (const stat_node *a, const stat_node *b, int sort_column, |
1202 | bool_Bool sort_descending) |
1203 | { |
1204 | int result = 0; |
1205 | float avg_a = 0, avg_b = 0; |
1206 | |
1207 | if (prefs.st_sort_rng_nameonly&&(a->rng&&b->rng)) { |
1208 | /* always sort ranges by range name */ |
1209 | result = a->rng->floor - b->rng->floor; |
1210 | if (sort_descending&&(!prefs.st_sort_rng_fixorder)) { |
1211 | result = -result; |
1212 | } |
1213 | return result; |
1214 | } |
1215 | |
1216 | switch (sort_column) { |
1217 | case COL_NAME: |
1218 | if (a->rng&&b->rng) { |
1219 | result = a->rng->floor - b->rng->floor; |
1220 | } |
1221 | else if (prefs.st_sort_casesensitve) { |
1222 | result = strcmp(a->name,b->name); |
1223 | } |
1224 | else { |
1225 | result = g_ascii_strcasecmp(a->name,b->name); |
1226 | } |
1227 | break; |
1228 | |
1229 | case COL_RATE: |
1230 | case COL_PERCENT: |
1231 | case COL_COUNT: |
1232 | result = a->counter - b->counter; |
1233 | break; |
1234 | |
1235 | case COL_AVERAGE: |
1236 | switch (a->datatype) |
1237 | { |
1238 | case STAT_DT_INT: |
1239 | avg_a = a->counter ? ((float)a->total.int_total)/a->counter : 0; |
1240 | avg_b = b->counter ? ((float)b->total.int_total)/b->counter : 0; |
1241 | break; |
1242 | case STAT_DT_FLOAT: |
1243 | avg_a = a->counter ? ((float)a->total.float_total) / a->counter : 0; |
1244 | avg_b = b->counter ? ((float)b->total.float_total) / b->counter : 0; |
1245 | break; |
1246 | } |
1247 | result = (avg_a>avg_b) ? 1 : ( (avg_a<avg_b) ? -1 : 0); |
1248 | break; |
1249 | |
1250 | case COL_MIN: |
1251 | switch (a->datatype) |
1252 | { |
1253 | case STAT_DT_INT: |
1254 | result = a->minvalue.int_min - b->minvalue.int_min; |
1255 | break; |
1256 | case STAT_DT_FLOAT: |
1257 | result = (a->minvalue.float_min>b->minvalue.int_min) ? 1 : ((a->minvalue.float_min<b->minvalue.int_min) ? -1 : 0); |
1258 | break; |
1259 | } |
1260 | break; |
1261 | |
1262 | case COL_MAX: |
1263 | switch (a->datatype) |
1264 | { |
1265 | case STAT_DT_INT: |
1266 | result = a->maxvalue.int_max - b->maxvalue.int_max; |
1267 | break; |
1268 | case STAT_DT_FLOAT: |
1269 | result = (a->maxvalue.float_max>b->maxvalue.float_max) ? 1 : ((a->maxvalue.float_max<b->maxvalue.float_max) ? -1 : 0); |
1270 | break; |
1271 | } |
1272 | break; |
1273 | |
1274 | case COL_BURSTRATE: |
1275 | result = a->max_burst - b->max_burst; |
1276 | break; |
1277 | |
1278 | case COL_BURSTTIME: |
1279 | result = (a->burst_time>b->burst_time)?1:((a->burst_time<b->burst_time)?-1:0); |
1280 | break; |
1281 | |
1282 | default: |
1283 | /* no sort comparison found for column - must update this switch statement */ |
1284 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "epan/stats_tree.c", 1284 , __func__, "assertion \"not reached\" failed"); |
1285 | } |
1286 | |
1287 | /* break tie between items with same primary search result */ |
1288 | if (!result) { |
1289 | if (sort_column==COL_NAME) { |
1290 | result = a->counter - b->counter; |
1291 | } |
1292 | else { |
1293 | if (a->rng&&b->rng) { |
1294 | result = a->rng->floor - b->rng->floor; |
1295 | } |
1296 | else if (prefs.st_sort_casesensitve) { |
1297 | result = strcmp(a->name,b->name); |
1298 | } |
1299 | else { |
1300 | result = g_ascii_strcasecmp(a->name,b->name); |
1301 | } |
1302 | } |
1303 | } |
1304 | |
1305 | /* take into account sort order */ |
1306 | if (sort_descending) { |
1307 | result = -result; |
1308 | } |
1309 | |
1310 | if ((a->st_flags&ST_FLG_SORT_TOP0x00400000)!=(b->st_flags&ST_FLG_SORT_TOP0x00400000)) { |
1311 | /* different sort groups top vs non-top */ |
1312 | result = (a->st_flags&ST_FLG_SORT_TOP0x00400000)?-1:1; |
1313 | } |
1314 | return result; |
1315 | } |
1316 | |
1317 | extern GString* |
1318 | stats_tree_format_as_str(const stats_tree* st, st_format_type format_type, |
1319 | int sort_column, bool_Bool sort_descending) |
1320 | { |
1321 | int maxnamelen = stats_tree_branch_max_namelen(&st->root,0); |
1322 | stat_node *child; |
1323 | GString *s; |
1324 | int count; |
1325 | char *separator = NULL((void*)0); |
1326 | |
1327 | switch(format_type) { |
1328 | case ST_FORMAT_YAML: |
1329 | s = g_string_new("---\n"); |
1330 | break; |
1331 | case ST_FORMAT_XML: |
1332 | s = g_string_new("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); |
1333 | break; |
1334 | case ST_FORMAT_CSV: |
1335 | s = g_string_new("\"level\",\"parent\","); |
1336 | for (count = 0; count<st->num_columns; count++) { |
1337 | g_string_append_printf(s,"\"%s\",",stats_tree_get_column_name(st->cfg, count)); |
1338 | } |
1339 | g_string_append (s,"\n"); |
1340 | break; |
1341 | case ST_FORMAT_PLAIN: |
1342 | { |
1343 | char fmt[16]; |
1344 | int sep_length; |
1345 | |
1346 | sep_length = maxnamelen; |
1347 | for (count = 1; count<st->num_columns; count++) { |
1348 | sep_length += stats_tree_get_column_size(count)+2; |
1349 | } |
1350 | separator = (char *)g_malloc(sep_length+1); |
1351 | memset (separator, '=', sep_length); |
1352 | separator[sep_length] = 0; |
1353 | |
1354 | s = g_string_new("\n"); |
1355 | g_string_append(s,separator); |
1356 | g_string_append_printf(s,"\n%s:\n",st->cfg->title); |
1357 | snprintf (fmt,sizeof(fmt),"%%-%us",maxnamelen); |
1358 | g_string_append_printf(s,fmt,stats_tree_get_column_name(st->cfg, 0)); |
1359 | for (count = 1; count<st->num_columns; count++) { |
1360 | snprintf (fmt,sizeof(fmt)," %%-%ds",stats_tree_get_column_size(count)+1); |
1361 | g_string_append_printf(s,fmt,stats_tree_get_column_name(st->cfg, count)); |
1362 | } |
1363 | memset (separator, '-', sep_length); |
1364 | g_string_append_printf(s,"\n%s\n",separator); |
1365 | break; |
1366 | } |
1367 | default: |
1368 | return g_string_new("unknown format for stats_tree\n"); |
1369 | } |
1370 | |
1371 | for (child = st->root.children; child; child = child->next ) { |
1372 | stats_tree_format_node_as_str(child,s,format_type,0,"",maxnamelen,sort_column,sort_descending); |
1373 | |
1374 | } |
1375 | |
1376 | if (format_type==ST_FORMAT_PLAIN) { |
1377 | g_string_append_printf(s,"\n%s\n",separator); |
1378 | g_free(separator); |
1379 | } |
1380 | |
1381 | return s; |
1382 | } |
1383 | |
1384 | typedef struct { |
1385 | int sort_column; |
1386 | bool_Bool sort_descending; |
1387 | } sortinfo; |
1388 | |
1389 | /* Function to compare elements for child array sort. a and b are children, user_data |
1390 | points to a st_flags value */ |
1391 | extern int |
1392 | stat_node_array_sortcmp (gconstpointer a, gconstpointer b, void *user_data) |
1393 | { |
1394 | /* user_data is *unsigned value to st_flags */ |
1395 | return stats_tree_sort_compare (*(const stat_node*const*)a,*(const stat_node*const*)b, |
1396 | ((sortinfo*)user_data)->sort_column,((sortinfo*)user_data)->sort_descending); |
1397 | } |
1398 | |
1399 | static char* |
1400 | clean_for_xml_tag (char *str) |
1401 | { |
1402 | char *s = str; |
1403 | while ((s=strpbrk(s,"!\"#$%%&'()*+,/;<=>?@[\\]^`{|}~ ")) != NULL((void*)0)) { |
1404 | *(s++) = '-'; |
1405 | } |
1406 | return str; |
1407 | } |
1408 | |
1409 | /** helper function to add note to formatted stats_tree */ |
1410 | // NOLINTNEXTLINE(misc-no-recursion) |
1411 | WS_DLL_PUBLIC__attribute__ ((visibility ("default"))) extern void stats_tree_format_node_as_str(const stat_node *node, |
1412 | GString *s, |
1413 | st_format_type format_type, |
1414 | unsigned indent, |
1415 | const char *path, |
1416 | int maxnamelen, |
1417 | int sort_column, |
1418 | bool_Bool sort_descending) |
1419 | { |
1420 | int count; |
1421 | int num_columns = node->st->num_columns; |
1422 | char **values = stats_tree_get_values_from_node(node); |
1423 | stat_node *child; |
1424 | sortinfo si; |
1425 | char *full_path; |
1426 | char fmt[16] = "%s%s%s"; |
1427 | |
1428 | switch(format_type) { |
1429 | case ST_FORMAT_YAML: |
1430 | if (indent) { |
1431 | snprintf(fmt, sizeof(fmt), "%%%ds%%s%%s", indent*4-2); |
1432 | } |
1433 | g_string_append_printf(s, fmt, "", indent?"- ":"", "Description"); |
1434 | g_string_append_printf(s, ": \"%s\"\n", values[0]); |
1435 | |
1436 | for (count = 1; count<num_columns; count++) { |
1437 | if (*values[count]) { |
1438 | g_string_append_printf(s, fmt, "", indent?" ":"", |
1439 | stats_tree_get_column_name(node->st->cfg, count)); |
1440 | g_string_append_printf(s, ": %s\n", values[count]); |
1441 | } |
1442 | } |
1443 | if (node->children) { |
1444 | g_string_append_printf(s, fmt, "", indent?" ":"", "Items:\n"); |
1445 | } |
1446 | break; |
1447 | case ST_FORMAT_XML: |
1448 | { |
1449 | char *itemname = xml_escape(values[0]); |
1450 | g_string_append_printf(s,"<stat-node name=\"%s\"%s>\n",itemname, |
1451 | node->rng?" isrange=\"true\"":""); |
1452 | g_free(itemname); |
1453 | for (count = 1; count<num_columns; count++) { |
1454 | char *colname = g_strdup(stats_tree_get_column_name(node->st->cfg, count)); |
1455 | g_string_append_printf(s,"<%s>",clean_for_xml_tag(colname)); |
1456 | g_string_append_printf(s,"%s</%s>\n",values[count],colname); |
1457 | g_free(colname); |
1458 | } |
1459 | break; |
1460 | } |
1461 | case ST_FORMAT_CSV: |
1462 | g_string_append_printf(s,"%d,\"%s\",\"%s\"",indent,path,values[0]); |
1463 | for (count = 1; count<num_columns; count++) { |
1464 | g_string_append_printf(s,",%s",values[count]); |
1465 | } |
1466 | g_string_append (s,"\n"); |
1467 | break; |
1468 | case ST_FORMAT_PLAIN: |
1469 | snprintf (fmt,sizeof(fmt),"%%%ds%%-%us",indent,maxnamelen-indent); |
1470 | g_string_append_printf(s,fmt,"",values[0]); |
1471 | for (count = 1; count<num_columns; count++) { |
1472 | snprintf (fmt,sizeof(fmt)," %%-%us",stats_tree_get_column_size(count)+1); |
1473 | g_string_append_printf(s,fmt,values[count]); |
1474 | } |
1475 | g_string_append (s,"\n"); |
1476 | break; |
1477 | } |
1478 | |
1479 | indent++; |
1480 | indent = indent > INDENT_MAX32 ? INDENT_MAX32 : indent; |
1481 | full_path = ws_strdup_printf ("%s/%s",path,values[0])wmem_strdup_printf(((void*)0), "%s/%s",path,values[0]); |
1482 | |
1483 | for (count = 0; count<num_columns; count++) { |
1484 | g_free(values[count]); |
1485 | } |
1486 | g_free(values); |
1487 | |
1488 | if (node->children) { |
1489 | GArray *Children = g_array_new(FALSE(0),FALSE(0),sizeof(child)); |
1490 | for (child = node->children; child; child = child->next ) { |
1491 | g_array_append_val(Children,child)g_array_append_vals (Children, &(child), 1); |
1492 | } |
1493 | si.sort_column = sort_column; |
1494 | si.sort_descending = sort_descending; |
1495 | g_array_sort_with_data(Children,stat_node_array_sortcmp,&si); |
1496 | for (count = 0; count<((int)Children->len); count++) { |
1497 | stats_tree_format_node_as_str(g_array_index(Children,stat_node*,count)(((stat_node**) (void *) (Children)->data) [(count)]), s, format_type, |
1498 | indent, full_path, maxnamelen, sort_column, sort_descending); |
1499 | } |
1500 | g_array_free(Children, TRUE(!(0))); |
1501 | } |
1502 | g_free(full_path); |
1503 | |
1504 | if (format_type==ST_FORMAT_XML) { |
1505 | g_string_append(s,"</stat-node>\n"); |
1506 | } |
1507 | } |
1508 | |
1509 | void stats_tree_cleanup(void) |
1510 | { |
1511 | g_hash_table_destroy(registry); |
1512 | } |
1513 | |
1514 | /* |
1515 | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
1516 | * |
1517 | * Local variables: |
1518 | * c-basic-offset: 4 |
1519 | * tab-width: 8 |
1520 | * indent-tabs-mode: nil |
1521 | * End: |
1522 | * |
1523 | * vi: set shiftwidth=4 tabstop=8 expandtab: |
1524 | * :indentSize=4:tabSize=8:noTabs=true: |
1525 | */ |