Show the Display filter in Protocol Hierarchy Statistics.
[metze/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 = PITEM_FINFO(ptree_node);
89         g_assert(finfo);
90
91         stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
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 = ptree_node->next;
98
99         /* If the field info isn't related to a protocol but to a field (parent != -1),
100          * don't count them, as they don't belong to any protocol.
101          * (happens e.g. for toplevel tree item of desegmentation "[Reassembled TCP Segments]") */
102         if (proto_sibling_node && proto_sibling_node->finfo->hfinfo->parent == -1) {
103                 /* If the name does not exist for this proto_sibling_node, then it is
104                  * not a normal protocol in the top-level tree.  It was instead
105                  * added as a normal tree such as IPv6's Hop-by-hop Option Header and
106                  * should be skipped when creating the protocol hierarchy display. */
107                 if(strlen(proto_sibling_node->finfo->hfinfo->name) == 0 && ptree_node->next)
108                         proto_sibling_node = proto_sibling_node->next;
109
110                 process_node(proto_sibling_node, stat_node, ps, pkt_len);
111         } else {
112                 stats->num_pkts_last++;
113                 stats->num_bytes_last += pkt_len;
114         }
115 }
116
117
118
119 static void
120 process_tree(proto_tree *protocol_tree, ph_stats_t* ps, guint pkt_len)
121 {
122         proto_node      *ptree_node;
123
124         ptree_node = ((proto_node *)protocol_tree)->first_child;
125         if (!ptree_node) {
126                 return;
127         }
128
129         process_node(ptree_node, ps->stats_tree, ps, pkt_len);
130 }
131
132 static gboolean
133 process_frame(frame_data *frame, column_info *cinfo, ph_stats_t* ps)
134 {
135         epan_dissect_t                  *edt;
136         union wtap_pseudo_header        phdr;
137         guint8                          pd[WTAP_MAX_PACKET_SIZE];
138         int                             err;
139         gchar                           *err_info;
140         double                          cur_time;
141
142         /* Load the frame from the capture file */
143         if (!wtap_seek_read(cfile.wth, frame->file_off, &phdr, pd,
144             frame->cap_len, &err, &err_info)) {
145                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
146                     cf_read_error_message(err, err_info), cfile.filename);
147                 return FALSE;   /* failure */
148         }
149
150         /* Dissect the frame   tree  not visible */
151         edt = epan_dissect_new(TRUE, FALSE);
152         epan_dissect_run(edt, &phdr, pd, frame, cinfo);
153
154         /* Get stats from this protocol tree */
155         process_tree(edt->tree, ps, frame->pkt_len);
156
157         /* Update times */
158         cur_time = nstime_to_sec(&frame->abs_ts);
159         if (cur_time < ps->first_time) {
160           ps->first_time = cur_time;
161         }
162         if (cur_time > ps->last_time){
163           ps->last_time = cur_time;
164         }
165
166         /* Free our memory. */
167         epan_dissect_free(edt);
168
169         return TRUE;    /* success */
170 }
171
172 ph_stats_t*
173 ph_stats_new(void)
174 {
175         ph_stats_t      *ps;
176         frame_data      *frame;
177         guint           tot_packets, tot_bytes;
178         progdlg_t       *progbar = NULL;
179         gboolean        stop_flag;
180         int             count;
181         float           progbar_val;
182         GTimeVal        start_time;
183         gchar           status_str[100];
184         int             progbar_nextstep;
185         int             progbar_quantum;
186
187         /* Initialize the data */
188         ps = g_new(ph_stats_t, 1);
189         ps->tot_packets = 0;
190         ps->tot_bytes = 0;
191         ps->stats_tree = g_node_new(NULL);
192         ps->first_time = 0.0;
193         ps->last_time = 0.0;
194         ps->dfilter = cfile.dfilter;
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 }