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