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