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