Put the structure of a capture_file back in cfile.h.
[metze/wireshark/wip.git] / ui / proto_hier_stats.c
1 /* proto_hier_stats.c
2  * Routines for calculating statistics based on protocol.
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * SPDX-License-Identifier: GPL-2.0+
9  */
10
11 #include "config.h"
12
13 #include <string.h>
14
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
22 /* Update the progress bar this many times when scanning the packet list. */
23 #define N_PROGBAR_UPDATES       100
24
25 #define STAT_NODE_STATS(n)   ((ph_stats_node_t*)(n)->data)
26 #define STAT_NODE_HFINFO(n)  (STAT_NODE_STATS(n)->hfinfo)
27
28 static int pc_proto_id = -1;
29
30 static GNode*
31 find_stat_node(GNode *parent_stat_node, header_field_info *needle_hfinfo)
32 {
33     GNode               *needle_stat_node, *up_parent_stat_node;
34     header_field_info   *hfinfo;
35     ph_stats_node_t     *stats;
36
37     /* Look down the tree */
38     needle_stat_node = g_node_first_child(parent_stat_node);
39
40     while (needle_stat_node) {
41         hfinfo = STAT_NODE_HFINFO(needle_stat_node);
42         if (hfinfo &&  hfinfo->id == needle_hfinfo->id) {
43             return needle_stat_node;
44         }
45         needle_stat_node = g_node_next_sibling(needle_stat_node);
46     }
47
48     /* Look up the tree */
49     up_parent_stat_node = parent_stat_node;
50     while (up_parent_stat_node && up_parent_stat_node->parent)
51     {
52         needle_stat_node = g_node_first_child(up_parent_stat_node->parent);
53         while (needle_stat_node) {
54             hfinfo = STAT_NODE_HFINFO(needle_stat_node);
55             if (hfinfo &&  hfinfo->id == needle_hfinfo->id) {
56                 return needle_stat_node;
57             }
58             needle_stat_node = g_node_next_sibling(needle_stat_node);
59         }
60
61         up_parent_stat_node = up_parent_stat_node->parent;
62     }
63
64     /* None found. Create one. */
65     stats = g_new(ph_stats_node_t, 1);
66
67     /* Intialize counters */
68     stats->hfinfo = needle_hfinfo;
69     stats->num_pkts_total = 0;
70     stats->num_pkts_last = 0;
71     stats->num_bytes_total = 0;
72     stats->num_bytes_last = 0;
73
74     needle_stat_node = g_node_new(stats);
75     g_node_append(parent_stat_node, needle_stat_node);
76     return needle_stat_node;
77 }
78
79
80     static void
81 process_node(proto_node *ptree_node, GNode *parent_stat_node, ph_stats_t *ps)
82 {
83     field_info          *finfo;
84     ph_stats_node_t     *stats;
85     proto_node          *proto_sibling_node;
86     GNode               *stat_node;
87
88     finfo = PNODE_FINFO(ptree_node);
89     /* We don't fake protocol nodes we expect them to have a field_info.
90      * Dissection with faked proto tree? */
91     g_assert(finfo);
92
93     /* If the field info isn't related to a protocol but to a field,
94      * don't count them, as they don't belong to any protocol.
95      * (happens e.g. for toplevel tree item of desegmentation "[Reassembled TCP Segments]") */
96     if (finfo->hfinfo->parent != -1) {
97         /* Skip this element, use parent status node */
98         stat_node = parent_stat_node;
99         stats = STAT_NODE_STATS(stat_node);
100     } else {
101         stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
102
103         stats = STAT_NODE_STATS(stat_node);
104         stats->num_pkts_total++;
105         stats->num_bytes_total += finfo->length;
106     }
107
108     proto_sibling_node = ptree_node->next;
109
110     if (proto_sibling_node) {
111         /* If the name does not exist for this proto_sibling_node, then it is
112          * not a normal protocol in the top-level tree.  It was instead
113          * added as a normal tree such as IPv6's Hop-by-hop Option Header and
114          * should be skipped when creating the protocol hierarchy display. */
115         if(strlen(PNODE_FINFO(proto_sibling_node)->hfinfo->name) == 0 && ptree_node->next)
116             proto_sibling_node = proto_sibling_node->next;
117
118         process_node(proto_sibling_node, stat_node, ps);
119     } else {
120         stats->num_pkts_last++;
121         stats->num_bytes_last += finfo->length;
122     }
123 }
124
125
126
127     static void
128 process_tree(proto_tree *protocol_tree, ph_stats_t* ps)
129 {
130     proto_node  *ptree_node;
131
132     /*
133      * If our first item is a comment, skip over it. This keeps
134      * us from having a top-level "Packet comments" item that
135      * steals items from "Frame".
136      */
137     ptree_node = ((proto_node *)protocol_tree)->first_child;
138     if (ptree_node && ptree_node->finfo->hfinfo->id == pc_proto_id) {
139         ptree_node = ptree_node->next;
140     }
141
142     if (!ptree_node) {
143         return;
144     }
145
146     process_node(ptree_node, ps->stats_tree, ps);
147 }
148
149     static gboolean
150 process_record(capture_file *cf, frame_data *frame, column_info *cinfo, ph_stats_t* ps)
151 {
152     epan_dissect_t      edt;
153     struct wtap_pkthdr  phdr;
154     Buffer              buf;
155     double              cur_time;
156
157     wtap_phdr_init(&phdr);
158
159     /* Load the record from the capture file */
160     ws_buffer_init(&buf, 1500);
161     if (!cf_read_record_r(cf, frame, &phdr, &buf))
162         return FALSE;   /* failure */
163
164     /* Dissect the record   tree  not visible */
165     epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
166     /* Don't fake protocols. We need them for the protocol hierarchy */
167     epan_dissect_fake_protocols(&edt, FALSE);
168     epan_dissect_run(&edt, cf->cd_t, &phdr, frame_tvbuff_new_buffer(frame, &buf), frame, cinfo);
169
170     /* Get stats from this protocol tree */
171     process_tree(edt.tree, ps);
172
173     if (frame->flags.has_ts) {
174         /* Update times */
175         cur_time = nstime_to_sec(&frame->abs_ts);
176         if (cur_time < ps->first_time)
177             ps->first_time = cur_time;
178         if (cur_time > ps->last_time)
179             ps->last_time = cur_time;
180     }
181
182     /* Free our memory. */
183     epan_dissect_cleanup(&edt);
184     wtap_phdr_cleanup(&phdr);
185     ws_buffer_free(&buf);
186
187     return TRUE;        /* success */
188 }
189
190     ph_stats_t*
191 ph_stats_new(capture_file *cf)
192 {
193     ph_stats_t  *ps;
194     guint32     framenum;
195     frame_data  *frame;
196     guint       tot_packets, tot_bytes;
197     progdlg_t   *progbar = NULL;
198     gboolean    stop_flag;
199     int         count;
200     float       progbar_val;
201     GTimeVal    start_time;
202     gchar       status_str[100];
203     int         progbar_nextstep;
204     int         progbar_quantum;
205
206     if (!cf) return NULL;
207
208     pc_proto_id = proto_registrar_get_id_byname("pkt_comment");
209
210     /* Initialize the data */
211     ps = g_new(ph_stats_t, 1);
212     ps->tot_packets = 0;
213     ps->tot_bytes = 0;
214     ps->stats_tree = g_node_new(NULL);
215     ps->first_time = 0.0;
216     ps->last_time = 0.0;
217
218     /* Update the progress bar when it gets to this value. */
219     progbar_nextstep = 0;
220     /* When we reach the value that triggers a progress bar update,
221        bump that value by this amount. */
222     progbar_quantum = cf->count/N_PROGBAR_UPDATES;
223     /* Count of packets at which we've looked. */
224     count = 0;
225     /* Progress so far. */
226     progbar_val = 0.0f;
227
228     stop_flag = FALSE;
229     g_get_current_time(&start_time);
230
231     tot_packets = 0;
232     tot_bytes = 0;
233
234     for (framenum = 1; framenum <= cf->count; framenum++) {
235         frame = frame_data_sequence_find(cf->frame_set_info.frames, framenum);
236
237         /* Create the progress bar if necessary.
238            We check on every iteration of the loop, so that
239            it takes no longer than the standard time to create
240            it (otherwise, for a large file, we might take
241            considerably longer than that standard time in order
242            to get to the next progress bar step). */
243         if (progbar == NULL)
244             progbar = delayed_create_progress_dlg(
245                     cf->window, "Computing",
246                     "protocol hierarchy statistics",
247                     TRUE, &stop_flag, &start_time, progbar_val);
248
249         /* Update the progress bar, but do it only N_PROGBAR_UPDATES
250            times; when we update it, we have to run the GTK+ main
251            loop to get it to repaint what's pending, and doing so
252            may involve an "ioctl()" to see if there's any pending
253            input from an X server, and doing that for every packet
254            can be costly, especially on a big file. */
255         if (count >= progbar_nextstep) {
256             /* let's not divide by zero. I should never be started
257              * with count == 0, so let's assert that
258              */
259             g_assert(cf->count > 0);
260
261             progbar_val = (gfloat) count / cf->count;
262
263             if (progbar != NULL) {
264                 g_snprintf(status_str, sizeof(status_str),
265                         "%4u of %u frames", count, cf->count);
266                 update_progress_dlg(progbar, progbar_val, status_str);
267             }
268
269             progbar_nextstep += progbar_quantum;
270         }
271
272         if (stop_flag) {
273             /* Well, the user decided to abort the statistics.
274                computation process  Just stop. */
275             break;
276         }
277
278         /* Skip frames that are hidden due to the display filter.
279            XXX - should the progress bar count only packets that
280            passed the display filter?  If so, it should
281            probably do so for other loops (see "file.c") that
282            look only at those packets. */
283         if (frame->flags.passed_dfilter) {
284
285             if (frame->flags.has_ts) {
286                 if (tot_packets == 0) {
287                     double cur_time = nstime_to_sec(&frame->abs_ts);
288                     ps->first_time = cur_time;
289                     ps->last_time = cur_time;
290                 }
291             }
292
293             /* we don't care about colinfo */
294             if (!process_record(cf, frame, NULL, ps)) {
295                 /*
296                  * Give up, and set "stop_flag" so we
297                  * just abort rather than popping up
298                  * the statistics window.
299                  */
300                 stop_flag = TRUE;
301                 break;
302             }
303
304             tot_packets++;
305             tot_bytes += frame->pkt_len;
306         }
307
308         count++;
309     }
310
311     /* We're done calculating the statistics; destroy the progress bar
312        if it was created. */
313     if (progbar != NULL)
314         destroy_progress_dlg(progbar);
315
316     if (stop_flag) {
317         /*
318          * We quit in the middle; throw away the statistics
319          * and return NULL, so our caller doesn't pop up a
320          * window with the incomplete statistics.
321          */
322         ph_stats_free(ps);
323         return NULL;
324     }
325
326     ps->tot_packets = tot_packets;
327     ps->tot_bytes = tot_bytes;
328
329     return ps;
330 }
331
332     static gboolean
333 stat_node_free(GNode *node, gpointer data _U_)
334 {
335     ph_stats_node_t     *stats = (ph_stats_node_t *)node->data;
336     g_free(stats);
337     return FALSE;
338 }
339
340     void
341 ph_stats_free(ph_stats_t *ps)
342 {
343     if (ps->stats_tree) {
344         g_node_traverse(ps->stats_tree, G_IN_ORDER,
345                 G_TRAVERSE_ALL, -1,
346                 stat_node_free, NULL);
347         g_node_destroy(ps->stats_tree);
348     }
349
350     g_free(ps);
351 }
352
353 /*
354  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
355  *
356  * Local Variables:
357  * c-basic-offset: 4
358  * tab-width: 8
359  * indent-tabs-mode: nil
360  * End:
361  *
362  * ex: set shiftwidth=4 tabstop=8 expandtab:
363  * :indentSize=4:tabSize=8:noTabs=true:
364  */