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 TL_REQUIRES_PROTO_TREE,
1215 gtk_iostat_reset, gtk_iostat_packet, gtk_iostat_draw);
1219 disable_graph(io_stat_graph_t *gio)
1223 protect_thread_critical_region();
1224 remove_tap_listener(gio);
1225 unprotect_thread_critical_region();
1226 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
1232 gtk_iostat_init(const char *optarg _U_, void* userdata _U_)
1236 static color_t col[MAX_GRAPHS] = {
1237 {0, 0x0000, 0x0000, 0x0000},
1238 {0, 0xffff, 0x0000, 0x0000},
1239 {0, 0x0000, 0xffff, 0x0000},
1240 {0, 0x0000, 0x0000, 0xffff},
1241 {0, 0xffff, 0x5000, 0xffff}
1243 GString *error_string;
1245 io=g_malloc(sizeof(io_stat_t));
1246 io->needs_redraw=TRUE;
1252 io->scrollbar_adjustment=NULL;
1253 io->pixmap_width=500;
1254 io->pixmap_height=200;
1255 io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
1256 io->max_y_units=AUTO_MAX_YSCALE;
1258 io->last_interval=0xffffffff;
1261 io->left_x_border=0;
1262 io->right_x_border=500;
1263 io->view_as_time=FALSE;
1264 io->start_time.secs=0;
1265 io->start_time.nsecs=0;
1267 for(i=0;i<MAX_GRAPHS;i++){
1268 io->graphs[i].gc=NULL;
1269 io->graphs[i].color.pixel=col[i].pixel;
1270 io->graphs[i].color.red=col[i].red;
1271 io->graphs[i].color.green=col[i].green;
1272 io->graphs[i].color.blue=col[i].blue;
1273 io->graphs[i].display=0;
1274 io->graphs[i].display_button=NULL;
1275 io->graphs[i].filter_field=NULL;
1276 io->graphs[i].advanced_buttons=NULL;
1277 io->graphs[i].io=io;
1279 io->graphs[i].args=g_malloc(sizeof(construct_args_t));
1280 io->graphs[i].args->title = NULL;
1281 io->graphs[i].args->wants_apply_button=TRUE;
1282 io->graphs[i].args->activate_on_ok=TRUE;
1283 io->graphs[i].args->modal_and_transient=FALSE;
1285 io->graphs[i].filter_bt=NULL;
1289 error_string=enable_graph(&io->graphs[0], NULL, NULL);
1291 fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
1293 g_string_free(error_string, TRUE);
1294 io->graphs[0].display=0;
1295 io->graphs[0].display_button=NULL;
1296 io->graphs[0].filter_field=NULL;
1297 io->graphs[0].advanced_buttons=NULL;
1302 init_io_stat_window(io);
1304 cf_retap_packets(&cfile);
1305 gdk_window_raise(io->window->window);
1310 quit(GtkWidget *widget, GdkEventExpose *event _U_)
1315 io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1317 for(i=0;i<MAX_GRAPHS;i++){
1318 if(io->graphs[i].display){
1319 protect_thread_critical_region();
1320 remove_tap_listener(&io->graphs[i]);
1321 unprotect_thread_critical_region();
1323 g_free( (gpointer) (io->graphs[i].args->title) );
1324 io->graphs[i].args->title=NULL;
1326 g_free(io->graphs[i].args);
1327 io->graphs[i].args=NULL;
1336 pixmap_clicked_event(GtkWidget *widget, GdkEventButton *event)
1338 io_stat_t *io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1339 guint32 draw_width, interval, last_interval;
1346 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
1348 if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) ||
1349 (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) {
1350 /* Outside draw area */
1354 if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
1356 * Button 1 selects the first package in the interval.
1357 * Button 3 selects the last package in the interval.
1359 if (io->last_interval==0xffffffff) {
1360 last_interval=io->max_interval;
1362 last_interval=io->last_interval;
1365 interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick);
1366 frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE);
1367 if (frame_num != 0) {
1368 cf_goto_frame(&cfile, frame_num);
1375 /* create a new backing pixmap of the appropriate size */
1377 configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1381 #if GTK_CHECK_VERSION(2,6,0)
1385 io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1391 gdk_pixmap_unref(io->pixmap);
1395 io->pixmap=gdk_pixmap_new(widget->window,
1396 widget->allocation.width,
1397 widget->allocation.height,
1399 io->pixmap_width=widget->allocation.width;
1400 io->pixmap_height=widget->allocation.height;
1402 #if GTK_CHECK_VERSION(2,6,0)
1403 save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1404 g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
1405 gtk_widget_set_sensitive(save_bt, TRUE);
1408 gdk_draw_rectangle(io->pixmap,
1409 widget->style->white_gc,
1412 widget->allocation.width,
1413 widget->allocation.height);
1415 /* set up the colors and the GC structs for this pixmap */
1416 for(i=0;i<MAX_GRAPHS;i++){
1417 io->graphs[i].gc=gdk_gc_new(io->pixmap);
1418 gdk_gc_set_rgb_fg_color(io->graphs[i].gc, &io->graphs[i].color);
1426 scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1428 io_stat_t *io=(io_stat_t *)data;
1431 mi=(guint32) (io->scrollbar_adjustment->value+io->scrollbar_adjustment->page_size);
1432 if(io->last_interval==mi){
1435 if( (io->last_interval==0xffffffff)
1436 && (mi==io->max_interval) ){
1440 io->last_interval=(mi/io->interval)*io->interval;
1446 /* redraw the screen from the backing pixmap */
1448 expose_event(GtkWidget *widget, GdkEventExpose *event)
1452 io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1458 gdk_draw_pixmap(widget->window,
1459 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1461 event->area.x, event->area.y,
1462 event->area.x, event->area.y,
1463 event->area.width, event->area.height);
1470 create_draw_area(io_stat_t *io, GtkWidget *box)
1472 io->draw_area=gtk_drawing_area_new();
1473 g_signal_connect(io->draw_area, "destroy", G_CALLBACK(quit), io);
1474 g_object_set_data(G_OBJECT(io->draw_area), "io_stat_t", io);
1476 gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
1478 /* signals needed to handle backing pixmap */
1479 g_signal_connect(io->draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1480 g_signal_connect(io->draw_area, "configure_event", G_CALLBACK(configure_event), io);
1481 gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
1482 g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), NULL);
1484 gtk_widget_show(io->draw_area);
1485 gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1487 /* create the associated scrollbar */
1488 io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1489 io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
1490 gtk_widget_show(io->scrollbar);
1491 gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
1492 g_signal_connect(io->scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), io);
1497 tick_interval_select(GtkWidget *item, gpointer key)
1502 io=(io_stat_t *)key;
1503 val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1506 cf_retap_packets(&cfile);
1507 gdk_window_raise(io->window->window);
1512 pixels_per_tick_select(GtkWidget *item, gpointer key)
1517 io=(io_stat_t *)key;
1518 val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1519 io->pixels_per_tick=val;
1524 plot_style_select(GtkWidget *item, gpointer key)
1527 io_stat_graph_t *ppt;
1529 ppt=(io_stat_graph_t *)key;
1530 val=(long)g_object_get_data(G_OBJECT(item), "plot_style");
1532 ppt->plot_style=val;
1534 io_stat_redraw(ppt->io);
1538 create_pixels_per_tick_menu_items(io_stat_t *io, GtkWidget *menu)
1541 GtkWidget *menu_item;
1544 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1545 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1546 menu_item=gtk_menu_item_new_with_label(str);
1548 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1549 GUINT_TO_POINTER(pixels_per_tick[i]));
1550 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), io);
1551 gtk_widget_show(menu_item);
1552 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1554 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1560 yscale_select(GtkWidget *item, gpointer key)
1565 io=(io_stat_t *)key;
1566 val=(long)g_object_get_data(G_OBJECT(item), "yscale_max");
1568 io->max_y_units=val;
1573 create_tick_interval_menu_items(io_stat_t *io, GtkWidget *menu)
1576 GtkWidget *menu_item;
1579 for(i=0;i<MAX_TICK_VALUES;i++){
1580 if(tick_interval_values[i]>=60000){
1581 g_snprintf(str, 15, "%u min", tick_interval_values[i]/60000);
1582 } else if(tick_interval_values[i]>=1000){
1583 g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
1584 } else if(tick_interval_values[i]>=100){
1585 g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
1586 } else if(tick_interval_values[i]>=10){
1587 g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
1589 g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
1592 menu_item=gtk_menu_item_new_with_label(str);
1593 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1594 GUINT_TO_POINTER(tick_interval_values[i]));
1595 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)io);
1596 gtk_widget_show(menu_item);
1597 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1599 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1604 create_yscale_max_menu_items(io_stat_t *io, GtkWidget *menu)
1607 GtkWidget *menu_item;
1610 for(i=0;i<MAX_YSCALE;i++){
1611 if(yscale_max[i]==LOGARITHMIC_YSCALE){
1612 g_strlcpy(str, "Logarithmic", 15);
1613 } else if(yscale_max[i]==AUTO_MAX_YSCALE){
1614 g_strlcpy(str, "Auto", 15);
1616 g_snprintf(str, 15, "%u", yscale_max[i]);
1618 menu_item=gtk_menu_item_new_with_label(str);
1619 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1620 GUINT_TO_POINTER(yscale_max[i]));
1621 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), io);
1622 gtk_widget_show(menu_item);
1623 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1625 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_YSCALE);
1630 count_type_select(GtkWidget *item, gpointer key)
1632 static gboolean advanced_visible=FALSE;
1636 io=(io_stat_t *)key;
1637 val=(long)g_object_get_data(G_OBJECT(item), "count_type");
1641 if(io->count_type==COUNT_TYPE_ADVANCED){
1642 for(i=0;i<MAX_GRAPHS;i++){
1643 disable_graph(&io->graphs[i]);
1644 gtk_widget_show(io->graphs[i].advanced_buttons);
1645 /* redraw the entire window so the unhidden widgets show up, hopefully */
1646 {GdkRectangle update_rect;
1649 update_rect.width=io->window->allocation.width;
1650 update_rect.height=io->window->allocation.height;
1651 gtk_widget_draw(io->window, &update_rect);
1654 advanced_visible=TRUE;
1656 } else if (advanced_visible) {
1657 for(i=0;i<MAX_GRAPHS;i++){
1658 gtk_widget_hide(io->graphs[i].advanced_buttons);
1659 filter_callback(item, &io->graphs[i]);
1661 advanced_visible=FALSE;
1668 create_frames_or_bytes_menu_items(io_stat_t *io, GtkWidget *menu)
1670 GtkWidget *menu_item;
1673 for(i=0;i<MAX_COUNT_TYPES;i++){
1674 menu_item=gtk_menu_item_new_with_label(count_type_names[i]);
1675 g_object_set_data(G_OBJECT(menu_item), "count_type", GINT_TO_POINTER(i));
1676 g_signal_connect(menu_item, "activate", G_CALLBACK(count_type_select), io);
1677 gtk_widget_show(menu_item);
1678 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1684 create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, void (*func)(io_stat_t *io, GtkWidget *menu))
1688 GtkWidget *option_menu;
1691 hbox=gtk_hbox_new(FALSE, 0);
1692 gtk_container_add(GTK_CONTAINER(box), hbox);
1693 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1694 gtk_widget_show(hbox);
1696 label=gtk_label_new(name);
1697 gtk_widget_show(label);
1698 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1700 option_menu=gtk_option_menu_new();
1701 menu=gtk_menu_new();
1703 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1704 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1705 gtk_widget_show(option_menu);
1709 view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer key)
1713 io=(io_stat_t *)key;
1714 io->view_as_time = io->view_as_time ? FALSE : TRUE;
1720 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1722 GtkWidget *frame_vbox;
1727 frame_vbox=gtk_vbox_new(FALSE, 0);
1728 gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0);
1729 gtk_widget_show(frame_vbox);
1731 frame = gtk_frame_new("X Axis");
1732 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1733 gtk_widget_show(frame);
1735 vbox=gtk_vbox_new(FALSE, 0);
1736 gtk_container_add(GTK_CONTAINER(frame), vbox);
1737 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1738 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1739 gtk_widget_show(vbox);
1741 create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
1742 create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1744 view_cb = gtk_check_button_new_with_mnemonic("_View as time of day");
1745 gtk_container_add(GTK_CONTAINER(vbox), view_cb);
1746 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time);
1747 g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io);
1748 gtk_widget_show(view_cb);
1750 frame = gtk_frame_new("Y Axis");
1751 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1752 gtk_widget_show(frame);
1754 vbox=gtk_vbox_new(FALSE, 0);
1755 gtk_container_add(GTK_CONTAINER(frame), vbox);
1756 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1757 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1758 gtk_widget_show(vbox);
1760 create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
1761 create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
1768 filter_callback(GtkWidget *widget _U_, io_stat_graph_t *gio)
1771 const char *field=NULL;
1772 header_field_info *hfi;
1775 /* this graph is not active, just update display and redraw */
1776 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1778 io_stat_redraw(gio->io);
1782 /* first check if the field string is valid */
1783 if(gio->io->count_type==COUNT_TYPE_ADVANCED){
1784 field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
1786 /* warn and bail out if there was no field specified */
1787 if(field==NULL || field[0]==0){
1788 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
1790 io_stat_redraw(gio->io);
1793 /* warn and bail out if the field could not be found */
1794 hfi=proto_registrar_get_byname(field);
1796 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
1798 io_stat_redraw(gio->io);
1801 gio->hf_index=hfi->id;
1802 /* check that the type is compatible */
1814 /* these values support all calculations except LOAD */
1815 switch(gio->calc_type){
1816 case CALC_TYPE_LOAD:
1817 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1818 "LOAD(*) is only supported for relative-time fields.");
1820 io_stat_redraw(gio->io);
1823 /* these types support all calculations */
1825 case FT_RELATIVE_TIME:
1826 /* this type only supports COUNT, MAX, MIN, AVG */
1827 switch(gio->calc_type){
1829 case CALC_TYPE_COUNT:
1833 case CALC_TYPE_LOAD:
1836 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1837 "%s is a relative-time field, so %s calculations are not supported on it.",
1839 calc_type_names[gio->calc_type]);
1841 io_stat_redraw(gio->io);
1848 * XXX - support this if gint64/guint64 are
1851 if(gio->calc_type!=CALC_TYPE_COUNT){
1852 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1853 "%s is a 64-bit integer, so %s calculations are not supported on it.",
1855 calc_type_names[gio->calc_type]);
1857 io_stat_redraw(gio->io);
1862 if(gio->calc_type!=CALC_TYPE_COUNT){
1863 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1864 "%s doesn't have integral or float values, so %s calculations are not supported on it.",
1866 calc_type_names[gio->calc_type]);
1868 io_stat_redraw(gio->io);
1875 /* first check if the filter string is valid. */
1876 filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
1877 if(!dfilter_compile(filter, &dfilter)) {
1878 bad_dfilter_alert_box(filter);
1880 io_stat_redraw(gio->io);
1883 if (dfilter != NULL)
1884 dfilter_free(dfilter);
1886 /* ok, we have a valid filter and the graph is active.
1887 first just try to delete any previous settings and then apply
1890 protect_thread_critical_region();
1891 remove_tap_listener(gio);
1892 unprotect_thread_critical_region();
1894 io_stat_reset(gio->io);
1895 enable_graph(gio, filter, field);
1896 cf_retap_packets(&cfile);
1897 gdk_window_raise(gio->io->window->window);
1898 io_stat_redraw(gio->io);
1905 calc_type_select(GtkWidget *item _U_, gpointer key)
1907 io_stat_calc_type_t *ct=(io_stat_calc_type_t *)key;
1909 ct->gio->calc_type=ct->calc_type;
1911 /* disable the graph */
1912 disable_graph(ct->gio);
1913 io_stat_redraw(ct->gio->io);
1918 create_calc_types_menu_items(io_stat_graph_t *gio, GtkWidget *menu)
1920 GtkWidget *menu_item;
1923 for(i=0;i<MAX_CALC_TYPES;i++){
1924 gio->calc_types[i].gio=gio;
1925 gio->calc_types[i].calc_type=i;
1926 menu_item=gtk_menu_item_new_with_label(calc_type_names[i]);
1927 g_signal_connect(menu_item, "activate", G_CALLBACK(calc_type_select), &gio->calc_types[i]);
1928 gtk_widget_show(menu_item);
1929 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1936 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name, void (*func)(io_stat_graph_t *io, GtkWidget *menu))
1940 GtkWidget *option_menu;
1943 hbox=gtk_hbox_new(FALSE, 0);
1944 gtk_container_add(GTK_CONTAINER(box), hbox);
1945 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1946 gtk_widget_show(hbox);
1948 label=gtk_label_new(name);
1949 gtk_widget_show(label);
1950 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1952 option_menu=gtk_option_menu_new();
1953 menu=gtk_menu_new();
1955 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1956 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1957 gtk_widget_show(option_menu);
1961 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
1964 gio->calc_field=gtk_entry_new();
1965 gtk_entry_set_max_length(GTK_ENTRY(gio->calc_field),50);
1966 gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0);
1967 gtk_widget_show(gio->calc_field);
1968 g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio);
1969 g_object_set_data (G_OBJECT(gio->calc_field), E_FILT_FIELD_NAME_ONLY_KEY, "");
1970 g_signal_connect(gio->calc_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
1971 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
1972 g_signal_connect(gio->calc_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
1973 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
1974 colorize_filter_te_as_empty(gio->calc_field);
1979 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
1983 hbox=gtk_hbox_new(FALSE, 0);
1984 gio->advanced_buttons=hbox;
1985 gtk_container_add(GTK_CONTAINER(box), hbox);
1986 gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START);
1987 gtk_widget_hide(hbox);
1989 gio->calc_type=CALC_TYPE_SUM;
1990 create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
1991 create_advanced_field(gio, hbox);
1996 filter_button_clicked(GtkWidget *w, gpointer uio)
1998 io_stat_graph_t *gio=(io_stat_graph_t *)uio;
2000 display_filter_construct_cb(w, gio->args);
2005 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
2007 GtkWidget *option_menu;
2009 GtkWidget *menu_item;
2015 hbox=gtk_hbox_new(FALSE, 3);
2016 gtk_container_add(GTK_CONTAINER(box), hbox);
2017 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2018 gtk_widget_show(hbox);
2020 g_snprintf(str, 256, "Graph %d", num);
2021 gio->display_button=gtk_toggle_button_new_with_label(str);
2022 gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
2023 gtk_widget_show(gio->display_button);
2024 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
2025 g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);
2027 label=gtk_label_new("Color");
2028 gtk_widget_show(label);
2029 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2031 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color);
2032 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color);
2033 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color);
2034 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color);
2035 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color);
2036 /* g_signal_connect(gio->display_button, "toggled", filter_callback, gio);*/
2039 /* filter prefs dialog */
2040 gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
2042 g_snprintf(str, 256, "Wireshark: Display Filter IO-Stat (Filter:%d)", num);
2043 g_free( (gpointer) (gio->args->title) );
2044 gio->args->title=g_strdup(str);
2046 g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio);
2047 g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
2049 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
2050 gtk_widget_show(gio->filter_bt);
2052 gio->filter_field=gtk_entry_new();
2053 gtk_entry_set_max_length(GTK_ENTRY(gio->filter_field),256);
2054 /* filter prefs dialog */
2055 g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field);
2056 /* filter prefs dialog */
2058 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0);
2059 gtk_widget_show(gio->filter_field);
2060 g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio);
2061 g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2062 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2063 g_signal_connect(gio->filter_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2064 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2065 colorize_filter_te_as_empty(gio->filter_field);
2067 create_advanced_box(gio, hbox);
2071 * create PlotStyle menu
2073 g_snprintf(str, 256, " Style:");
2074 label=gtk_label_new(str);
2075 gtk_widget_show(label);
2076 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2078 option_menu=gtk_option_menu_new();
2079 menu=gtk_menu_new();
2080 for(i=0;i<MAX_PLOT_STYLES;i++){
2081 menu_item=gtk_menu_item_new_with_label(plot_style_name[i]);
2082 g_object_set_data(G_OBJECT(menu_item), "plot_style", GINT_TO_POINTER(i));
2083 g_signal_connect(menu_item, "activate", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]);
2084 gtk_widget_show(menu_item);
2085 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
2087 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PLOT_STYLE);
2089 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
2090 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
2091 gtk_widget_show(option_menu);
2098 create_filter_area(io_stat_t *io, GtkWidget *box)
2104 frame=gtk_frame_new("Graphs");
2105 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2106 gtk_widget_show(frame);
2108 vbox=gtk_vbox_new(FALSE, 1);
2109 gtk_container_add(GTK_CONTAINER(frame), vbox);
2110 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
2111 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
2112 gtk_widget_show(vbox);
2114 for(i=0;i<MAX_GRAPHS;i++){
2115 create_filter_box(&io->graphs[i], vbox, i+1);
2123 copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer data)
2125 guint32 i, interval, val;
2128 GString *CSV_str=g_string_new("");
2129 io_stat_t *io=(io_stat_t *)data;
2131 g_string_append(CSV_str, "Interval start");
2132 for(i=0;i<MAX_GRAPHS;i++) {
2133 if (io->graphs[i].display) {
2134 g_string_append_printf(CSV_str, ",Graph %d", i+1);
2137 g_string_append(CSV_str,"\n");
2139 for(interval=0; interval<io->max_interval; interval+=io->interval) {
2140 print_interval_string (string, 15, interval, io, FALSE);
2141 g_string_append(CSV_str, string);
2142 for(i=0;i<MAX_GRAPHS;i++) {
2143 if (io->graphs[i].display) {
2144 val=get_it_value(io, i, interval/io->interval);
2145 g_string_append_printf(CSV_str, ",%d", val);
2148 g_string_append(CSV_str,"\n");
2151 /* Now that we have the CSV data, copy it into the default clipboard */
2152 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
2153 gtk_clipboard_set_text(cb, CSV_str->str, -1); /* Copy the CSV data into the clipboard */
2154 g_string_free(CSV_str, TRUE); /* Free the memory */
2159 init_io_stat_window(io_stat_t *io)
2164 GtkWidget *close_bt, *help_bt;
2165 GtkTooltips *tooltips = gtk_tooltips_new();
2167 #if GTK_CHECK_VERSION(2,6,0)
2171 /* create the main window */
2172 io->window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
2174 vbox=gtk_vbox_new(FALSE, 0);
2175 gtk_container_add(GTK_CONTAINER(io->window), vbox);
2176 gtk_widget_show(vbox);
2178 create_draw_area(io, vbox);
2180 hbox=gtk_hbox_new(FALSE, 3);
2181 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2182 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
2183 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2184 gtk_widget_show(hbox);
2186 create_filter_area(io, hbox);
2187 create_ctrl_area(io, hbox);
2189 io_stat_set_title(io);
2191 #if GTK_CHECK_VERSION(2,6,0)
2192 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE,
2193 GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
2195 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY,
2196 GTK_STOCK_HELP, NULL);
2198 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
2199 gtk_widget_show(bbox);
2201 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
2202 window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
2203 gtk_tooltips_set_tip(tooltips, close_bt, "Close this dialog", NULL);
2205 #if GTK_CHECK_VERSION(2,6,0)
2206 save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
2207 gtk_widget_set_sensitive(save_bt, FALSE);
2208 gtk_tooltips_set_tip(tooltips, save_bt, "Save the displayed graph to a file", NULL);
2209 g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
2210 g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt);
2213 copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
2214 gtk_tooltips_set_tip(tooltips, copy_bt,
2215 "Copy values from selected graphs to the clipboard in CSV (Comma Separated Values) format", NULL);
2216 g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io);
2218 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
2219 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG);
2220 gtk_tooltips_set_tip (tooltips, help_bt, "Show topic specific help", NULL);
2222 g_signal_connect(io->window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
2224 gtk_widget_show(io->window);
2225 window_present(io->window);
2230 gtk_iostat_cb(GtkWidget *w _U_, gpointer d _U_)
2232 gtk_iostat_init(NULL,NULL);
2239 register_tap_listener_gtk_iostat(void)
2241 register_stat_cmd_arg("io,stat", gtk_iostat_init,NULL);
2243 register_stat_menu_item_stock("_IO Graphs",
2244 REGISTER_STAT_GROUP_GENERIC, WIRESHARK_STOCK_GRAPHS,
2245 gtk_iostat_cb, NULL, NULL, NULL);