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