nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* proto_hier_stats.c |
2 | * Routines for calculating statistics based on protocol. |
||
3 | * |
||
4 | * Wireshark - Network traffic analyzer |
||
5 | * By Gerald Combs <gerald@wireshark.org> |
||
6 | * Copyright 1998 Gerald Combs |
||
7 | * |
||
8 | * This program is free software; you can redistribute it and/or |
||
9 | * modify it under the terms of the GNU General Public License |
||
10 | * as published by the Free Software Foundation; either version 2 |
||
11 | * of the License, or (at your option) any later version. |
||
12 | * |
||
13 | * This program is distributed in the hope that it will be useful, |
||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
16 | * GNU General Public License for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU General Public License |
||
19 | * along with this program; if not, write to the Free Software |
||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
||
21 | */ |
||
22 | |||
23 | #include "config.h" |
||
24 | |||
25 | #include "file.h" |
||
26 | #include "frame_tvbuff.h" |
||
27 | #include "ui/proto_hier_stats.h" |
||
28 | #include "ui/progress_dlg.h" |
||
29 | #include "epan/epan_dissect.h" |
||
30 | #include "epan/proto.h" |
||
31 | |||
32 | #include <string.h> |
||
33 | |||
34 | /* Update the progress bar this many times when scanning the packet list. */ |
||
35 | #define N_PROGBAR_UPDATES 100 |
||
36 | |||
37 | #define STAT_NODE_STATS(n) ((ph_stats_node_t*)(n)->data) |
||
38 | #define STAT_NODE_HFINFO(n) (STAT_NODE_STATS(n)->hfinfo) |
||
39 | |||
40 | static int pc_proto_id = -1; |
||
41 | |||
42 | static GNode* |
||
43 | find_stat_node(GNode *parent_stat_node, header_field_info *needle_hfinfo) |
||
44 | { |
||
45 | GNode *needle_stat_node, *up_parent_stat_node; |
||
46 | header_field_info *hfinfo; |
||
47 | ph_stats_node_t *stats; |
||
48 | |||
49 | /* Look down the tree */ |
||
50 | needle_stat_node = g_node_first_child(parent_stat_node); |
||
51 | |||
52 | while (needle_stat_node) { |
||
53 | hfinfo = STAT_NODE_HFINFO(needle_stat_node); |
||
54 | if (hfinfo && hfinfo->id == needle_hfinfo->id) { |
||
55 | return needle_stat_node; |
||
56 | } |
||
57 | needle_stat_node = g_node_next_sibling(needle_stat_node); |
||
58 | } |
||
59 | |||
60 | /* Look up the tree */ |
||
61 | up_parent_stat_node = parent_stat_node; |
||
62 | while (up_parent_stat_node && up_parent_stat_node->parent) |
||
63 | { |
||
64 | needle_stat_node = g_node_first_child(up_parent_stat_node->parent); |
||
65 | while (needle_stat_node) { |
||
66 | hfinfo = STAT_NODE_HFINFO(needle_stat_node); |
||
67 | if (hfinfo && hfinfo->id == needle_hfinfo->id) { |
||
68 | return needle_stat_node; |
||
69 | } |
||
70 | needle_stat_node = g_node_next_sibling(needle_stat_node); |
||
71 | } |
||
72 | |||
73 | up_parent_stat_node = up_parent_stat_node->parent; |
||
74 | } |
||
75 | |||
76 | /* None found. Create one. */ |
||
77 | stats = g_new(ph_stats_node_t, 1); |
||
78 | |||
79 | /* Intialize counters */ |
||
80 | stats->hfinfo = needle_hfinfo; |
||
81 | stats->num_pkts_total = 0; |
||
82 | stats->num_pkts_last = 0; |
||
83 | stats->num_bytes_total = 0; |
||
84 | stats->num_bytes_last = 0; |
||
85 | |||
86 | needle_stat_node = g_node_new(stats); |
||
87 | g_node_append(parent_stat_node, needle_stat_node); |
||
88 | return needle_stat_node; |
||
89 | } |
||
90 | |||
91 | |||
92 | static void |
||
93 | process_node(proto_node *ptree_node, GNode *parent_stat_node, ph_stats_t *ps) |
||
94 | { |
||
95 | field_info *finfo; |
||
96 | ph_stats_node_t *stats; |
||
97 | proto_node *proto_sibling_node; |
||
98 | GNode *stat_node; |
||
99 | |||
100 | finfo = PNODE_FINFO(ptree_node); |
||
101 | /* We don't fake protocol nodes we expect them to have a field_info. |
||
102 | * Dissection with faked proto tree? */ |
||
103 | g_assert(finfo); |
||
104 | |||
105 | /* If the field info isn't related to a protocol but to a field, |
||
106 | * don't count them, as they don't belong to any protocol. |
||
107 | * (happens e.g. for toplevel tree item of desegmentation "[Reassembled TCP Segments]") */ |
||
108 | if (finfo->hfinfo->parent != -1) { |
||
109 | /* Skip this element, use parent status node */ |
||
110 | stat_node = parent_stat_node; |
||
111 | stats = STAT_NODE_STATS(stat_node); |
||
112 | } else { |
||
113 | stat_node = find_stat_node(parent_stat_node, finfo->hfinfo); |
||
114 | |||
115 | stats = STAT_NODE_STATS(stat_node); |
||
116 | stats->num_pkts_total++; |
||
117 | stats->num_bytes_total += finfo->length; |
||
118 | } |
||
119 | |||
120 | proto_sibling_node = ptree_node->next; |
||
121 | |||
122 | if (proto_sibling_node) { |
||
123 | /* If the name does not exist for this proto_sibling_node, then it is |
||
124 | * not a normal protocol in the top-level tree. It was instead |
||
125 | * added as a normal tree such as IPv6's Hop-by-hop Option Header and |
||
126 | * should be skipped when creating the protocol hierarchy display. */ |
||
127 | if(strlen(PNODE_FINFO(proto_sibling_node)->hfinfo->name) == 0 && ptree_node->next) |
||
128 | proto_sibling_node = proto_sibling_node->next; |
||
129 | |||
130 | process_node(proto_sibling_node, stat_node, ps); |
||
131 | } else { |
||
132 | stats->num_pkts_last++; |
||
133 | stats->num_bytes_last += finfo->length; |
||
134 | } |
||
135 | } |
||
136 | |||
137 | |||
138 | |||
139 | static void |
||
140 | process_tree(proto_tree *protocol_tree, ph_stats_t* ps) |
||
141 | { |
||
142 | proto_node *ptree_node; |
||
143 | |||
144 | /* |
||
145 | * If our first item is a comment, skip over it. This keeps |
||
146 | * us from having a top-level "Packet comments" item that |
||
147 | * steals items from "Frame". |
||
148 | */ |
||
149 | ptree_node = ((proto_node *)protocol_tree)->first_child; |
||
150 | if (ptree_node && ptree_node->finfo->hfinfo->id == pc_proto_id) { |
||
151 | ptree_node = ptree_node->next; |
||
152 | } |
||
153 | |||
154 | if (!ptree_node) { |
||
155 | return; |
||
156 | } |
||
157 | |||
158 | process_node(ptree_node, ps->stats_tree, ps); |
||
159 | } |
||
160 | |||
161 | static gboolean |
||
162 | process_record(capture_file *cf, frame_data *frame, column_info *cinfo, ph_stats_t* ps) |
||
163 | { |
||
164 | epan_dissect_t edt; |
||
165 | struct wtap_pkthdr phdr; |
||
166 | Buffer buf; |
||
167 | double cur_time; |
||
168 | |||
169 | wtap_phdr_init(&phdr); |
||
170 | |||
171 | /* Load the record from the capture file */ |
||
172 | ws_buffer_init(&buf, 1500); |
||
173 | if (!cf_read_record_r(cf, frame, &phdr, &buf)) |
||
174 | return FALSE; /* failure */ |
||
175 | |||
176 | /* Dissect the record tree not visible */ |
||
177 | epan_dissect_init(&edt, cf->epan, TRUE, FALSE); |
||
178 | /* Don't fake protocols. We need them for the protocol hierarchy */ |
||
179 | epan_dissect_fake_protocols(&edt, FALSE); |
||
180 | epan_dissect_run(&edt, cf->cd_t, &phdr, frame_tvbuff_new_buffer(frame, &buf), frame, cinfo); |
||
181 | |||
182 | /* Get stats from this protocol tree */ |
||
183 | process_tree(edt.tree, ps); |
||
184 | |||
185 | if (frame->flags.has_ts) { |
||
186 | /* Update times */ |
||
187 | cur_time = nstime_to_sec(&frame->abs_ts); |
||
188 | if (cur_time < ps->first_time) |
||
189 | ps->first_time = cur_time; |
||
190 | if (cur_time > ps->last_time) |
||
191 | ps->last_time = cur_time; |
||
192 | } |
||
193 | |||
194 | /* Free our memory. */ |
||
195 | epan_dissect_cleanup(&edt); |
||
196 | wtap_phdr_cleanup(&phdr); |
||
197 | ws_buffer_free(&buf); |
||
198 | |||
199 | return TRUE; /* success */ |
||
200 | } |
||
201 | |||
202 | ph_stats_t* |
||
203 | ph_stats_new(capture_file *cf) |
||
204 | { |
||
205 | ph_stats_t *ps; |
||
206 | guint32 framenum; |
||
207 | frame_data *frame; |
||
208 | guint tot_packets, tot_bytes; |
||
209 | progdlg_t *progbar = NULL; |
||
210 | gboolean stop_flag; |
||
211 | int count; |
||
212 | float progbar_val; |
||
213 | GTimeVal start_time; |
||
214 | gchar status_str[100]; |
||
215 | int progbar_nextstep; |
||
216 | int progbar_quantum; |
||
217 | |||
218 | if (!cf) return NULL; |
||
219 | |||
220 | pc_proto_id = proto_get_id_by_filter_name("pkt_comment"); |
||
221 | |||
222 | /* Initialize the data */ |
||
223 | ps = g_new(ph_stats_t, 1); |
||
224 | ps->tot_packets = 0; |
||
225 | ps->tot_bytes = 0; |
||
226 | ps->stats_tree = g_node_new(NULL); |
||
227 | ps->first_time = 0.0; |
||
228 | ps->last_time = 0.0; |
||
229 | |||
230 | /* Update the progress bar when it gets to this value. */ |
||
231 | progbar_nextstep = 0; |
||
232 | /* When we reach the value that triggers a progress bar update, |
||
233 | bump that value by this amount. */ |
||
234 | progbar_quantum = cf->count/N_PROGBAR_UPDATES; |
||
235 | /* Count of packets at which we've looked. */ |
||
236 | count = 0; |
||
237 | /* Progress so far. */ |
||
238 | progbar_val = 0.0f; |
||
239 | |||
240 | stop_flag = FALSE; |
||
241 | g_get_current_time(&start_time); |
||
242 | |||
243 | tot_packets = 0; |
||
244 | tot_bytes = 0; |
||
245 | |||
246 | for (framenum = 1; framenum <= cf->count; framenum++) { |
||
247 | frame = frame_data_sequence_find(cf->frames, framenum); |
||
248 | |||
249 | /* Create the progress bar if necessary. |
||
250 | We check on every iteration of the loop, so that |
||
251 | it takes no longer than the standard time to create |
||
252 | it (otherwise, for a large file, we might take |
||
253 | considerably longer than that standard time in order |
||
254 | to get to the next progress bar step). */ |
||
255 | if (progbar == NULL) |
||
256 | progbar = delayed_create_progress_dlg( |
||
257 | cf->window, "Computing", |
||
258 | "protocol hierarchy statistics", |
||
259 | TRUE, &stop_flag, &start_time, progbar_val); |
||
260 | |||
261 | /* Update the progress bar, but do it only N_PROGBAR_UPDATES |
||
262 | times; when we update it, we have to run the GTK+ main |
||
263 | loop to get it to repaint what's pending, and doing so |
||
264 | may involve an "ioctl()" to see if there's any pending |
||
265 | input from an X server, and doing that for every packet |
||
266 | can be costly, especially on a big file. */ |
||
267 | if (count >= progbar_nextstep) { |
||
268 | /* let's not divide by zero. I should never be started |
||
269 | * with count == 0, so let's assert that |
||
270 | */ |
||
271 | g_assert(cf->count > 0); |
||
272 | |||
273 | progbar_val = (gfloat) count / cf->count; |
||
274 | |||
275 | if (progbar != NULL) { |
||
276 | g_snprintf(status_str, sizeof(status_str), |
||
277 | "%4u of %u frames", count, cf->count); |
||
278 | update_progress_dlg(progbar, progbar_val, status_str); |
||
279 | } |
||
280 | |||
281 | progbar_nextstep += progbar_quantum; |
||
282 | } |
||
283 | |||
284 | if (stop_flag) { |
||
285 | /* Well, the user decided to abort the statistics. |
||
286 | computation process Just stop. */ |
||
287 | break; |
||
288 | } |
||
289 | |||
290 | /* Skip frames that are hidden due to the display filter. |
||
291 | XXX - should the progress bar count only packets that |
||
292 | passed the display filter? If so, it should |
||
293 | probably do so for other loops (see "file.c") that |
||
294 | look only at those packets. */ |
||
295 | if (frame->flags.passed_dfilter) { |
||
296 | |||
297 | if (frame->flags.has_ts) { |
||
298 | if (tot_packets == 0) { |
||
299 | double cur_time = nstime_to_sec(&frame->abs_ts); |
||
300 | ps->first_time = cur_time; |
||
301 | ps->last_time = cur_time; |
||
302 | } |
||
303 | } |
||
304 | |||
305 | /* we don't care about colinfo */ |
||
306 | if (!process_record(cf, frame, NULL, ps)) { |
||
307 | /* |
||
308 | * Give up, and set "stop_flag" so we |
||
309 | * just abort rather than popping up |
||
310 | * the statistics window. |
||
311 | */ |
||
312 | stop_flag = TRUE; |
||
313 | break; |
||
314 | } |
||
315 | |||
316 | tot_packets++; |
||
317 | tot_bytes += frame->pkt_len; |
||
318 | } |
||
319 | |||
320 | count++; |
||
321 | } |
||
322 | |||
323 | /* We're done calculating the statistics; destroy the progress bar |
||
324 | if it was created. */ |
||
325 | if (progbar != NULL) |
||
326 | destroy_progress_dlg(progbar); |
||
327 | |||
328 | if (stop_flag) { |
||
329 | /* |
||
330 | * We quit in the middle; throw away the statistics |
||
331 | * and return NULL, so our caller doesn't pop up a |
||
332 | * window with the incomplete statistics. |
||
333 | */ |
||
334 | ph_stats_free(ps); |
||
335 | return NULL; |
||
336 | } |
||
337 | |||
338 | ps->tot_packets = tot_packets; |
||
339 | ps->tot_bytes = tot_bytes; |
||
340 | |||
341 | return ps; |
||
342 | } |
||
343 | |||
344 | static gboolean |
||
345 | stat_node_free(GNode *node, gpointer data _U_) |
||
346 | { |
||
347 | ph_stats_node_t *stats = (ph_stats_node_t *)node->data; |
||
348 | |||
349 | if (stats) { |
||
350 | g_free(stats); |
||
351 | } |
||
352 | return FALSE; |
||
353 | } |
||
354 | |||
355 | void |
||
356 | ph_stats_free(ph_stats_t *ps) |
||
357 | { |
||
358 | |||
359 | if (ps->stats_tree) { |
||
360 | g_node_traverse(ps->stats_tree, G_IN_ORDER, |
||
361 | G_TRAVERSE_ALL, -1, |
||
362 | stat_node_free, NULL); |
||
363 | g_node_destroy(ps->stats_tree); |
||
364 | } |
||
365 | |||
366 | g_free(ps); |
||
367 | } |
||
368 | |||
369 | /* |
||
370 | * Editor modelines - http://www.wireshark.org/tools/modelines.html |
||
371 | * |
||
372 | * Local variables: |
||
373 | * c-basic-offset: 8 |
||
374 | * tab-width: 8 |
||
375 | * indent-tabs-mode: t |
||
376 | * End: |
||
377 | * |
||
378 | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
||
379 | * :indentSize=8:tabSize=8:noTabs=false: |
||
380 | */ |