Don't use "cf_read_error_message()" when reporting errors not from
[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.23 2004/01/25 21:55:10 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         gchar                           *err_info;
132
133         /* Load the frame from the capture file */
134         if (!wtap_seek_read(cfile.wth, frame->file_off, &phdr, pd,
135             frame->cap_len, &err, &err_info)) {
136                 simple_dialog(ESD_TYPE_CRIT, NULL,
137                     cf_read_error_message(err, err_info), cfile.filename);
138                 return FALSE;   /* failure */
139         }
140
141         /* Dissect the frame */
142         edt = epan_dissect_new(TRUE, FALSE);
143         epan_dissect_run(edt, &phdr, pd, frame, cinfo);
144
145         /* Get stats from this protocol tree */
146         process_tree(edt->tree, ps, frame->pkt_len);
147
148         /* Free our memory. */
149         epan_dissect_free(edt);
150
151         return TRUE;    /* success */
152 }
153
154
155
156 ph_stats_t*
157 ph_stats_new(void)
158 {
159         ph_stats_t      *ps;
160         frame_data      *frame;
161         guint           tot_packets, tot_bytes;
162         progdlg_t       *progbar = NULL;
163         gboolean        stop_flag;
164         int             count;
165         float           prog_val;
166         GTimeVal        start_time;
167         gchar           status_str[100];
168         int             progbar_nextstep;
169         int             progbar_quantum;
170
171         /* Initialize the data */
172         ps = g_new(ph_stats_t, 1);
173         ps->tot_packets = 0;
174         ps->tot_bytes = 0;
175         ps->stats_tree = g_node_new(NULL);
176
177         /* Update the progress bar when it gets to this value. */
178         progbar_nextstep = 0;
179         /* When we reach the value that triggers a progress bar update,
180            bump that value by this amount. */
181         progbar_quantum = cfile.count/N_PROGBAR_UPDATES;
182         /* Count of packets at which we've looked. */
183         count = 0;
184
185         stop_flag = FALSE;
186         g_get_current_time(&start_time);
187
188         tot_packets = 0;
189         tot_bytes = 0;
190
191         for (frame = cfile.plist; frame != NULL; frame = frame->next) {
192                 /* Update the progress bar, but do it only N_PROGBAR_UPDATES
193                    times; when we update it, we have to run the GTK+ main
194                    loop to get it to repaint what's pending, and doing so
195                    may involve an "ioctl()" to see if there's any pending
196                    input from an X server, and doing that for every packet
197                    can be costly, especially on a big file. */
198                 if (count >= progbar_nextstep) {
199                         /* let's not divide by zero. I should never be started
200                          * with count == 0, so let's assert that
201                          */
202                         g_assert(cfile.count > 0);
203
204                         prog_val = (gfloat) count / cfile.count;
205
206                         if (progbar == NULL)
207                                 /* Create the progress bar if necessary */
208                                 progbar = delayed_create_progress_dlg(
209                                     "Computing", "protocol hierarchy statistics", 
210                                     &stop_flag, &start_time, prog_val);
211
212                         if (progbar != NULL) {
213                                 g_snprintf(status_str, sizeof(status_str),
214                                         "%4u of %u frames", count, cfile.count);
215                                 update_progress_dlg(progbar, prog_val, status_str);
216                         }
217
218                         progbar_nextstep += progbar_quantum;
219                 }
220
221                 if (stop_flag) {
222                         /* Well, the user decided to abort the statistics.
223                            computation process  Just stop. */
224                         break;
225                 }
226
227                 /* Skip frames that are hidden due to the display filter.
228                    XXX - should the progress bar count only packets that
229                    passed the display filter?  If so, it should
230                    probably do so for other loops (see "file.c") that
231                    look only at those packets. */
232                 if (frame->flags.passed_dfilter) {
233                         if (!process_frame(frame, &cfile.cinfo, ps)) {
234                                 /*
235                                  * Give up, and set "stop_flag" so we
236                                  * just abort rather than popping up
237                                  * the statistics window.
238                                  */
239                                 stop_flag = TRUE;
240                                 break;
241                         }
242
243                         tot_packets++;
244                         tot_bytes += frame->pkt_len;
245                 }
246
247                 count++;
248         }
249
250         /* We're done calculating the statistics; destroy the progress bar
251            if it was created. */
252         if (progbar != NULL)
253                 destroy_progress_dlg(progbar);
254
255         if (stop_flag) {
256                 /*
257                  * We quit in the middle; throw away the statistics
258                  * and return NULL, so our caller doesn't pop up a
259                  * window with the incomplete statistics.
260                  */
261                 ph_stats_free(ps);
262                 return NULL;
263         }
264
265         ps->tot_packets = tot_packets;
266         ps->tot_bytes = tot_bytes;
267
268         return ps;
269 }
270
271 static gboolean
272 stat_node_free(GNode *node, gpointer data _U_)
273 {
274         ph_stats_node_t *stats = node->data;
275
276         if (stats) {
277                 g_free(stats);
278         }
279         return FALSE;
280 }
281
282 void
283 ph_stats_free(ph_stats_t *ps)
284 {
285
286         if (ps->stats_tree) {
287                 g_node_traverse(ps->stats_tree, G_IN_ORDER,
288                                 G_TRAVERSE_ALL, -1,
289                                 stat_node_free, NULL);
290                 g_node_destroy(ps->stats_tree);
291         }
292
293         g_free(ps);
294 }