update epan/dissectors/pidl/drsuapi/drsuapi.idl from samba
[wireshark-sm.git] / ui / proto_hier_stats.c
blobf7aea2bef77234ac07522021eae7f0ec6cf8b0a0
1 /* proto_hier_stats.c
2 * Routines for calculating statistics based on protocol.
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
11 #include "config.h"
13 #include <string.h>
15 #include "file.h"
16 #include "frame_tvbuff.h"
17 #include "ui/proto_hier_stats.h"
18 #include "ui/progress_dlg.h"
19 #include "epan/epan_dissect.h"
20 #include "epan/proto.h"
21 #include <wsutil/ws_assert.h>
23 /* Update the progress bar this many times when scanning the packet list. */
24 #define N_PROGBAR_UPDATES 100
26 #define STAT_NODE_STATS(n) ((ph_stats_node_t*)(n)->data)
27 #define STAT_NODE_HFINFO(n) (STAT_NODE_STATS(n)->hfinfo)
29 static int pc_proto_id = -1;
31 static GNode*
32 find_stat_node(GNode *parent_stat_node, const header_field_info *needle_hfinfo)
34 GNode *needle_stat_node, *up_parent_stat_node;
35 const header_field_info *hfinfo;
36 ph_stats_node_t *stats;
38 /* Look down the tree */
39 needle_stat_node = g_node_first_child(parent_stat_node);
41 while (needle_stat_node) {
42 hfinfo = STAT_NODE_HFINFO(needle_stat_node);
43 if (hfinfo && hfinfo->id == needle_hfinfo->id) {
44 return needle_stat_node;
46 needle_stat_node = g_node_next_sibling(needle_stat_node);
49 /* Look up the tree */
50 up_parent_stat_node = parent_stat_node;
51 while (up_parent_stat_node && up_parent_stat_node->parent)
53 needle_stat_node = g_node_first_child(up_parent_stat_node->parent);
54 while (needle_stat_node) {
55 hfinfo = STAT_NODE_HFINFO(needle_stat_node);
56 if (hfinfo && hfinfo->id == needle_hfinfo->id) {
57 return needle_stat_node;
59 needle_stat_node = g_node_next_sibling(needle_stat_node);
62 up_parent_stat_node = up_parent_stat_node->parent;
65 /* None found. Create one. */
66 stats = g_new(ph_stats_node_t, 1);
68 /* Initialize counters */
69 stats->hfinfo = needle_hfinfo;
70 stats->num_pkts_total = 0;
71 stats->num_pdus_total = 0;
72 stats->num_pkts_last = 0;
73 stats->num_bytes_total = 0;
74 stats->num_bytes_last = 0;
75 stats->last_pkt = 0;
77 needle_stat_node = g_node_new(stats);
78 g_node_append(parent_stat_node, needle_stat_node);
79 return needle_stat_node;
83 static void
84 process_node(proto_node *ptree_node, GNode *parent_stat_node, ph_stats_t *ps)
86 field_info *finfo;
87 ph_stats_node_t *stats;
88 proto_node *proto_sibling_node;
89 GNode *stat_node;
91 finfo = PNODE_FINFO(ptree_node);
92 /* We don't fake protocol nodes we expect them to have a field_info.
93 * Even with a faked proto tree, we don't fake nodes when PTREE_FINFO(tree)
94 * is NULL in order to avoid crashes here and elsewhere. (See epan/proto.c)
96 ws_assert(finfo);
98 stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
100 stats = STAT_NODE_STATS(stat_node);
101 /* Only increment the total packet count once per packet for a given
102 * node, since there could be multiple PDUs in a frame.
103 * (All the other statistics should be incremented every time,
104 * including the count for how often a protocol was the last
105 * protocol in a packet.)
107 if (stats->last_pkt != ps->tot_packets) {
108 stats->num_pkts_total++;
109 stats->last_pkt = ps->tot_packets;
111 stats->num_pdus_total++;
112 stats->num_bytes_total += finfo->length + finfo->appendix_length;
114 proto_sibling_node = ptree_node->next;
116 /* Skip entries that are not protocols, e.g.
117 * toplevel tree item of desegmentation "[Reassembled TCP Segments]")
118 * XXX: We should probably skip PINOs with field_type FT_BYTES too.
120 * XXX: We look at siblings not children, and thus don't descend into
121 * the tree to pick up embedded protocols not added to the toplevel of
122 * the tree.
124 while (proto_sibling_node && !proto_registrar_is_protocol(PNODE_FINFO(proto_sibling_node)->hfinfo->id)) {
125 proto_sibling_node = proto_sibling_node->next;
128 if (proto_sibling_node) {
129 process_node(proto_sibling_node, stat_node, ps);
130 } else {
131 stats->num_pkts_last++;
132 stats->num_bytes_last += finfo->length + finfo->appendix_length;
138 static void
139 process_tree(proto_tree *protocol_tree, ph_stats_t* ps)
141 proto_node *ptree_node;
144 * Skip over non-protocols and comments. (Packet comments are a PINO
145 * with FT_PROTOCOL field type). This keeps us from having a top-level
146 * "Packet comments" item that steals items from "Frame".
148 ptree_node = ((proto_node *)protocol_tree)->first_child;
149 while (ptree_node && (ptree_node->finfo->hfinfo->id == pc_proto_id || !proto_registrar_is_protocol(ptree_node->finfo->hfinfo->id))) {
150 ptree_node = ptree_node->next;
153 if (!ptree_node) {
154 return;
157 process_node(ptree_node, ps->stats_tree, ps);
160 static bool
161 process_record(capture_file *cf, frame_data *frame, column_info *cinfo,
162 wtap_rec *rec, Buffer *buf, ph_stats_t* ps)
164 epan_dissect_t edt;
165 double cur_time;
167 /* Load the record from the capture file */
168 if (!cf_read_record(cf, frame, rec, buf))
169 return false; /* failure */
171 /* Dissect the record tree not visible */
172 epan_dissect_init(&edt, cf->epan, true, false);
173 /* Don't fake protocols. We need them for the protocol hierarchy */
174 epan_dissect_fake_protocols(&edt, false);
175 epan_dissect_run(&edt, cf->cd_t, rec,
176 frame_tvbuff_new_buffer(&cf->provider, frame, buf),
177 frame, cinfo);
179 /* Get stats from this protocol tree */
180 process_tree(edt.tree, ps);
182 if (frame->has_ts) {
183 /* Update times */
184 cur_time = nstime_to_sec(&frame->abs_ts);
185 if (cur_time < ps->first_time)
186 ps->first_time = cur_time;
187 if (cur_time > ps->last_time)
188 ps->last_time = cur_time;
191 /* Free our memory. */
192 epan_dissect_cleanup(&edt);
194 return true; /* success */
197 ph_stats_t*
198 ph_stats_new(capture_file *cf)
200 ph_stats_t *ps;
201 uint32_t framenum;
202 frame_data *frame;
203 progdlg_t *progbar = NULL;
204 int count;
205 wtap_rec rec;
206 Buffer buf;
207 float progbar_val;
208 char status_str[100];
209 int progbar_nextstep;
210 int progbar_quantum;
212 if (!cf) return NULL;
214 if (cf->read_lock) {
215 ws_warning("Failing to compute protocol hierarchy stats on \"%s\" since a read is in progress", cf->filename);
216 return NULL;
218 cf->read_lock = true;
220 cf->stop_flag = false;
222 pc_proto_id = proto_registrar_get_id_byname("pkt_comment");
224 /* Initialize the data */
225 ps = g_new(ph_stats_t, 1);
226 ps->tot_packets = 0;
227 ps->tot_bytes = 0;
228 ps->stats_tree = g_node_new(NULL);
229 ps->first_time = 0.0;
230 ps->last_time = 0.0;
232 /* Update the progress bar when it gets to this value. */
233 progbar_nextstep = 0;
234 /* When we reach the value that triggers a progress bar update,
235 bump that value by this amount. */
236 progbar_quantum = cf->count/N_PROGBAR_UPDATES;
237 /* Count of packets at which we've looked. */
238 count = 0;
239 /* Progress so far. */
240 progbar_val = 0.0f;
242 wtap_rec_init(&rec);
243 ws_buffer_init(&buf, 1514);
245 for (framenum = 1; framenum <= cf->count; framenum++) {
246 frame = frame_data_sequence_find(cf->provider.frames, framenum);
248 /* Create the progress bar if necessary.
249 We check on every iteration of the loop, so that
250 it takes no longer than the standard time to create
251 it (otherwise, for a large file, we might take
252 considerably longer than that standard time in order
253 to get to the next progress bar step). */
254 if (progbar == NULL)
255 progbar = delayed_create_progress_dlg(
256 cf->window, "Computing",
257 "protocol hierarchy statistics",
258 true, &cf->stop_flag, progbar_val);
260 /* Update the progress bar, but do it only N_PROGBAR_UPDATES
261 times; when we update it, we have to run the GTK+ main
262 loop to get it to repaint what's pending, and doing so
263 may involve an "ioctl()" to see if there's any pending
264 input from an X server, and doing that for every packet
265 can be costly, especially on a big file. */
266 if (count >= progbar_nextstep) {
267 /* let's not divide by zero. I should never be started
268 * with count == 0, so let's assert that
270 ws_assert(cf->count > 0);
272 progbar_val = (float) count / cf->count;
274 if (progbar != NULL) {
275 snprintf(status_str, sizeof(status_str),
276 "%4u of %u frames", count, cf->count);
277 update_progress_dlg(progbar, progbar_val, status_str);
280 progbar_nextstep += progbar_quantum;
283 if (cf->stop_flag) {
284 /* Well, the user decided to abort the statistics.
285 computation process Just stop. */
286 break;
289 /* Skip frames that are hidden due to the display filter.
290 XXX - should the progress bar count only packets that
291 passed the display filter? If so, it should
292 probably do so for other loops (see "file.c") that
293 look only at those packets. */
294 if (frame->passed_dfilter) {
296 if (frame->has_ts) {
297 if (ps->tot_packets == 0) {
298 double cur_time = nstime_to_sec(&frame->abs_ts);
299 ps->first_time = cur_time;
300 ps->last_time = cur_time;
304 /* We throw away the statistics if we quit in the middle,
305 * so increment this first so that the count starts at 1
306 * when processing records, since we initialize the stat
307 * nodes' last_pkt to 0.
309 ps->tot_packets++;
311 /* we don't care about colinfo */
312 if (!process_record(cf, frame, NULL, &rec, &buf, ps)) {
314 * Give up, and set "stop_flag" so we
315 * just abort rather than popping up
316 * the statistics window.
318 cf->stop_flag = true;
319 break;
322 ps->tot_bytes += frame->pkt_len;
325 count++;
328 wtap_rec_cleanup(&rec);
329 ws_buffer_free(&buf);
331 /* We're done calculating the statistics; destroy the progress bar
332 if it was created. */
333 if (progbar != NULL)
334 destroy_progress_dlg(progbar);
336 if (cf->stop_flag) {
338 * We quit in the middle; throw away the statistics
339 * and return NULL, so our caller doesn't pop up a
340 * window with the incomplete statistics.
342 ph_stats_free(ps);
343 ps = NULL;
346 ws_assert(cf->read_lock);
347 cf->read_lock = false;
349 return ps;
352 static gboolean
353 stat_node_free(GNode *node, void *data _U_)
355 ph_stats_node_t *stats = (ph_stats_node_t *)node->data;
356 g_free(stats);
357 return false;
360 void
361 ph_stats_free(ph_stats_t *ps)
363 if (ps->stats_tree) {
364 g_node_traverse(ps->stats_tree, G_IN_ORDER,
365 G_TRAVERSE_ALL, -1,
366 stat_node_free, NULL);
367 g_node_destroy(ps->stats_tree);
370 g_free(ps);