Don't use GNodes for the protocol tree, put the sibling pointer, and
[metze/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.20 2003/12/04 10:59:33 guy 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
168         /* Initialize the data */
169         ps = g_new(ph_stats_t, 1);
170         ps->tot_packets = 0;
171         ps->tot_bytes = 0;
172         ps->stats_tree = g_node_new(NULL);
173
174         /* Update the progress bar when it gets to this value. */
175         cfile.progbar_nextstep = 0;
176         /* When we reach the value that triggers a progress bar update,
177            bump that value by this amount. */
178         cfile.progbar_quantum = cfile.count/N_PROGBAR_UPDATES;
179         /* Count of packets at which we've looked. */
180         count = 0;
181
182         stop_flag = FALSE;
183         g_get_current_time(&start_time);
184
185         tot_packets = 0;
186         tot_bytes = 0;
187
188         for (frame = cfile.plist; frame != NULL; frame = frame->next) {
189                 /* Update the progress bar, but do it only N_PROGBAR_UPDATES
190                    times; when we update it, we have to run the GTK+ main
191                    loop to get it to repaint what's pending, and doing so
192                    may involve an "ioctl()" to see if there's any pending
193                    input from an X server, and doing that for every packet
194                    can be costly, especially on a big file. */
195                 if (count >= cfile.progbar_nextstep) {
196                         /* let's not divide by zero. I should never be started
197                          * with count == 0, so let's assert that
198                          */
199                         g_assert(cfile.count > 0);
200
201                         prog_val = (gfloat) count / cfile.count;
202
203                         if (progbar == NULL)
204                                 /* Create the progress bar if necessary */
205                                 progbar = delayed_create_progress_dlg(
206                                     "Computing", "protocol hierarchy statistics", "Stop",
207                                     &stop_flag, &start_time, prog_val);
208
209                         if (progbar != NULL) {
210                                 g_snprintf(status_str, sizeof(status_str),
211                                         "%4u of %u frames", count, cfile.count);
212                                 update_progress_dlg(progbar, prog_val, status_str);
213                         }
214
215                         cfile.progbar_nextstep += cfile.progbar_quantum;
216                 }
217
218                 if (stop_flag) {
219                         /* Well, the user decided to abort the statistics.
220                            computation process  Just stop. */
221                         break;
222                 }
223
224                 /* Skip frames that are hidden due to the display filter.
225                    XXX - should the progress bar count only packets that
226                    passed the display filter?  If so, it should
227                    probably do so for other loops (see "file.c") that
228                    look only at those packets. */
229                 if (frame->flags.passed_dfilter) {
230                         if (!process_frame(frame, &cfile.cinfo, ps)) {
231                                 /*
232                                  * Give up, and set "stop_flag" so we
233                                  * just abort rather than popping up
234                                  * the statistics window.
235                                  */
236                                 stop_flag = TRUE;
237                                 break;
238                         }
239
240                         tot_packets++;
241                         tot_bytes += frame->pkt_len;
242                 }
243
244                 count++;
245         }
246
247         /* We're done calculating the statistics; destroy the progress bar
248            if it was created. */
249         if (progbar != NULL)
250                 destroy_progress_dlg(progbar);
251
252         if (stop_flag) {
253                 /*
254                  * We quit in the middle; throw away the statistics
255                  * and return NULL, so our caller doesn't pop up a
256                  * window with the incomplete statistics.
257                  */
258                 ph_stats_free(ps);
259                 return NULL;
260         }
261
262         ps->tot_packets = tot_packets;
263         ps->tot_bytes = tot_bytes;
264
265         return ps;
266 }
267
268 static gboolean
269 stat_node_free(GNode *node, gpointer data _U_)
270 {
271         ph_stats_node_t *stats = node->data;
272
273         if (stats) {
274                 g_free(stats);
275         }
276         return FALSE;
277 }
278
279 void
280 ph_stats_free(ph_stats_t *ps)
281 {
282
283         if (ps->stats_tree) {
284                 g_node_traverse(ps->stats_tree, G_IN_ORDER,
285                                 G_TRAVERSE_ALL, -1,
286                                 stat_node_free, NULL);
287                 g_node_destroy(ps->stats_tree);
288         }
289
290         g_free(ps);
291 }