Put the "-s" option in the SYNOPSIS section.
[obnox/wireshark/wip.git] / proto_hier_stats.c
1 /* proto_hier_stats.c
2  * Routines for calculating statistics based on protocol.
3  *
4  * $Id: proto_hier_stats.c,v 1.10 2002/01/02 20:23:46 gram Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
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
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include "globals.h"
31 #include "proto_hier_stats.h"
32 #include "progress_dlg.h"
33 #include "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 static GNode*
46 find_stat_node(GNode *parent_stat_node, header_field_info *needle_hfinfo)
47 {
48         GNode                   *needle_stat_node;
49         header_field_info       *hfinfo;
50         ph_stats_node_t         *stats;
51
52         needle_stat_node = g_node_first_child(parent_stat_node);
53
54         while (needle_stat_node) {
55                 hfinfo = STAT_NODE_HFINFO(needle_stat_node);
56                 if (hfinfo &&  hfinfo->id == needle_hfinfo->id) {
57                         return needle_stat_node;
58                 }
59                 needle_stat_node = g_node_next_sibling(needle_stat_node);
60         }
61
62         /* None found. Create one. */
63         stats = g_new(ph_stats_node_t, 1);
64
65         /* Intialize counters */
66         stats->hfinfo = needle_hfinfo;
67         stats->num_pkts_total = 0;
68         stats->num_pkts_last = 0;
69         stats->num_bytes_total = 0;
70         stats->num_bytes_last = 0;
71
72         needle_stat_node = g_node_new(stats);
73         g_node_append(parent_stat_node, needle_stat_node);
74         return needle_stat_node;
75 }
76
77
78 static void
79 process_node(proto_item *ptree_node, GNode *parent_stat_node, ph_stats_t *ps, guint pkt_len)
80 {
81         field_info              *finfo;
82         ph_stats_node_t *stats;
83         proto_item              *proto_sibling_node;
84         GNode                   *stat_node;
85
86         finfo = PITEM_FINFO(ptree_node);
87         g_assert(finfo);
88
89         stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
90         
91         /* Assert that the finfo is related to a protocol, not a field. */
92         g_assert(finfo->hfinfo->parent == -1);
93
94         stats = STAT_NODE_STATS(stat_node);
95         stats->num_pkts_total++;
96         stats->num_bytes_total += pkt_len;
97
98         proto_sibling_node = g_node_next_sibling(ptree_node);
99
100         if (proto_sibling_node) {
101                 process_node(proto_sibling_node, stat_node, ps, pkt_len);
102         }
103         else {
104                 stats->num_pkts_last++;
105                 stats->num_bytes_last += pkt_len;
106         }
107 }
108
109
110
111 static void
112 process_tree(proto_tree *protocol_tree, ph_stats_t* ps, guint pkt_len)
113 {
114         proto_item      *ptree_node;
115
116         ptree_node = g_node_first_child(protocol_tree);
117         if (!ptree_node) {
118                 return;
119         }
120
121         process_node(ptree_node, ps->stats_tree, ps, pkt_len);
122 }
123
124 static void
125 process_frame(frame_data *frame, column_info *cinfo, ph_stats_t* ps)
126 {
127         epan_dissect_t                  *edt;
128         union wtap_pseudo_header        phdr;
129         guint8                          pd[WTAP_MAX_PACKET_SIZE];
130
131         /* Load the frame from the capture file */
132         wtap_seek_read(cfile.wth, frame->file_off, &phdr,
133                         pd, frame->cap_len);
134
135         /* Dissect the frame */
136         edt = epan_dissect_new(TRUE, FALSE);
137     epan_dissect_run(edt, &phdr, pd, frame, cinfo);
138
139         /* Get stats from this protocol tree */
140         process_tree(edt->tree, ps, frame->pkt_len);
141
142         /* Free our memory. */
143         epan_dissect_free(edt);
144 }
145
146
147
148 ph_stats_t*
149 ph_stats_new(void)
150 {
151         ph_stats_t      *ps;
152         frame_data      *frame;
153         guint           tot_packets, tot_bytes;
154         progdlg_t       *progbar;
155         gboolean        stop_flag;
156         guint32         progbar_quantum;
157         guint32         progbar_nextstep;
158         unsigned int    count;
159
160         /* Initialize the data */
161         ps = g_new(ph_stats_t, 1);
162         ps->tot_packets = 0;
163         ps->tot_bytes = 0;
164         ps->stats_tree = g_node_new(NULL);
165
166         /* Update the progress bar when it gets to this value. */
167         progbar_nextstep = 0;
168         /* When we reach the value that triggers a progress bar update,
169            bump that value by this amount. */
170         progbar_quantum = cfile.count/N_PROGBAR_UPDATES;
171         /* Count of packets at which we've looked. */
172         count = 0;
173
174         stop_flag = FALSE;
175         progbar = create_progress_dlg("Computing protocol statistics", "Stop",
176             &stop_flag);
177
178         tot_packets = 0;
179         tot_bytes = 0;
180
181         for (frame = cfile.plist; frame != NULL; frame = frame->next) {
182                 /* Update the progress bar, but do it only N_PROGBAR_UPDATES
183                    times; when we update it, we have to run the GTK+ main
184                    loop to get it to repaint what's pending, and doing so
185                    may involve an "ioctl()" to see if there's any pending
186                    input from an X server, and doing that for every packet
187                    can be costly, especially on a big file. */
188                 if (count >= progbar_nextstep) {
189                         /* let's not divide by zero. I should never be started
190                          * with count == 0, so let's assert that
191                          */
192                         g_assert(cfile.count > 0);
193
194                         update_progress_dlg(progbar,
195                             (gfloat) count / cfile.count);
196
197                         progbar_nextstep += progbar_quantum;
198                 }
199
200                 if (stop_flag) {
201                         /* Well, the user decided to abort the statistics.
202                            computation process  Just stop. */
203                         break;
204                 }
205
206                 /* Skip frames that are hidden due to the display filter.
207                    XXX - should the progress bar count only packets that
208                    passed the display filter?  If so, it should
209                    probably do so for other loops (see "file.c") that
210                    look only at those packets. */
211                 if (frame->flags.passed_dfilter) {
212                         process_frame(frame, &cfile.cinfo, ps);
213
214                         tot_packets++;
215                         tot_bytes += frame->pkt_len;
216                 }
217
218                 count++;
219         }
220
221         /* We're done calculating the statistics; destroy the progress bar. */
222         destroy_progress_dlg(progbar);
223
224         if (stop_flag) {
225                 /*
226                  * We quit in the middle; throw away the statistics
227                  * and return NULL, so our caller doesn't pop up a
228                  * window with the incomplete statistics.
229                  */
230                 ph_stats_free(ps);
231                 return NULL;
232         }
233
234         ps->tot_packets = tot_packets;
235         ps->tot_bytes = tot_bytes;
236
237         return ps;
238 }
239
240 static gboolean
241 stat_node_free(GNode *node, gpointer data)
242 {
243         ph_stats_node_t *stats = node->data;
244
245         if (stats) {
246                 g_free(stats);
247         }
248         return FALSE;
249 }
250
251 void
252 ph_stats_free(ph_stats_t *ps)
253 {
254
255         if (ps->stats_tree) {
256                 g_node_traverse(ps->stats_tree, G_IN_ORDER,
257                                 G_TRAVERSE_ALL, -1,
258                                 stat_node_free, NULL);
259                 g_node_destroy(ps->stats_tree);
260         }
261
262         g_free(ps);
263 }