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