From Olivier Jacques: Add #defines to disable XML validation.
[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         stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
96
97         /* Assert that the finfo is related to a protocol, not a field. */
98         g_assert(finfo->hfinfo->parent == -1);
99
100         stats = STAT_NODE_STATS(stat_node);
101         stats->num_pkts_total++;
102         stats->num_bytes_total += pkt_len;
103
104         proto_sibling_node = ptree_node->next;
105
106         if (proto_sibling_node) {
107                 process_node(proto_sibling_node, stat_node, ps, pkt_len);
108         }
109         else {
110                 stats->num_pkts_last++;
111                 stats->num_bytes_last += pkt_len;
112         }
113 }
114
115
116
117 static void
118 process_tree(proto_tree *protocol_tree, ph_stats_t* ps, guint pkt_len)
119 {
120         proto_node      *ptree_node;
121
122         ptree_node = ((proto_node *)protocol_tree)->first_child;
123         if (!ptree_node) {
124                 return;
125         }
126
127         process_node(ptree_node, ps->stats_tree, ps, pkt_len);
128 }
129
130 static gboolean
131 process_frame(frame_data *frame, column_info *cinfo, ph_stats_t* ps)
132 {
133         epan_dissect_t                  *edt;
134         union wtap_pseudo_header        phdr;
135         guint8                          pd[WTAP_MAX_PACKET_SIZE];
136         int                             err;
137         gchar                           *err_info;
138         double                          cur_time;
139
140         /* Load the frame from the capture file */
141         if (!wtap_seek_read(cfile.wth, frame->file_off, &phdr, pd,
142             frame->cap_len, &err, &err_info)) {
143                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
144                     cf_read_error_message(err, err_info), cfile.filename);
145                 return FALSE;   /* failure */
146         }
147
148         /* Dissect the frame */
149         edt = epan_dissect_new(TRUE, FALSE);
150         epan_dissect_run(edt, &phdr, pd, frame, cinfo);
151
152         /* Get stats from this protocol tree */
153         process_tree(edt->tree, ps, frame->pkt_len);
154
155         /* Update times */
156         cur_time = secs_usecs(frame->abs_secs, frame->abs_usecs);
157         if (cur_time < ps->first_time) {
158           ps->first_time = cur_time;
159         }
160         if (cur_time > ps->last_time){
161           ps->last_time = cur_time;
162         }
163
164         /* Free our memory. */
165         epan_dissect_free(edt);
166
167         return TRUE;    /* success */
168 }
169
170 ph_stats_t*
171 ph_stats_new(void)
172 {
173         ph_stats_t      *ps;
174         frame_data      *frame;
175         guint           tot_packets, tot_bytes;
176         progdlg_t       *progbar = NULL;
177         gboolean        stop_flag;
178         int             count;
179         float           prog_val;
180         GTimeVal        start_time;
181         gchar           status_str[100];
182         int             progbar_nextstep;
183         int             progbar_quantum;
184
185         /* Initialize the data */
186         ps = g_new(ph_stats_t, 1);
187         ps->tot_packets = 0;
188         ps->tot_bytes = 0;
189         ps->stats_tree = g_node_new(NULL);
190         ps->first_time = 0.0;
191         ps->last_time = 0.0;
192
193         /* Update the progress bar when it gets to this value. */
194         progbar_nextstep = 0;
195         /* When we reach the value that triggers a progress bar update,
196            bump that value by this amount. */
197         progbar_quantum = cfile.count/N_PROGBAR_UPDATES;
198         /* Count of packets at which we've looked. */
199         count = 0;
200
201         stop_flag = FALSE;
202         g_get_current_time(&start_time);
203
204         tot_packets = 0;
205         tot_bytes = 0;
206
207         for (frame = cfile.plist; frame != NULL; frame = frame->next) {
208                 /* Update the progress bar, but do it only N_PROGBAR_UPDATES
209                    times; when we update it, we have to run the GTK+ main
210                    loop to get it to repaint what's pending, and doing so
211                    may involve an "ioctl()" to see if there's any pending
212                    input from an X server, and doing that for every packet
213                    can be costly, especially on a big file. */
214                 if (count >= progbar_nextstep) {
215                         /* let's not divide by zero. I should never be started
216                          * with count == 0, so let's assert that
217                          */
218                         g_assert(cfile.count > 0);
219
220                         prog_val = (gfloat) count / cfile.count;
221
222                         if (progbar == NULL)
223                                 /* Create the progress bar if necessary */
224                                 progbar = delayed_create_progress_dlg(
225                                     "Computing", "protocol hierarchy statistics", 
226                                     &stop_flag, &start_time, prog_val);
227
228                         if (progbar != NULL) {
229                                 g_snprintf(status_str, sizeof(status_str),
230                                         "%4u of %u frames", count, cfile.count);
231                                 update_progress_dlg(progbar, prog_val, status_str);
232                         }
233
234                         progbar_nextstep += progbar_quantum;
235                 }
236
237                 if (stop_flag) {
238                         /* Well, the user decided to abort the statistics.
239                            computation process  Just stop. */
240                         break;
241                 }
242
243                 /* Skip frames that are hidden due to the display filter.
244                    XXX - should the progress bar count only packets that
245                    passed the display filter?  If so, it should
246                    probably do so for other loops (see "file.c") that
247                    look only at those packets. */
248                 if (frame->flags.passed_dfilter) {
249
250                         if (tot_packets == 0) {
251                                 double cur_time = secs_usecs(frame->abs_secs,
252                                                              frame->abs_usecs);
253                                 ps->first_time = cur_time;
254                                 ps->last_time = cur_time;
255                         }
256
257                         if (!process_frame(frame, &cfile.cinfo, ps)) {
258                                 /*
259                                  * Give up, and set "stop_flag" so we
260                                  * just abort rather than popping up
261                                  * the statistics window.
262                                  */
263                                 stop_flag = TRUE;
264                                 break;
265                         }
266
267                         tot_packets++;
268                         tot_bytes += frame->pkt_len;
269                 }
270
271                 count++;
272         }
273
274         /* We're done calculating the statistics; destroy the progress bar
275            if it was created. */
276         if (progbar != NULL)
277                 destroy_progress_dlg(progbar);
278
279         if (stop_flag) {
280                 /*
281                  * We quit in the middle; throw away the statistics
282                  * and return NULL, so our caller doesn't pop up a
283                  * window with the incomplete statistics.
284                  */
285                 ph_stats_free(ps);
286                 return NULL;
287         }
288
289         ps->tot_packets = tot_packets;
290         ps->tot_bytes = tot_bytes;
291
292         return ps;
293 }
294
295 static gboolean
296 stat_node_free(GNode *node, gpointer data _U_)
297 {
298         ph_stats_node_t *stats = node->data;
299
300         if (stats) {
301                 g_free(stats);
302         }
303         return FALSE;
304 }
305
306 void
307 ph_stats_free(ph_stats_t *ps)
308 {
309
310         if (ps->stats_tree) {
311                 g_node_traverse(ps->stats_tree, G_IN_ORDER,
312                                 G_TRAVERSE_ALL, -1,
313                                 stat_node_free, NULL);
314                 g_node_destroy(ps->stats_tree);
315         }
316
317         g_free(ps);
318 }