summaryrefslogtreecommitdiff
path: root/ui/gtk/io_stat.c
diff options
context:
space:
mode:
authorGerald Combs <gerald@wireshark.org>2014-02-11 16:07:10 -0800
committerGerald Combs <gerald@wireshark.org>2014-04-07 20:56:42 +0000
commita5cb72fe9eadfaf8cb0aefccb106a7eaad9266c9 (patch)
tree34a1965805b7373553d6057e9981de3ae6a01460 /ui/gtk/io_stat.c
parentcc3c05ed5f9e2d3eb8d72b3acc66bbacd50a26e7 (diff)
downloadwireshark-a5cb72fe9eadfaf8cb0aefccb106a7eaad9266c9.tar.gz
Add a Qt I/O Graph dialog.
For each graph you can set: - Its visibility - A name - A display filter - Color, from a fixed list - Plot style: Line, Impulse, Bar, Stacked Bar, Dot, Square, Diamond - Basic Y Axes (packets/s, bytes/s, bits/s) - Computed Y Axes (SUM, MIN, AVG, MAX) - Smoothing You can pan and zoom using the mouse and keyboard. Clicking on a graph selects the last packet for that interval. If all graphs have the same Y axis a single label is shown, otherwise a legend is shown. The time scale (X axis) can be toggled between relative seconds and the time of day. Graphs can be saved as PDF, PNG, BMP, and JPEG. Settings are "sticky" via the io_graphs UAT. To do: - Minimize graph drawing delays. - Figure out why smoothing differs from GTK+ - Everything else at the top of io_graph_dialog.cpp - Fix empty resets. A fair amount of code was copied from TCPStreamDialog. We might want to subclass QCustomPlot and place the shared code there. Move common syntax checking to SyntaxLineEdit. Move some common code from ui/gtk/io_stat.c to ui/io_graph_item.[ch] and use it in both GTK+ and Qt. Make the io_graph_item_t array allocation in io_stat.c static. The behavior should be identical and this gives us additional compile-time checks. Change-Id: I9a3d544469b7048f0761fdbf7bcf20f44ae76577 Reviewed-on: https://code.wireshark.org/review/435 Reviewed-by: Gerald Combs <gerald@wireshark.org> Tested-by: Gerald Combs <gerald@wireshark.org>
Diffstat (limited to 'ui/gtk/io_stat.c')
-rw-r--r--ui/gtk/io_stat.c363
1 files changed, 39 insertions, 324 deletions
diff --git a/ui/gtk/io_stat.c b/ui/gtk/io_stat.c
index e10d58f31d..053ccc8924 100644
--- a/ui/gtk/io_stat.c
+++ b/ui/gtk/io_stat.c
@@ -35,8 +35,9 @@
#include <epan/tap.h>
#include <epan/strutil.h>
-#include "../stat_menu.h"
+#include "../../stat_menu.h"
#include "ui/alert_box.h"
+#include "ui/io_graph_item.h"
#include "ui/simple_dialog.h"
#include "ui/gtk/gtkglobals.h"
@@ -95,6 +96,11 @@ static const char *plot_style_name[MAX_PLOT_STYLES] = {
"Dot",
};
+/*
+ * XXX - "Count types" and "calc types" are combined in io_graph_item_unit_t
+ * in io_graph_item_t. The Qt port treats these as a single Y Axis "value unit"
+ * type. Should we do the same here?
+ */
#define DEFAULT_COUNT_TYPE 0
#define COUNT_TYPE_FRAMES 0
#define COUNT_TYPE_BYTES 1
@@ -130,36 +136,20 @@ static const char *calc_type_names[MAX_CALC_TYPES] = {
"AVG(*)",
"LOAD(*)"};
+#define CALC_TYPE_TO_ITEM_UNIT(ct) ((io_graph_item_unit_t)(ct + IOG_ITEM_UNIT_CALC_SUM))
+/* Unused? */
+#if 0
typedef struct _io_stat_calc_type_t {
struct _io_stat_graph_t *gio;
int calc_type;
} io_stat_calc_type_t;
+#endif
#define NUM_IO_ITEMS 100000
-typedef struct _io_item_t {
- guint32 frames; /* always calculated, will hold number of frames*/
- guint64 bytes; /* always calculated, will hold number of bytes*/
- guint64 fields;
- gint64 int_max;
- gint64 int_min;
- gint64 int_tot;
- gfloat float_max;
- gfloat float_min;
- gfloat float_tot;
- gdouble double_max;
- gdouble double_min;
- gdouble double_tot;
- nstime_t time_max;
- nstime_t time_min;
- nstime_t time_tot;
- guint32 first_frame_in_invl;
- guint32 last_frame_in_invl;
-} io_item_t;
-
typedef struct _io_stat_graph_t {
struct _io_stat_t *io;
- gpointer items[NUM_IO_ITEMS];
+ io_graph_item_t items[NUM_IO_ITEMS];
int plot_style;
gboolean display;
GtkWidget *display_button;
@@ -226,32 +216,11 @@ io_stat_set_title(io_stat_t *io)
static void
io_stat_reset(io_stat_t *io)
{
- int i, j;
+ int i;
io->needs_redraw = TRUE;
for (i=0; i<MAX_GRAPHS; i++) {
- for (j=0; j<NUM_IO_ITEMS; j++) {
- io_item_t *ioi;
- ioi = (io_item_t *)io->graphs[i].items[j];
-
- ioi->frames = 0;
- ioi->bytes = 0;
- ioi->fields = 0;
- ioi->int_max = 0;
- ioi->int_min = 0;
- ioi->int_tot = 0;
- ioi->float_max = 0;
- ioi->float_min = 0;
- ioi->float_tot = 0;
- ioi->double_max = 0;
- ioi->double_min = 0;
- ioi->double_tot = 0;
- nstime_set_zero(&ioi->time_max);
- nstime_set_zero(&ioi->time_min);
- nstime_set_zero(&ioi->time_tot);
- ioi->first_frame_in_invl = 0;
- ioi->last_frame_in_invl = 0;
- }
+ reset_io_graph_items((io_graph_item_t *)io->graphs[i].items, NUM_IO_ITEMS);
}
io->last_interval = 0xffffffff;
io->max_interval = 0;
@@ -274,8 +243,7 @@ tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *
{
io_stat_graph_t *graph = (io_stat_graph_t *)g;
io_stat_t *io;
- io_item_t *it;
- nstime_t time_delta;
+ epan_dissect_t *adv_edt = NULL;
int idx;
/* we sometimes get called when the graph is disabled.
@@ -287,19 +255,8 @@ tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *
io = graph->io; /* Point up to the parent io_stat_t struct */
io->needs_redraw = TRUE;
- /*
- * Find in which interval this is supposed to go and store the interval index as idx
- */
- time_delta = pinfo->rel_ts;
- if (time_delta.nsecs<0) {
- time_delta.secs--;
- time_delta.nsecs += 1000000000;
- }
- if (time_delta.secs<0) {
- return FALSE;
- }
- idx = (int) ((time_delta.secs*1000 + time_delta.nsecs/1000000) / io->interval);
-
+ idx = get_io_graph_index(pinfo, io->interval);
+
/* some sanity checks */
if ((idx < 0) || (idx >= NUM_IO_ITEMS)) {
io->num_items = NUM_IO_ITEMS-1;
@@ -316,184 +273,14 @@ tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *
nstime_delta(&io->start_time, &pinfo->fd->abs_ts, &pinfo->rel_ts);
}
- /* Point to the appropriate io_item_t struct */
- it = (io_item_t *)graph->items[idx];
-
- /* Set the first and last frame num in current interval matching the target field+filter */
- if (it->first_frame_in_invl == 0) {
- it->first_frame_in_invl = pinfo->fd->num;
- }
- it->last_frame_in_invl = pinfo->fd->num;
-
- /*
- * For ADVANCED mode we need to keep track of some more stuff than just frame and byte counts */
+ /* For ADVANCED mode we need to keep track of some more stuff than just frame and byte counts */
if (io->count_type == COUNT_TYPE_ADVANCED) {
- GPtrArray *gp;
- guint i;
-
- gp = proto_get_finfo_ptr_array(edt->tree, graph->hf_index);
- if (!gp) {
- return FALSE;
- }
-
- /* Update the appropriate counters. If fields == 0, this is the first seen
- * value so set any min/max values accordingly. */
- for (i=0; i<gp->len; i++) {
- int new_int;
- gint64 new_int64;
- float new_float;
- double new_double;
- nstime_t *new_time;
-
- switch (proto_registrar_get_ftype(graph->hf_index)) {
- case FT_UINT8:
- case FT_UINT16:
- case FT_UINT24:
- case FT_UINT32:
- new_int = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
-
- if ((new_int > it->int_max) || (it->fields == 0)) {
- it->int_max = new_int;
- }
- if ((new_int < it->int_min) || (it->fields == 0)) {
- it->int_min = new_int;
- }
- it->int_tot += new_int;
- it->fields++;
- break;
- case FT_INT8:
- case FT_INT16:
- case FT_INT24:
- case FT_INT32:
- new_int = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
- if ((new_int > it->int_max) || (it->fields == 0)) {
- it->int_max = new_int;
- }
- if ((new_int < it->int_min) || (it->fields == 0)) {
- it->int_min = new_int;
- }
- it->int_tot += new_int;
- it->fields++;
- break;
- case FT_UINT64:
- case FT_INT64:
- new_int64 = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
- if ((new_int64 > it->int_max) || (it->fields == 0)) {
- it->int_max = new_int64;
- }
- if ((new_int64 < it->int_min) || (it->fields == 0)) {
- it->int_min = new_int64;
- }
- it->int_tot += new_int64;
- it->fields++;
- break;
- case FT_FLOAT:
- new_float = (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
- if ((new_float > it->float_max) || (it->fields == 0)) {
- it->float_max = new_float;
- }
- if ((new_float < it->float_min) || (it->fields == 0)) {
- it->float_min = new_float;
- }
- it->float_tot += new_float;
- it->fields++;
- break;
- case FT_DOUBLE:
- new_double = fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
- if ((new_double > it->double_max) || (it->fields == 0)) {
- it->double_max = new_double;
- }
- if ((new_double < it->double_min) || (it->fields == 0)) {
- it->double_min = new_double;
- }
- it->double_tot += new_double;
- it->fields++;
- break;
- case FT_RELATIVE_TIME:
- new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
-
- switch (graph->calc_type) {
- guint64 t, pt; /* time in us */
- int j;
- case CALC_TYPE_LOAD:
- /*
- * Add the time this call spanned each interval according to its contribution
- * to that interval.
- */
- t = new_time->secs;
- t = t * 1000000 + new_time->nsecs / 1000;
- j = idx;
- /*
- * Handle current interval */
- pt = pinfo->rel_ts.secs * 1000000 + pinfo->rel_ts.nsecs / 1000;
- pt = pt % (io->interval * 1000);
- if (pt > t) {
- pt = t;
- }
- while (t) {
- io_item_t *item;
-
- item = (io_item_t*)graph->items[j];
- item->time_tot.nsecs += (int) (pt * 1000);
- if (item->time_tot.nsecs > 1000000000) {
- item->time_tot.secs++;
- item->time_tot.nsecs -= 1000000000;
- }
-
- if (j == 0) {
- break;
- }
- j--;
- t -= pt;
- if (t > (guint64) io->interval * 1000) {
- pt = (guint64) io->interval * 1000;
- } else {
- pt = t;
- }
- }
- break;
- default:
- if ( (new_time->secs > it->time_max.secs)
- || ( (new_time->secs == it->time_max.secs)
- && (new_time->nsecs > it->time_max.nsecs))
- || (it->fields == 0)) {
- it->time_max = *new_time;
- }
- if ( (new_time->secs<it->time_min.secs)
- || ( (new_time->secs == it->time_min.secs)
- && (new_time->nsecs < it->time_min.nsecs))
- || (it->fields == 0)) {
- it->time_min = *new_time;
- }
- nstime_add(&it->time_tot, new_time);
- it->fields++;
- }
- break;
- default:
- if ((graph->calc_type == CALC_TYPE_COUNT_FRAMES) ||
- (graph->calc_type == CALC_TYPE_COUNT_FIELDS)) {
- /*
- * It's not an integeresque type, but
- * all we want to do is count it, so
- * that's all right.
- */
- it->fields++;
- }
- else {
- /*
- * "Can't happen"; see the "check that the
- * type is compatible" check in
- * filter_callback().
- */
- g_assert_not_reached();
- }
- break;
- }
- }
+ adv_edt = edt;
}
- it->frames++;
- it->bytes += pinfo->fd->pkt_len;
+ if (!update_io_graph_item((io_graph_item_t*) graph->items, idx, pinfo, adv_edt, graph->hf_index, CALC_TYPE_TO_ITEM_UNIT(graph->calc_type), io->interval)) {
+ return FALSE;
+ }
return TRUE;
}
@@ -503,13 +290,13 @@ get_it_value(io_stat_t *io, int graph, int idx)
{
guint64 value = 0; /* FIXME: loss of precision, visible on the graph for small values */
int adv_type;
- io_item_t *it;
+ io_graph_item_t *it;
guint32 interval;
g_assert(graph < MAX_GRAPHS);
g_assert(idx < NUM_IO_ITEMS);
- it = (io_item_t *)io->graphs[graph].items[idx];
+ it = &io->graphs[graph].items[idx];
switch (io->count_type) {
case COUNT_TYPE_FRAMES:
@@ -533,7 +320,6 @@ get_it_value(io_stat_t *io, int graph, int idx)
break;
}
-
adv_type = proto_registrar_get_ftype(io->graphs[graph].hf_index);
switch (adv_type) {
case FT_UINT8:
@@ -1374,7 +1160,7 @@ static void
iostat_init(const char *opt_arg _U_, void* userdata _U_)
{
io_stat_t *io;
- int i = 0, j = 0;
+ int i = 0;
static GdkColor col[MAX_GRAPHS] = {
{0, 0x0000, 0x0000, 0x0000}, /* Black */
{0, 0xffff, 0x0000, 0x0000}, /* Red */
@@ -1442,9 +1228,6 @@ iostat_init(const char *opt_arg _U_, void* userdata _U_)
io->graphs[i].filter_bt = NULL;
- for (j=0; j<NUM_IO_ITEMS; j++) {
- io->graphs[i].items[j] = g_new(io_item_t,1);
- }
io->graphs[i].follow_smooth = GRAPH_FOLLOWFILTER;
}
io_stat_reset(io);
@@ -1477,7 +1260,7 @@ static void
draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
{
io_stat_t *io = (io_stat_t *)user_data;
- int i,j;
+ int i;
GtkWidget *save_bt = (GtkWidget *)g_object_get_data(G_OBJECT(io->window), "save_bt");
surface_info_t *surface_info = (surface_info_t *)g_object_get_data(G_OBJECT(save_bt), "surface-info");
@@ -1492,10 +1275,6 @@ draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
g_free(io->graphs[i].args);
io->graphs[i].args = NULL;
- for (j=0; j<NUM_IO_ITEMS; j++) {
- g_free(io->graphs[i].items[j]);
- io->graphs[i].items[j] = NULL;
- }
}
}
g_free(io);
@@ -1508,7 +1287,7 @@ pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer g)
{
io_stat_t *io = (io_stat_t *)g;
io_stat_graph_t *graph;
- io_item_t *it;
+ io_graph_item_t *it;
guint32 draw_width, interval, last_interval;
guint32 frame_num = 0;
int i;
@@ -1555,7 +1334,7 @@ pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer g)
for (i=0; i<MAX_GRAPHS; i++) {
graph = &io->graphs[i];
if (graph->display) {
- it = (io_item_t *)graph->items[interval];
+ it = &graph->items[interval];
if (event->button == 1) {
if ((frame_num == 0) || (it->first_frame_in_invl < frame_num))
frame_num = it->first_frame_in_invl;
@@ -2023,9 +1802,8 @@ filter_callback(GtkWidget *widget, gpointer user_data)
{
io_stat_graph_t *gio = (io_stat_graph_t *)user_data;
const char *filter;
- const char *field = NULL;
- header_field_info *hfi;
dfilter_t *dfilter;
+ const char *field_name = NULL;
/* this graph is not active, just update display and redraw */
if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))) {
@@ -2036,85 +1814,22 @@ filter_callback(GtkWidget *widget, gpointer user_data)
/* first check if the field string is valid */
if (gio->io->count_type == COUNT_TYPE_ADVANCED) {
- field = gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
+ GString *err_str;
+ field_name = gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
- /* warn and bail out if there was no field specified */
- if ((field == NULL) || (field[0] == 0)) {
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
- disable_graph(gio);
- io_stat_redraw(gio->io);
- return;
- }
- /* warn and bail out if the field could not be found */
- hfi = proto_registrar_get_byname(field);
- if (hfi == NULL) {
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
+ err_str = check_field_unit(field_name, &gio->hf_index, CALC_TYPE_TO_ITEM_UNIT(gio->calc_type));
+
+ if (err_str) {
+ /* warn and bail out */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str->str);
+ g_string_free(err_str, TRUE);
disable_graph(gio);
io_stat_redraw(gio->io);
return;
}
- gio->hf_index = hfi->id;
- /* check that the type is compatible */
- switch (hfi->type) {
- case FT_UINT8:
- case FT_UINT16:
- case FT_UINT24:
- case FT_UINT32:
- case FT_UINT64:
- case FT_INT8:
- case FT_INT16:
- case FT_INT24:
- case FT_INT32:
- case FT_INT64:
- case FT_FLOAT:
- case FT_DOUBLE:
- /* these values support all calculations except LOAD */
- switch (gio->calc_type) {
- case CALC_TYPE_LOAD:
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "LOAD(*) is only supported for relative-time fields.");
- disable_graph(gio);
- io_stat_redraw(gio->io);
- return;
- }
- /* these types support all calculations */
- break;
- case FT_RELATIVE_TIME:
- /* this type only supports COUNT, MAX, MIN, AVG */
- switch (gio->calc_type) {
- case CALC_TYPE_SUM:
- case CALC_TYPE_COUNT_FRAMES:
- case CALC_TYPE_COUNT_FIELDS:
- case CALC_TYPE_MAX:
- case CALC_TYPE_MIN:
- case CALC_TYPE_AVG:
- case CALC_TYPE_LOAD:
- break;
- default:
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "%s is a relative-time field, so %s calculations are not supported on it.",
- field,
- calc_type_names[gio->calc_type]);
- disable_graph(gio);
- io_stat_redraw(gio->io);
- return;
- }
- break;
- default:
- if ((gio->calc_type != CALC_TYPE_COUNT_FRAMES) &&
- (gio->calc_type != CALC_TYPE_COUNT_FIELDS)) {
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "%s doesn't have integral or float values, so %s calculations are not supported on it.",
- field,
- calc_type_names[gio->calc_type]);
- disable_graph(gio);
- io_stat_redraw(gio->io);
- return;
- }
- break;
- }
}
+
/* first check if the filter string is valid. */
filter = gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
if (!dfilter_compile(filter, &dfilter)) {
@@ -2134,7 +1849,7 @@ filter_callback(GtkWidget *widget, gpointer user_data)
remove_tap_listener(gio);
io_stat_reset(gio->io);
- enable_graph(gio, filter, field);
+ enable_graph(gio, filter, field_name);
cf_retap_packets(&cfile);
gdk_window_raise(gtk_widget_get_window(gio->io->window));
io_stat_redraw(gio->io);