change all file offsets from long to gint64 so we can - theoretically - handle files...
[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  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
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
46 static GNode*
47 find_stat_node(GNode *parent_stat_node, header_field_info *needle_hfinfo)
48 {
49         GNode                   *needle_stat_node;
50         header_field_info       *hfinfo;
51         ph_stats_node_t         *stats;
52
53         needle_stat_node = g_node_first_child(parent_stat_node);
54
55         while (needle_stat_node) {
56                 hfinfo = STAT_NODE_HFINFO(needle_stat_node);
57                 if (hfinfo &&  hfinfo->id == needle_hfinfo->id) {
58                         return needle_stat_node;
59                 }
60                 needle_stat_node = g_node_next_sibling(needle_stat_node);
61         }
62
63         /* None found. Create one. */
64         stats = g_new(ph_stats_node_t, 1);
65
66         /* Intialize counters */
67         stats->hfinfo = needle_hfinfo;
68         stats->num_pkts_total = 0;
69         stats->num_pkts_last = 0;
70         stats->num_bytes_total = 0;
71         stats->num_bytes_last = 0;
72
73         needle_stat_node = g_node_new(stats);
74         g_node_append(parent_stat_node, needle_stat_node);
75         return needle_stat_node;
76 }
77
78
79 static void
80 process_node(proto_node *ptree_node, GNode *parent_stat_node, ph_stats_t *ps, guint pkt_len)
81 {
82         field_info              *finfo;
83         ph_stats_node_t         *stats;
84         proto_node              *proto_sibling_node;
85         GNode                   *stat_node;
86
87         finfo = PITEM_FINFO(ptree_node);
88         g_assert(finfo);
89
90         /* if the field info isn't related to a protocol but to a field, don't count them,
91      * as they don't belong to any protocol.
92      * (happens e.g. for toplevel tree item of desegmentation "[Reassembled TCP Segments]") */
93     if(finfo->hfinfo->parent != -1) {
94         /* there are some cases where helpful generated items are added
95          * to the decode tree so do not test for it any more
96          *g_assert(PROTO_ITEM_IS_GENERATED(ptree_node));
97          */
98         return;
99     }
100
101         stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
102
103         stats = STAT_NODE_STATS(stat_node);
104         stats->num_pkts_total++;
105         stats->num_bytes_total += pkt_len;
106
107         proto_sibling_node = ptree_node->next;
108
109         if (proto_sibling_node) {
110                 process_node(proto_sibling_node, stat_node, ps, pkt_len);
111         }
112         else {
113                 stats->num_pkts_last++;
114                 stats->num_bytes_last += pkt_len;
115         }
116 }
117
118
119
120 static void
121 process_tree(proto_tree *protocol_tree, ph_stats_t* ps, guint pkt_len)
122 {
123         proto_node      *ptree_node;
124
125         ptree_node = ((proto_node *)protocol_tree)->first_child;
126         if (!ptree_node) {
127                 return;
128         }
129
130         process_node(ptree_node, ps->stats_tree, ps, pkt_len);
131 }
132
133 static gboolean
134 process_frame(frame_data *frame, column_info *cinfo, ph_stats_t* ps)
135 {
136         epan_dissect_t                  *edt;
137         union wtap_pseudo_header        phdr;
138         guint8                          pd[WTAP_MAX_PACKET_SIZE];
139         int                             err;
140         gchar                           *err_info;
141         double                          cur_time;
142
143         /* Load the frame from the capture file */
144         if (!wtap_seek_read(cfile.wth, frame->file_off, &phdr, pd,
145             frame->cap_len, &err, &err_info)) {
146                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
147                     cf_read_error_message(err, err_info), cfile.filename);
148                 return FALSE;   /* failure */
149         }
150
151         /* Dissect the frame   tree  not visible */
152         edt = epan_dissect_new(TRUE, FALSE);
153         epan_dissect_run(edt, &phdr, pd, frame, cinfo);
154
155         /* Get stats from this protocol tree */
156         process_tree(edt->tree, ps, frame->pkt_len);
157
158         /* Update times */
159         cur_time = nstime_to_sec(&frame->abs_ts);
160         if (cur_time < ps->first_time) {
161           ps->first_time = cur_time;
162         }
163         if (cur_time > ps->last_time){
164           ps->last_time = cur_time;
165         }
166
167         /* Free our memory. */
168         epan_dissect_free(edt);
169
170         return TRUE;    /* success */
171 }
172
173 ph_stats_t*
174 ph_stats_new(void)
175 {
176         ph_stats_t      *ps;
177         frame_data      *frame;
178         guint           tot_packets, tot_bytes;
179         progdlg_t       *progbar = NULL;
180         gboolean        stop_flag;
181         int             count;
182         float           progbar_val;
183         GTimeVal        start_time;
184         gchar           status_str[100];
185         int             progbar_nextstep;
186         int             progbar_quantum;
187
188         /* Initialize the data */
189         ps = g_new(ph_stats_t, 1);
190         ps->tot_packets = 0;
191         ps->tot_bytes = 0;
192         ps->stats_tree = g_node_new(NULL);
193         ps->first_time = 0.0;
194         ps->last_time = 0.0;
195
196         /* Update the progress bar when it gets to this value. */
197         progbar_nextstep = 0;
198         /* When we reach the value that triggers a progress bar update,
199            bump that value by this amount. */
200         progbar_quantum = cfile.count/N_PROGBAR_UPDATES;
201         /* Count of packets at which we've looked. */
202         count = 0;
203         /* Progress so far. */
204         progbar_val = 0.0;
205
206         stop_flag = FALSE;
207         g_get_current_time(&start_time);
208
209         tot_packets = 0;
210         tot_bytes = 0;
211
212         for (frame = cfile.plist; frame != NULL; frame = frame->next) {
213                 /* Create the progress bar if necessary.
214                    We check on every iteration of the loop, so that
215                    it takes no longer than the standard time to create
216                    it (otherwise, for a large file, we might take
217                    considerably longer than that standard time in order
218                    to get to the next progress bar step). */
219                 if (progbar == NULL)
220                         progbar = delayed_create_progress_dlg(
221                             "Computing", "protocol hierarchy statistics", 
222                             TRUE, &stop_flag, &start_time, progbar_val);
223
224                 /* Update the progress bar, but do it only N_PROGBAR_UPDATES
225                    times; when we update it, we have to run the GTK+ main
226                    loop to get it to repaint what's pending, and doing so
227                    may involve an "ioctl()" to see if there's any pending
228                    input from an X server, and doing that for every packet
229                    can be costly, especially on a big file. */
230                 if (count >= progbar_nextstep) {
231                         /* let's not divide by zero. I should never be started
232                          * with count == 0, so let's assert that
233                          */
234                         g_assert(cfile.count > 0);
235
236                         progbar_val = (gfloat) count / cfile.count;
237
238                         if (progbar != NULL) {
239                                 g_snprintf(status_str, sizeof(status_str),
240                                         "%4u of %u frames", count, cfile.count);
241                                 update_progress_dlg(progbar, progbar_val, status_str);
242                         }
243
244                         progbar_nextstep += progbar_quantum;
245                 }
246
247                 if (stop_flag) {
248                         /* Well, the user decided to abort the statistics.
249                            computation process  Just stop. */
250                         break;
251                 }
252
253                 /* Skip frames that are hidden due to the display filter.
254                    XXX - should the progress bar count only packets that
255                    passed the display filter?  If so, it should
256                    probably do so for other loops (see "file.c") that
257                    look only at those packets. */
258                 if (frame->flags.passed_dfilter) {
259
260                         if (tot_packets == 0) {
261                                 double cur_time = nstime_to_sec(&frame->abs_ts);
262                                 ps->first_time = cur_time;
263                                 ps->last_time = cur_time;
264                         }
265                         
266                         /* we don't care about colinfo */
267                         if (!process_frame(frame, NULL, ps)) {
268                                 /*
269                                  * Give up, and set "stop_flag" so we
270                                  * just abort rather than popping up
271                                  * the statistics window.
272                                  */
273                                 stop_flag = TRUE;
274                                 break;
275                         }
276
277                         tot_packets++;
278                         tot_bytes += frame->pkt_len;
279                 }
280
281                 count++;
282         }
283
284         /* We're done calculating the statistics; destroy the progress bar
285            if it was created. */
286         if (progbar != NULL)
287                 destroy_progress_dlg(progbar);
288
289         if (stop_flag) {
290                 /*
291                  * We quit in the middle; throw away the statistics
292                  * and return NULL, so our caller doesn't pop up a
293                  * window with the incomplete statistics.
294                  */
295                 ph_stats_free(ps);
296                 return NULL;
297         }
298
299         ps->tot_packets = tot_packets;
300         ps->tot_bytes = tot_bytes;
301
302         return ps;
303 }
304
305 static gboolean
306 stat_node_free(GNode *node, gpointer data _U_)
307 {
308         ph_stats_node_t *stats = node->data;
309
310         if (stats) {
311                 g_free(stats);
312         }
313         return FALSE;
314 }
315
316 void
317 ph_stats_free(ph_stats_t *ps)
318 {
319
320         if (ps->stats_tree) {
321                 g_node_traverse(ps->stats_tree, G_IN_ORDER,
322                                 G_TRAVERSE_ALL, -1,
323                                 stat_node_free, NULL);
324                 g_node_destroy(ps->stats_tree);
325         }
326
327         g_free(ps);
328 }