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>
40 #include <epan/epan_dissect.h>
41 #include <epan/packet_info.h>
42 #include <epan/stat_cmd_args.h>
44 #include <epan/strutil.h>
46 #include "../stat_menu.h"
47 #include "../register.h"
48 #include "../alert_box.h"
49 #include "../simple_dialog.h"
50 #include "../globals.h"
53 #include "gtk/gtkglobals.h"
54 #include "gtk/gui_utils.h"
55 #include "gtk/gui_stat_menu.h"
56 #include "gtk/stock_icons.h"
57 #include "gtk/dlg_utils.h"
58 #include "gtk/filter_dlg.h"
59 #include "gtk/help_dlg.h"
60 #include "gtk/pixmap_save.h"
62 #include "gtk/filter_autocomplete.h"
68 #define LOGARITHMIC_YSCALE 0
69 #define AUTO_MAX_YSCALE 1
70 #define DEFAULT_YSCALE 1
71 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};
73 #define MAX_PIXELS_PER_TICK 4
74 #define DEFAULT_PIXELS_PER_TICK 2
75 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
78 #define DEFAULT_PLOT_STYLE 0
79 #define PLOT_STYLE_LINE 0
80 #define PLOT_STYLE_IMPULSE 1
81 #define PLOT_STYLE_FILLED_BAR 2
82 #define PLOT_STYLE_DOT 3
83 #define MAX_PLOT_STYLES 4
84 static const char *plot_style_name[MAX_PLOT_STYLES] = {
92 #define COUNT_TYPE_FRAMES 0
93 #define COUNT_TYPE_BYTES 1
94 #define COUNT_TYPE_BITS 2
95 #define COUNT_TYPE_ADVANCED 3
96 #define MAX_COUNT_TYPES 4
97 static const char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Bits/Tick", "Advanced..."};
100 #define MAX_TICK_VALUES 7
101 #define DEFAULT_TICK_VALUE 3
102 static const guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000, 60000, 600000 };
104 #define CALC_TYPE_SUM 0
105 #define CALC_TYPE_COUNT 1
106 #define CALC_TYPE_MAX 2
107 #define CALC_TYPE_MIN 3
108 #define CALC_TYPE_AVG 4
109 #define CALC_TYPE_LOAD 5
110 #define MAX_CALC_TYPES 6
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;
147 io_stat_calc_type_t calc_types[MAX_CALC_TYPES];
149 GtkWidget *calc_field;
152 construct_args_t *args;
153 GtkWidget *filter_bt;
157 typedef struct _io_stat_t {
158 gboolean needs_redraw;
159 gint32 interval; /* measurement interval in ms */
160 guint32 last_interval;
161 guint32 max_interval; /* XXX max_interval and num_items are redundant */
163 guint32 left_x_border;
164 guint32 right_x_border;
165 gboolean view_as_time;
168 struct _io_stat_graph_t graphs[MAX_GRAPHS];
170 GtkWidget *draw_area;
172 GtkAdjustment *scrollbar_adjustment;
173 GtkWidget *scrollbar;
174 guint first_frame_num[NUM_IO_ITEMS];
175 guint last_frame_num;
184 static void init_io_stat_window(io_stat_t *io);
185 static gint filter_callback(GtkWidget *widget _U_, io_stat_graph_t *gio);
188 io_stat_set_title(io_stat_t *io)
195 title = g_strdup_printf("Wireshark IO Graphs: %s", cf_get_display_name(&cfile));
196 gtk_window_set_title(GTK_WINDOW(io->window), title);
201 io_stat_reset(io_stat_t *io)
205 io->needs_redraw=TRUE;
206 for(i=0;i<MAX_GRAPHS;i++){
207 for(j=0;j<NUM_IO_ITEMS;j++){
209 ioi=&io->graphs[i].items[j];
223 nstime_set_zero(&ioi->time_max);
224 nstime_set_zero(&ioi->time_min);
225 nstime_set_zero(&ioi->time_tot);
228 io->last_interval=0xffffffff;
231 io->start_time.secs=0;
232 io->start_time.nsecs=0;
233 for(j=0;j<NUM_IO_ITEMS;j++) {
234 io->first_frame_num[j]=0;
236 io->last_frame_num=0;
238 io_stat_set_title(io);
242 gtk_iostat_reset(void *g)
244 io_stat_graph_t *gio=g;
246 io_stat_reset(gio->io);
250 gtk_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
252 io_stat_graph_t *git=g;
257 /* we sometimes get called when git is disabled.
258 this is a bug since the tap listener should be removed first */
263 git->io->needs_redraw=TRUE;
266 * Find which interval this is supposed to go in and store the
267 * interval index as idx
269 time_delta=pinfo->fd->rel_ts;
270 if(time_delta.nsecs<0){
272 time_delta.nsecs+=1000000000;
274 if(time_delta.secs<0){
277 idx=(int) ((time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval);
279 /* some sanity checks */
280 if((idx<0)||(idx>=NUM_IO_ITEMS)){
281 git->io->num_items = NUM_IO_ITEMS-1;
285 /* update num_items */
286 if((guint32)idx > git->io->num_items){
287 git->io->num_items=idx;
288 git->io->max_interval=(idx+1)*git->io->interval;
292 if(git->io->start_time.secs == 0 && git->io->start_time.nsecs == 0) {
293 git->io->start_time = pinfo->fd->abs_ts;
296 /* set first and last frame num in current interval */
297 if (git->io->first_frame_num[idx] == 0) {
298 git->io->first_frame_num[idx]=pinfo->fd->num;
300 git->io->last_frame_num=pinfo->fd->num;
303 * Find the appropriate io_item_t structure
309 * For ADVANCED mode we need to keep track of some more stuff
310 * than just frame and byte counts
312 if(git->io->count_type==COUNT_TYPE_ADVANCED){
316 gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index);
321 /* update the appropriate counters, make sure that if
322 * fields==0 then this is the first seen value so
323 * set any min/max values accordingly
325 for(i=0;i<gp->len;i++){
331 switch(proto_registrar_get_ftype(git->hf_index)){
336 new_int=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
338 if((new_int>it->int_max)||(it->fields==0)){
341 if((new_int<it->int_min)||(it->fields==0)){
344 it->int_tot+=new_int;
351 new_int=fvalue_get_sinteger(&((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;
362 new_float=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
363 if((new_float>it->float_max)||(it->fields==0)){
364 it->float_max=new_float;
366 if((new_float<it->float_min)||(it->fields==0)){
367 it->float_min=new_float;
369 it->float_tot+=new_float;
373 new_double=fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
374 if((new_double>it->double_max)||(it->fields==0)){
375 it->double_max=new_double;
377 if((new_double<it->double_min)||(it->fields==0)){
378 it->double_min=new_double;
380 it->double_tot+=new_double;
383 case FT_RELATIVE_TIME:
384 new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
386 switch(git->calc_type){
387 guint64 t, pt; /* time in us */
390 /* it is a LOAD calculation of a relative time field.
391 * add the time this call spanned to each
392 * interval it spanned according to its contribution
396 t=t*1000000+new_time->nsecs/1000;
398 /* handle current interval */
399 pt=pinfo->fd->rel_ts.secs*1000000+pinfo->fd->rel_ts.nsecs/1000;
400 pt=pt%(git->io->interval*1000);
405 git->items[i].time_tot.nsecs+=(int) (pt*1000);
406 if(git->items[i].time_tot.nsecs>1000000000){
407 git->items[i].time_tot.secs++;
408 git->items[i].time_tot.nsecs-=1000000000;
416 if(t > (guint32) (git->io->interval*1000)){
417 pt=git->io->interval*1000;
424 if( (new_time->secs>it->time_max.secs)
425 ||( (new_time->secs==it->time_max.secs)
426 &&(new_time->nsecs>it->time_max.nsecs))
428 it->time_max=*new_time;
430 if( (new_time->secs<it->time_min.secs)
431 ||( (new_time->secs==it->time_min.secs)
432 &&(new_time->nsecs<it->time_min.nsecs))
434 it->time_min=*new_time;
436 nstime_add(&it->time_tot, new_time);
444 it->bytes+=pinfo->fd->pkt_len;
450 get_frame_num(io_stat_t *io, guint32 idx, gboolean first)
452 guint i, frame_num=0;
454 if (idx>io->num_items) {
459 frame_num=io->first_frame_num[idx];
464 * If first frame not found we select the last
465 * frame in the previous interval
467 * If selecting the last frame we select the frame
468 * before the first frame in the next interval
470 for(i=idx+1;i<=io->num_items;i++) {
471 frame_num=io->first_frame_num[i];
472 if (frame_num != 0) {
478 * If not found we select the last frame
480 frame_num=io->last_frame_num;
487 get_it_value(io_stat_t *io, int graph_id, int idx)
493 it=&io->graphs[graph_id].items[idx];
495 switch(io->count_type){
496 case COUNT_TYPE_FRAMES:
498 case COUNT_TYPE_BYTES:
500 case COUNT_TYPE_BITS:
501 return (it->bytes * 8);
505 adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
508 switch(io->graphs[graph_id].calc_type){
509 case CALC_TYPE_COUNT:
524 switch(io->graphs[graph_id].calc_type){
528 case CALC_TYPE_COUNT:
539 value=it->int_tot/it->fields;
549 switch(io->graphs[graph_id].calc_type){
553 case CALC_TYPE_COUNT:
564 value=it->float_tot/it->fields;
574 switch(io->graphs[graph_id].calc_type){
576 value=it->double_tot;
578 case CALC_TYPE_COUNT:
582 value=it->double_max;
585 value=it->double_min;
589 value=it->double_tot/it->fields;
598 case FT_RELATIVE_TIME:
599 switch(io->graphs[graph_id].calc_type){
600 case CALC_TYPE_COUNT:
604 value=(guint32) (it->time_max.secs*1000000+it->time_max.nsecs/1000);
607 value=(guint32) (it->time_min.secs*1000000+it->time_min.nsecs/1000);
610 value=(guint32) (it->time_tot.secs*1000000+it->time_tot.nsecs/1000);
614 guint64 t; /* time in us */
617 t=t*1000000+it->time_tot.nsecs/1000;
618 value=(guint32) (t/it->fields);
624 value=(guint32) ((it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval);
633 return (guint32)value; /* FIXME: loss of precision, visible on the graph for small values */
638 print_time_scale_string(char *buf, int buf_len, guint32 t, guint32 t_max, gboolean log)
640 if(t_max>=10000000 || (log && t_max>=1000000)){
641 g_snprintf(buf, buf_len, "%ds",t/1000000);
642 } else if(t_max>=1000000){
643 g_snprintf(buf, buf_len, "%d.%1ds",t/1000000,(t%1000000)/100000);
644 } else if(t_max>=10000 || (log && t_max>=1000)){
645 g_snprintf(buf, buf_len, "%dms",t/1000);
646 } else if(t_max>=1000){
647 g_snprintf(buf, buf_len, "%d.%1dms",t/1000,(t%1000)/100);
649 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);
678 if(io->interval>=60000 && ext){
679 g_snprintf(buf, buf_len, "%d%s", interval/60000, ext?"m":"");
680 } else if(io->interval>=1000){
681 g_snprintf(buf, buf_len, "%d%s", interval/1000, ext?"s":"");
682 } else if(io->interval>=100){
683 g_snprintf(buf, buf_len, "%d.%1d%s", interval/1000,(interval/100)%10, ext?"s":"");
684 } else if(io->interval>=10){
685 g_snprintf(buf, buf_len, "%d.%02d%s", interval/1000,(interval/10)%100, ext?"s":"");
687 g_snprintf(buf, buf_len, "%d.%03d%s", interval/1000,interval%1000, ext?"s":"");
693 io_stat_draw(io_stat_t *io)
695 int i, tics, ystart, ys;
696 guint32 last_interval, first_interval, interval_delta;
697 gint32 current_interval;
698 guint32 top_y_border;
699 guint32 bottom_y_border;
701 int label_width, label_height;
702 guint32 draw_width, draw_height;
703 char label_string[45];
706 guint32 num_time_intervals;
707 guint32 max_value; /* max value of seen data */
708 guint32 max_y; /* max value of the Y scale */
709 gboolean draw_y_as_time;
711 if(!io->needs_redraw){
714 io->needs_redraw=FALSE;
718 * Find the length of the intervals we have data for
719 * so we know how large arrays we need to malloc()
721 num_time_intervals=io->num_items+1;
723 /* XXX move this check to _packet() */
724 if(num_time_intervals>NUM_IO_ITEMS){
725 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
731 * find the max value so we can autoscale the y axis
734 for(i=0;i<MAX_GRAPHS;i++){
737 if(!io->graphs[i].display){
740 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
743 val=get_it_value(io, i, idx);
745 /* keep track of the max value we have encountered */
757 gdk_draw_rectangle(io->pixmap,
758 io->draw_area->style->white_gc,
761 io->draw_area->allocation.width,
762 io->draw_area->allocation.height);
765 * Calculate the y scale we should use
767 if(io->max_y_units==AUTO_MAX_YSCALE){
768 max_y=yscale_max[MAX_YSCALE-1];
769 for(i=MAX_YSCALE-1;i>1;i--){
770 if(max_value<yscale_max[i]){
774 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
776 for(i=1000000000;i>1;i/=10){
777 if(max_value<(guint32)i){
782 /* the user had specified an explicit y scale to use */
783 max_y=io->max_y_units;
788 * If we use ADVANCED and all the graphs are plotting
789 * either MIN/MAX/AVG of an FT_RELATIVE_TIME field
790 * then we will do some some special processing for the
791 * labels for the Y axis below:
792 * we will append the time unit " s" " ms" or " us"
793 * and we will present the unit in decimal
795 draw_y_as_time=FALSE;
796 if(io->count_type==COUNT_TYPE_ADVANCED){
798 for(i=0;i<MAX_GRAPHS;i++){
801 if(!io->graphs[i].display){
804 adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
806 case FT_RELATIVE_TIME:
807 switch(io->graphs[i].calc_type){
814 draw_y_as_time=FALSE;
818 draw_y_as_time=FALSE;
826 * Calculate size of borders surrounding the plot
827 * The border on the right side needs to be adjusted depending
828 * on the width of the text labels. For simplicity we assume that the
829 * top y scale label will be the widest one
832 if(io->max_y_units==LOGARITHMIC_YSCALE){
833 print_time_scale_string(label_string, 15, 100000, 100000, TRUE); /* 100 ms */
835 print_time_scale_string(label_string, 15, max_y, max_y, FALSE);
838 g_snprintf(label_string, 15, "%d", max_y);
841 layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
842 pango_layout_get_pixel_size(layout, &label_width, &label_height);
844 io->left_x_border=10;
845 io->right_x_border=label_width+20;
847 bottom_y_border=label_height+20;
851 * Calculate the size of the drawing area for the actual plot
853 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
854 draw_height=io->pixmap_height-top_y_border-bottom_y_border;
858 * Add a warning if too many entries
860 if (num_time_intervals == NUM_IO_ITEMS) {
861 g_snprintf (label_string, 45, "Warning: Graph limited to %d entries", NUM_IO_ITEMS);
862 pango_layout_set_text(layout, label_string, -1);
863 gdk_draw_layout(io->pixmap,
864 io->draw_area->style->black_gc, 5,
865 io->pixmap_height-bottom_y_border-draw_height-label_height/2,
870 * Draw the y axis and labels
871 * (we always draw the y scale with 11 ticks along the axis)
873 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
874 io->pixmap_width-io->right_x_border+1,
876 io->pixmap_width-io->right_x_border+1,
877 io->pixmap_height-bottom_y_border);
879 if(io->max_y_units==LOGARITHMIC_YSCALE){
880 tics=(int)log10((double)max_y);
881 ystart=draw_height/10;
888 for(i=ys;i<=tics;i++){
889 int xwidth, lwidth, ypos;
892 if(io->max_y_units==LOGARITHMIC_YSCALE){
894 /* position for the 0 value */
895 ypos=io->pixmap_height-bottom_y_border;
897 /* position for the top value, do not draw logarithmic tics above graph */
898 ypos=io->pixmap_height-bottom_y_border-draw_height;
901 /* draw the logarithmic tics */
903 ypos=(int)(io->pixmap_height-bottom_y_border-(draw_height-ystart)*(i+log10((double)j))/tics-ystart);
905 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
906 io->pixmap_width-io->right_x_border+1, ypos,
907 io->pixmap_width-io->right_x_border+1+xwidth, ypos);
909 ypos=io->pixmap_height-bottom_y_border-(draw_height-ystart)*i/tics-ystart;
911 /* all "main" logarithmic lines are slightly longer */
915 /* first, middle and last tick are slightly longer */
918 ypos=io->pixmap_height-bottom_y_border-draw_height*i/10;
921 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
922 io->pixmap_width-io->right_x_border+1, ypos,
923 io->pixmap_width-io->right_x_border+1+xwidth, ypos);
924 /* draw the labels */
927 if(io->max_y_units==LOGARITHMIC_YSCALE){
928 value=(guint32)(max_y/pow(10,tics-i));
930 print_time_scale_string(label_string, 15, value, value, TRUE);
932 g_snprintf(label_string, 15, "%d", value);
937 print_time_scale_string(label_string, 15, value, max_y, FALSE);
939 g_snprintf(label_string, 15, "%d", value);
943 pango_layout_set_text(layout, label_string, -1);
944 pango_layout_get_pixel_size(layout, &lwidth, NULL);
945 gdk_draw_layout(io->pixmap,
946 io->draw_area->style->black_gc,
947 io->pixmap_width-io->right_x_border+15+label_width-lwidth,
955 * if we have not specified the last_interval via the gui,
956 * then just pick the current end of the capture so that is scrolls
957 * nicely when doing live captures
959 if(io->last_interval==0xffffffff){
960 last_interval=io->max_interval;
962 last_interval=io->last_interval;
969 /* plot the x-scale */
970 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc, io->left_x_border, io->pixmap_height-bottom_y_border+1, io->pixmap_width-io->right_x_border+1, io->pixmap_height-bottom_y_border+1);
972 if((last_interval/io->interval)>=draw_width/io->pixels_per_tick){
973 first_interval=(last_interval/io->interval)-draw_width/io->pixels_per_tick+1;
974 first_interval*=io->interval;
979 interval_delta=(100/io->pixels_per_tick)*io->interval;
980 for(current_interval=last_interval;current_interval>=(gint32)first_interval;current_interval=current_interval-io->interval){
983 /* if pixels_per_tick is 1 or 2, only draw every 10 ticks */
984 /* if pixels_per_tick is 5, only draw every 5 ticks */
985 if(((io->pixels_per_tick<5) && (current_interval%(10*io->interval))) ||
986 ((io->pixels_per_tick==5) && (current_interval%(5*io->interval)))){
990 if(!(current_interval%interval_delta)){
992 } else if(!(current_interval%(interval_delta/2))){
998 x=draw_width+io->left_x_border-((last_interval-current_interval)/io->interval)*io->pixels_per_tick;
999 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
1000 x-1-io->pixels_per_tick/2,
1001 io->pixmap_height-bottom_y_border+1,
1002 x-1-io->pixels_per_tick/2,
1003 io->pixmap_height-bottom_y_border+xlen+1);
1007 print_interval_string (label_string, 15, current_interval, io, TRUE);
1008 pango_layout_set_text(layout, label_string, -1);
1009 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1011 if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) {
1013 } else if ((x-1-io->pixels_per_tick/2+lwidth/2) > (io->pixmap_width-5)) {
1014 x_pos=io->pixmap_width-lwidth-5;
1016 x_pos=x-1-io->pixels_per_tick/2-lwidth/2;
1019 gdk_draw_layout(io->pixmap,
1020 io->draw_area->style->black_gc,
1022 io->pixmap_height-bottom_y_border+15,
1027 g_object_unref(G_OBJECT(layout));
1031 * Loop over all graphs and draw them
1033 for(i=MAX_GRAPHS-1;i>=0;i--){
1035 guint32 interval, x_pos, y_pos, prev_x_pos, prev_y_pos;
1037 if(!io->graphs[i].display){
1041 /* initialize prev x/y to the value of the first interval */
1042 prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval)+io->left_x_border;
1043 val=get_it_value(io, i, first_interval/io->interval);
1046 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1048 prev_y_pos=(guint32)(draw_height-1+top_y_border);
1050 prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1053 prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1056 for(interval=first_interval;interval<last_interval;interval+=io->interval){
1057 x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval)+io->left_x_border;
1059 val=get_it_value(io, i, interval/io->interval);
1062 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1064 y_pos=(guint32)(draw_height-1+top_y_border);
1066 y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1069 y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1072 switch(io->graphs[i].plot_style){
1073 case PLOT_STYLE_LINE:
1074 /* dont need to draw anything if the segment
1075 * is entirely above the top of the graph
1077 if( (prev_y_pos!=0) || (y_pos!=0) ){
1078 gdk_draw_line(io->pixmap, io->graphs[i].gc,
1079 prev_x_pos, prev_y_pos,
1083 case PLOT_STYLE_IMPULSE:
1085 gdk_draw_line(io->pixmap, io->graphs[i].gc,
1086 x_pos, draw_height-1+top_y_border,
1090 case PLOT_STYLE_FILLED_BAR:
1092 gdk_draw_rectangle(io->pixmap,
1093 io->graphs[i].gc, TRUE,
1094 x_pos-io->pixels_per_tick/2,
1096 io->pixels_per_tick,
1097 draw_height-1+top_y_border-y_pos);
1100 case PLOT_STYLE_DOT:
1102 gdk_draw_rectangle(io->pixmap,
1103 io->graphs[i].gc, TRUE,
1104 x_pos-io->pixels_per_tick/2,
1105 y_pos-io->pixels_per_tick/2,
1106 io->pixels_per_tick,
1107 io->pixels_per_tick);
1119 gdk_draw_pixmap(io->draw_area->window,
1120 io->draw_area->style->fg_gc[GTK_WIDGET_STATE(io->draw_area)],
1124 io->pixmap_width, io->pixmap_height);
1127 /* update the scrollbar */
1128 if (io->max_interval == 0) {
1129 io->scrollbar_adjustment->upper=(gfloat) io->interval;
1130 io->scrollbar_adjustment->step_increment=(gfloat) (io->interval/10);
1131 io->scrollbar_adjustment->page_increment=(gfloat) io->interval;
1133 io->scrollbar_adjustment->upper=(gfloat) io->max_interval;
1134 io->scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1135 io->scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1137 io->scrollbar_adjustment->page_size=io->scrollbar_adjustment->page_increment;
1138 io->scrollbar_adjustment->value=(gfloat)first_interval;
1139 gtk_adjustment_changed(io->scrollbar_adjustment);
1140 gtk_adjustment_value_changed(io->scrollbar_adjustment);
1145 io_stat_redraw(io_stat_t *io)
1147 io->needs_redraw=TRUE;
1152 gtk_iostat_draw(void *g)
1154 io_stat_graph_t *git=g;
1156 io_stat_draw(git->io);
1160 /* ok we get called with both the filter and the field.
1161 make sure the field is part of the filter.
1162 (make sure and make sure just append it)
1163 the field MUST be part of the filter or else we wont
1164 be able to pick up the field values after the edt tree has been
1168 enable_graph(io_stat_graph_t *gio, const char *filter, const char *field)
1170 char real_filter[262];
1176 /* skip all whitespaces */
1189 g_snprintf(real_filter, 257, "(%s)", filter);
1194 /* skip all whitespaces */
1207 if(real_filter[0]!=0){
1208 g_strlcat(real_filter, " && ", 262);
1210 g_strlcat(real_filter, field, 262);
1213 return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
1214 gtk_iostat_reset, gtk_iostat_packet, gtk_iostat_draw);
1218 disable_graph(io_stat_graph_t *gio)
1222 protect_thread_critical_region();
1223 remove_tap_listener(gio);
1224 unprotect_thread_critical_region();
1225 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
1231 gtk_iostat_init(const char *optarg _U_, void* userdata _U_)
1235 static color_t col[MAX_GRAPHS] = {
1236 {0, 0x0000, 0x0000, 0x0000},
1237 {0, 0xffff, 0x0000, 0x0000},
1238 {0, 0x0000, 0xffff, 0x0000},
1239 {0, 0x0000, 0x0000, 0xffff},
1240 {0, 0xffff, 0x5000, 0xffff}
1242 GString *error_string;
1244 io=g_malloc(sizeof(io_stat_t));
1245 io->needs_redraw=TRUE;
1251 io->scrollbar_adjustment=NULL;
1252 io->pixmap_width=500;
1253 io->pixmap_height=200;
1254 io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
1255 io->max_y_units=AUTO_MAX_YSCALE;
1257 io->last_interval=0xffffffff;
1260 io->left_x_border=0;
1261 io->right_x_border=500;
1262 io->view_as_time=FALSE;
1263 io->start_time.secs=0;
1264 io->start_time.nsecs=0;
1266 for(i=0;i<MAX_GRAPHS;i++){
1267 io->graphs[i].gc=NULL;
1268 io->graphs[i].color.pixel=col[i].pixel;
1269 io->graphs[i].color.red=col[i].red;
1270 io->graphs[i].color.green=col[i].green;
1271 io->graphs[i].color.blue=col[i].blue;
1272 io->graphs[i].display=0;
1273 io->graphs[i].display_button=NULL;
1274 io->graphs[i].filter_field=NULL;
1275 io->graphs[i].advanced_buttons=NULL;
1276 io->graphs[i].io=io;
1278 io->graphs[i].args=g_malloc(sizeof(construct_args_t));
1279 io->graphs[i].args->title = NULL;
1280 io->graphs[i].args->wants_apply_button=TRUE;
1281 io->graphs[i].args->activate_on_ok=TRUE;
1282 io->graphs[i].args->modal_and_transient=FALSE;
1284 io->graphs[i].filter_bt=NULL;
1288 error_string=enable_graph(&io->graphs[0], NULL, NULL);
1290 fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
1292 g_string_free(error_string, TRUE);
1293 io->graphs[0].display=0;
1294 io->graphs[0].display_button=NULL;
1295 io->graphs[0].filter_field=NULL;
1296 io->graphs[0].advanced_buttons=NULL;
1301 init_io_stat_window(io);
1303 cf_retap_packets(&cfile, FALSE);
1304 gdk_window_raise(io->window->window);
1309 quit(GtkWidget *widget, GdkEventExpose *event _U_)
1314 io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1316 for(i=0;i<MAX_GRAPHS;i++){
1317 if(io->graphs[i].display){
1318 protect_thread_critical_region();
1319 remove_tap_listener(&io->graphs[i]);
1320 unprotect_thread_critical_region();
1322 g_free( (gpointer) (io->graphs[i].args->title) );
1323 io->graphs[i].args->title=NULL;
1325 g_free(io->graphs[i].args);
1326 io->graphs[i].args=NULL;
1335 pixmap_clicked_event(GtkWidget *widget, GdkEventButton *event)
1337 io_stat_t *io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1338 guint32 draw_width, interval, last_interval;
1345 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
1347 if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) ||
1348 (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) {
1349 /* Outside draw area */
1353 if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
1355 * Button 1 selects the first package in the interval.
1356 * Button 3 selects the last package in the interval.
1358 if (io->last_interval==0xffffffff) {
1359 last_interval=io->max_interval;
1361 last_interval=io->last_interval;
1364 interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick);
1365 frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE);
1366 if (frame_num != 0) {
1367 cf_goto_frame(&cfile, frame_num);
1374 /* create a new backing pixmap of the appropriate size */
1376 configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1380 #if GTK_CHECK_VERSION(2,6,0)
1384 io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1390 gdk_pixmap_unref(io->pixmap);
1394 io->pixmap=gdk_pixmap_new(widget->window,
1395 widget->allocation.width,
1396 widget->allocation.height,
1398 io->pixmap_width=widget->allocation.width;
1399 io->pixmap_height=widget->allocation.height;
1401 #if GTK_CHECK_VERSION(2,6,0)
1402 save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1403 g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
1404 gtk_widget_set_sensitive(save_bt, TRUE);
1407 gdk_draw_rectangle(io->pixmap,
1408 widget->style->white_gc,
1411 widget->allocation.width,
1412 widget->allocation.height);
1414 /* set up the colors and the GC structs for this pixmap */
1415 for(i=0;i<MAX_GRAPHS;i++){
1416 io->graphs[i].gc=gdk_gc_new(io->pixmap);
1417 gdk_gc_set_rgb_fg_color(io->graphs[i].gc, &io->graphs[i].color);
1425 scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1427 io_stat_t *io=(io_stat_t *)data;
1430 mi=(guint32) (io->scrollbar_adjustment->value+io->scrollbar_adjustment->page_size);
1431 if(io->last_interval==mi){
1434 if( (io->last_interval==0xffffffff)
1435 && (mi==io->max_interval) ){
1439 io->last_interval=(mi/io->interval)*io->interval;
1445 /* redraw the screen from the backing pixmap */
1447 expose_event(GtkWidget *widget, GdkEventExpose *event)
1451 io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1457 gdk_draw_pixmap(widget->window,
1458 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1460 event->area.x, event->area.y,
1461 event->area.x, event->area.y,
1462 event->area.width, event->area.height);
1469 create_draw_area(io_stat_t *io, GtkWidget *box)
1471 io->draw_area=gtk_drawing_area_new();
1472 g_signal_connect(io->draw_area, "destroy", G_CALLBACK(quit), io);
1473 g_object_set_data(G_OBJECT(io->draw_area), "io_stat_t", io);
1475 gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
1477 /* signals needed to handle backing pixmap */
1478 g_signal_connect(io->draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1479 g_signal_connect(io->draw_area, "configure_event", G_CALLBACK(configure_event), io);
1480 gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
1481 g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), NULL);
1483 gtk_widget_show(io->draw_area);
1484 gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1486 /* create the associated scrollbar */
1487 io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1488 io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
1489 gtk_widget_show(io->scrollbar);
1490 gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
1491 g_signal_connect(io->scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), io);
1496 tick_interval_select(GtkWidget *item, gpointer key)
1501 io=(io_stat_t *)key;
1502 val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1505 cf_retap_packets(&cfile, FALSE);
1506 gdk_window_raise(io->window->window);
1511 pixels_per_tick_select(GtkWidget *item, gpointer key)
1516 io=(io_stat_t *)key;
1517 val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1518 io->pixels_per_tick=val;
1523 plot_style_select(GtkWidget *item, gpointer key)
1526 io_stat_graph_t *ppt;
1528 ppt=(io_stat_graph_t *)key;
1529 val=(long)g_object_get_data(G_OBJECT(item), "plot_style");
1531 ppt->plot_style=val;
1533 io_stat_redraw(ppt->io);
1537 create_pixels_per_tick_menu_items(io_stat_t *io, GtkWidget *menu)
1540 GtkWidget *menu_item;
1543 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1544 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1545 menu_item=gtk_menu_item_new_with_label(str);
1547 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1548 GUINT_TO_POINTER(pixels_per_tick[i]));
1549 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), io);
1550 gtk_widget_show(menu_item);
1551 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1553 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1559 yscale_select(GtkWidget *item, gpointer key)
1564 io=(io_stat_t *)key;
1565 val=(long)g_object_get_data(G_OBJECT(item), "yscale_max");
1567 io->max_y_units=val;
1572 create_tick_interval_menu_items(io_stat_t *io, GtkWidget *menu)
1575 GtkWidget *menu_item;
1578 for(i=0;i<MAX_TICK_VALUES;i++){
1579 if(tick_interval_values[i]>=60000){
1580 g_snprintf(str, 15, "%u min", tick_interval_values[i]/60000);
1581 } else if(tick_interval_values[i]>=1000){
1582 g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
1583 } else if(tick_interval_values[i]>=100){
1584 g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
1585 } else if(tick_interval_values[i]>=10){
1586 g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
1588 g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
1591 menu_item=gtk_menu_item_new_with_label(str);
1592 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1593 GUINT_TO_POINTER(tick_interval_values[i]));
1594 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)io);
1595 gtk_widget_show(menu_item);
1596 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1598 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1603 create_yscale_max_menu_items(io_stat_t *io, GtkWidget *menu)
1606 GtkWidget *menu_item;
1609 for(i=0;i<MAX_YSCALE;i++){
1610 if(yscale_max[i]==LOGARITHMIC_YSCALE){
1611 g_strlcpy(str, "Logarithmic", 15);
1612 } else if(yscale_max[i]==AUTO_MAX_YSCALE){
1613 g_strlcpy(str, "Auto", 15);
1615 g_snprintf(str, 15, "%u", yscale_max[i]);
1617 menu_item=gtk_menu_item_new_with_label(str);
1618 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1619 GUINT_TO_POINTER(yscale_max[i]));
1620 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), io);
1621 gtk_widget_show(menu_item);
1622 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1624 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_YSCALE);
1629 count_type_select(GtkWidget *item, gpointer key)
1631 static gboolean advanced_visible=FALSE;
1635 io=(io_stat_t *)key;
1636 val=(long)g_object_get_data(G_OBJECT(item), "count_type");
1640 if(io->count_type==COUNT_TYPE_ADVANCED){
1641 for(i=0;i<MAX_GRAPHS;i++){
1642 disable_graph(&io->graphs[i]);
1643 gtk_widget_show(io->graphs[i].advanced_buttons);
1644 /* redraw the entire window so the unhidden widgets show up, hopefully */
1645 {GdkRectangle update_rect;
1648 update_rect.width=io->window->allocation.width;
1649 update_rect.height=io->window->allocation.height;
1650 gtk_widget_draw(io->window, &update_rect);
1653 advanced_visible=TRUE;
1655 } else if (advanced_visible) {
1656 for(i=0;i<MAX_GRAPHS;i++){
1657 gtk_widget_hide(io->graphs[i].advanced_buttons);
1658 filter_callback(item, &io->graphs[i]);
1660 advanced_visible=FALSE;
1667 create_frames_or_bytes_menu_items(io_stat_t *io, GtkWidget *menu)
1669 GtkWidget *menu_item;
1672 for(i=0;i<MAX_COUNT_TYPES;i++){
1673 menu_item=gtk_menu_item_new_with_label(count_type_names[i]);
1674 g_object_set_data(G_OBJECT(menu_item), "count_type", GINT_TO_POINTER(i));
1675 g_signal_connect(menu_item, "activate", G_CALLBACK(count_type_select), io);
1676 gtk_widget_show(menu_item);
1677 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1683 create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, void (*func)(io_stat_t *io, GtkWidget *menu))
1687 GtkWidget *option_menu;
1690 hbox=gtk_hbox_new(FALSE, 0);
1691 gtk_container_add(GTK_CONTAINER(box), hbox);
1692 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1693 gtk_widget_show(hbox);
1695 label=gtk_label_new(name);
1696 gtk_widget_show(label);
1697 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1699 option_menu=gtk_option_menu_new();
1700 menu=gtk_menu_new();
1702 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1703 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1704 gtk_widget_show(option_menu);
1708 view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer key)
1712 io=(io_stat_t *)key;
1713 io->view_as_time = io->view_as_time ? FALSE : TRUE;
1719 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1721 GtkWidget *frame_vbox;
1726 frame_vbox=gtk_vbox_new(FALSE, 0);
1727 gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0);
1728 gtk_widget_show(frame_vbox);
1730 frame = gtk_frame_new("X Axis");
1731 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1732 gtk_widget_show(frame);
1734 vbox=gtk_vbox_new(FALSE, 0);
1735 gtk_container_add(GTK_CONTAINER(frame), vbox);
1736 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1737 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1738 gtk_widget_show(vbox);
1740 create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
1741 create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1743 view_cb = gtk_check_button_new_with_mnemonic("_View as time of day");
1744 gtk_container_add(GTK_CONTAINER(vbox), view_cb);
1745 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time);
1746 g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io);
1747 gtk_widget_show(view_cb);
1749 frame = gtk_frame_new("Y Axis");
1750 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1751 gtk_widget_show(frame);
1753 vbox=gtk_vbox_new(FALSE, 0);
1754 gtk_container_add(GTK_CONTAINER(frame), vbox);
1755 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1756 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1757 gtk_widget_show(vbox);
1759 create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
1760 create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
1767 filter_callback(GtkWidget *widget _U_, io_stat_graph_t *gio)
1770 const char *field=NULL;
1771 header_field_info *hfi;
1774 /* this graph is not active, just update display and redraw */
1775 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1777 io_stat_redraw(gio->io);
1781 /* first check if the field string is valid */
1782 if(gio->io->count_type==COUNT_TYPE_ADVANCED){
1783 field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
1785 /* warn and bail out if there was no field specified */
1786 if(field==NULL || field[0]==0){
1787 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
1789 io_stat_redraw(gio->io);
1792 /* warn and bail out if the field could not be found */
1793 hfi=proto_registrar_get_byname(field);
1795 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
1797 io_stat_redraw(gio->io);
1800 gio->hf_index=hfi->id;
1801 /* check that the type is compatible */
1813 /* these values support all calculations except LOAD */
1814 switch(gio->calc_type){
1815 case CALC_TYPE_LOAD:
1816 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1817 "LOAD(*) is only supported for relative-time fields.");
1819 io_stat_redraw(gio->io);
1822 /* these types support all calculations */
1824 case FT_RELATIVE_TIME:
1825 /* this type only supports COUNT, MAX, MIN, AVG */
1826 switch(gio->calc_type){
1828 case CALC_TYPE_COUNT:
1832 case CALC_TYPE_LOAD:
1835 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1836 "%s is a relative-time field, so %s calculations are not supported on it.",
1838 calc_type_names[gio->calc_type]);
1840 io_stat_redraw(gio->io);
1847 * XXX - support this if gint64/guint64 are
1850 if(gio->calc_type!=CALC_TYPE_COUNT){
1851 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1852 "%s is a 64-bit integer, so %s calculations are not supported on it.",
1854 calc_type_names[gio->calc_type]);
1856 io_stat_redraw(gio->io);
1861 if(gio->calc_type!=CALC_TYPE_COUNT){
1862 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1863 "%s doesn't have integral or float values, so %s calculations are not supported on it.",
1865 calc_type_names[gio->calc_type]);
1867 io_stat_redraw(gio->io);
1874 /* first check if the filter string is valid. */
1875 filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
1876 if(!dfilter_compile(filter, &dfilter)) {
1877 bad_dfilter_alert_box(filter);
1879 io_stat_redraw(gio->io);
1882 if (dfilter != NULL)
1883 dfilter_free(dfilter);
1885 /* ok, we have a valid filter and the graph is active.
1886 first just try to delete any previous settings and then apply
1889 protect_thread_critical_region();
1890 remove_tap_listener(gio);
1891 unprotect_thread_critical_region();
1893 io_stat_reset(gio->io);
1894 enable_graph(gio, filter, field);
1895 cf_retap_packets(&cfile, FALSE);
1896 gdk_window_raise(gio->io->window->window);
1897 io_stat_redraw(gio->io);
1904 calc_type_select(GtkWidget *item _U_, gpointer key)
1906 io_stat_calc_type_t *ct=(io_stat_calc_type_t *)key;
1908 ct->gio->calc_type=ct->calc_type;
1910 /* disable the graph */
1911 disable_graph(ct->gio);
1912 io_stat_redraw(ct->gio->io);
1917 create_calc_types_menu_items(io_stat_graph_t *gio, GtkWidget *menu)
1919 GtkWidget *menu_item;
1922 for(i=0;i<MAX_CALC_TYPES;i++){
1923 gio->calc_types[i].gio=gio;
1924 gio->calc_types[i].calc_type=i;
1925 menu_item=gtk_menu_item_new_with_label(calc_type_names[i]);
1926 g_signal_connect(menu_item, "activate", G_CALLBACK(calc_type_select), &gio->calc_types[i]);
1927 gtk_widget_show(menu_item);
1928 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1935 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name, void (*func)(io_stat_graph_t *io, GtkWidget *menu))
1939 GtkWidget *option_menu;
1942 hbox=gtk_hbox_new(FALSE, 0);
1943 gtk_container_add(GTK_CONTAINER(box), hbox);
1944 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1945 gtk_widget_show(hbox);
1947 label=gtk_label_new(name);
1948 gtk_widget_show(label);
1949 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1951 option_menu=gtk_option_menu_new();
1952 menu=gtk_menu_new();
1954 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1955 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1956 gtk_widget_show(option_menu);
1960 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
1963 gio->calc_field=gtk_entry_new();
1964 gtk_entry_set_max_length(GTK_ENTRY(gio->calc_field),50);
1965 gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0);
1966 gtk_widget_show(gio->calc_field);
1967 g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio);
1968 g_object_set_data (G_OBJECT(gio->calc_field), E_FILT_FIELD_NAME_ONLY_KEY, "");
1969 g_signal_connect(gio->calc_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
1970 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
1971 g_signal_connect(gio->calc_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
1972 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
1973 colorize_filter_te_as_empty(gio->calc_field);
1978 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
1982 hbox=gtk_hbox_new(FALSE, 0);
1983 gio->advanced_buttons=hbox;
1984 gtk_container_add(GTK_CONTAINER(box), hbox);
1985 gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START);
1986 gtk_widget_hide(hbox);
1988 gio->calc_type=CALC_TYPE_SUM;
1989 create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
1990 create_advanced_field(gio, hbox);
1995 filter_button_clicked(GtkWidget *w, gpointer uio)
1997 io_stat_graph_t *gio=(io_stat_graph_t *)uio;
1999 display_filter_construct_cb(w, gio->args);
2004 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
2006 GtkWidget *option_menu;
2008 GtkWidget *menu_item;
2014 hbox=gtk_hbox_new(FALSE, 3);
2015 gtk_container_add(GTK_CONTAINER(box), hbox);
2016 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2017 gtk_widget_show(hbox);
2019 g_snprintf(str, 256, "Graph %d", num);
2020 gio->display_button=gtk_toggle_button_new_with_label(str);
2021 gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
2022 gtk_widget_show(gio->display_button);
2023 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
2024 g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);
2026 label=gtk_label_new("Color");
2027 gtk_widget_show(label);
2028 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2030 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color);
2031 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color);
2032 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color);
2033 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color);
2034 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color);
2035 /* g_signal_connect(gio->display_button, "toggled", filter_callback, gio);*/
2038 /* filter prefs dialog */
2039 gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
2041 g_snprintf(str, 256, "Wireshark: Display Filter IO-Stat (Filter:%d)", num);
2042 g_free( (gpointer) (gio->args->title) );
2043 gio->args->title=g_strdup(str);
2045 g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio);
2046 g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
2048 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
2049 gtk_widget_show(gio->filter_bt);
2051 gio->filter_field=gtk_entry_new();
2052 gtk_entry_set_max_length(GTK_ENTRY(gio->filter_field),256);
2053 /* filter prefs dialog */
2054 g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field);
2055 /* filter prefs dialog */
2057 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0);
2058 gtk_widget_show(gio->filter_field);
2059 g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio);
2060 g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2061 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2062 g_signal_connect(gio->filter_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2063 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2064 colorize_filter_te_as_empty(gio->filter_field);
2066 create_advanced_box(gio, hbox);
2070 * create PlotStyle menu
2072 g_snprintf(str, 256, " Style:");
2073 label=gtk_label_new(str);
2074 gtk_widget_show(label);
2075 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2077 option_menu=gtk_option_menu_new();
2078 menu=gtk_menu_new();
2079 for(i=0;i<MAX_PLOT_STYLES;i++){
2080 menu_item=gtk_menu_item_new_with_label(plot_style_name[i]);
2081 g_object_set_data(G_OBJECT(menu_item), "plot_style", GINT_TO_POINTER(i));
2082 g_signal_connect(menu_item, "activate", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]);
2083 gtk_widget_show(menu_item);
2084 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
2086 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PLOT_STYLE);
2088 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
2089 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
2090 gtk_widget_show(option_menu);
2097 create_filter_area(io_stat_t *io, GtkWidget *box)
2103 frame=gtk_frame_new("Graphs");
2104 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2105 gtk_widget_show(frame);
2107 vbox=gtk_vbox_new(FALSE, 1);
2108 gtk_container_add(GTK_CONTAINER(frame), vbox);
2109 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
2110 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
2111 gtk_widget_show(vbox);
2113 for(i=0;i<MAX_GRAPHS;i++){
2114 create_filter_box(&io->graphs[i], vbox, i+1);
2122 copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer data)
2124 guint32 i, interval, val;
2127 GString *CSV_str=g_string_new("");
2128 io_stat_t *io=(io_stat_t *)data;
2130 g_string_append(CSV_str, "Interval start");
2131 for(i=0;i<MAX_GRAPHS;i++) {
2132 if (io->graphs[i].display) {
2133 g_string_append_printf(CSV_str, ",Graph %d", i+1);
2136 g_string_append(CSV_str,"\n");
2138 for(interval=0; interval<io->max_interval; interval+=io->interval) {
2139 print_interval_string (string, 15, interval, io, FALSE);
2140 g_string_append(CSV_str, string);
2141 for(i=0;i<MAX_GRAPHS;i++) {
2142 if (io->graphs[i].display) {
2143 val=get_it_value(io, i, interval/io->interval);
2144 g_string_append_printf(CSV_str, ",%d", val);
2147 g_string_append(CSV_str,"\n");
2150 /* Now that we have the CSV data, copy it into the default clipboard */
2151 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
2152 gtk_clipboard_set_text(cb, CSV_str->str, -1); /* Copy the CSV data into the clipboard */
2153 g_string_free(CSV_str, TRUE); /* Free the memory */
2158 init_io_stat_window(io_stat_t *io)
2163 GtkWidget *close_bt, *help_bt;
2164 GtkTooltips *tooltips = gtk_tooltips_new();
2166 #if GTK_CHECK_VERSION(2,6,0)
2170 /* create the main window */
2171 io->window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
2173 vbox=gtk_vbox_new(FALSE, 0);
2174 gtk_container_add(GTK_CONTAINER(io->window), vbox);
2175 gtk_widget_show(vbox);
2177 create_draw_area(io, vbox);
2179 hbox=gtk_hbox_new(FALSE, 3);
2180 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2181 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
2182 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2183 gtk_widget_show(hbox);
2185 create_filter_area(io, hbox);
2186 create_ctrl_area(io, hbox);
2188 io_stat_set_title(io);
2190 #if GTK_CHECK_VERSION(2,6,0)
2191 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE,
2192 GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
2194 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY,
2195 GTK_STOCK_HELP, NULL);
2197 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
2198 gtk_widget_show(bbox);
2200 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
2201 window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
2202 gtk_tooltips_set_tip(tooltips, close_bt, "Close this dialog", NULL);
2204 #if GTK_CHECK_VERSION(2,6,0)
2205 save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
2206 gtk_widget_set_sensitive(save_bt, FALSE);
2207 gtk_tooltips_set_tip(tooltips, save_bt, "Save the displayed graph to a file", NULL);
2208 g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
2209 g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt);
2212 copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
2213 gtk_tooltips_set_tip(tooltips, copy_bt,
2214 "Copy values from selected graphs to the clipboard in CSV (Comma Separated Values) format", NULL);
2215 g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io);
2217 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
2218 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG);
2219 gtk_tooltips_set_tip (tooltips, help_bt, "Show topic specific help", NULL);
2221 g_signal_connect(io->window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
2223 gtk_widget_show(io->window);
2224 window_present(io->window);
2229 gtk_iostat_cb(GtkWidget *w _U_, gpointer d _U_)
2231 gtk_iostat_init(NULL,NULL);
2238 register_tap_listener_gtk_iostat(void)
2240 register_stat_cmd_arg("io,stat", gtk_iostat_init,NULL);
2242 register_stat_menu_item_stock("_IO Graphs",
2243 REGISTER_STAT_GROUP_GENERIC, WIRESHARK_STOCK_GRAPHS,
2244 gtk_iostat_cb, NULL, NULL, NULL);