2 * io_stat 2002 Ronnie Sahlberg
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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.
30 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
41 #include <epan/epan_dissect.h>
42 #include <epan/packet_info.h>
43 #include <epan/stat_cmd_args.h>
45 #include <epan/strutil.h>
47 #include "../stat_menu.h"
48 #include "../alert_box.h"
49 #include "../simple_dialog.h"
51 #include "gtk/gtkglobals.h"
52 #include "gtk/gui_utils.h"
53 #include "gtk/gui_stat_menu.h"
54 #include "gtk/stock_icons.h"
55 #include "gtk/dlg_utils.h"
56 #include "gtk/filter_dlg.h"
57 #include "gtk/help_dlg.h"
58 #include "gtk/pixmap_save.h"
60 #include "gtk/filter_autocomplete.h"
62 #include "gtk/old-gtk-compat.h"
67 #define LOGARITHMIC_YSCALE 0
68 #define AUTO_MAX_YSCALE 1
69 #define DEFAULT_YSCALE_INDEX 1
70 static guint32 yscale_max[MAX_YSCALE] = {LOGARITHMIC_YSCALE, AUTO_MAX_YSCALE, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000, 100000000, 200000000, 500000000, 1000000000, 2000000000};
72 #define NO_FILTER_ORDER 0
73 #define MAX_MOVING_AVERAGE_ORDER 10
74 static guint32 moving_average_orders[MAX_MOVING_AVERAGE_ORDER] = {NO_FILTER_ORDER, 2, 5, 10, 20, 50, 100, 250, 500, 1000};
76 #define MOVING_AVERAGE_FILTER 1
78 #define MAX_PIXELS_PER_TICK 4
79 #define DEFAULT_PIXELS_PER_TICK_INDEX 2
80 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
83 #define DEFAULT_PLOT_STYLE 0
84 #define PLOT_STYLE_LINE 0
85 #define PLOT_STYLE_IMPULSE 1
86 #define PLOT_STYLE_FILLED_BAR 2
87 #define PLOT_STYLE_DOT 3
88 #define MAX_PLOT_STYLES 4
89 static const char *plot_style_name[MAX_PLOT_STYLES] = {
96 #define DEFAULT_COUNT_TYPE 0
97 #define COUNT_TYPE_FRAMES 0
98 #define COUNT_TYPE_BYTES 1
99 #define COUNT_TYPE_BITS 2
100 #define COUNT_TYPE_ADVANCED 3
101 #define MAX_COUNT_TYPES 4
102 static const char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Bits/Tick", "Advanced..."};
105 #define MAX_TICK_VALUES 7
106 #define DEFAULT_TICK_VALUE_INDEX 3
107 static const guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000, 60000, 600000 };
109 #define CALC_TYPE_SUM 0
110 #define CALC_TYPE_COUNT 1
111 #define CALC_TYPE_MAX 2
112 #define CALC_TYPE_MIN 3
113 #define CALC_TYPE_AVG 4
114 #define CALC_TYPE_LOAD 5
115 #define MAX_CALC_TYPES 6
116 #define DEFAULT_CALC_TYPE 0
117 static const char *calc_type_names[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)", "LOAD(*)"};
120 typedef struct _io_stat_calc_type_t {
121 struct _io_stat_graph_t *gio;
123 } io_stat_calc_type_t;
125 #define NUM_IO_ITEMS 100000
126 typedef struct _io_item_t {
127 guint32 frames; /* always calculated, will hold number of frames*/
128 guint32 bytes; /* always calculated, will hold number of bytes*/
144 typedef struct _io_stat_graph_t {
145 struct _io_stat_t *io;
146 io_item_t items[NUM_IO_ITEMS];
149 GtkWidget *display_button;
150 GtkWidget *filter_field;
151 GtkWidget *advanced_buttons;
154 GtkWidget *calc_field;
156 #if GTK_CHECK_VERSION(3,0,0)
159 construct_args_t *args;
160 GtkWidget *filter_bt;
164 typedef struct _io_stat_t {
165 gboolean needs_redraw;
166 gint32 interval; /* measurement interval in ms */
167 guint32 last_interval;
168 guint32 max_interval; /* XXX max_interval and num_items are redundant */
170 guint32 left_x_border;
171 guint32 right_x_border;
172 gboolean view_as_time;
175 struct _io_stat_graph_t graphs[MAX_GRAPHS];
177 GtkWidget *draw_area;
178 #if GTK_CHECK_VERSION(2,22,0)
179 cairo_surface_t *surface;
183 GtkAdjustment *scrollbar_adjustment;
184 GtkWidget *scrollbar;
185 guint first_frame_num[NUM_IO_ITEMS];
186 guint last_frame_num;
193 guint32 filter_order;
198 static void init_io_stat_window(io_stat_t *io);
199 static void filter_callback(GtkWidget *widget _U_, gpointer user_data);
202 io_stat_set_title(io_stat_t *io)
209 title = g_strdup_printf("Wireshark IO Graphs: %s", cf_get_display_name(&cfile));
210 gtk_window_set_title(GTK_WINDOW(io->window), title);
215 io_stat_reset(io_stat_t *io)
219 io->needs_redraw=TRUE;
220 for(i=0;i<MAX_GRAPHS;i++){
221 for(j=0;j<NUM_IO_ITEMS;j++){
223 ioi=&io->graphs[i].items[j];
237 nstime_set_zero(&ioi->time_max);
238 nstime_set_zero(&ioi->time_min);
239 nstime_set_zero(&ioi->time_tot);
242 io->last_interval=0xffffffff;
245 io->start_time.secs=0;
246 io->start_time.nsecs=0;
247 for(j=0;j<NUM_IO_ITEMS;j++) {
248 io->first_frame_num[j]=0;
250 io->last_frame_num=0;
252 io_stat_set_title(io);
256 tap_iostat_reset(void *g)
258 io_stat_graph_t *gio=g;
260 io_stat_reset(gio->io);
264 tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
266 io_stat_graph_t *git=g;
271 /* we sometimes get called when git is disabled.
272 this is a bug since the tap listener should be removed first */
277 git->io->needs_redraw=TRUE;
280 * Find which interval this is supposed to go in and store the
281 * interval index as idx
283 time_delta=pinfo->fd->rel_ts;
284 if(time_delta.nsecs<0){
286 time_delta.nsecs+=1000000000;
288 if(time_delta.secs<0){
291 idx=(int) ((time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval);
293 /* some sanity checks */
294 if((idx<0)||(idx>=NUM_IO_ITEMS)){
295 git->io->num_items = NUM_IO_ITEMS-1;
299 /* update num_items */
300 if((guint32)idx > git->io->num_items){
301 git->io->num_items=idx;
302 git->io->max_interval=(idx+1)*git->io->interval;
306 if(git->io->start_time.secs == 0 && git->io->start_time.nsecs == 0) {
307 nstime_delta (&git->io->start_time, &pinfo->fd->abs_ts, &pinfo->fd->rel_ts);
310 /* set first and last frame num in current interval */
311 if (git->io->first_frame_num[idx] == 0) {
312 git->io->first_frame_num[idx]=pinfo->fd->num;
314 git->io->last_frame_num=pinfo->fd->num;
317 * Find the appropriate io_item_t structure
323 * For ADVANCED mode we need to keep track of some more stuff
324 * than just frame and byte counts
326 if(git->io->count_type==COUNT_TYPE_ADVANCED){
330 gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index);
335 /* update the appropriate counters, make sure that if
336 * fields==0 then this is the first seen value so
337 * set any min/max values accordingly
339 for(i=0;i<gp->len;i++){
345 switch(proto_registrar_get_ftype(git->hf_index)){
350 new_int=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
352 if((new_int>it->int_max)||(it->fields==0)){
355 if((new_int<it->int_min)||(it->fields==0)){
358 it->int_tot+=new_int;
365 new_int=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
366 if((new_int>it->int_max)||(it->fields==0)){
369 if((new_int<it->int_min)||(it->fields==0)){
372 it->int_tot+=new_int;
376 new_float=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
377 if((new_float>it->float_max)||(it->fields==0)){
378 it->float_max=new_float;
380 if((new_float<it->float_min)||(it->fields==0)){
381 it->float_min=new_float;
383 it->float_tot+=new_float;
387 new_double=fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
388 if((new_double>it->double_max)||(it->fields==0)){
389 it->double_max=new_double;
391 if((new_double<it->double_min)||(it->fields==0)){
392 it->double_min=new_double;
394 it->double_tot+=new_double;
397 case FT_RELATIVE_TIME:
398 new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
400 switch(git->calc_type){
401 guint64 t, pt; /* time in us */
404 /* it is a LOAD calculation of a relative time field.
405 * add the time this call spanned to each
406 * interval it spanned according to its contribution
410 t=t*1000000+new_time->nsecs/1000;
412 /* handle current interval */
413 pt=pinfo->fd->rel_ts.secs*1000000+pinfo->fd->rel_ts.nsecs/1000;
414 pt=pt%(git->io->interval*1000);
419 git->items[j].time_tot.nsecs+=(int) (pt*1000);
420 if(git->items[j].time_tot.nsecs>1000000000){
421 git->items[j].time_tot.secs++;
422 git->items[j].time_tot.nsecs-=1000000000;
430 if(t > (guint32) (git->io->interval*1000)){
431 pt=git->io->interval*1000;
438 if( (new_time->secs>it->time_max.secs)
439 ||( (new_time->secs==it->time_max.secs)
440 &&(new_time->nsecs>it->time_max.nsecs))
442 it->time_max=*new_time;
444 if( (new_time->secs<it->time_min.secs)
445 ||( (new_time->secs==it->time_min.secs)
446 &&(new_time->nsecs<it->time_min.nsecs))
448 it->time_min=*new_time;
450 nstime_add(&it->time_tot, new_time);
458 it->bytes+=pinfo->fd->pkt_len;
464 get_frame_num(io_stat_t *io, guint32 idx, gboolean first)
466 guint i, frame_num=0;
468 if (idx>io->num_items) {
473 frame_num=io->first_frame_num[idx];
478 * If first frame not found we select the last
479 * frame in the previous interval
481 * If selecting the last frame we select the frame
482 * before the first frame in the next interval
484 for(i=idx+1;i<=io->num_items;i++) {
485 frame_num=io->first_frame_num[i];
486 if (frame_num != 0) {
492 * If not found we select the last frame
494 frame_num=io->last_frame_num;
501 get_it_value(io_stat_t *io, int graph_id, int idx)
507 it=&io->graphs[graph_id].items[idx];
509 switch(io->count_type){
510 case COUNT_TYPE_FRAMES:
512 case COUNT_TYPE_BYTES:
514 case COUNT_TYPE_BITS:
515 return (it->bytes * 8);
519 adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
522 switch(io->graphs[graph_id].calc_type){
523 case CALC_TYPE_COUNT:
538 switch(io->graphs[graph_id].calc_type){
542 case CALC_TYPE_COUNT:
553 value=it->int_tot/it->fields;
563 switch(io->graphs[graph_id].calc_type){
567 case CALC_TYPE_COUNT:
578 value=it->float_tot/it->fields;
588 switch(io->graphs[graph_id].calc_type){
590 value=it->double_tot;
592 case CALC_TYPE_COUNT:
596 value=it->double_max;
599 value=it->double_min;
603 value=it->double_tot/it->fields;
612 case FT_RELATIVE_TIME:
613 switch(io->graphs[graph_id].calc_type){
614 case CALC_TYPE_COUNT:
618 value=(guint32) (it->time_max.secs*1000000+it->time_max.nsecs/1000);
621 value=(guint32) (it->time_min.secs*1000000+it->time_min.nsecs/1000);
624 value=(guint32) (it->time_tot.secs*1000000+it->time_tot.nsecs/1000);
628 guint64 t; /* time in us */
631 t=t*1000000+it->time_tot.nsecs/1000;
632 value=(guint32) (t/it->fields);
638 value=(guint32) ((it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval);
647 return (guint32)value; /* FIXME: loss of precision, visible on the graph for small values */
651 print_time_scale_string(char *buf, int buf_len, guint32 t, guint32 t_max, gboolean log_flag)
653 if(t_max>=10000000 || (log_flag && t_max>=1000000)){
654 g_snprintf(buf, buf_len, "%ds",t/1000000);
655 } else if(t_max>=1000000){
656 g_snprintf(buf, buf_len, "%d.%1ds",t/1000000,(t%1000000)/100000);
657 } else if(t_max>=10000 || (log_flag && t_max>=1000)){
658 g_snprintf(buf, buf_len, "%dms",t/1000);
659 } else if(t_max>=1000){
660 g_snprintf(buf, buf_len, "%d.%1dms",t/1000,(t%1000)/100);
662 g_snprintf(buf, buf_len, "%dus",t);
667 print_interval_string(char *buf, int buf_len, guint32 interval, io_stat_t *io,
670 if (io->view_as_time) {
672 time_t sec_val = interval/1000 + io->start_time.secs;
673 gint32 nsec_val = interval%1000 + io->start_time.nsecs/1000000;
675 if(nsec_val >= 1000) {
679 tmp = localtime (&sec_val);
680 if(io->interval>=1000){
681 g_snprintf(buf, buf_len, "%02d:%02d:%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
682 } else if(io->interval>=100){
683 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%1d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/100);
684 } else if(io->interval>=10){
685 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/10);
687 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val);
691 g_snprintf(buf, buf_len, "%d.%03d", interval/1000,interval%1000);
692 } else if(io->interval>=60000){
693 g_snprintf(buf, buf_len, "%dm", interval/60000);
694 } else if(io->interval>=1000){
695 g_snprintf(buf, buf_len, "%ds", interval/1000);
696 } else if(io->interval>=100){
697 g_snprintf(buf, buf_len, "%d.%1ds", interval/1000,(interval/100)%10);
698 } else if(io->interval>=10){
699 g_snprintf(buf, buf_len, "%d.%02ds", interval/1000,(interval/10)%100);
701 g_snprintf(buf, buf_len, "%d.%03ds", interval/1000,interval%1000);
707 io_stat_draw(io_stat_t *io)
709 int i, tics, ystart, ys;
710 guint32 last_interval, first_interval, interval_delta;
711 gint32 current_interval;
712 guint32 top_y_border;
713 guint32 bottom_y_border;
715 int label_width, label_height;
716 guint32 draw_width, draw_height;
717 char label_string[45];
718 GtkAllocation widget_alloc;
721 guint32 num_time_intervals;
722 guint32 max_value; /* max value of seen data */
723 guint32 max_y; /* max value of the Y scale */
724 gboolean draw_y_as_time;
726 if(!io->needs_redraw){
729 io->needs_redraw=FALSE;
732 * Find the length of the intervals we have data for
733 * so we know how large arrays we need to malloc()
735 num_time_intervals=io->num_items+1;
737 /* XXX move this check to _packet() */
738 if(num_time_intervals>NUM_IO_ITEMS){
739 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
745 * find the max value so we can autoscale the y axis
748 for(i=0;i<MAX_GRAPHS;i++){
751 if(!io->graphs[i].display){
754 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
757 val=get_it_value(io, i, idx);
759 /* keep track of the max value we have encountered */
771 #if GTK_CHECK_VERSION(2,22,0)
772 cr = cairo_create (io->surface);
774 cr = gdk_cairo_create (io->pixmap);
776 cairo_set_source_rgb (cr, 1, 1, 1);
777 gtk_widget_get_allocation(io->draw_area, &widget_alloc);
778 cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
782 * Calculate the y scale we should use
784 if(io->max_y_units==AUTO_MAX_YSCALE){
785 max_y=yscale_max[MAX_YSCALE-1];
786 for(i=MAX_YSCALE-1;i>1;i--){
787 if(max_value<yscale_max[i]){
791 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
793 for(i=1000000000;i>1;i/=10){
794 if(max_value<(guint32)i){
799 /* the user had specified an explicit y scale to use */
800 max_y=io->max_y_units;
805 * If we use ADVANCED and all the graphs are plotting
806 * either MIN/MAX/AVG of an FT_RELATIVE_TIME field
807 * then we will do some some special processing for the
808 * labels for the Y axis below:
809 * we will append the time unit " s" " ms" or " us"
810 * and we will present the unit in decimal
812 draw_y_as_time=FALSE;
813 if(io->count_type==COUNT_TYPE_ADVANCED){
815 for(i=0;i<MAX_GRAPHS;i++){
818 if(!io->graphs[i].display){
821 adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
823 case FT_RELATIVE_TIME:
824 switch(io->graphs[i].calc_type){
831 draw_y_as_time=FALSE;
835 draw_y_as_time=FALSE;
843 * Calculate size of borders surrounding the plot
844 * The border on the right side needs to be adjusted depending
845 * on the width of the text labels. For simplicity we assume that the
846 * top y scale label will be the widest one
849 if(io->max_y_units==LOGARITHMIC_YSCALE){
850 print_time_scale_string(label_string, 15, 100000, 100000, TRUE); /* 100 ms */
852 print_time_scale_string(label_string, 15, max_y, max_y, FALSE);
855 g_snprintf(label_string, 15, "%d", max_y);
857 layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
858 pango_layout_get_pixel_size(layout, &label_width, &label_height);
860 io->left_x_border=10;
861 io->right_x_border=label_width+20;
863 bottom_y_border=label_height+20;
867 * Calculate the size of the drawing area for the actual plot
869 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
870 draw_height=io->pixmap_height-top_y_border-bottom_y_border;
874 * Add a warning if too many entries
876 if (num_time_intervals == NUM_IO_ITEMS) {
877 g_snprintf (label_string, 45, "Warning: Graph limited to %d entries", NUM_IO_ITEMS);
878 pango_layout_set_text(layout, label_string, -1);
880 #if GTK_CHECK_VERSION(2,22,0)
881 cr = cairo_create (io->surface);
883 cr = gdk_cairo_create (io->pixmap);
885 cairo_move_to (cr, 5, io->pixmap_height-bottom_y_border-draw_height-label_height/2);
886 pango_cairo_show_layout (cr, layout);
892 * Draw the y axis and labels
893 * (we always draw the y scale with 11 ticks along the axis)
895 #if GTK_CHECK_VERSION(2,22,0)
896 cr = cairo_create (io->surface);
898 cr = gdk_cairo_create (io->pixmap);
900 cairo_set_line_width (cr, 1.0);
901 cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, top_y_border+0.5);
902 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5, io->pixmap_height-bottom_y_border+0.5);
905 if(io->max_y_units==LOGARITHMIC_YSCALE){
906 tics=(int)log10((double)max_y);
907 ystart=draw_height/10;
914 for(i=ys;i<=tics;i++){
915 int xwidth, lwidth, ypos;
918 if(io->max_y_units==LOGARITHMIC_YSCALE){
920 /* position for the 0 value */
921 ypos=io->pixmap_height-bottom_y_border;
923 /* position for the top value, do not draw logarithmic tics above graph */
924 ypos=io->pixmap_height-bottom_y_border-draw_height;
927 /* draw the logarithmic tics */
929 ypos=(int)(io->pixmap_height-bottom_y_border-(draw_height-ystart)*(i+log10((double)j))/tics-ystart);
931 #if GTK_CHECK_VERSION(2,22,0)
932 cr = cairo_create (io->surface);
934 cr = gdk_cairo_create (io->pixmap);
936 cairo_set_line_width (cr, 1.0);
937 cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5);
938 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5);
942 ypos=io->pixmap_height-bottom_y_border-(draw_height-ystart)*i/tics-ystart;
944 /* all "main" logarithmic lines are slightly longer */
948 /* first, middle and last tick are slightly longer */
951 ypos=io->pixmap_height-bottom_y_border-draw_height*i/10;
954 #if GTK_CHECK_VERSION(2,22,0)
955 cr = cairo_create (io->surface);
957 cr = gdk_cairo_create (io->pixmap);
959 cairo_set_line_width (cr, 1.0);
960 cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5);
961 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5);
964 /* draw the labels */
967 if(io->max_y_units==LOGARITHMIC_YSCALE){
968 value=(guint32)(max_y/pow(10,tics-i));
970 print_time_scale_string(label_string, 15, value, value, TRUE);
972 g_snprintf(label_string, 15, "%d", value);
977 print_time_scale_string(label_string, 15, value, max_y, FALSE);
979 g_snprintf(label_string, 15, "%d", value);
983 pango_layout_set_text(layout, label_string, -1);
984 pango_layout_get_pixel_size(layout, &lwidth, NULL);
986 #if GTK_CHECK_VERSION(2,22,0)
987 cr = cairo_create (io->surface);
989 cr = gdk_cairo_create (io->pixmap);
991 cairo_move_to (cr, io->pixmap_width-io->right_x_border+15+label_width-lwidth, ypos-label_height/2);
992 pango_cairo_show_layout (cr, layout);
1000 * if we have not specified the last_interval via the gui,
1001 * then just pick the current end of the capture so that is scrolls
1002 * nicely when doing live captures
1004 if(io->last_interval==0xffffffff){
1005 last_interval=io->max_interval;
1007 last_interval=io->last_interval;
1014 /* plot the x-scale */
1015 #if GTK_CHECK_VERSION(2,22,0)
1016 cr = cairo_create (io->surface);
1018 cr = gdk_cairo_create (io->pixmap);
1020 cairo_set_line_width (cr, 1.0);
1021 cairo_move_to(cr, io->left_x_border+0.5, io->pixmap_height-bottom_y_border+1.5);
1022 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5,io->pixmap_height-bottom_y_border+1.5);
1025 if((last_interval/io->interval)>=draw_width/io->pixels_per_tick){
1026 first_interval=(last_interval/io->interval)-draw_width/io->pixels_per_tick+1;
1027 first_interval*=io->interval;
1032 interval_delta=(100/io->pixels_per_tick)*io->interval;
1033 for(current_interval=last_interval;current_interval>=(gint32)first_interval;current_interval=current_interval-io->interval){
1036 /* if pixels_per_tick is 1 or 2, only draw every 10 ticks */
1037 /* if pixels_per_tick is 5, only draw every 5 ticks */
1038 if(((io->pixels_per_tick<5) && (current_interval%(10*io->interval))) ||
1039 ((io->pixels_per_tick==5) && (current_interval%(5*io->interval)))){
1043 if(!(current_interval%interval_delta)){
1045 } else if(!(current_interval%(interval_delta/2))){
1050 x=draw_width+io->left_x_border-((last_interval-current_interval)/io->interval)*io->pixels_per_tick;
1051 #if GTK_CHECK_VERSION(2,22,0)
1052 cr = cairo_create (io->surface);
1054 cr = gdk_cairo_create (io->pixmap);
1056 cairo_set_line_width (cr, 1.0);
1057 cairo_move_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+1.5);
1058 cairo_line_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+xlen+1.5);
1063 print_interval_string (label_string, 15, current_interval, io, TRUE);
1064 pango_layout_set_text(layout, label_string, -1);
1065 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1067 if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) {
1069 } else if ((x-1-io->pixels_per_tick/2+lwidth/2) > (io->pixmap_width-5)) {
1070 x_pos=io->pixmap_width-lwidth-5;
1072 x_pos=x-1-io->pixels_per_tick/2-lwidth/2;
1074 #if GTK_CHECK_VERSION(2,22,0)
1075 cr = cairo_create (io->surface);
1077 cr = gdk_cairo_create (io->pixmap);
1079 cairo_move_to (cr, x_pos, io->pixmap_height-bottom_y_border+15);
1080 pango_cairo_show_layout (cr, layout);
1086 g_object_unref(G_OBJECT(layout));
1090 * Loop over all graphs and draw them
1092 for(i=MAX_GRAPHS-1;i>=0;i--){
1094 guint32 interval, x_pos, y_pos, prev_x_pos, prev_y_pos;
1095 /* Moving average variables */
1096 guint32 mavg_in_average_count;
1097 guint64 mavg_cumulated;
1098 guint32 mavg_to_remove;
1100 if(!io->graphs[i].display){
1104 /* initialize prev x/y to the value of the first interval */
1105 prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval)+io->left_x_border;
1106 val=get_it_value(io, i, first_interval/io->interval);
1109 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1111 prev_y_pos=(guint32)(draw_height-1+top_y_border);
1113 prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1116 prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1118 /* Moving average initialization */
1119 mavg_to_remove = first_interval;
1120 mavg_in_average_count = 0;
1123 for(interval=first_interval;interval<last_interval;interval+=io->interval){
1124 x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval)+io->left_x_border;
1126 val=get_it_value(io, i, interval/io->interval);
1127 /* Moving average calculation */
1128 if(io->filter_type == MOVING_AVERAGE_FILTER){
1129 mavg_cumulated += val;
1130 mavg_in_average_count++;
1131 if(mavg_in_average_count > io->filter_order){
1132 mavg_in_average_count--;
1133 mavg_cumulated -= get_it_value(io, i, mavg_to_remove/io->interval);
1134 mavg_to_remove += io->interval;
1136 val = mavg_cumulated / mavg_in_average_count;
1141 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1143 y_pos=(guint32)(draw_height-1+top_y_border);
1145 y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1148 y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1151 switch(io->graphs[i].plot_style){
1152 case PLOT_STYLE_LINE:
1153 /* dont need to draw anything if the segment
1154 * is entirely above the top of the graph
1156 if( (prev_y_pos!=0) || (y_pos!=0) ){
1157 #if GTK_CHECK_VERSION(2,22,0)
1158 cr = cairo_create (io->surface);
1160 cr = gdk_cairo_create (io->pixmap);
1162 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1163 cairo_set_line_width (cr, 1.0);
1164 cairo_move_to(cr, prev_x_pos+0.5, prev_y_pos+0.5);
1165 cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1170 case PLOT_STYLE_IMPULSE:
1172 #if GTK_CHECK_VERSION(2,22,0)
1173 cr = cairo_create (io->surface);
1175 cr = gdk_cairo_create (io->pixmap);
1177 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1178 cairo_set_line_width (cr, 1.0);
1179 cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
1180 cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1185 case PLOT_STYLE_FILLED_BAR:
1187 #if GTK_CHECK_VERSION(2,22,0)
1188 cr = cairo_create (io->surface);
1190 cr = gdk_cairo_create (io->pixmap);
1192 cairo_rectangle (cr,
1193 x_pos-(gdouble)io->pixels_per_tick/2+0.5,
1195 io->pixels_per_tick,
1196 draw_height-1+top_y_border-y_pos);
1197 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1202 case PLOT_STYLE_DOT:
1204 #if GTK_CHECK_VERSION(2,22,0)
1205 cr = cairo_create (io->surface);
1207 cr = gdk_cairo_create (io->pixmap);
1212 (gdouble)io->pixels_per_tick/2,
1215 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1227 cr = gdk_cairo_create (gtk_widget_get_window(io->draw_area));
1229 #if GTK_CHECK_VERSION(2,22,0)
1230 cairo_set_source_surface (cr, io->surface, 0, 0);
1232 gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1234 cairo_rectangle (cr, 0, 0, io->pixmap_width, io->pixmap_height);
1239 /* update the scrollbar */
1240 if (io->max_interval == 0) {
1241 gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->interval);
1242 gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) (io->interval/10));
1243 gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) io->interval);
1245 gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->max_interval);
1246 gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
1247 gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1249 gtk_adjustment_set_page_size(io->scrollbar_adjustment, gtk_adjustment_get_page_increment(io->scrollbar_adjustment));
1250 gtk_adjustment_set_value(io->scrollbar_adjustment, (gfloat)first_interval);
1251 gtk_adjustment_changed(io->scrollbar_adjustment);
1252 gtk_adjustment_value_changed(io->scrollbar_adjustment);
1257 io_stat_redraw(io_stat_t *io)
1259 io->needs_redraw=TRUE;
1264 tap_iostat_draw(void *g)
1266 io_stat_graph_t *git=g;
1268 io_stat_draw(git->io);
1271 /* ok we get called with both the filter and the field.
1272 make sure the field is part of the filter.
1273 (make sure and make sure just append it)
1274 the field MUST be part of the filter or else we wont
1275 be able to pick up the field values after the edt tree has been
1279 enable_graph(io_stat_graph_t *gio, const char *filter, const char *field)
1281 char real_filter[262];
1287 /* skip all whitespaces */
1300 g_snprintf(real_filter, 257, "(%s)", filter);
1305 /* skip all whitespaces */
1318 if(real_filter[0]!=0){
1319 g_strlcat(real_filter, " && ", 262);
1321 g_strlcat(real_filter, field, 262);
1324 return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
1325 TL_REQUIRES_PROTO_TREE,
1326 tap_iostat_reset, tap_iostat_packet, tap_iostat_draw);
1330 disable_graph(io_stat_graph_t *gio)
1334 protect_thread_critical_region();
1335 remove_tap_listener(gio);
1336 unprotect_thread_critical_region();
1337 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
1343 iostat_init(const char *optarg _U_, void* userdata _U_)
1347 static GdkColor col[MAX_GRAPHS] = {
1348 {0, 0x0000, 0x0000, 0x0000}, /* Black */
1349 {0, 0xffff, 0x0000, 0x0000}, /* Red */
1350 {0, 0x0000, 0xffff, 0x0000}, /* Green */
1351 {0, 0x0000, 0x0000, 0xffff}, /* Blue */
1352 {0, 0xffff, 0x5000, 0xffff} /* Light brilliant magenta */
1354 #if GTK_CHECK_VERSION(3,0,0)
1355 static GdkRGBA rgba_col[MAX_GRAPHS] = {
1356 {0.0, 0.0, 0.0, 1.0}, /* Black */
1357 {1.0, 0.0, 0.1, 1.0}, /* Red */
1358 {0.0, 1.0, 0.0, 1.0}, /* Green */
1359 {0.0, 0.0, 1.0, 1.0}, /* Blue */
1360 {1.0, 0.314, 1.0, 1.0} /* Light brilliant magenta */
1363 GString *error_string;
1365 io=g_malloc(sizeof(io_stat_t));
1366 io->needs_redraw=TRUE;
1367 io->interval=tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
1370 #if GTK_CHECK_VERSION(2,22,0)
1376 io->scrollbar_adjustment=NULL;
1377 io->pixmap_width=500;
1378 io->pixmap_height=200;
1379 io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
1380 io->max_y_units=AUTO_MAX_YSCALE;
1382 io->last_interval=0xffffffff;
1385 io->left_x_border=0;
1386 io->right_x_border=500;
1387 io->view_as_time=FALSE;
1388 io->start_time.secs=0;
1389 io->start_time.nsecs=0;
1391 for(i=0;i<MAX_GRAPHS;i++){
1392 io->graphs[i].color.pixel=col[i].pixel;
1393 io->graphs[i].color.red=col[i].red;
1394 io->graphs[i].color.green=col[i].green;
1395 io->graphs[i].color.blue=col[i].blue;
1396 #if GTK_CHECK_VERSION(3,0,0)
1397 io->graphs[i].rgba_color.red=rgba_col[i].red;
1398 io->graphs[i].rgba_color.green=rgba_col[i].green;
1399 io->graphs[i].rgba_color.blue=rgba_col[i].blue;
1400 io->graphs[i].rgba_color.alpha=rgba_col[i].alpha;
1402 io->graphs[i].display=0;
1403 io->graphs[i].display_button=NULL;
1404 io->graphs[i].filter_field=NULL;
1405 io->graphs[i].advanced_buttons=NULL;
1406 io->graphs[i].io=io;
1408 io->graphs[i].args=g_malloc(sizeof(construct_args_t));
1409 io->graphs[i].args->title = NULL;
1410 io->graphs[i].args->wants_apply_button=TRUE;
1411 io->graphs[i].args->activate_on_ok=TRUE;
1412 io->graphs[i].args->modal_and_transient=FALSE;
1414 io->graphs[i].filter_bt=NULL;
1418 error_string=enable_graph(&io->graphs[0], NULL, NULL);
1419 g_assert((error_string == NULL) && "Can't attach io_stat tap !");
1423 fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
1425 g_string_free(error_string, TRUE);
1426 io->graphs[0].display=0;
1427 io->graphs[0].display_button=NULL;
1428 io->graphs[0].filter_field=NULL;
1429 io->graphs[0].advanced_buttons=NULL;
1434 init_io_stat_window(io);
1436 cf_retap_packets(&cfile);
1437 gdk_window_raise(gtk_widget_get_window(io->window));
1442 draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
1444 io_stat_t *io = user_data;
1446 GtkWidget *save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1447 surface_info_t *surface_info = g_object_get_data(G_OBJECT(save_bt), "surface-info");
1449 g_free(surface_info);
1451 for(i=0;i<MAX_GRAPHS;i++){
1452 if(io->graphs[i].display){
1453 protect_thread_critical_region();
1454 remove_tap_listener(&io->graphs[i]);
1455 unprotect_thread_critical_region();
1457 g_free( (gpointer) (io->graphs[i].args->title) );
1458 io->graphs[i].args->title=NULL;
1460 g_free(io->graphs[i].args);
1461 io->graphs[i].args=NULL;
1470 pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
1472 io_stat_t *io = user_data;
1473 guint32 draw_width, interval, last_interval;
1476 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
1478 if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) ||
1479 (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) {
1480 /* Outside draw area */
1484 #if GTK_CHECK_VERSION(2,22,0)
1485 if ((event->button==1 || event->button==3) && io->surface!=NULL) {
1487 if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
1490 * Button 1 selects the first package in the interval.
1491 * Button 3 selects the last package in the interval.
1493 if (io->last_interval==0xffffffff) {
1494 last_interval=io->max_interval;
1496 last_interval=io->last_interval;
1499 interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick);
1500 frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE);
1501 if (frame_num != 0) {
1502 cf_goto_frame(&cfile, frame_num);
1509 /* create a new backing pixmap of the appropriate size */
1511 draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
1513 io_stat_t *io = user_data;
1515 GtkAllocation widget_alloc;
1517 #if GTK_CHECK_VERSION(2,22,0)
1518 surface_info_t *surface_info = g_new(surface_info_t, 1);
1521 #if GTK_CHECK_VERSION(2,22,0)
1523 cairo_surface_destroy (io->surface);
1528 g_object_unref(io->pixmap);
1533 gtk_widget_get_allocation(widget, &widget_alloc);
1534 #if GTK_CHECK_VERSION(2,22,0)
1535 io->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1536 CAIRO_CONTENT_COLOR,
1538 widget_alloc.height);
1541 io->pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
1543 widget_alloc.height,
1546 io->pixmap_width=widget_alloc.width;
1547 io->pixmap_height=widget_alloc.height;
1549 save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1550 #if GTK_CHECK_VERSION(2,22,0)
1551 surface_info->surface = io->surface;
1552 surface_info->width = widget_alloc.width;
1553 surface_info->height = widget_alloc.height;
1554 g_object_set_data(G_OBJECT(save_bt), "surface-info", surface_info);
1555 gtk_widget_set_sensitive(save_bt, TRUE);
1557 cr = cairo_create (io->surface);
1559 g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
1560 gtk_widget_set_sensitive(save_bt, TRUE);
1562 cr = gdk_cairo_create (io->pixmap);
1564 cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1565 cairo_set_source_rgb (cr, 1, 1, 1);
1574 scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
1576 io_stat_t *io = user_data;
1579 mi=(guint32) (gtk_adjustment_get_value(io->scrollbar_adjustment) + gtk_adjustment_get_page_size(io->scrollbar_adjustment));
1580 if(io->last_interval==mi){
1583 if( (io->last_interval==0xffffffff)
1584 && (mi==io->max_interval) ){
1588 io->last_interval=(mi/io->interval)*io->interval;
1593 #if GTK_CHECK_VERSION(3,0,0)
1595 draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
1597 io_stat_t *io = user_data;
1598 GtkAllocation allocation;
1600 gtk_widget_get_allocation (widget, &allocation);
1601 cairo_set_source_surface (cr, io->surface, 0, 0);
1602 cairo_rectangle (cr, 0, 0, allocation.width, allocation.width);
1608 /* redraw the screen from the backing pixmap */
1610 draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
1612 io_stat_t *io = user_data;
1613 cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1615 #if GTK_CHECK_VERSION(2,22,0)
1616 cairo_set_source_surface (cr, io->surface, 0, 0);
1618 gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1620 cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1629 create_draw_area(io_stat_t *io, GtkWidget *box)
1631 io->draw_area=gtk_drawing_area_new();
1632 g_signal_connect(io->draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), io);
1634 gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
1636 /* signals needed to handle backing pixmap */
1637 #if GTK_CHECK_VERSION(3,0,0)
1638 g_signal_connect(io->draw_area, "draw", G_CALLBACK(draw_area_draw), io);
1640 g_signal_connect(io->draw_area, "expose-event", G_CALLBACK(draw_area_expose_event), io);
1642 g_signal_connect(io->draw_area, "configure-event", G_CALLBACK(draw_area_configure_event), io);
1643 gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
1644 g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), io);
1646 gtk_widget_show(io->draw_area);
1647 gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1649 /* create the associated scrollbar */
1650 io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1651 io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
1652 gtk_widget_show(io->scrollbar);
1653 gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
1654 g_signal_connect(io->scrollbar_adjustment, "value-changed", G_CALLBACK(scrollbar_changed), io);
1658 tick_interval_select(GtkWidget *item, gpointer user_data)
1660 io_stat_t *io = user_data;
1663 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1665 io->interval=tick_interval_values[i];
1666 cf_retap_packets(&cfile);
1667 gdk_window_raise(gtk_widget_get_window(io->window));
1672 pixels_per_tick_select(GtkWidget *item, gpointer user_data)
1674 io_stat_t *io = user_data;
1677 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1678 io->pixels_per_tick=pixels_per_tick[i];
1683 plot_style_select(GtkWidget *item, gpointer user_data)
1685 io_stat_graph_t *ppt = user_data;
1688 val=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1690 ppt->plot_style=val;
1692 io_stat_redraw(ppt->io);
1696 create_pixels_per_tick_menu_items(io_stat_t *io)
1699 GtkWidget *combo_box;
1701 combo_box = gtk_combo_box_text_new ();
1703 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1704 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1705 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1707 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX);
1708 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), io);
1714 yscale_select(GtkWidget *item, gpointer user_data)
1716 io_stat_t *io = user_data;
1719 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1721 io->max_y_units = yscale_max[i];
1726 filter_select(GtkWidget *item, gpointer user_data)
1728 io_stat_t *io = user_data;
1731 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1733 if(i==NO_FILTER_ORDER){
1734 io->filter_type = NO_FILTER;
1736 io->filter_type = MOVING_AVERAGE_FILTER;
1737 io->filter_order = moving_average_orders[i];
1743 create_tick_interval_menu_items(io_stat_t *io)
1745 GtkWidget *combo_box;
1749 combo_box = gtk_combo_box_text_new ();
1751 for(i=0;i<MAX_TICK_VALUES;i++){
1752 if(tick_interval_values[i]>=60000){
1753 g_snprintf(str, sizeof(str), "%u min", tick_interval_values[i]/60000);
1754 } else if(tick_interval_values[i]>=1000){
1755 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1756 } else if(tick_interval_values[i]>=100){
1757 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1758 } else if(tick_interval_values[i]>=10){
1759 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1761 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1763 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1765 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX);
1766 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), io);
1772 create_yscale_max_menu_items(io_stat_t *io)
1775 GtkWidget *combo_box;
1778 combo_box = gtk_combo_box_text_new ();
1779 for(i=0;i<MAX_YSCALE;i++){
1780 if(yscale_max[i]==LOGARITHMIC_YSCALE){
1781 g_strlcpy(str, "Logarithmic", 15);
1782 } else if(yscale_max[i]==AUTO_MAX_YSCALE){
1783 g_strlcpy(str, "Auto", 15);
1785 g_snprintf(str, 15, "%u", yscale_max[i]);
1787 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1789 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_YSCALE_INDEX);
1790 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), io);
1795 create_filter_menu_items(io_stat_t *io)
1798 GtkWidget *combo_box;
1801 combo_box = gtk_combo_box_text_new ();
1803 for(i=0;i<MAX_MOVING_AVERAGE_ORDER;i++){
1804 if(i==NO_FILTER_ORDER){
1805 g_strlcpy(str, "No filter", 15);
1807 g_snprintf(str, 15, "M.avg %u", moving_average_orders[i]);
1809 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1811 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
1812 g_signal_connect(combo_box, "changed", G_CALLBACK(filter_select), io);
1817 count_type_select(GtkWidget *item, gpointer user_data)
1819 io_stat_t *io = user_data;
1820 static gboolean advanced_visible=FALSE;
1822 GtkAllocation widget_alloc;
1824 io->count_type = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1826 if(io->count_type==COUNT_TYPE_ADVANCED){
1827 for(i=0;i<MAX_GRAPHS;i++){
1828 disable_graph(&io->graphs[i]);
1829 gtk_widget_show(io->graphs[i].advanced_buttons);
1830 /* redraw the entire window so the unhidden widgets show up, hopefully */
1831 gtk_widget_get_allocation(io->window, &widget_alloc);
1832 gtk_widget_queue_draw_area(io->window,
1836 widget_alloc.height);
1838 advanced_visible=TRUE;
1840 } else if (advanced_visible) {
1841 for(i=0;i<MAX_GRAPHS;i++){
1842 gtk_widget_hide(io->graphs[i].advanced_buttons);
1843 filter_callback(item, &io->graphs[i]);
1845 advanced_visible=FALSE;
1852 create_frames_or_bytes_menu_items(io_stat_t *io)
1854 GtkWidget *combo_box;
1857 combo_box = gtk_combo_box_text_new ();
1859 for(i=0;i<MAX_COUNT_TYPES;i++){
1860 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), count_type_names[i]);
1862 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_COUNT_TYPE);
1863 g_signal_connect(combo_box, "changed", G_CALLBACK(count_type_select), io);
1868 create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, GtkWidget * (*func)(io_stat_t *io))
1872 GtkWidget *combo_box;
1874 hbox=gtk_hbox_new(FALSE, 0);
1875 gtk_container_add(GTK_CONTAINER(box), hbox);
1876 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1877 gtk_widget_show(hbox);
1879 label=gtk_label_new(name);
1880 gtk_widget_show(label);
1881 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1883 combo_box = (*func)(io);
1884 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1885 gtk_widget_show(combo_box);
1889 view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer user_data)
1891 io_stat_t *io = user_data;
1893 io->view_as_time = io->view_as_time ? FALSE : TRUE;
1899 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1901 GtkWidget *frame_vbox;
1906 frame_vbox=gtk_vbox_new(FALSE, 0);
1907 gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0);
1908 gtk_widget_show(frame_vbox);
1910 frame = gtk_frame_new("X Axis");
1911 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1912 gtk_widget_show(frame);
1914 vbox=gtk_vbox_new(FALSE, 0);
1915 gtk_container_add(GTK_CONTAINER(frame), vbox);
1916 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1917 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1918 gtk_widget_show(vbox);
1920 create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
1921 create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1923 view_cb = gtk_check_button_new_with_mnemonic("_View as time of day");
1924 gtk_container_add(GTK_CONTAINER(vbox), view_cb);
1925 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time);
1926 g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io);
1927 gtk_widget_show(view_cb);
1929 frame = gtk_frame_new("Y Axis");
1930 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1931 gtk_widget_show(frame);
1933 vbox=gtk_vbox_new(FALSE, 0);
1934 gtk_container_add(GTK_CONTAINER(frame), vbox);
1935 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1936 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1937 gtk_widget_show(vbox);
1939 create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
1940 create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
1941 create_ctrl_menu(io, vbox, "Filter:", create_filter_menu_items);
1947 filter_callback(GtkWidget *widget _U_, gpointer user_data)
1949 io_stat_graph_t *gio = user_data;
1951 const char *field=NULL;
1952 header_field_info *hfi;
1955 /* this graph is not active, just update display and redraw */
1956 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1958 io_stat_redraw(gio->io);
1962 /* first check if the field string is valid */
1963 if(gio->io->count_type==COUNT_TYPE_ADVANCED){
1964 field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
1966 /* warn and bail out if there was no field specified */
1967 if(field==NULL || field[0]==0){
1968 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
1970 io_stat_redraw(gio->io);
1973 /* warn and bail out if the field could not be found */
1974 hfi=proto_registrar_get_byname(field);
1976 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
1978 io_stat_redraw(gio->io);
1981 gio->hf_index=hfi->id;
1982 /* check that the type is compatible */
1994 /* these values support all calculations except LOAD */
1995 switch(gio->calc_type){
1996 case CALC_TYPE_LOAD:
1997 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1998 "LOAD(*) is only supported for relative-time fields.");
2000 io_stat_redraw(gio->io);
2003 /* these types support all calculations */
2005 case FT_RELATIVE_TIME:
2006 /* this type only supports COUNT, MAX, MIN, AVG */
2007 switch(gio->calc_type){
2009 case CALC_TYPE_COUNT:
2013 case CALC_TYPE_LOAD:
2016 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2017 "%s is a relative-time field, so %s calculations are not supported on it.",
2019 calc_type_names[gio->calc_type]);
2021 io_stat_redraw(gio->io);
2028 * XXX - support this if gint64/guint64 are
2031 if(gio->calc_type!=CALC_TYPE_COUNT){
2032 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2033 "%s is a 64-bit integer, so %s calculations are not supported on it.",
2035 calc_type_names[gio->calc_type]);
2037 io_stat_redraw(gio->io);
2042 if(gio->calc_type!=CALC_TYPE_COUNT){
2043 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2044 "%s doesn't have integral or float values, so %s calculations are not supported on it.",
2046 calc_type_names[gio->calc_type]);
2048 io_stat_redraw(gio->io);
2055 /* first check if the filter string is valid. */
2056 filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
2057 if(!dfilter_compile(filter, &dfilter)) {
2058 bad_dfilter_alert_box(filter);
2060 io_stat_redraw(gio->io);
2063 if (dfilter != NULL)
2064 dfilter_free(dfilter);
2066 /* ok, we have a valid filter and the graph is active.
2067 first just try to delete any previous settings and then apply
2070 protect_thread_critical_region();
2071 remove_tap_listener(gio);
2072 unprotect_thread_critical_region();
2074 io_stat_reset(gio->io);
2075 enable_graph(gio, filter, field);
2076 cf_retap_packets(&cfile);
2077 gdk_window_raise(gtk_widget_get_window(gio->io->window));
2078 io_stat_redraw(gio->io);
2084 calc_type_select(GtkWidget *item, gpointer user_data)
2086 io_stat_graph_t *gio = user_data;
2088 gio->calc_type=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
2090 /* disable the graph */
2092 io_stat_redraw(gio->io);
2096 create_calc_types_menu_items(io_stat_graph_t *gio)
2098 GtkWidget *combo_box;
2101 combo_box = gtk_combo_box_text_new ();
2102 for(i=0;i<MAX_CALC_TYPES;i++){
2103 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), calc_type_names[i]);
2105 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_CALC_TYPE);
2106 g_signal_connect(combo_box, "changed", G_CALLBACK(calc_type_select), gio);
2111 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name, GtkWidget *(*func)(io_stat_graph_t *io))
2115 GtkWidget *combo_box;
2117 hbox=gtk_hbox_new(FALSE, 0);
2118 gtk_container_add(GTK_CONTAINER(box), hbox);
2119 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2120 gtk_widget_show(hbox);
2122 label=gtk_label_new(name);
2123 gtk_widget_show(label);
2124 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2126 combo_box = (*func)(gio);
2127 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2128 gtk_widget_show(combo_box);
2132 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
2135 gio->calc_field=gtk_entry_new();
2136 gtk_entry_set_max_length(GTK_ENTRY(gio->calc_field),100);
2137 gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0);
2138 gtk_widget_show(gio->calc_field);
2139 g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio);
2140 g_object_set_data (G_OBJECT(gio->calc_field), E_FILT_FIELD_NAME_ONLY_KEY, "");
2141 g_signal_connect(gio->calc_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2142 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2143 g_signal_connect(gio->calc_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2144 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2145 colorize_filter_te_as_empty(gio->calc_field);
2149 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
2153 hbox=gtk_hbox_new(FALSE, 0);
2154 gio->advanced_buttons=hbox;
2155 gtk_container_add(GTK_CONTAINER(box), hbox);
2156 gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START);
2157 gtk_widget_hide(hbox);
2159 gio->calc_type=CALC_TYPE_SUM;
2160 create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
2161 create_advanced_field(gio, hbox);
2165 filter_button_clicked(GtkWidget *w, gpointer user_data)
2167 io_stat_graph_t *gio = user_data;
2169 display_filter_construct_cb(w, gio->args);
2174 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
2176 GtkWidget *combo_box;
2182 hbox=gtk_hbox_new(FALSE, 3);
2183 gtk_container_add(GTK_CONTAINER(box), hbox);
2184 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2185 gtk_widget_show(hbox);
2187 g_snprintf(str, 256, "Graph %d", num);
2188 gio->display_button=gtk_toggle_button_new_with_label(str);
2189 gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
2190 gtk_widget_show(gio->display_button);
2191 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
2192 g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);
2194 label=gtk_label_new("Color");
2195 gtk_widget_show(label);
2196 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2198 #if GTK_CHECK_VERSION(3,0,0)
2199 gtk_widget_override_color(label, GTK_STATE_NORMAL, &gio->rgba_color);
2200 gtk_widget_override_color(label, GTK_STATE_ACTIVE, &gio->rgba_color);
2201 gtk_widget_override_color(label, GTK_STATE_PRELIGHT, &gio->rgba_color);
2202 gtk_widget_override_color(label, GTK_STATE_SELECTED, &gio->rgba_color);
2203 gtk_widget_override_color(label, GTK_STATE_INSENSITIVE, &gio->rgba_color);
2205 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color);
2206 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color);
2207 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color);
2208 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color);
2209 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color);
2211 /* g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);*/
2214 /* filter prefs dialog */
2215 gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
2217 g_snprintf(str, 256, "Wireshark: Display Filter IO-Stat (Filter:%d)", num);
2218 g_free( (gpointer) (gio->args->title) );
2219 gio->args->title=g_strdup(str);
2221 g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio);
2222 g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
2224 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
2225 gtk_widget_show(gio->filter_bt);
2227 gio->filter_field=gtk_entry_new();
2228 gtk_entry_set_max_length(GTK_ENTRY(gio->filter_field),256);
2229 /* filter prefs dialog */
2230 g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field);
2231 /* filter prefs dialog */
2233 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0);
2234 gtk_widget_show(gio->filter_field);
2235 g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio);
2236 g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2237 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2238 g_signal_connect(gio->filter_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2239 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2240 colorize_filter_te_as_empty(gio->filter_field);
2242 create_advanced_box(gio, hbox);
2245 * create PlotStyle menu
2247 g_snprintf(str, 256, " Style:");
2248 label=gtk_label_new(str);
2249 gtk_widget_show(label);
2250 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2252 combo_box = gtk_combo_box_text_new ();
2253 for(i=0;i<MAX_PLOT_STYLES;i++){
2254 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), plot_style_name[i]);
2256 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PLOT_STYLE);
2257 g_signal_connect(combo_box, "changed", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]);
2259 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2260 gtk_widget_show(combo_box);
2266 create_filter_area(io_stat_t *io, GtkWidget *box)
2272 frame=gtk_frame_new("Graphs");
2273 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2274 gtk_widget_show(frame);
2276 vbox=gtk_vbox_new(FALSE, 1);
2277 gtk_container_add(GTK_CONTAINER(frame), vbox);
2278 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
2279 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
2280 gtk_widget_show(vbox);
2282 for(i=0;i<MAX_GRAPHS;i++){
2283 create_filter_box(&io->graphs[i], vbox, i+1);
2290 copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer user_data)
2292 guint32 i, interval, val;
2295 GString *CSV_str=g_string_new("");
2296 io_stat_t *io = user_data;
2298 g_string_append(CSV_str, "\"Interval start\"");
2299 for(i=0;i<MAX_GRAPHS;i++) {
2300 if (io->graphs[i].display) {
2301 g_string_append_printf(CSV_str, ",\"Graph %d\"", i+1);
2304 g_string_append(CSV_str,"\n");
2306 for(interval=0; interval<io->max_interval; interval+=io->interval) {
2307 print_interval_string (string, 15, interval, io, FALSE);
2308 g_string_append_printf(CSV_str, "\"%s\"", string);
2309 for(i=0;i<MAX_GRAPHS;i++) {
2310 if (io->graphs[i].display) {
2311 val=get_it_value(io, i, interval/io->interval);
2312 g_string_append_printf(CSV_str, ",\"%d\"", val);
2315 g_string_append(CSV_str,"\n");
2318 /* Now that we have the CSV data, copy it into the default clipboard */
2319 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
2320 gtk_clipboard_set_text(cb, CSV_str->str, -1); /* Copy the CSV data into the clipboard */
2321 g_string_free(CSV_str, TRUE); /* Free the memory */
2325 init_io_stat_window(io_stat_t *io)
2330 GtkWidget *close_bt, *help_bt;
2334 /* create the main window, transient_for top_level */
2335 io->window = dlg_window_new("I/O Graphs");
2336 gtk_window_set_destroy_with_parent (GTK_WINDOW(io->window), TRUE);
2338 vbox=gtk_vbox_new(FALSE, 0);
2339 gtk_container_add(GTK_CONTAINER(io->window), vbox);
2340 gtk_widget_show(vbox);
2342 create_draw_area(io, vbox);
2344 hbox=gtk_hbox_new(FALSE, 3);
2345 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2346 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
2347 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2348 gtk_widget_show(hbox);
2350 create_filter_area(io, hbox);
2351 create_ctrl_area(io, hbox);
2353 io_stat_set_title(io);
2355 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE,
2356 GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
2357 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
2358 gtk_widget_show(bbox);
2360 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
2361 window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
2362 gtk_widget_set_tooltip_text(close_bt, "Close this dialog");
2363 save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
2364 gtk_widget_set_sensitive(save_bt, FALSE);
2365 gtk_widget_set_tooltip_text(save_bt, "Save the displayed graph to a file");
2366 g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
2367 g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt);
2369 copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
2370 gtk_widget_set_tooltip_text(copy_bt, "Copy values from selected graphs to the clipboard in CSV (Comma Separated Values) format");
2371 g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io);
2373 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
2374 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG);
2375 gtk_widget_set_tooltip_text (help_bt, "Show topic specific help");
2376 g_signal_connect(io->window, "delete-event", G_CALLBACK(window_delete_event_cb), NULL);
2378 gtk_widget_show(io->window);
2379 window_present(io->window);
2382 #ifdef MAIN_MENU_USE_UIMANAGER
2384 gui_iostat_cb(GtkAction *action _U_, gpointer user_data _U_)
2386 iostat_init(NULL,NULL);
2390 gui_iostat_cb(GtkWidget *w _U_, gpointer d _U_)
2392 iostat_init(NULL,NULL);
2397 register_tap_listener_gtk_iostat(void)
2399 register_stat_cmd_arg("io,stat", iostat_init,NULL);
2401 #ifdef MAIN_MENU_USE_UIMANAGER
2403 register_stat_menu_item_stock("_IO Graphs",
2404 REGISTER_STAT_GROUP_GENERIC, WIRESHARK_STOCK_GRAPHS,
2405 gui_iostat_cb, NULL, NULL, NULL);