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 MAX_PIXELS_PER_TICK 4
73 #define DEFAULT_PIXELS_PER_TICK_INDEX 2
74 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
77 #define DEFAULT_PLOT_STYLE 0
78 #define PLOT_STYLE_LINE 0
79 #define PLOT_STYLE_IMPULSE 1
80 #define PLOT_STYLE_FILLED_BAR 2
81 #define PLOT_STYLE_DOT 3
82 #define MAX_PLOT_STYLES 4
83 static const char *plot_style_name[MAX_PLOT_STYLES] = {
90 #define DEFAULT_COUNT_TYPE 0
91 #define COUNT_TYPE_FRAMES 0
92 #define COUNT_TYPE_BYTES 1
93 #define COUNT_TYPE_BITS 2
94 #define COUNT_TYPE_ADVANCED 3
95 #define MAX_COUNT_TYPES 4
96 static const char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Bits/Tick", "Advanced..."};
99 #define MAX_TICK_VALUES 7
100 #define DEFAULT_TICK_VALUE_INDEX 3
101 static const guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000, 60000, 600000 };
103 #define CALC_TYPE_SUM 0
104 #define CALC_TYPE_COUNT 1
105 #define CALC_TYPE_MAX 2
106 #define CALC_TYPE_MIN 3
107 #define CALC_TYPE_AVG 4
108 #define CALC_TYPE_LOAD 5
109 #define MAX_CALC_TYPES 6
110 #define DEFAULT_CALC_TYPE 0
111 static const char *calc_type_names[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)", "LOAD(*)"};
114 typedef struct _io_stat_calc_type_t {
115 struct _io_stat_graph_t *gio;
117 } io_stat_calc_type_t;
119 #define NUM_IO_ITEMS 100000
120 typedef struct _io_item_t {
121 guint32 frames; /* always calculated, will hold number of frames*/
122 guint32 bytes; /* always calculated, will hold number of bytes*/
138 typedef struct _io_stat_graph_t {
139 struct _io_stat_t *io;
140 io_item_t items[NUM_IO_ITEMS];
143 GtkWidget *display_button;
144 GtkWidget *filter_field;
145 GtkWidget *advanced_buttons;
148 GtkWidget *calc_field;
150 construct_args_t *args;
151 GtkWidget *filter_bt;
155 typedef struct _io_stat_t {
156 gboolean needs_redraw;
157 gint32 interval; /* measurement interval in ms */
158 guint32 last_interval;
159 guint32 max_interval; /* XXX max_interval and num_items are redundant */
161 guint32 left_x_border;
162 guint32 right_x_border;
163 gboolean view_as_time;
166 struct _io_stat_graph_t graphs[MAX_GRAPHS];
168 GtkWidget *draw_area;
169 #if GTK_CHECK_VERSION(2,22,0)
170 cairo_surface_t *surface;
174 GtkAdjustment *scrollbar_adjustment;
175 GtkWidget *scrollbar;
176 guint first_frame_num[NUM_IO_ITEMS];
177 guint last_frame_num;
186 static void init_io_stat_window(io_stat_t *io);
187 static void filter_callback(GtkWidget *widget _U_, gpointer user_data);
190 io_stat_set_title(io_stat_t *io)
197 title = g_strdup_printf("Wireshark IO Graphs: %s", cf_get_display_name(&cfile));
198 gtk_window_set_title(GTK_WINDOW(io->window), title);
203 io_stat_reset(io_stat_t *io)
207 io->needs_redraw=TRUE;
208 for(i=0;i<MAX_GRAPHS;i++){
209 for(j=0;j<NUM_IO_ITEMS;j++){
211 ioi=&io->graphs[i].items[j];
225 nstime_set_zero(&ioi->time_max);
226 nstime_set_zero(&ioi->time_min);
227 nstime_set_zero(&ioi->time_tot);
230 io->last_interval=0xffffffff;
233 io->start_time.secs=0;
234 io->start_time.nsecs=0;
235 for(j=0;j<NUM_IO_ITEMS;j++) {
236 io->first_frame_num[j]=0;
238 io->last_frame_num=0;
240 io_stat_set_title(io);
244 tap_iostat_reset(void *g)
246 io_stat_graph_t *gio=g;
248 io_stat_reset(gio->io);
252 tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
254 io_stat_graph_t *git=g;
259 /* we sometimes get called when git is disabled.
260 this is a bug since the tap listener should be removed first */
265 git->io->needs_redraw=TRUE;
268 * Find which interval this is supposed to go in and store the
269 * interval index as idx
271 time_delta=pinfo->fd->rel_ts;
272 if(time_delta.nsecs<0){
274 time_delta.nsecs+=1000000000;
276 if(time_delta.secs<0){
279 idx=(int) ((time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval);
281 /* some sanity checks */
282 if((idx<0)||(idx>=NUM_IO_ITEMS)){
283 git->io->num_items = NUM_IO_ITEMS-1;
287 /* update num_items */
288 if((guint32)idx > git->io->num_items){
289 git->io->num_items=idx;
290 git->io->max_interval=(idx+1)*git->io->interval;
294 if(git->io->start_time.secs == 0 && git->io->start_time.nsecs == 0) {
295 nstime_delta (&git->io->start_time, &pinfo->fd->abs_ts, &pinfo->fd->rel_ts);
298 /* set first and last frame num in current interval */
299 if (git->io->first_frame_num[idx] == 0) {
300 git->io->first_frame_num[idx]=pinfo->fd->num;
302 git->io->last_frame_num=pinfo->fd->num;
305 * Find the appropriate io_item_t structure
311 * For ADVANCED mode we need to keep track of some more stuff
312 * than just frame and byte counts
314 if(git->io->count_type==COUNT_TYPE_ADVANCED){
318 gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index);
323 /* update the appropriate counters, make sure that if
324 * fields==0 then this is the first seen value so
325 * set any min/max values accordingly
327 for(i=0;i<gp->len;i++){
333 switch(proto_registrar_get_ftype(git->hf_index)){
338 new_int=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
340 if((new_int>it->int_max)||(it->fields==0)){
343 if((new_int<it->int_min)||(it->fields==0)){
346 it->int_tot+=new_int;
353 new_int=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
354 if((new_int>it->int_max)||(it->fields==0)){
357 if((new_int<it->int_min)||(it->fields==0)){
360 it->int_tot+=new_int;
364 new_float=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
365 if((new_float>it->float_max)||(it->fields==0)){
366 it->float_max=new_float;
368 if((new_float<it->float_min)||(it->fields==0)){
369 it->float_min=new_float;
371 it->float_tot+=new_float;
375 new_double=fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
376 if((new_double>it->double_max)||(it->fields==0)){
377 it->double_max=new_double;
379 if((new_double<it->double_min)||(it->fields==0)){
380 it->double_min=new_double;
382 it->double_tot+=new_double;
385 case FT_RELATIVE_TIME:
386 new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
388 switch(git->calc_type){
389 guint64 t, pt; /* time in us */
392 /* it is a LOAD calculation of a relative time field.
393 * add the time this call spanned to each
394 * interval it spanned according to its contribution
398 t=t*1000000+new_time->nsecs/1000;
400 /* handle current interval */
401 pt=pinfo->fd->rel_ts.secs*1000000+pinfo->fd->rel_ts.nsecs/1000;
402 pt=pt%(git->io->interval*1000);
407 git->items[j].time_tot.nsecs+=(int) (pt*1000);
408 if(git->items[j].time_tot.nsecs>1000000000){
409 git->items[j].time_tot.secs++;
410 git->items[j].time_tot.nsecs-=1000000000;
418 if(t > (guint32) (git->io->interval*1000)){
419 pt=git->io->interval*1000;
426 if( (new_time->secs>it->time_max.secs)
427 ||( (new_time->secs==it->time_max.secs)
428 &&(new_time->nsecs>it->time_max.nsecs))
430 it->time_max=*new_time;
432 if( (new_time->secs<it->time_min.secs)
433 ||( (new_time->secs==it->time_min.secs)
434 &&(new_time->nsecs<it->time_min.nsecs))
436 it->time_min=*new_time;
438 nstime_add(&it->time_tot, new_time);
446 it->bytes+=pinfo->fd->pkt_len;
452 get_frame_num(io_stat_t *io, guint32 idx, gboolean first)
454 guint i, frame_num=0;
456 if (idx>io->num_items) {
461 frame_num=io->first_frame_num[idx];
466 * If first frame not found we select the last
467 * frame in the previous interval
469 * If selecting the last frame we select the frame
470 * before the first frame in the next interval
472 for(i=idx+1;i<=io->num_items;i++) {
473 frame_num=io->first_frame_num[i];
474 if (frame_num != 0) {
480 * If not found we select the last frame
482 frame_num=io->last_frame_num;
489 get_it_value(io_stat_t *io, int graph_id, int idx)
495 it=&io->graphs[graph_id].items[idx];
497 switch(io->count_type){
498 case COUNT_TYPE_FRAMES:
500 case COUNT_TYPE_BYTES:
502 case COUNT_TYPE_BITS:
503 return (it->bytes * 8);
507 adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
510 switch(io->graphs[graph_id].calc_type){
511 case CALC_TYPE_COUNT:
526 switch(io->graphs[graph_id].calc_type){
530 case CALC_TYPE_COUNT:
541 value=it->int_tot/it->fields;
551 switch(io->graphs[graph_id].calc_type){
555 case CALC_TYPE_COUNT:
566 value=it->float_tot/it->fields;
576 switch(io->graphs[graph_id].calc_type){
578 value=it->double_tot;
580 case CALC_TYPE_COUNT:
584 value=it->double_max;
587 value=it->double_min;
591 value=it->double_tot/it->fields;
600 case FT_RELATIVE_TIME:
601 switch(io->graphs[graph_id].calc_type){
602 case CALC_TYPE_COUNT:
606 value=(guint32) (it->time_max.secs*1000000+it->time_max.nsecs/1000);
609 value=(guint32) (it->time_min.secs*1000000+it->time_min.nsecs/1000);
612 value=(guint32) (it->time_tot.secs*1000000+it->time_tot.nsecs/1000);
616 guint64 t; /* time in us */
619 t=t*1000000+it->time_tot.nsecs/1000;
620 value=(guint32) (t/it->fields);
626 value=(guint32) ((it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval);
635 return (guint32)value; /* FIXME: loss of precision, visible on the graph for small values */
639 print_time_scale_string(char *buf, int buf_len, guint32 t, guint32 t_max, gboolean log_flag)
641 if(t_max>=10000000 || (log_flag && t_max>=1000000)){
642 g_snprintf(buf, buf_len, "%ds",t/1000000);
643 } else if(t_max>=1000000){
644 g_snprintf(buf, buf_len, "%d.%1ds",t/1000000,(t%1000000)/100000);
645 } else if(t_max>=10000 || (log_flag && t_max>=1000)){
646 g_snprintf(buf, buf_len, "%dms",t/1000);
647 } else if(t_max>=1000){
648 g_snprintf(buf, buf_len, "%d.%1dms",t/1000,(t%1000)/100);
650 g_snprintf(buf, buf_len, "%dus",t);
655 print_interval_string(char *buf, int buf_len, guint32 interval, io_stat_t *io,
658 if (io->view_as_time) {
660 time_t sec_val = interval/1000 + io->start_time.secs;
661 gint32 nsec_val = interval%1000 + io->start_time.nsecs/1000000;
663 if(nsec_val >= 1000) {
667 tmp = localtime (&sec_val);
668 if(io->interval>=1000){
669 g_snprintf(buf, buf_len, "%02d:%02d:%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
670 } else if(io->interval>=100){
671 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%1d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/100);
672 } else if(io->interval>=10){
673 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/10);
675 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val);
679 g_snprintf(buf, buf_len, "%d.%03d", interval/1000,interval%1000);
680 } else if(io->interval>=60000){
681 g_snprintf(buf, buf_len, "%dm", interval/60000);
682 } else if(io->interval>=1000){
683 g_snprintf(buf, buf_len, "%ds", interval/1000);
684 } else if(io->interval>=100){
685 g_snprintf(buf, buf_len, "%d.%1ds", interval/1000,(interval/100)%10);
686 } else if(io->interval>=10){
687 g_snprintf(buf, buf_len, "%d.%02ds", interval/1000,(interval/10)%100);
689 g_snprintf(buf, buf_len, "%d.%03ds", interval/1000,interval%1000);
695 io_stat_draw(io_stat_t *io)
697 int i, tics, ystart, ys;
698 guint32 last_interval, first_interval, interval_delta;
699 gint32 current_interval;
700 guint32 top_y_border;
701 guint32 bottom_y_border;
703 int label_width, label_height;
704 guint32 draw_width, draw_height;
705 char label_string[45];
706 GtkAllocation widget_alloc;
709 guint32 num_time_intervals;
710 guint32 max_value; /* max value of seen data */
711 guint32 max_y; /* max value of the Y scale */
712 gboolean draw_y_as_time;
714 if(!io->needs_redraw){
717 io->needs_redraw=FALSE;
720 * Find the length of the intervals we have data for
721 * so we know how large arrays we need to malloc()
723 num_time_intervals=io->num_items+1;
725 /* XXX move this check to _packet() */
726 if(num_time_intervals>NUM_IO_ITEMS){
727 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
733 * find the max value so we can autoscale the y axis
736 for(i=0;i<MAX_GRAPHS;i++){
739 if(!io->graphs[i].display){
742 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
745 val=get_it_value(io, i, idx);
747 /* keep track of the max value we have encountered */
759 #if GTK_CHECK_VERSION(2,22,0)
760 cr = cairo_create (io->surface);
762 cr = gdk_cairo_create (io->pixmap);
764 cairo_set_source_rgb (cr, 1, 1, 1);
765 gtk_widget_get_allocation(io->draw_area, &widget_alloc);
766 cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
770 * Calculate the y scale we should use
772 if(io->max_y_units==AUTO_MAX_YSCALE){
773 max_y=yscale_max[MAX_YSCALE-1];
774 for(i=MAX_YSCALE-1;i>1;i--){
775 if(max_value<yscale_max[i]){
779 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
781 for(i=1000000000;i>1;i/=10){
782 if(max_value<(guint32)i){
787 /* the user had specified an explicit y scale to use */
788 max_y=io->max_y_units;
793 * If we use ADVANCED and all the graphs are plotting
794 * either MIN/MAX/AVG of an FT_RELATIVE_TIME field
795 * then we will do some some special processing for the
796 * labels for the Y axis below:
797 * we will append the time unit " s" " ms" or " us"
798 * and we will present the unit in decimal
800 draw_y_as_time=FALSE;
801 if(io->count_type==COUNT_TYPE_ADVANCED){
803 for(i=0;i<MAX_GRAPHS;i++){
806 if(!io->graphs[i].display){
809 adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
811 case FT_RELATIVE_TIME:
812 switch(io->graphs[i].calc_type){
819 draw_y_as_time=FALSE;
823 draw_y_as_time=FALSE;
831 * Calculate size of borders surrounding the plot
832 * The border on the right side needs to be adjusted depending
833 * on the width of the text labels. For simplicity we assume that the
834 * top y scale label will be the widest one
837 if(io->max_y_units==LOGARITHMIC_YSCALE){
838 print_time_scale_string(label_string, 15, 100000, 100000, TRUE); /* 100 ms */
840 print_time_scale_string(label_string, 15, max_y, max_y, FALSE);
843 g_snprintf(label_string, 15, "%d", max_y);
845 layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
846 pango_layout_get_pixel_size(layout, &label_width, &label_height);
848 io->left_x_border=10;
849 io->right_x_border=label_width+20;
851 bottom_y_border=label_height+20;
855 * Calculate the size of the drawing area for the actual plot
857 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
858 draw_height=io->pixmap_height-top_y_border-bottom_y_border;
862 * Add a warning if too many entries
864 if (num_time_intervals == NUM_IO_ITEMS) {
865 g_snprintf (label_string, 45, "Warning: Graph limited to %d entries", NUM_IO_ITEMS);
866 pango_layout_set_text(layout, label_string, -1);
868 #if GTK_CHECK_VERSION(2,22,0)
869 cr = cairo_create (io->surface);
871 cr = gdk_cairo_create (io->pixmap);
873 cairo_move_to (cr, 5, io->pixmap_height-bottom_y_border-draw_height-label_height/2);
874 pango_cairo_show_layout (cr, layout);
880 * Draw the y axis and labels
881 * (we always draw the y scale with 11 ticks along the axis)
883 #if GTK_CHECK_VERSION(2,22,0)
884 cr = cairo_create (io->surface);
886 cr = gdk_cairo_create (io->pixmap);
888 cairo_set_line_width (cr, 1.0);
889 cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, top_y_border+0.5);
890 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5, io->pixmap_height-bottom_y_border+0.5);
893 if(io->max_y_units==LOGARITHMIC_YSCALE){
894 tics=(int)log10((double)max_y);
895 ystart=draw_height/10;
902 for(i=ys;i<=tics;i++){
903 int xwidth, lwidth, ypos;
906 if(io->max_y_units==LOGARITHMIC_YSCALE){
908 /* position for the 0 value */
909 ypos=io->pixmap_height-bottom_y_border;
911 /* position for the top value, do not draw logarithmic tics above graph */
912 ypos=io->pixmap_height-bottom_y_border-draw_height;
915 /* draw the logarithmic tics */
917 ypos=(int)(io->pixmap_height-bottom_y_border-(draw_height-ystart)*(i+log10((double)j))/tics-ystart);
919 #if GTK_CHECK_VERSION(2,22,0)
920 cr = cairo_create (io->surface);
922 cr = gdk_cairo_create (io->pixmap);
924 cairo_set_line_width (cr, 1.0);
925 cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5);
926 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5);
930 ypos=io->pixmap_height-bottom_y_border-(draw_height-ystart)*i/tics-ystart;
932 /* all "main" logarithmic lines are slightly longer */
936 /* first, middle and last tick are slightly longer */
939 ypos=io->pixmap_height-bottom_y_border-draw_height*i/10;
942 #if GTK_CHECK_VERSION(2,22,0)
943 cr = cairo_create (io->surface);
945 cr = gdk_cairo_create (io->pixmap);
947 cairo_set_line_width (cr, 1.0);
948 cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5);
949 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5);
952 /* draw the labels */
955 if(io->max_y_units==LOGARITHMIC_YSCALE){
956 value=(guint32)(max_y/pow(10,tics-i));
958 print_time_scale_string(label_string, 15, value, value, TRUE);
960 g_snprintf(label_string, 15, "%d", value);
965 print_time_scale_string(label_string, 15, value, max_y, FALSE);
967 g_snprintf(label_string, 15, "%d", value);
971 pango_layout_set_text(layout, label_string, -1);
972 pango_layout_get_pixel_size(layout, &lwidth, NULL);
974 #if GTK_CHECK_VERSION(2,22,0)
975 cr = cairo_create (io->surface);
977 cr = gdk_cairo_create (io->pixmap);
979 cairo_move_to (cr, io->pixmap_width-io->right_x_border+15+label_width-lwidth, ypos-label_height/2);
980 pango_cairo_show_layout (cr, layout);
988 * if we have not specified the last_interval via the gui,
989 * then just pick the current end of the capture so that is scrolls
990 * nicely when doing live captures
992 if(io->last_interval==0xffffffff){
993 last_interval=io->max_interval;
995 last_interval=io->last_interval;
1002 /* plot the x-scale */
1003 #if GTK_CHECK_VERSION(2,22,0)
1004 cr = cairo_create (io->surface);
1006 cr = gdk_cairo_create (io->pixmap);
1008 cairo_set_line_width (cr, 1.0);
1009 cairo_move_to(cr, io->left_x_border+0.5, io->pixmap_height-bottom_y_border+1.5);
1010 cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5,io->pixmap_height-bottom_y_border+1.5);
1013 if((last_interval/io->interval)>=draw_width/io->pixels_per_tick){
1014 first_interval=(last_interval/io->interval)-draw_width/io->pixels_per_tick+1;
1015 first_interval*=io->interval;
1020 interval_delta=(100/io->pixels_per_tick)*io->interval;
1021 for(current_interval=last_interval;current_interval>=(gint32)first_interval;current_interval=current_interval-io->interval){
1024 /* if pixels_per_tick is 1 or 2, only draw every 10 ticks */
1025 /* if pixels_per_tick is 5, only draw every 5 ticks */
1026 if(((io->pixels_per_tick<5) && (current_interval%(10*io->interval))) ||
1027 ((io->pixels_per_tick==5) && (current_interval%(5*io->interval)))){
1031 if(!(current_interval%interval_delta)){
1033 } else if(!(current_interval%(interval_delta/2))){
1038 x=draw_width+io->left_x_border-((last_interval-current_interval)/io->interval)*io->pixels_per_tick;
1039 #if GTK_CHECK_VERSION(2,22,0)
1040 cr = cairo_create (io->surface);
1042 cr = gdk_cairo_create (io->pixmap);
1044 cairo_set_line_width (cr, 1.0);
1045 cairo_move_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+1.5);
1046 cairo_line_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+xlen+1.5);
1051 print_interval_string (label_string, 15, current_interval, io, TRUE);
1052 pango_layout_set_text(layout, label_string, -1);
1053 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1055 if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) {
1057 } else if ((x-1-io->pixels_per_tick/2+lwidth/2) > (io->pixmap_width-5)) {
1058 x_pos=io->pixmap_width-lwidth-5;
1060 x_pos=x-1-io->pixels_per_tick/2-lwidth/2;
1062 #if GTK_CHECK_VERSION(2,22,0)
1063 cr = cairo_create (io->surface);
1065 cr = gdk_cairo_create (io->pixmap);
1067 cairo_move_to (cr, x_pos, io->pixmap_height-bottom_y_border+15);
1068 pango_cairo_show_layout (cr, layout);
1074 g_object_unref(G_OBJECT(layout));
1078 * Loop over all graphs and draw them
1080 for(i=MAX_GRAPHS-1;i>=0;i--){
1082 guint32 interval, x_pos, y_pos, prev_x_pos, prev_y_pos;
1084 if(!io->graphs[i].display){
1088 /* initialize prev x/y to the value of the first interval */
1089 prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval)+io->left_x_border;
1090 val=get_it_value(io, i, first_interval/io->interval);
1093 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1095 prev_y_pos=(guint32)(draw_height-1+top_y_border);
1097 prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1100 prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1103 for(interval=first_interval;interval<last_interval;interval+=io->interval){
1104 x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval)+io->left_x_border;
1106 val=get_it_value(io, i, interval/io->interval);
1109 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1111 y_pos=(guint32)(draw_height-1+top_y_border);
1113 y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1116 y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1119 switch(io->graphs[i].plot_style){
1120 case PLOT_STYLE_LINE:
1121 /* dont need to draw anything if the segment
1122 * is entirely above the top of the graph
1124 if( (prev_y_pos!=0) || (y_pos!=0) ){
1125 #if GTK_CHECK_VERSION(2,22,0)
1126 cr = cairo_create (io->surface);
1128 cr = gdk_cairo_create (io->pixmap);
1130 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1131 cairo_set_line_width (cr, 1.0);
1132 cairo_move_to(cr, prev_x_pos+0.5, prev_y_pos+0.5);
1133 cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1138 case PLOT_STYLE_IMPULSE:
1140 #if GTK_CHECK_VERSION(2,22,0)
1141 cr = cairo_create (io->surface);
1143 cr = gdk_cairo_create (io->pixmap);
1145 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1146 cairo_set_line_width (cr, 1.0);
1147 cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
1148 cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1153 case PLOT_STYLE_FILLED_BAR:
1155 #if GTK_CHECK_VERSION(2,22,0)
1156 cr = cairo_create (io->surface);
1158 cr = gdk_cairo_create (io->pixmap);
1160 cairo_rectangle (cr,
1161 x_pos-(gdouble)io->pixels_per_tick/2+0.5,
1163 io->pixels_per_tick,
1164 draw_height-1+top_y_border-y_pos);
1165 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1170 case PLOT_STYLE_DOT:
1172 #if GTK_CHECK_VERSION(2,22,0)
1173 cr = cairo_create (io->surface);
1175 cr = gdk_cairo_create (io->pixmap);
1180 (gdouble)io->pixels_per_tick/2,
1183 gdk_cairo_set_source_color (cr, &io->graphs[i].color);
1195 cr = gdk_cairo_create (gtk_widget_get_window(io->draw_area));
1197 #if GTK_CHECK_VERSION(2,22,0)
1198 cairo_set_source_surface (cr, io->surface, 0, 0);
1200 gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1202 cairo_rectangle (cr, 0, 0, io->pixmap_width, io->pixmap_height);
1207 /* update the scrollbar */
1208 if (io->max_interval == 0) {
1209 gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->interval);
1210 gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) (io->interval/10));
1211 gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) io->interval);
1213 gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->max_interval);
1214 gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
1215 gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1217 gtk_adjustment_set_page_size(io->scrollbar_adjustment, gtk_adjustment_get_page_increment(io->scrollbar_adjustment));
1218 gtk_adjustment_set_value(io->scrollbar_adjustment, (gfloat)first_interval);
1219 gtk_adjustment_changed(io->scrollbar_adjustment);
1220 gtk_adjustment_value_changed(io->scrollbar_adjustment);
1225 io_stat_redraw(io_stat_t *io)
1227 io->needs_redraw=TRUE;
1232 tap_iostat_draw(void *g)
1234 io_stat_graph_t *git=g;
1236 io_stat_draw(git->io);
1239 /* ok we get called with both the filter and the field.
1240 make sure the field is part of the filter.
1241 (make sure and make sure just append it)
1242 the field MUST be part of the filter or else we wont
1243 be able to pick up the field values after the edt tree has been
1247 enable_graph(io_stat_graph_t *gio, const char *filter, const char *field)
1249 char real_filter[262];
1255 /* skip all whitespaces */
1268 g_snprintf(real_filter, 257, "(%s)", filter);
1273 /* skip all whitespaces */
1286 if(real_filter[0]!=0){
1287 g_strlcat(real_filter, " && ", 262);
1289 g_strlcat(real_filter, field, 262);
1292 return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
1293 TL_REQUIRES_PROTO_TREE,
1294 tap_iostat_reset, tap_iostat_packet, tap_iostat_draw);
1298 disable_graph(io_stat_graph_t *gio)
1302 protect_thread_critical_region();
1303 remove_tap_listener(gio);
1304 unprotect_thread_critical_region();
1305 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
1311 iostat_init(const char *optarg _U_, void* userdata _U_)
1315 static GdkColor col[MAX_GRAPHS] = {
1316 {0, 0x0000, 0x0000, 0x0000},
1317 {0, 0xffff, 0x0000, 0x0000},
1318 {0, 0x0000, 0xffff, 0x0000},
1319 {0, 0x0000, 0x0000, 0xffff},
1320 {0, 0xffff, 0x5000, 0xffff}
1322 GString *error_string;
1324 io=g_malloc(sizeof(io_stat_t));
1325 io->needs_redraw=TRUE;
1326 io->interval=tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
1329 #if GTK_CHECK_VERSION(2,22,0)
1335 io->scrollbar_adjustment=NULL;
1336 io->pixmap_width=500;
1337 io->pixmap_height=200;
1338 io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
1339 io->max_y_units=AUTO_MAX_YSCALE;
1341 io->last_interval=0xffffffff;
1344 io->left_x_border=0;
1345 io->right_x_border=500;
1346 io->view_as_time=FALSE;
1347 io->start_time.secs=0;
1348 io->start_time.nsecs=0;
1350 for(i=0;i<MAX_GRAPHS;i++){
1351 io->graphs[i].color.pixel=col[i].pixel;
1352 io->graphs[i].color.red=col[i].red;
1353 io->graphs[i].color.green=col[i].green;
1354 io->graphs[i].color.blue=col[i].blue;
1355 io->graphs[i].display=0;
1356 io->graphs[i].display_button=NULL;
1357 io->graphs[i].filter_field=NULL;
1358 io->graphs[i].advanced_buttons=NULL;
1359 io->graphs[i].io=io;
1361 io->graphs[i].args=g_malloc(sizeof(construct_args_t));
1362 io->graphs[i].args->title = NULL;
1363 io->graphs[i].args->wants_apply_button=TRUE;
1364 io->graphs[i].args->activate_on_ok=TRUE;
1365 io->graphs[i].args->modal_and_transient=FALSE;
1367 io->graphs[i].filter_bt=NULL;
1371 error_string=enable_graph(&io->graphs[0], NULL, NULL);
1372 g_assert((error_string == NULL) && "Can't attach io_stat tap !");
1376 fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
1378 g_string_free(error_string, TRUE);
1379 io->graphs[0].display=0;
1380 io->graphs[0].display_button=NULL;
1381 io->graphs[0].filter_field=NULL;
1382 io->graphs[0].advanced_buttons=NULL;
1387 init_io_stat_window(io);
1389 cf_retap_packets(&cfile);
1390 gdk_window_raise(gtk_widget_get_window(io->window));
1395 draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
1397 io_stat_t *io = user_data;
1399 GtkWidget *save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1400 surface_info_t *surface_info = g_object_get_data(G_OBJECT(save_bt), "surface-info");
1402 g_free(surface_info);
1404 for(i=0;i<MAX_GRAPHS;i++){
1405 if(io->graphs[i].display){
1406 protect_thread_critical_region();
1407 remove_tap_listener(&io->graphs[i]);
1408 unprotect_thread_critical_region();
1410 g_free( (gpointer) (io->graphs[i].args->title) );
1411 io->graphs[i].args->title=NULL;
1413 g_free(io->graphs[i].args);
1414 io->graphs[i].args=NULL;
1423 pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
1425 io_stat_t *io = user_data;
1426 guint32 draw_width, interval, last_interval;
1429 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
1431 if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) ||
1432 (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) {
1433 /* Outside draw area */
1437 #if GTK_CHECK_VERSION(2,22,0)
1438 if ((event->button==1 || event->button==3) && io->surface!=NULL) {
1440 if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
1443 * Button 1 selects the first package in the interval.
1444 * Button 3 selects the last package in the interval.
1446 if (io->last_interval==0xffffffff) {
1447 last_interval=io->max_interval;
1449 last_interval=io->last_interval;
1452 interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick);
1453 frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE);
1454 if (frame_num != 0) {
1455 cf_goto_frame(&cfile, frame_num);
1462 /* create a new backing pixmap of the appropriate size */
1464 draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
1466 io_stat_t *io = user_data;
1468 GtkAllocation widget_alloc;
1470 #if GTK_CHECK_VERSION(2,22,0)
1471 surface_info_t *surface_info = g_new(surface_info_t, 1);
1474 #if GTK_CHECK_VERSION(2,22,0)
1476 cairo_surface_destroy (io->surface);
1481 g_object_unref(io->pixmap);
1486 gtk_widget_get_allocation(widget, &widget_alloc);
1487 #if GTK_CHECK_VERSION(2,22,0)
1488 io->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1489 CAIRO_CONTENT_COLOR,
1491 widget_alloc.height);
1494 io->pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
1496 widget_alloc.height,
1499 io->pixmap_width=widget_alloc.width;
1500 io->pixmap_height=widget_alloc.height;
1502 save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1503 #if GTK_CHECK_VERSION(2,22,0)
1504 surface_info->surface = io->surface;
1505 surface_info->width = widget_alloc.width;
1506 surface_info->height = widget_alloc.height;
1507 g_object_set_data(G_OBJECT(save_bt), "surface-info", surface_info);
1508 gtk_widget_set_sensitive(save_bt, TRUE);
1510 cr = cairo_create (io->surface);
1512 g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
1513 gtk_widget_set_sensitive(save_bt, TRUE);
1515 cr = gdk_cairo_create (io->pixmap);
1517 cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1518 cairo_set_source_rgb (cr, 1, 1, 1);
1527 scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
1529 io_stat_t *io = user_data;
1532 mi=(guint32) (gtk_adjustment_get_value(io->scrollbar_adjustment) + gtk_adjustment_get_page_size(io->scrollbar_adjustment));
1533 if(io->last_interval==mi){
1536 if( (io->last_interval==0xffffffff)
1537 && (mi==io->max_interval) ){
1541 io->last_interval=(mi/io->interval)*io->interval;
1547 /* redraw the screen from the backing pixmap */
1549 draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
1551 io_stat_t *io = user_data;
1552 cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1554 #if GTK_CHECK_VERSION(2,22,0)
1555 cairo_set_source_surface (cr, io->surface, 0, 0);
1557 gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
1559 cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1568 create_draw_area(io_stat_t *io, GtkWidget *box)
1570 io->draw_area=gtk_drawing_area_new();
1571 g_signal_connect(io->draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), io);
1573 gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
1575 /* signals needed to handle backing pixmap */
1576 g_signal_connect(io->draw_area, "expose-event", G_CALLBACK(draw_area_expose_event), io);
1577 g_signal_connect(io->draw_area, "configure-event", G_CALLBACK(draw_area_configure_event), io);
1578 gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
1579 g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), io);
1581 gtk_widget_show(io->draw_area);
1582 gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1584 /* create the associated scrollbar */
1585 io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1586 io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
1587 gtk_widget_show(io->scrollbar);
1588 gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
1589 g_signal_connect(io->scrollbar_adjustment, "value-changed", G_CALLBACK(scrollbar_changed), io);
1593 tick_interval_select(GtkWidget *item, gpointer user_data)
1595 io_stat_t *io = user_data;
1598 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1600 io->interval=tick_interval_values[i];
1601 cf_retap_packets(&cfile);
1602 gdk_window_raise(gtk_widget_get_window(io->window));
1607 pixels_per_tick_select(GtkWidget *item, gpointer user_data)
1609 io_stat_t *io = user_data;
1612 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1613 io->pixels_per_tick=pixels_per_tick[i];
1618 plot_style_select(GtkWidget *item, gpointer user_data)
1620 io_stat_graph_t *ppt = user_data;
1623 val=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1625 ppt->plot_style=val;
1627 io_stat_redraw(ppt->io);
1631 create_pixels_per_tick_menu_items(io_stat_t *io)
1634 GtkWidget *combo_box;
1636 combo_box = gtk_combo_box_text_new ();
1638 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1639 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1640 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1642 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX);
1643 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), io);
1649 yscale_select(GtkWidget *item, gpointer user_data)
1651 io_stat_t *io = user_data;
1654 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1656 io->max_y_units = yscale_max[i];
1661 create_tick_interval_menu_items(io_stat_t *io)
1663 GtkWidget *combo_box;
1667 combo_box = gtk_combo_box_text_new ();
1669 for(i=0;i<MAX_TICK_VALUES;i++){
1670 if(tick_interval_values[i]>=60000){
1671 g_snprintf(str, sizeof(str), "%u min", tick_interval_values[i]/60000);
1672 } else if(tick_interval_values[i]>=1000){
1673 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1674 } else if(tick_interval_values[i]>=100){
1675 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1676 } else if(tick_interval_values[i]>=10){
1677 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1679 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1681 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1683 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX);
1684 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), io);
1690 create_yscale_max_menu_items(io_stat_t *io)
1693 GtkWidget *combo_box;
1696 combo_box = gtk_combo_box_text_new ();
1697 for(i=0;i<MAX_YSCALE;i++){
1698 if(yscale_max[i]==LOGARITHMIC_YSCALE){
1699 g_strlcpy(str, "Logarithmic", 15);
1700 } else if(yscale_max[i]==AUTO_MAX_YSCALE){
1701 g_strlcpy(str, "Auto", 15);
1703 g_snprintf(str, 15, "%u", yscale_max[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_YSCALE_INDEX);
1708 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), io);
1713 count_type_select(GtkWidget *item, gpointer user_data)
1715 io_stat_t *io = user_data;
1716 static gboolean advanced_visible=FALSE;
1718 GtkAllocation widget_alloc;
1720 io->count_type = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1722 if(io->count_type==COUNT_TYPE_ADVANCED){
1723 for(i=0;i<MAX_GRAPHS;i++){
1724 disable_graph(&io->graphs[i]);
1725 gtk_widget_show(io->graphs[i].advanced_buttons);
1726 /* redraw the entire window so the unhidden widgets show up, hopefully */
1727 gtk_widget_get_allocation(io->window, &widget_alloc);
1728 gtk_widget_queue_draw_area(io->window,
1732 widget_alloc.height);
1734 advanced_visible=TRUE;
1736 } else if (advanced_visible) {
1737 for(i=0;i<MAX_GRAPHS;i++){
1738 gtk_widget_hide(io->graphs[i].advanced_buttons);
1739 filter_callback(item, &io->graphs[i]);
1741 advanced_visible=FALSE;
1748 create_frames_or_bytes_menu_items(io_stat_t *io)
1750 GtkWidget *combo_box;
1753 combo_box = gtk_combo_box_text_new ();
1755 for(i=0;i<MAX_COUNT_TYPES;i++){
1756 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), count_type_names[i]);
1758 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_COUNT_TYPE);
1759 g_signal_connect(combo_box, "changed", G_CALLBACK(count_type_select), io);
1764 create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, GtkWidget * (*func)(io_stat_t *io))
1768 GtkWidget *combo_box;
1770 hbox=gtk_hbox_new(FALSE, 0);
1771 gtk_container_add(GTK_CONTAINER(box), hbox);
1772 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1773 gtk_widget_show(hbox);
1775 label=gtk_label_new(name);
1776 gtk_widget_show(label);
1777 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1779 combo_box = (*func)(io);
1780 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1781 gtk_widget_show(combo_box);
1785 view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer user_data)
1787 io_stat_t *io = user_data;
1789 io->view_as_time = io->view_as_time ? FALSE : TRUE;
1795 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1797 GtkWidget *frame_vbox;
1802 frame_vbox=gtk_vbox_new(FALSE, 0);
1803 gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0);
1804 gtk_widget_show(frame_vbox);
1806 frame = gtk_frame_new("X Axis");
1807 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1808 gtk_widget_show(frame);
1810 vbox=gtk_vbox_new(FALSE, 0);
1811 gtk_container_add(GTK_CONTAINER(frame), vbox);
1812 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1813 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1814 gtk_widget_show(vbox);
1816 create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
1817 create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1819 view_cb = gtk_check_button_new_with_mnemonic("_View as time of day");
1820 gtk_container_add(GTK_CONTAINER(vbox), view_cb);
1821 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time);
1822 g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io);
1823 gtk_widget_show(view_cb);
1825 frame = gtk_frame_new("Y Axis");
1826 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1827 gtk_widget_show(frame);
1829 vbox=gtk_vbox_new(FALSE, 0);
1830 gtk_container_add(GTK_CONTAINER(frame), vbox);
1831 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1832 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1833 gtk_widget_show(vbox);
1835 create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
1836 create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
1842 filter_callback(GtkWidget *widget _U_, gpointer user_data)
1844 io_stat_graph_t *gio = user_data;
1846 const char *field=NULL;
1847 header_field_info *hfi;
1850 /* this graph is not active, just update display and redraw */
1851 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1853 io_stat_redraw(gio->io);
1857 /* first check if the field string is valid */
1858 if(gio->io->count_type==COUNT_TYPE_ADVANCED){
1859 field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
1861 /* warn and bail out if there was no field specified */
1862 if(field==NULL || field[0]==0){
1863 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
1865 io_stat_redraw(gio->io);
1868 /* warn and bail out if the field could not be found */
1869 hfi=proto_registrar_get_byname(field);
1871 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
1873 io_stat_redraw(gio->io);
1876 gio->hf_index=hfi->id;
1877 /* check that the type is compatible */
1889 /* these values support all calculations except LOAD */
1890 switch(gio->calc_type){
1891 case CALC_TYPE_LOAD:
1892 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1893 "LOAD(*) is only supported for relative-time fields.");
1895 io_stat_redraw(gio->io);
1898 /* these types support all calculations */
1900 case FT_RELATIVE_TIME:
1901 /* this type only supports COUNT, MAX, MIN, AVG */
1902 switch(gio->calc_type){
1904 case CALC_TYPE_COUNT:
1908 case CALC_TYPE_LOAD:
1911 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1912 "%s is a relative-time field, so %s calculations are not supported on it.",
1914 calc_type_names[gio->calc_type]);
1916 io_stat_redraw(gio->io);
1923 * XXX - support this if gint64/guint64 are
1926 if(gio->calc_type!=CALC_TYPE_COUNT){
1927 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1928 "%s is a 64-bit integer, so %s calculations are not supported on it.",
1930 calc_type_names[gio->calc_type]);
1932 io_stat_redraw(gio->io);
1937 if(gio->calc_type!=CALC_TYPE_COUNT){
1938 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1939 "%s doesn't have integral or float values, so %s calculations are not supported on it.",
1941 calc_type_names[gio->calc_type]);
1943 io_stat_redraw(gio->io);
1950 /* first check if the filter string is valid. */
1951 filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
1952 if(!dfilter_compile(filter, &dfilter)) {
1953 bad_dfilter_alert_box(filter);
1955 io_stat_redraw(gio->io);
1958 if (dfilter != NULL)
1959 dfilter_free(dfilter);
1961 /* ok, we have a valid filter and the graph is active.
1962 first just try to delete any previous settings and then apply
1965 protect_thread_critical_region();
1966 remove_tap_listener(gio);
1967 unprotect_thread_critical_region();
1969 io_stat_reset(gio->io);
1970 enable_graph(gio, filter, field);
1971 cf_retap_packets(&cfile);
1972 gdk_window_raise(gtk_widget_get_window(gio->io->window));
1973 io_stat_redraw(gio->io);
1979 calc_type_select(GtkWidget *item, gpointer user_data)
1981 io_stat_graph_t *gio = user_data;
1983 gio->calc_type=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1985 /* disable the graph */
1987 io_stat_redraw(gio->io);
1991 create_calc_types_menu_items(io_stat_graph_t *gio)
1993 GtkWidget *combo_box;
1996 combo_box = gtk_combo_box_text_new ();
1997 for(i=0;i<MAX_CALC_TYPES;i++){
1998 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), calc_type_names[i]);
2000 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_CALC_TYPE);
2001 g_signal_connect(combo_box, "changed", G_CALLBACK(calc_type_select), gio);
2006 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name, GtkWidget *(*func)(io_stat_graph_t *io))
2010 GtkWidget *combo_box;
2012 hbox=gtk_hbox_new(FALSE, 0);
2013 gtk_container_add(GTK_CONTAINER(box), hbox);
2014 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2015 gtk_widget_show(hbox);
2017 label=gtk_label_new(name);
2018 gtk_widget_show(label);
2019 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2021 combo_box = (*func)(gio);
2022 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2023 gtk_widget_show(combo_box);
2027 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
2030 gio->calc_field=gtk_entry_new();
2031 gtk_entry_set_max_length(GTK_ENTRY(gio->calc_field),100);
2032 gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0);
2033 gtk_widget_show(gio->calc_field);
2034 g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio);
2035 g_object_set_data (G_OBJECT(gio->calc_field), E_FILT_FIELD_NAME_ONLY_KEY, "");
2036 g_signal_connect(gio->calc_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2037 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2038 g_signal_connect(gio->calc_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2039 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2040 colorize_filter_te_as_empty(gio->calc_field);
2044 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
2048 hbox=gtk_hbox_new(FALSE, 0);
2049 gio->advanced_buttons=hbox;
2050 gtk_container_add(GTK_CONTAINER(box), hbox);
2051 gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START);
2052 gtk_widget_hide(hbox);
2054 gio->calc_type=CALC_TYPE_SUM;
2055 create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
2056 create_advanced_field(gio, hbox);
2060 filter_button_clicked(GtkWidget *w, gpointer user_data)
2062 io_stat_graph_t *gio = user_data;
2064 display_filter_construct_cb(w, gio->args);
2069 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
2071 GtkWidget *combo_box;
2077 hbox=gtk_hbox_new(FALSE, 3);
2078 gtk_container_add(GTK_CONTAINER(box), hbox);
2079 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2080 gtk_widget_show(hbox);
2082 g_snprintf(str, 256, "Graph %d", num);
2083 gio->display_button=gtk_toggle_button_new_with_label(str);
2084 gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
2085 gtk_widget_show(gio->display_button);
2086 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
2087 g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);
2089 label=gtk_label_new("Color");
2090 gtk_widget_show(label);
2091 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2093 #if GTK_CHECK_VERSION(3,0,0)
2094 /* Fix me: Add the GTK 3.0 equivalents */
2096 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color);
2097 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color);
2098 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color);
2099 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color);
2100 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color);
2102 /* g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);*/
2105 /* filter prefs dialog */
2106 gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
2108 g_snprintf(str, 256, "Wireshark: Display Filter IO-Stat (Filter:%d)", num);
2109 g_free( (gpointer) (gio->args->title) );
2110 gio->args->title=g_strdup(str);
2112 g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio);
2113 g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
2115 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
2116 gtk_widget_show(gio->filter_bt);
2118 gio->filter_field=gtk_entry_new();
2119 gtk_entry_set_max_length(GTK_ENTRY(gio->filter_field),256);
2120 /* filter prefs dialog */
2121 g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field);
2122 /* filter prefs dialog */
2124 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0);
2125 gtk_widget_show(gio->filter_field);
2126 g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio);
2127 g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2128 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2129 g_signal_connect(gio->filter_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2130 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2131 colorize_filter_te_as_empty(gio->filter_field);
2133 create_advanced_box(gio, hbox);
2136 * create PlotStyle menu
2138 g_snprintf(str, 256, " Style:");
2139 label=gtk_label_new(str);
2140 gtk_widget_show(label);
2141 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2143 combo_box = gtk_combo_box_text_new ();
2144 for(i=0;i<MAX_PLOT_STYLES;i++){
2145 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), plot_style_name[i]);
2147 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PLOT_STYLE);
2148 g_signal_connect(combo_box, "changed", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]);
2150 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2151 gtk_widget_show(combo_box);
2157 create_filter_area(io_stat_t *io, GtkWidget *box)
2163 frame=gtk_frame_new("Graphs");
2164 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2165 gtk_widget_show(frame);
2167 vbox=gtk_vbox_new(FALSE, 1);
2168 gtk_container_add(GTK_CONTAINER(frame), vbox);
2169 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
2170 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
2171 gtk_widget_show(vbox);
2173 for(i=0;i<MAX_GRAPHS;i++){
2174 create_filter_box(&io->graphs[i], vbox, i+1);
2181 copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer user_data)
2183 guint32 i, interval, val;
2186 GString *CSV_str=g_string_new("");
2187 io_stat_t *io = user_data;
2189 g_string_append(CSV_str, "\"Interval start\"");
2190 for(i=0;i<MAX_GRAPHS;i++) {
2191 if (io->graphs[i].display) {
2192 g_string_append_printf(CSV_str, ",\"Graph %d\"", i+1);
2195 g_string_append(CSV_str,"\n");
2197 for(interval=0; interval<io->max_interval; interval+=io->interval) {
2198 print_interval_string (string, 15, interval, io, FALSE);
2199 g_string_append_printf(CSV_str, "\"%s\"", string);
2200 for(i=0;i<MAX_GRAPHS;i++) {
2201 if (io->graphs[i].display) {
2202 val=get_it_value(io, i, interval/io->interval);
2203 g_string_append_printf(CSV_str, ",\"%d\"", val);
2206 g_string_append(CSV_str,"\n");
2209 /* Now that we have the CSV data, copy it into the default clipboard */
2210 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
2211 gtk_clipboard_set_text(cb, CSV_str->str, -1); /* Copy the CSV data into the clipboard */
2212 g_string_free(CSV_str, TRUE); /* Free the memory */
2216 init_io_stat_window(io_stat_t *io)
2221 GtkWidget *close_bt, *help_bt;
2225 /* create the main window, transient_for top_level */
2226 io->window = dlg_window_new("I/O Graphs");
2227 gtk_window_set_destroy_with_parent (GTK_WINDOW(io->window), TRUE);
2229 vbox=gtk_vbox_new(FALSE, 0);
2230 gtk_container_add(GTK_CONTAINER(io->window), vbox);
2231 gtk_widget_show(vbox);
2233 create_draw_area(io, vbox);
2235 hbox=gtk_hbox_new(FALSE, 3);
2236 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2237 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
2238 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2239 gtk_widget_show(hbox);
2241 create_filter_area(io, hbox);
2242 create_ctrl_area(io, hbox);
2244 io_stat_set_title(io);
2246 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE,
2247 GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
2248 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
2249 gtk_widget_show(bbox);
2251 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
2252 window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
2253 gtk_widget_set_tooltip_text(close_bt, "Close this dialog");
2254 save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
2255 gtk_widget_set_sensitive(save_bt, FALSE);
2256 gtk_widget_set_tooltip_text(save_bt, "Save the displayed graph to a file");
2257 g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
2258 g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt);
2260 copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
2261 gtk_widget_set_tooltip_text(copy_bt, "Copy values from selected graphs to the clipboard in CSV (Comma Separated Values) format");
2262 g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io);
2264 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
2265 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG);
2266 gtk_widget_set_tooltip_text (help_bt, "Show topic specific help");
2267 g_signal_connect(io->window, "delete-event", G_CALLBACK(window_delete_event_cb), NULL);
2269 gtk_widget_show(io->window);
2270 window_present(io->window);
2273 #ifdef MAIN_MENU_USE_UIMANAGER
2275 gui_iostat_cb(GtkAction *action _U_, gpointer user_data _U_)
2277 iostat_init(NULL,NULL);
2281 gui_iostat_cb(GtkWidget *w _U_, gpointer d _U_)
2283 iostat_init(NULL,NULL);
2288 register_tap_listener_gtk_iostat(void)
2290 register_stat_cmd_arg("io,stat", iostat_init,NULL);
2292 #ifdef MAIN_MENU_USE_UIMANAGER
2294 register_stat_menu_item_stock("_IO Graphs",
2295 REGISTER_STAT_GROUP_GENERIC, WIRESHARK_STOCK_GRAPHS,
2296 gui_iostat_cb, NULL, NULL, NULL);