Allow wtap_read() and wtap_seek_read() to return non-packet records.
[metze/wireshark/wip.git] / 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  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #include "config.h"
24
25 #include <stdio.h>
26
27 #include "globals.h"
28 #include "proto_hier_stats.h"
29 #include "frame_tvbuff.h"
30 #include "ui/progress_dlg.h"
31 #include <epan/epan_dissect.h>
32 #include <wtap.h>
33
34 #include <stdio.h>
35 #include <string.h>
36 #include <glib.h>
37
38 /* Update the progress bar this many times when scanning the packet list. */
39 #define N_PROGBAR_UPDATES       100
40
41 #define STAT_NODE_STATS(n)   ((ph_stats_node_t*)(n)->data)
42 #define STAT_NODE_HFINFO(n)  (STAT_NODE_STATS(n)->hfinfo)
43
44
45 static GNode*
46 find_stat_node(GNode *parent_stat_node, header_field_info *needle_hfinfo)
47 {
48         GNode                   *needle_stat_node;
49         header_field_info       *hfinfo;
50         ph_stats_node_t         *stats;
51
52         needle_stat_node = g_node_first_child(parent_stat_node);
53
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;
58                 }
59                 needle_stat_node = g_node_next_sibling(needle_stat_node);
60         }
61
62         /* None found. Create one. */
63         stats = g_new(ph_stats_node_t, 1);
64
65         /* Intialize counters */
66         stats->hfinfo = needle_hfinfo;
67         stats->num_pkts_total = 0;
68         stats->num_pkts_last = 0;
69         stats->num_bytes_total = 0;
70         stats->num_bytes_last = 0;
71
72         needle_stat_node = g_node_new(stats);
73         g_node_append(parent_stat_node, needle_stat_node);
74         return needle_stat_node;
75 }
76
77
78 static void
79 process_node(proto_node *ptree_node, GNode *parent_stat_node, ph_stats_t *ps, guint pkt_len)
80 {
81         field_info              *finfo;
82         ph_stats_node_t         *stats;
83         proto_node              *proto_sibling_node;
84         GNode                   *stat_node;
85
86         finfo = PNODE_FINFO(ptree_node);
87         /* We don't fake protocol nodes we expect them to have a field_info.
88          * Dissection with faked proto tree? */
89         g_assert(finfo);
90
91         /* If the field info isn't related to a protocol but to a field,
92          * don't count them, as they don't belong to any protocol.
93          * (happens e.g. for toplevel tree item of desegmentation "[Reassembled TCP Segments]") */
94         if (finfo->hfinfo->parent != -1) {
95                 /* Skip this element, use parent status node */
96                 stat_node = parent_stat_node;
97                 stats = STAT_NODE_STATS(stat_node);
98         } else {
99                 stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
100
101                 stats = STAT_NODE_STATS(stat_node);
102                 stats->num_pkts_total++;
103                 stats->num_bytes_total += pkt_len;
104         }
105
106         proto_sibling_node = ptree_node->next;
107
108         if (proto_sibling_node) {
109                 /* If the name does not exist for this proto_sibling_node, then it is
110                  * not a normal protocol in the top-level tree.  It was instead
111                  * added as a normal tree such as IPv6's Hop-by-hop Option Header and
112                  * should be skipped when creating the protocol hierarchy display. */
113                 if(strlen(PNODE_FINFO(proto_sibling_node)->hfinfo->name) == 0 && ptree_node->next)
114                         proto_sibling_node = proto_sibling_node->next;
115
116                 process_node(proto_sibling_node, stat_node, ps, pkt_len);
117         } else {
118                 stats->num_pkts_last++;
119                 stats->num_bytes_last += pkt_len;
120         }
121 }
122
123
124
125 static void
126 process_tree(proto_tree *protocol_tree, ph_stats_t* ps, guint pkt_len)
127 {
128         proto_node      *ptree_node;
129
130         ptree_node = ((proto_node *)protocol_tree)->first_child;
131         if (!ptree_node) {
132                 return;
133         }
134
135         process_node(ptree_node, ps->stats_tree, ps, pkt_len);
136 }
137
138 static gboolean
139 process_frame(frame_data *frame, column_info *cinfo, ph_stats_t* ps)
140 {
141         epan_dissect_t                  edt;
142         struct wtap_pkthdr              phdr;
143         Buffer                          buf;
144         double                          cur_time;
145
146         memset(&phdr, 0, sizeof(struct wtap_pkthdr));
147
148         /* Load the frame from the capture file */
149         buffer_init(&buf, 1500);
150         if (cf_read_frame_r(&cfile, frame, &phdr, &buf) == -1)
151                 return FALSE;   /* failure */
152
153         /* Dissect the frame   tree  not visible */
154         epan_dissect_init(&edt, cfile.epan, TRUE, FALSE);
155         /* Don't fake protocols. We need them for the protocol hierarchy */
156         epan_dissect_fake_protocols(&edt, FALSE);
157         epan_dissect_run(&edt, &phdr, frame_tvbuff_new_buffer(frame, &buf), frame, cinfo);
158
159         /* Get stats from this protocol tree */
160         process_tree(edt.tree, ps, frame->pkt_len);
161
162         /* Update times */
163         cur_time = nstime_to_sec(&frame->abs_ts);
164         if (cur_time < ps->first_time) {
165           ps->first_time = cur_time;
166         }
167         if (cur_time > ps->last_time){
168           ps->last_time = cur_time;
169         }
170
171         /* Free our memory. */
172         epan_dissect_cleanup(&edt);
173         buffer_free(&buf);
174
175         return TRUE;    /* success */
176 }
177
178 ph_stats_t*
179 ph_stats_new(void)
180 {
181         ph_stats_t      *ps;
182         guint32         framenum;
183         frame_data      *frame;
184         guint           tot_packets, tot_bytes;
185         progdlg_t       *progbar = NULL;
186         gboolean        stop_flag;
187         int             count;
188         float           progbar_val;
189         GTimeVal        start_time;
190         gchar           status_str[100];
191         int             progbar_nextstep;
192         int             progbar_quantum;
193
194         /* Initialize the data */
195         ps = g_new(ph_stats_t, 1);
196         ps->tot_packets = 0;
197         ps->tot_bytes = 0;
198         ps->stats_tree = g_node_new(NULL);
199         ps->first_time = 0.0;
200         ps->last_time = 0.0;
201
202         /* Update the progress bar when it gets to this value. */
203         progbar_nextstep = 0;
204         /* When we reach the value that triggers a progress bar update,
205            bump that value by this amount. */
206         progbar_quantum = cfile.count/N_PROGBAR_UPDATES;
207         /* Count of packets at which we've looked. */
208         count = 0;
209         /* Progress so far. */
210         progbar_val = 0.0f;
211
212         stop_flag = FALSE;
213         g_get_current_time(&start_time);
214
215         tot_packets = 0;
216         tot_bytes = 0;
217
218         for (framenum = 1; framenum <= cfile.count; framenum++) {
219                 frame = frame_data_sequence_find(cfile.frames, framenum);
220
221                 /* Create the progress bar if necessary.
222                    We check on every iteration of the loop, so that
223                    it takes no longer than the standard time to create
224                    it (otherwise, for a large file, we might take
225                    considerably longer than that standard time in order
226                    to get to the next progress bar step). */
227                 if (progbar == NULL)
228                         progbar = delayed_create_progress_dlg(
229                             cfile.window, "Computing",
230                             "protocol hierarchy statistics",
231                             TRUE, &stop_flag, &start_time, progbar_val);
232
233                 /* Update the progress bar, but do it only N_PROGBAR_UPDATES
234                    times; when we update it, we have to run the GTK+ main
235                    loop to get it to repaint what's pending, and doing so
236                    may involve an "ioctl()" to see if there's any pending
237                    input from an X server, and doing that for every packet
238                    can be costly, especially on a big file. */
239                 if (count >= progbar_nextstep) {
240                         /* let's not divide by zero. I should never be started
241                          * with count == 0, so let's assert that
242                          */
243                         g_assert(cfile.count > 0);
244
245                         progbar_val = (gfloat) count / cfile.count;
246
247                         if (progbar != NULL) {
248                                 g_snprintf(status_str, sizeof(status_str),
249                                         "%4u of %u frames", count, cfile.count);
250                                 update_progress_dlg(progbar, progbar_val, status_str);
251                         }
252
253                         progbar_nextstep += progbar_quantum;
254                 }
255
256                 if (stop_flag) {
257                         /* Well, the user decided to abort the statistics.
258                            computation process  Just stop. */
259                         break;
260                 }
261
262                 /* Skip frames that are hidden due to the display filter.
263                    XXX - should the progress bar count only packets that
264                    passed the display filter?  If so, it should
265                    probably do so for other loops (see "file.c") that
266                    look only at those packets. */
267                 if (frame->flags.passed_dfilter) {
268
269                         if (tot_packets == 0) {
270                                 double cur_time = nstime_to_sec(&frame->abs_ts);
271                                 ps->first_time = cur_time;
272                                 ps->last_time = cur_time;
273                         }
274
275                         /* we don't care about colinfo */
276                         if (!process_frame(frame, NULL, ps)) {
277                                 /*
278                                  * Give up, and set "stop_flag" so we
279                                  * just abort rather than popping up
280                                  * the statistics window.
281                                  */
282                                 stop_flag = TRUE;
283                                 break;
284                         }
285
286                         tot_packets++;
287                         tot_bytes += frame->pkt_len;
288                 }
289
290                 count++;
291         }
292
293         /* We're done calculating the statistics; destroy the progress bar
294            if it was created. */
295         if (progbar != NULL)
296                 destroy_progress_dlg(progbar);
297
298         if (stop_flag) {
299                 /*
300                  * We quit in the middle; throw away the statistics
301                  * and return NULL, so our caller doesn't pop up a
302                  * window with the incomplete statistics.
303                  */
304                 ph_stats_free(ps);
305                 return NULL;
306         }
307
308         ps->tot_packets = tot_packets;
309         ps->tot_bytes = tot_bytes;
310
311         return ps;
312 }
313
314 static gboolean
315 stat_node_free(GNode *node, gpointer data _U_)
316 {
317         ph_stats_node_t *stats = (ph_stats_node_t *)node->data;
318
319         if (stats) {
320                 g_free(stats);
321         }
322         return FALSE;
323 }
324
325 void
326 ph_stats_free(ph_stats_t *ps)
327 {
328
329         if (ps->stats_tree) {
330                 g_node_traverse(ps->stats_tree, G_IN_ORDER,
331                                 G_TRAVERSE_ALL, -1,
332                                 stat_node_free, NULL);
333                 g_node_destroy(ps->stats_tree);
334         }
335
336         g_free(ps);
337 }