Minor spelling etc updates.
[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.16 2002/08/28 21:00:41 jmayer 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 <epan/epan_dissect.h>
33 #include <wtap.h>
34
35 #include <stdio.h>
36 #include <glib.h>
37
38 /* Update the progress bar this many times when scanning the packet list. */
39 #define N_PROGBAR_UPDATES       100
40
41 #define STAT_NODE_STATS(n)   ((ph_stats_node_t*)(n)->data)
42 #define STAT_NODE_HFINFO(n)  (STAT_NODE_STATS(n)->hfinfo)
43
44 static GNode*
45 find_stat_node(GNode *parent_stat_node, header_field_info *needle_hfinfo)
46 {
47         GNode                   *needle_stat_node;
48         header_field_info       *hfinfo;
49         ph_stats_node_t         *stats;
50
51         needle_stat_node = g_node_first_child(parent_stat_node);
52
53         while (needle_stat_node) {
54                 hfinfo = STAT_NODE_HFINFO(needle_stat_node);
55                 if (hfinfo &&  hfinfo->id == needle_hfinfo->id) {
56                         return needle_stat_node;
57                 }
58                 needle_stat_node = g_node_next_sibling(needle_stat_node);
59         }
60
61         /* None found. Create one. */
62         stats = g_new(ph_stats_node_t, 1);
63
64         /* Intialize counters */
65         stats->hfinfo = needle_hfinfo;
66         stats->num_pkts_total = 0;
67         stats->num_pkts_last = 0;
68         stats->num_bytes_total = 0;
69         stats->num_bytes_last = 0;
70
71         needle_stat_node = g_node_new(stats);
72         g_node_append(parent_stat_node, needle_stat_node);
73         return needle_stat_node;
74 }
75
76
77 static void
78 process_node(proto_item *ptree_node, GNode *parent_stat_node, ph_stats_t *ps, guint pkt_len)
79 {
80         field_info              *finfo;
81         ph_stats_node_t *stats;
82         proto_item              *proto_sibling_node;
83         GNode                   *stat_node;
84
85         finfo = PITEM_FINFO(ptree_node);
86         g_assert(finfo);
87
88         stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
89
90         /* Assert that the finfo is related to a protocol, not a field. */
91         g_assert(finfo->hfinfo->parent == -1);
92
93         stats = STAT_NODE_STATS(stat_node);
94         stats->num_pkts_total++;
95         stats->num_bytes_total += pkt_len;
96
97         proto_sibling_node = g_node_next_sibling(ptree_node);
98
99         if (proto_sibling_node) {
100                 process_node(proto_sibling_node, stat_node, ps, pkt_len);
101         }
102         else {
103                 stats->num_pkts_last++;
104                 stats->num_bytes_last += pkt_len;
105         }
106 }
107
108
109
110 static void
111 process_tree(proto_tree *protocol_tree, ph_stats_t* ps, guint pkt_len)
112 {
113         proto_item      *ptree_node;
114
115         ptree_node = g_node_first_child(protocol_tree);
116         if (!ptree_node) {
117                 return;
118         }
119
120         process_node(ptree_node, ps->stats_tree, ps, pkt_len);
121 }
122
123 static void
124 process_frame(frame_data *frame, column_info *cinfo, ph_stats_t* ps)
125 {
126         epan_dissect_t                  *edt;
127         union wtap_pseudo_header        phdr;
128         guint8                          pd[WTAP_MAX_PACKET_SIZE];
129         int                             err;
130
131         /* Load the frame from the capture file */
132         /* XX - do something with "err" */
133         wtap_seek_read(cfile.wth, frame->file_off, &phdr,
134                         pd, frame->cap_len, &err);
135
136         /* Dissect the frame */
137         edt = epan_dissect_new(TRUE, FALSE);
138         epan_dissect_run(edt, &phdr, pd, frame, cinfo);
139
140         /* Get stats from this protocol tree */
141         process_tree(edt->tree, ps, frame->pkt_len);
142
143         /* Free our memory. */
144         epan_dissect_free(edt);
145 }
146
147
148
149 ph_stats_t*
150 ph_stats_new(void)
151 {
152         ph_stats_t      *ps;
153         frame_data      *frame;
154         guint           tot_packets, tot_bytes;
155         progdlg_t       *progbar = NULL;
156         gboolean        stop_flag;
157         int             count;
158         float           prog_val;
159         GTimeVal        start_time;
160         gchar           status_str[100];
161
162         /* Initialize the data */
163         ps = g_new(ph_stats_t, 1);
164         ps->tot_packets = 0;
165         ps->tot_bytes = 0;
166         ps->stats_tree = g_node_new(NULL);
167
168         /* Update the progress bar when it gets to this value. */
169         cfile.progbar_nextstep = 0;
170         /* When we reach the value that triggers a progress bar update,
171            bump that value by this amount. */
172         cfile.progbar_quantum = cfile.count/N_PROGBAR_UPDATES;
173         /* Count of packets at which we've looked. */
174         count = 0;
175
176         stop_flag = FALSE;
177         g_get_current_time(&start_time);
178
179         tot_packets = 0;
180         tot_bytes = 0;
181
182         for (frame = cfile.plist; frame != NULL; frame = frame->next) {
183                 /* Update the progress bar, but do it only N_PROGBAR_UPDATES
184                    times; when we update it, we have to run the GTK+ main
185                    loop to get it to repaint what's pending, and doing so
186                    may involve an "ioctl()" to see if there's any pending
187                    input from an X server, and doing that for every packet
188                    can be costly, especially on a big file. */
189                 if (count >= cfile.progbar_nextstep) {
190                         /* let's not divide by zero. I should never be started
191                          * with count == 0, so let's assert that
192                          */
193                         g_assert(cfile.count > 0);
194
195                         prog_val = (gfloat) count / cfile.count;
196
197                         if (progbar == NULL)
198                                 /* Create the progress bar if necessary */
199                                 progbar = delayed_create_progress_dlg(
200                                     "Computing", "protocol hierarchy statistics", "Stop",
201                                     &stop_flag, &start_time, prog_val);
202
203                         if (progbar != NULL) {
204                                 g_snprintf(status_str, sizeof(status_str),
205                                         "%4u of %u frames", count, cfile.count);
206                                 update_progress_dlg(progbar, prog_val, status_str);
207                         }
208
209                         cfile.progbar_nextstep += cfile.progbar_quantum;
210                 }
211
212                 if (stop_flag) {
213                         /* Well, the user decided to abort the statistics.
214                            computation process  Just stop. */
215                         break;
216                 }
217
218                 /* Skip frames that are hidden due to the display filter.
219                    XXX - should the progress bar count only packets that
220                    passed the display filter?  If so, it should
221                    probably do so for other loops (see "file.c") that
222                    look only at those packets. */
223                 if (frame->flags.passed_dfilter) {
224                         process_frame(frame, &cfile.cinfo, ps);
225
226                         tot_packets++;
227                         tot_bytes += frame->pkt_len;
228                 }
229
230                 count++;
231         }
232
233         /* We're done calculating the statistics; destroy the progress bar
234            if it was created. */
235         if (progbar != NULL)
236                 destroy_progress_dlg(progbar);
237
238         if (stop_flag) {
239                 /*
240                  * We quit in the middle; throw away the statistics
241                  * and return NULL, so our caller doesn't pop up a
242                  * window with the incomplete statistics.
243                  */
244                 ph_stats_free(ps);
245                 return NULL;
246         }
247
248         ps->tot_packets = tot_packets;
249         ps->tot_bytes = tot_bytes;
250
251         return ps;
252 }
253
254 static gboolean
255 stat_node_free(GNode *node, gpointer data _U_)
256 {
257         ph_stats_node_t *stats = node->data;
258
259         if (stats) {
260                 g_free(stats);
261         }
262         return FALSE;
263 }
264
265 void
266 ph_stats_free(ph_stats_t *ps)
267 {
268
269         if (ps->stats_tree) {
270                 g_node_traverse(ps->stats_tree, G_IN_ORDER,
271                                 G_TRAVERSE_ALL, -1,
272                                 stat_node_free, NULL);
273                 g_node_destroy(ps->stats_tree);
274         }
275
276         g_free(ps);
277 }