From 60d0faf9c9bee0defc42d7ad633ae46a8008bd6c Mon Sep 17 00:00:00 2001 From: Guy Harris Date: Mon, 30 Jun 2014 14:08:24 -0700 Subject: Move proto_hier_stats.[ch] to libui. Change-Id: Ib7c0617d88bf92cad0ac877176001d29960f1cd8 Reviewed-on: https://code.wireshark.org/review/2725 Reviewed-by: Guy Harris --- ui/proto_hier_stats.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 ui/proto_hier_stats.c (limited to 'ui/proto_hier_stats.c') diff --git a/ui/proto_hier_stats.c b/ui/proto_hier_stats.c new file mode 100644 index 0000000000..fd4adbd333 --- /dev/null +++ b/ui/proto_hier_stats.c @@ -0,0 +1,339 @@ +/* proto_hier_stats.c + * Routines for calculating statistics based on protocol. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include + +#include "globals.h" +#include "frame_tvbuff.h" +#include "ui/proto_hier_stats.h" +#include "ui/progress_dlg.h" +#include +#include + +#include +#include +#include + +/* Update the progress bar this many times when scanning the packet list. */ +#define N_PROGBAR_UPDATES 100 + +#define STAT_NODE_STATS(n) ((ph_stats_node_t*)(n)->data) +#define STAT_NODE_HFINFO(n) (STAT_NODE_STATS(n)->hfinfo) + + +static GNode* +find_stat_node(GNode *parent_stat_node, header_field_info *needle_hfinfo) +{ + GNode *needle_stat_node; + header_field_info *hfinfo; + ph_stats_node_t *stats; + + needle_stat_node = g_node_first_child(parent_stat_node); + + while (needle_stat_node) { + hfinfo = STAT_NODE_HFINFO(needle_stat_node); + if (hfinfo && hfinfo->id == needle_hfinfo->id) { + return needle_stat_node; + } + needle_stat_node = g_node_next_sibling(needle_stat_node); + } + + /* None found. Create one. */ + stats = g_new(ph_stats_node_t, 1); + + /* Intialize counters */ + stats->hfinfo = needle_hfinfo; + stats->num_pkts_total = 0; + stats->num_pkts_last = 0; + stats->num_bytes_total = 0; + stats->num_bytes_last = 0; + + needle_stat_node = g_node_new(stats); + g_node_append(parent_stat_node, needle_stat_node); + return needle_stat_node; +} + + +static void +process_node(proto_node *ptree_node, GNode *parent_stat_node, ph_stats_t *ps, guint pkt_len) +{ + field_info *finfo; + ph_stats_node_t *stats; + proto_node *proto_sibling_node; + GNode *stat_node; + + finfo = PNODE_FINFO(ptree_node); + /* We don't fake protocol nodes we expect them to have a field_info. + * Dissection with faked proto tree? */ + g_assert(finfo); + + /* If the field info isn't related to a protocol but to a field, + * don't count them, as they don't belong to any protocol. + * (happens e.g. for toplevel tree item of desegmentation "[Reassembled TCP Segments]") */ + if (finfo->hfinfo->parent != -1) { + /* Skip this element, use parent status node */ + stat_node = parent_stat_node; + stats = STAT_NODE_STATS(stat_node); + } else { + stat_node = find_stat_node(parent_stat_node, finfo->hfinfo); + + stats = STAT_NODE_STATS(stat_node); + stats->num_pkts_total++; + stats->num_bytes_total += pkt_len; + } + + proto_sibling_node = ptree_node->next; + + if (proto_sibling_node) { + /* If the name does not exist for this proto_sibling_node, then it is + * not a normal protocol in the top-level tree. It was instead + * added as a normal tree such as IPv6's Hop-by-hop Option Header and + * should be skipped when creating the protocol hierarchy display. */ + if(strlen(PNODE_FINFO(proto_sibling_node)->hfinfo->name) == 0 && ptree_node->next) + proto_sibling_node = proto_sibling_node->next; + + process_node(proto_sibling_node, stat_node, ps, pkt_len); + } else { + stats->num_pkts_last++; + stats->num_bytes_last += pkt_len; + } +} + + + +static void +process_tree(proto_tree *protocol_tree, ph_stats_t* ps, guint pkt_len) +{ + proto_node *ptree_node; + + ptree_node = ((proto_node *)protocol_tree)->first_child; + if (!ptree_node) { + return; + } + + process_node(ptree_node, ps->stats_tree, ps, pkt_len); +} + +static gboolean +process_record(frame_data *frame, column_info *cinfo, ph_stats_t* ps) +{ + epan_dissect_t edt; + struct wtap_pkthdr phdr; + Buffer buf; + double cur_time; + + memset(&phdr, 0, sizeof(struct wtap_pkthdr)); + + /* Load the record from the capture file */ + buffer_init(&buf, 1500); + if (!cf_read_record_r(&cfile, frame, &phdr, &buf)) + return FALSE; /* failure */ + + /* Dissect the record tree not visible */ + epan_dissect_init(&edt, cfile.epan, TRUE, FALSE); + /* Don't fake protocols. We need them for the protocol hierarchy */ + epan_dissect_fake_protocols(&edt, FALSE); + epan_dissect_run(&edt, cfile.cd_t, &phdr, frame_tvbuff_new_buffer(frame, &buf), frame, cinfo); + + /* Get stats from this protocol tree */ + process_tree(edt.tree, ps, frame->pkt_len); + + if (frame->flags.has_ts) { + /* Update times */ + cur_time = nstime_to_sec(&frame->abs_ts); + if (cur_time < ps->first_time) + ps->first_time = cur_time; + if (cur_time > ps->last_time) + ps->last_time = cur_time; + } + + /* Free our memory. */ + epan_dissect_cleanup(&edt); + buffer_free(&buf); + + return TRUE; /* success */ +} + +ph_stats_t* +ph_stats_new(void) +{ + ph_stats_t *ps; + guint32 framenum; + frame_data *frame; + guint tot_packets, tot_bytes; + progdlg_t *progbar = NULL; + gboolean stop_flag; + int count; + float progbar_val; + GTimeVal start_time; + gchar status_str[100]; + int progbar_nextstep; + int progbar_quantum; + + /* Initialize the data */ + ps = g_new(ph_stats_t, 1); + ps->tot_packets = 0; + ps->tot_bytes = 0; + ps->stats_tree = g_node_new(NULL); + ps->first_time = 0.0; + ps->last_time = 0.0; + + /* Update the progress bar when it gets to this value. */ + progbar_nextstep = 0; + /* When we reach the value that triggers a progress bar update, + bump that value by this amount. */ + progbar_quantum = cfile.count/N_PROGBAR_UPDATES; + /* Count of packets at which we've looked. */ + count = 0; + /* Progress so far. */ + progbar_val = 0.0f; + + stop_flag = FALSE; + g_get_current_time(&start_time); + + tot_packets = 0; + tot_bytes = 0; + + for (framenum = 1; framenum <= cfile.count; framenum++) { + frame = frame_data_sequence_find(cfile.frames, framenum); + + /* Create the progress bar if necessary. + We check on every iteration of the loop, so that + it takes no longer than the standard time to create + it (otherwise, for a large file, we might take + considerably longer than that standard time in order + to get to the next progress bar step). */ + if (progbar == NULL) + progbar = delayed_create_progress_dlg( + cfile.window, "Computing", + "protocol hierarchy statistics", + TRUE, &stop_flag, &start_time, progbar_val); + + /* Update the progress bar, but do it only N_PROGBAR_UPDATES + times; when we update it, we have to run the GTK+ main + loop to get it to repaint what's pending, and doing so + may involve an "ioctl()" to see if there's any pending + input from an X server, and doing that for every packet + can be costly, especially on a big file. */ + if (count >= progbar_nextstep) { + /* let's not divide by zero. I should never be started + * with count == 0, so let's assert that + */ + g_assert(cfile.count > 0); + + progbar_val = (gfloat) count / cfile.count; + + if (progbar != NULL) { + g_snprintf(status_str, sizeof(status_str), + "%4u of %u frames", count, cfile.count); + update_progress_dlg(progbar, progbar_val, status_str); + } + + progbar_nextstep += progbar_quantum; + } + + if (stop_flag) { + /* Well, the user decided to abort the statistics. + computation process Just stop. */ + break; + } + + /* Skip frames that are hidden due to the display filter. + XXX - should the progress bar count only packets that + passed the display filter? If so, it should + probably do so for other loops (see "file.c") that + look only at those packets. */ + if (frame->flags.passed_dfilter) { + + if (frame->flags.has_ts) { + if (tot_packets == 0) { + double cur_time = nstime_to_sec(&frame->abs_ts); + ps->first_time = cur_time; + ps->last_time = cur_time; + } + } + + /* we don't care about colinfo */ + if (!process_record(frame, NULL, ps)) { + /* + * Give up, and set "stop_flag" so we + * just abort rather than popping up + * the statistics window. + */ + stop_flag = TRUE; + break; + } + + tot_packets++; + tot_bytes += frame->pkt_len; + } + + count++; + } + + /* We're done calculating the statistics; destroy the progress bar + if it was created. */ + if (progbar != NULL) + destroy_progress_dlg(progbar); + + if (stop_flag) { + /* + * We quit in the middle; throw away the statistics + * and return NULL, so our caller doesn't pop up a + * window with the incomplete statistics. + */ + ph_stats_free(ps); + return NULL; + } + + ps->tot_packets = tot_packets; + ps->tot_bytes = tot_bytes; + + return ps; +} + +static gboolean +stat_node_free(GNode *node, gpointer data _U_) +{ + ph_stats_node_t *stats = (ph_stats_node_t *)node->data; + + if (stats) { + g_free(stats); + } + return FALSE; +} + +void +ph_stats_free(ph_stats_t *ps) +{ + + if (ps->stats_tree) { + g_node_traverse(ps->stats_tree, G_IN_ORDER, + G_TRAVERSE_ALL, -1, + stat_node_free, NULL); + g_node_destroy(ps->stats_tree); + } + + g_free(ps); +} -- cgit v1.2.1