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