Get rid of several megabytes worth of warnings about
[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  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
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 "globals.h"
30 #include "proto_hier_stats.h"
31 #include "progress_dlg.h"
32 #include "simple_dialog.h"
33 #include <epan/epan_dissect.h>
34 #include <wtap.h>
35
36 #include <stdio.h>
37 #include <glib.h>
38
39 /* Update the progress bar this many times when scanning the packet list. */
40 #define N_PROGBAR_UPDATES       100
41
42 #define STAT_NODE_STATS(n)   ((ph_stats_node_t*)(n)->data)
43 #define STAT_NODE_HFINFO(n)  (STAT_NODE_STATS(n)->hfinfo)
44
45 static double
46 secs_usecs(guint32 s, guint32 us)
47 {
48   return (us / 1000000.0) + (double)s;
49 }
50
51 static GNode*
52 find_stat_node(GNode *parent_stat_node, header_field_info *needle_hfinfo)
53 {
54         GNode                   *needle_stat_node;
55         header_field_info       *hfinfo;
56         ph_stats_node_t         *stats;
57
58         needle_stat_node = g_node_first_child(parent_stat_node);
59
60         while (needle_stat_node) {
61                 hfinfo = STAT_NODE_HFINFO(needle_stat_node);
62                 if (hfinfo &&  hfinfo->id == needle_hfinfo->id) {
63                         return needle_stat_node;
64                 }
65                 needle_stat_node = g_node_next_sibling(needle_stat_node);
66         }
67
68         /* None found. Create one. */
69         stats = g_new(ph_stats_node_t, 1);
70
71         /* Intialize counters */
72         stats->hfinfo = needle_hfinfo;
73         stats->num_pkts_total = 0;
74         stats->num_pkts_last = 0;
75         stats->num_bytes_total = 0;
76         stats->num_bytes_last = 0;
77
78         needle_stat_node = g_node_new(stats);
79         g_node_append(parent_stat_node, needle_stat_node);
80         return needle_stat_node;
81 }
82
83
84 static void
85 process_node(proto_node *ptree_node, GNode *parent_stat_node, ph_stats_t *ps, guint pkt_len)
86 {
87         field_info              *finfo;
88         ph_stats_node_t         *stats;
89         proto_node              *proto_sibling_node;
90         GNode                   *stat_node;
91
92         finfo = PITEM_FINFO(ptree_node);
93         g_assert(finfo);
94
95         /* if the field info isn't related to a protocol but to a field, don't count them,
96      * as they don't belong to any protocol.
97      * (happens e.g. for toplevel tree item of desegmentation "[Reassembled TCP Segments]") */
98     if(finfo->hfinfo->parent != -1) {
99         /* this should only happen for generated items */
100         g_assert(PROTO_ITEM_IS_GENERATED(ptree_node));
101         return;
102     }
103
104         stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
105
106         stats = STAT_NODE_STATS(stat_node);
107         stats->num_pkts_total++;
108         stats->num_bytes_total += pkt_len;
109
110         proto_sibling_node = ptree_node->next;
111
112         if (proto_sibling_node) {
113                 process_node(proto_sibling_node, stat_node, ps, pkt_len);
114         }
115         else {
116                 stats->num_pkts_last++;
117                 stats->num_bytes_last += pkt_len;
118         }
119 }
120
121
122
123 static void
124 process_tree(proto_tree *protocol_tree, ph_stats_t* ps, guint pkt_len)
125 {
126         proto_node      *ptree_node;
127
128         ptree_node = ((proto_node *)protocol_tree)->first_child;
129         if (!ptree_node) {
130                 return;
131         }
132
133         process_node(ptree_node, ps->stats_tree, ps, pkt_len);
134 }
135
136 static gboolean
137 process_frame(frame_data *frame, column_info *cinfo, ph_stats_t* ps)
138 {
139         epan_dissect_t                  *edt;
140         union wtap_pseudo_header        phdr;
141         guint8                          pd[WTAP_MAX_PACKET_SIZE];
142         int                             err;
143         gchar                           *err_info;
144         double                          cur_time;
145
146         /* Load the frame from the capture file */
147         if (!wtap_seek_read(cfile.wth, frame->file_off, &phdr, pd,
148             frame->cap_len, &err, &err_info)) {
149                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
150                     cf_read_error_message(err, err_info), cfile.filename);
151                 return FALSE;   /* failure */
152         }
153
154         /* Dissect the frame   tree  not visible */
155         edt = epan_dissect_new(TRUE, FALSE);
156         epan_dissect_run(edt, &phdr, pd, frame, cinfo);
157
158         /* Get stats from this protocol tree */
159         process_tree(edt->tree, ps, frame->pkt_len);
160
161         /* Update times */
162         cur_time = secs_usecs(frame->abs_secs, frame->abs_usecs);
163         if (cur_time < ps->first_time) {
164           ps->first_time = cur_time;
165         }
166         if (cur_time > ps->last_time){
167           ps->last_time = cur_time;
168         }
169
170         /* Free our memory. */
171         epan_dissect_free(edt);
172
173         return TRUE;    /* success */
174 }
175
176 ph_stats_t*
177 ph_stats_new(void)
178 {
179         ph_stats_t      *ps;
180         frame_data      *frame;
181         guint           tot_packets, tot_bytes;
182         progdlg_t       *progbar = NULL;
183         gboolean        stop_flag;
184         int             count;
185         float           prog_val;
186         GTimeVal        start_time;
187         gchar           status_str[100];
188         int             progbar_nextstep;
189         int             progbar_quantum;
190
191         /* Initialize the data */
192         ps = g_new(ph_stats_t, 1);
193         ps->tot_packets = 0;
194         ps->tot_bytes = 0;
195         ps->stats_tree = g_node_new(NULL);
196         ps->first_time = 0.0;
197         ps->last_time = 0.0;
198
199         /* Update the progress bar when it gets to this value. */
200         progbar_nextstep = 0;
201         /* When we reach the value that triggers a progress bar update,
202            bump that value by this amount. */
203         progbar_quantum = cfile.count/N_PROGBAR_UPDATES;
204         /* Count of packets at which we've looked. */
205         count = 0;
206
207         stop_flag = FALSE;
208         g_get_current_time(&start_time);
209
210         tot_packets = 0;
211         tot_bytes = 0;
212
213         for (frame = cfile.plist; frame != NULL; frame = frame->next) {
214                 /* Update the progress bar, but do it only N_PROGBAR_UPDATES
215                    times; when we update it, we have to run the GTK+ main
216                    loop to get it to repaint what's pending, and doing so
217                    may involve an "ioctl()" to see if there's any pending
218                    input from an X server, and doing that for every packet
219                    can be costly, especially on a big file. */
220                 if (count >= progbar_nextstep) {
221                         /* let's not divide by zero. I should never be started
222                          * with count == 0, so let's assert that
223                          */
224                         g_assert(cfile.count > 0);
225
226                         prog_val = (gfloat) count / cfile.count;
227
228                         if (progbar == NULL)
229                                 /* Create the progress bar if necessary */
230                                 progbar = delayed_create_progress_dlg(
231                                     "Computing", "protocol hierarchy statistics", 
232                                     &stop_flag, &start_time, prog_val);
233
234                         if (progbar != NULL) {
235                                 g_snprintf(status_str, sizeof(status_str),
236                                         "%4u of %u frames", count, cfile.count);
237                                 update_progress_dlg(progbar, prog_val, status_str);
238                         }
239
240                         progbar_nextstep += progbar_quantum;
241                 }
242
243                 if (stop_flag) {
244                         /* Well, the user decided to abort the statistics.
245                            computation process  Just stop. */
246                         break;
247                 }
248
249                 /* Skip frames that are hidden due to the display filter.
250                    XXX - should the progress bar count only packets that
251                    passed the display filter?  If so, it should
252                    probably do so for other loops (see "file.c") that
253                    look only at those packets. */
254                 if (frame->flags.passed_dfilter) {
255
256                         if (tot_packets == 0) {
257                                 double cur_time = secs_usecs(frame->abs_secs,
258                                                              frame->abs_usecs);
259                                 ps->first_time = cur_time;
260                                 ps->last_time = cur_time;
261                         }
262                         
263                         /* we don't care about colinfo */
264                         if (!process_frame(frame, NULL, ps)) {
265                                 /*
266                                  * Give up, and set "stop_flag" so we
267                                  * just abort rather than popping up
268                                  * the statistics window.
269                                  */
270                                 stop_flag = TRUE;
271                                 break;
272                         }
273
274                         tot_packets++;
275                         tot_bytes += frame->pkt_len;
276                 }
277
278                 count++;
279         }
280
281         /* We're done calculating the statistics; destroy the progress bar
282            if it was created. */
283         if (progbar != NULL)
284                 destroy_progress_dlg(progbar);
285
286         if (stop_flag) {
287                 /*
288                  * We quit in the middle; throw away the statistics
289                  * and return NULL, so our caller doesn't pop up a
290                  * window with the incomplete statistics.
291                  */
292                 ph_stats_free(ps);
293                 return NULL;
294         }
295
296         ps->tot_packets = tot_packets;
297         ps->tot_bytes = tot_bytes;
298
299         return ps;
300 }
301
302 static gboolean
303 stat_node_free(GNode *node, gpointer data _U_)
304 {
305         ph_stats_node_t *stats = node->data;
306
307         if (stats) {
308                 g_free(stats);
309         }
310         return FALSE;
311 }
312
313 void
314 ph_stats_free(ph_stats_t *ps)
315 {
316
317         if (ps->stats_tree) {
318                 g_node_traverse(ps->stats_tree, G_IN_ORDER,
319                                 G_TRAVERSE_ALL, -1,
320                                 stat_node_free, NULL);
321                 g_node_destroy(ps->stats_tree);
322         }
323
324         g_free(ps);
325 }