2 * io_stat 2002 Ronnie Sahlberg
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
41 #include <epan/epan_dissect.h>
42 #include <epan/packet_info.h>
43 #include <epan/stat_cmd_args.h>
45 #include <epan/strutil.h>
47 #include "../stat_menu.h"
48 #include "../alert_box.h"
49 #include "../simple_dialog.h"
52 #include "gtk/gtkglobals.h"
53 #include "gtk/gui_utils.h"
54 #include "gtk/gui_stat_menu.h"
55 #include "gtk/stock_icons.h"
56 #include "gtk/dlg_utils.h"
57 #include "gtk/filter_dlg.h"
58 #include "gtk/help_dlg.h"
59 #include "gtk/pixmap_save.h"
61 #include "gtk/filter_autocomplete.h"
67 #define LOGARITHMIC_YSCALE 0
68 #define AUTO_MAX_YSCALE 1
69 #define DEFAULT_YSCALE_INDEX 1
70 static guint32 yscale_max[MAX_YSCALE] = {LOGARITHMIC_YSCALE, AUTO_MAX_YSCALE, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000, 100000000, 200000000, 500000000, 1000000000, 2000000000};
72 #define MAX_PIXELS_PER_TICK 4
73 #define DEFAULT_PIXELS_PER_TICK_INDEX 2
74 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
77 #define DEFAULT_PLOT_STYLE 0
78 #define PLOT_STYLE_LINE 0
79 #define PLOT_STYLE_IMPULSE 1
80 #define PLOT_STYLE_FILLED_BAR 2
81 #define PLOT_STYLE_DOT 3
82 #define MAX_PLOT_STYLES 4
83 static const char *plot_style_name[MAX_PLOT_STYLES] = {
90 #define DEFAULT_COUNT_TYPE 0
91 #define COUNT_TYPE_FRAMES 0
92 #define COUNT_TYPE_BYTES 1
93 #define COUNT_TYPE_BITS 2
94 #define COUNT_TYPE_ADVANCED 3
95 #define MAX_COUNT_TYPES 4
96 static const char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Bits/Tick", "Advanced..."};
99 #define MAX_TICK_VALUES 7
100 #define DEFAULT_TICK_VALUE_INDEX 3
101 static const guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000, 60000, 600000 };
103 #define CALC_TYPE_SUM 0
104 #define CALC_TYPE_COUNT 1
105 #define CALC_TYPE_MAX 2
106 #define CALC_TYPE_MIN 3
107 #define CALC_TYPE_AVG 4
108 #define CALC_TYPE_LOAD 5
109 #define MAX_CALC_TYPES 6
110 #define DEFAULT_CALC_TYPE 0
111 static const char *calc_type_names[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)", "LOAD(*)"};
114 typedef struct _io_stat_calc_type_t {
115 struct _io_stat_graph_t *gio;
117 } io_stat_calc_type_t;
119 #define NUM_IO_ITEMS 100000
120 typedef struct _io_item_t {
121 guint32 frames; /* always calculated, will hold number of frames*/
122 guint32 bytes; /* always calculated, will hold number of bytes*/
138 typedef struct _io_stat_graph_t {
139 struct _io_stat_t *io;
140 io_item_t items[NUM_IO_ITEMS];
143 GtkWidget *display_button;
144 GtkWidget *filter_field;
145 GtkWidget *advanced_buttons;
148 GtkWidget *calc_field;
151 construct_args_t *args;
152 GtkWidget *filter_bt;
156 typedef struct _io_stat_t {
157 gboolean needs_redraw;
158 gint32 interval; /* measurement interval in ms */
159 guint32 last_interval;
160 guint32 max_interval; /* XXX max_interval and num_items are redundant */
162 guint32 left_x_border;
163 guint32 right_x_border;
164 gboolean view_as_time;
167 struct _io_stat_graph_t graphs[MAX_GRAPHS];
169 GtkWidget *draw_area;
171 GtkAdjustment *scrollbar_adjustment;
172 GtkWidget *scrollbar;
173 guint first_frame_num[NUM_IO_ITEMS];
174 guint last_frame_num;
183 static void init_io_stat_window(io_stat_t *io);
184 static void filter_callback(GtkWidget *widget _U_, gpointer user_data);
187 io_stat_set_title(io_stat_t *io)
194 title = g_strdup_printf("Wireshark IO Graphs: %s", cf_get_display_name(&cfile));
195 gtk_window_set_title(GTK_WINDOW(io->window), title);
200 io_stat_reset(io_stat_t *io)
204 io->needs_redraw=TRUE;
205 for(i=0;i<MAX_GRAPHS;i++){
206 for(j=0;j<NUM_IO_ITEMS;j++){
208 ioi=&io->graphs[i].items[j];
222 nstime_set_zero(&ioi->time_max);
223 nstime_set_zero(&ioi->time_min);
224 nstime_set_zero(&ioi->time_tot);
227 io->last_interval=0xffffffff;
230 io->start_time.secs=0;
231 io->start_time.nsecs=0;
232 for(j=0;j<NUM_IO_ITEMS;j++) {
233 io->first_frame_num[j]=0;
235 io->last_frame_num=0;
237 io_stat_set_title(io);
241 tap_iostat_reset(void *g)
243 io_stat_graph_t *gio=g;
245 io_stat_reset(gio->io);
249 tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
251 io_stat_graph_t *git=g;
256 /* we sometimes get called when git is disabled.
257 this is a bug since the tap listener should be removed first */
262 git->io->needs_redraw=TRUE;
265 * Find which interval this is supposed to go in and store the
266 * interval index as idx
268 time_delta=pinfo->fd->rel_ts;
269 if(time_delta.nsecs<0){
271 time_delta.nsecs+=1000000000;
273 if(time_delta.secs<0){
276 idx=(int) ((time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval);
278 /* some sanity checks */
279 if((idx<0)||(idx>=NUM_IO_ITEMS)){
280 git->io->num_items = NUM_IO_ITEMS-1;
284 /* update num_items */
285 if((guint32)idx > git->io->num_items){
286 git->io->num_items=idx;
287 git->io->max_interval=(idx+1)*git->io->interval;
291 if(git->io->start_time.secs == 0 && git->io->start_time.nsecs == 0) {
292 nstime_diff (&git->io->start_time, &pinfo->fd->abs_ts, &pinfo->fd->rel_ts);
295 /* set first and last frame num in current interval */
296 if (git->io->first_frame_num[idx] == 0) {
297 git->io->first_frame_num[idx]=pinfo->fd->num;
299 git->io->last_frame_num=pinfo->fd->num;
302 * Find the appropriate io_item_t structure
308 * For ADVANCED mode we need to keep track of some more stuff
309 * than just frame and byte counts
311 if(git->io->count_type==COUNT_TYPE_ADVANCED){
315 gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index);
320 /* update the appropriate counters, make sure that if
321 * fields==0 then this is the first seen value so
322 * set any min/max values accordingly
324 for(i=0;i<gp->len;i++){
330 switch(proto_registrar_get_ftype(git->hf_index)){
335 new_int=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
337 if((new_int>it->int_max)||(it->fields==0)){
340 if((new_int<it->int_min)||(it->fields==0)){
343 it->int_tot+=new_int;
350 new_int=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
351 if((new_int>it->int_max)||(it->fields==0)){
354 if((new_int<it->int_min)||(it->fields==0)){
357 it->int_tot+=new_int;
361 new_float=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
362 if((new_float>it->float_max)||(it->fields==0)){
363 it->float_max=new_float;
365 if((new_float<it->float_min)||(it->fields==0)){
366 it->float_min=new_float;
368 it->float_tot+=new_float;
372 new_double=fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
373 if((new_double>it->double_max)||(it->fields==0)){
374 it->double_max=new_double;
376 if((new_double<it->double_min)||(it->fields==0)){
377 it->double_min=new_double;
379 it->double_tot+=new_double;
382 case FT_RELATIVE_TIME:
383 new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
385 switch(git->calc_type){
386 guint64 t, pt; /* time in us */
389 /* it is a LOAD calculation of a relative time field.
390 * add the time this call spanned to each
391 * interval it spanned according to its contribution
395 t=t*1000000+new_time->nsecs/1000;
397 /* handle current interval */
398 pt=pinfo->fd->rel_ts.secs*1000000+pinfo->fd->rel_ts.nsecs/1000;
399 pt=pt%(git->io->interval*1000);
404 git->items[j].time_tot.nsecs+=(int) (pt*1000);
405 if(git->items[j].time_tot.nsecs>1000000000){
406 git->items[j].time_tot.secs++;
407 git->items[j].time_tot.nsecs-=1000000000;
415 if(t > (guint32) (git->io->interval*1000)){
416 pt=git->io->interval*1000;
423 if( (new_time->secs>it->time_max.secs)
424 ||( (new_time->secs==it->time_max.secs)
425 &&(new_time->nsecs>it->time_max.nsecs))
427 it->time_max=*new_time;
429 if( (new_time->secs<it->time_min.secs)
430 ||( (new_time->secs==it->time_min.secs)
431 &&(new_time->nsecs<it->time_min.nsecs))
433 it->time_min=*new_time;
435 nstime_add(&it->time_tot, new_time);
443 it->bytes+=pinfo->fd->pkt_len;
449 get_frame_num(io_stat_t *io, guint32 idx, gboolean first)
451 guint i, frame_num=0;
453 if (idx>io->num_items) {
458 frame_num=io->first_frame_num[idx];
463 * If first frame not found we select the last
464 * frame in the previous interval
466 * If selecting the last frame we select the frame
467 * before the first frame in the next interval
469 for(i=idx+1;i<=io->num_items;i++) {
470 frame_num=io->first_frame_num[i];
471 if (frame_num != 0) {
477 * If not found we select the last frame
479 frame_num=io->last_frame_num;
486 get_it_value(io_stat_t *io, int graph_id, int idx)
492 it=&io->graphs[graph_id].items[idx];
494 switch(io->count_type){
495 case COUNT_TYPE_FRAMES:
497 case COUNT_TYPE_BYTES:
499 case COUNT_TYPE_BITS:
500 return (it->bytes * 8);
504 adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
507 switch(io->graphs[graph_id].calc_type){
508 case CALC_TYPE_COUNT:
523 switch(io->graphs[graph_id].calc_type){
527 case CALC_TYPE_COUNT:
538 value=it->int_tot/it->fields;
548 switch(io->graphs[graph_id].calc_type){
552 case CALC_TYPE_COUNT:
563 value=it->float_tot/it->fields;
573 switch(io->graphs[graph_id].calc_type){
575 value=it->double_tot;
577 case CALC_TYPE_COUNT:
581 value=it->double_max;
584 value=it->double_min;
588 value=it->double_tot/it->fields;
597 case FT_RELATIVE_TIME:
598 switch(io->graphs[graph_id].calc_type){
599 case CALC_TYPE_COUNT:
603 value=(guint32) (it->time_max.secs*1000000+it->time_max.nsecs/1000);
606 value=(guint32) (it->time_min.secs*1000000+it->time_min.nsecs/1000);
609 value=(guint32) (it->time_tot.secs*1000000+it->time_tot.nsecs/1000);
613 guint64 t; /* time in us */
616 t=t*1000000+it->time_tot.nsecs/1000;
617 value=(guint32) (t/it->fields);
623 value=(guint32) ((it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval);
632 return (guint32)value; /* FIXME: loss of precision, visible on the graph for small values */
636 print_time_scale_string(char *buf, int buf_len, guint32 t, guint32 t_max, gboolean log_flag)
638 if(t_max>=10000000 || (log_flag && t_max>=1000000)){
639 g_snprintf(buf, buf_len, "%ds",t/1000000);
640 } else if(t_max>=1000000){
641 g_snprintf(buf, buf_len, "%d.%1ds",t/1000000,(t%1000000)/100000);
642 } else if(t_max>=10000 || (log_flag && t_max>=1000)){
643 g_snprintf(buf, buf_len, "%dms",t/1000);
644 } else if(t_max>=1000){
645 g_snprintf(buf, buf_len, "%d.%1dms",t/1000,(t%1000)/100);
647 g_snprintf(buf, buf_len, "%dus",t);
652 print_interval_string(char *buf, int buf_len, guint32 interval, io_stat_t *io,
655 if (io->view_as_time) {
657 time_t sec_val = interval/1000 + io->start_time.secs;
658 gint32 nsec_val = interval%1000 + io->start_time.nsecs/1000000;
660 if(nsec_val >= 1000) {
664 tmp = localtime (&sec_val);
665 if(io->interval>=1000){
666 g_snprintf(buf, buf_len, "%02d:%02d:%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
667 } else if(io->interval>=100){
668 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%1d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/100);
669 } else if(io->interval>=10){
670 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/10);
672 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val);
676 g_snprintf(buf, buf_len, "%d.%03d", interval/1000,interval%1000);
677 } else if(io->interval>=60000){
678 g_snprintf(buf, buf_len, "%dm", interval/60000);
679 } else if(io->interval>=1000){
680 g_snprintf(buf, buf_len, "%ds", interval/1000);
681 } else if(io->interval>=100){
682 g_snprintf(buf, buf_len, "%d.%1ds", interval/1000,(interval/100)%10);
683 } else if(io->interval>=10){
684 g_snprintf(buf, buf_len, "%d.%02ds", interval/1000,(interval/10)%100);
686 g_snprintf(buf, buf_len, "%d.%03ds", interval/1000,interval%1000);
692 io_stat_draw(io_stat_t *io)
694 int i, tics, ystart, ys;
695 guint32 last_interval, first_interval, interval_delta;
696 gint32 current_interval;
697 guint32 top_y_border;
698 guint32 bottom_y_border;
700 int label_width, label_height;
701 guint32 draw_width, draw_height;
702 char label_string[45];
705 guint32 num_time_intervals;
706 guint32 max_value; /* max value of seen data */
707 guint32 max_y; /* max value of the Y scale */
708 gboolean draw_y_as_time;
710 if(!io->needs_redraw){
713 io->needs_redraw=FALSE;
717 * Find the length of the intervals we have data for
718 * so we know how large arrays we need to malloc()
720 num_time_intervals=io->num_items+1;
722 /* XXX move this check to _packet() */
723 if(num_time_intervals>NUM_IO_ITEMS){
724 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
730 * find the max value so we can autoscale the y axis
733 for(i=0;i<MAX_GRAPHS;i++){
736 if(!io->graphs[i].display){
739 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
742 val=get_it_value(io, i, idx);
744 /* keep track of the max value we have encountered */
756 gdk_draw_rectangle(io->pixmap,
757 io->draw_area->style->white_gc,
760 io->draw_area->allocation.width,
761 io->draw_area->allocation.height);
764 * Calculate the y scale we should use
766 if(io->max_y_units==AUTO_MAX_YSCALE){
767 max_y=yscale_max[MAX_YSCALE-1];
768 for(i=MAX_YSCALE-1;i>1;i--){
769 if(max_value<yscale_max[i]){
773 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
775 for(i=1000000000;i>1;i/=10){
776 if(max_value<(guint32)i){
781 /* the user had specified an explicit y scale to use */
782 max_y=io->max_y_units;
787 * If we use ADVANCED and all the graphs are plotting
788 * either MIN/MAX/AVG of an FT_RELATIVE_TIME field
789 * then we will do some some special processing for the
790 * labels for the Y axis below:
791 * we will append the time unit " s" " ms" or " us"
792 * and we will present the unit in decimal
794 draw_y_as_time=FALSE;
795 if(io->count_type==COUNT_TYPE_ADVANCED){
797 for(i=0;i<MAX_GRAPHS;i++){
800 if(!io->graphs[i].display){
803 adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
805 case FT_RELATIVE_TIME:
806 switch(io->graphs[i].calc_type){
813 draw_y_as_time=FALSE;
817 draw_y_as_time=FALSE;
825 * Calculate size of borders surrounding the plot
826 * The border on the right side needs to be adjusted depending
827 * on the width of the text labels. For simplicity we assume that the
828 * top y scale label will be the widest one
831 if(io->max_y_units==LOGARITHMIC_YSCALE){
832 print_time_scale_string(label_string, 15, 100000, 100000, TRUE); /* 100 ms */
834 print_time_scale_string(label_string, 15, max_y, max_y, FALSE);
837 g_snprintf(label_string, 15, "%d", max_y);
840 layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
841 pango_layout_get_pixel_size(layout, &label_width, &label_height);
843 io->left_x_border=10;
844 io->right_x_border=label_width+20;
846 bottom_y_border=label_height+20;
850 * Calculate the size of the drawing area for the actual plot
852 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
853 draw_height=io->pixmap_height-top_y_border-bottom_y_border;
857 * Add a warning if too many entries
859 if (num_time_intervals == NUM_IO_ITEMS) {
860 g_snprintf (label_string, 45, "Warning: Graph limited to %d entries", NUM_IO_ITEMS);
861 pango_layout_set_text(layout, label_string, -1);
862 gdk_draw_layout(io->pixmap,
863 io->draw_area->style->black_gc, 5,
864 io->pixmap_height-bottom_y_border-draw_height-label_height/2,
869 * Draw the y axis and labels
870 * (we always draw the y scale with 11 ticks along the axis)
872 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
873 io->pixmap_width-io->right_x_border+1,
875 io->pixmap_width-io->right_x_border+1,
876 io->pixmap_height-bottom_y_border);
878 if(io->max_y_units==LOGARITHMIC_YSCALE){
879 tics=(int)log10((double)max_y);
880 ystart=draw_height/10;
887 for(i=ys;i<=tics;i++){
888 int xwidth, lwidth, ypos;
891 if(io->max_y_units==LOGARITHMIC_YSCALE){
893 /* position for the 0 value */
894 ypos=io->pixmap_height-bottom_y_border;
896 /* position for the top value, do not draw logarithmic tics above graph */
897 ypos=io->pixmap_height-bottom_y_border-draw_height;
900 /* draw the logarithmic tics */
902 ypos=(int)(io->pixmap_height-bottom_y_border-(draw_height-ystart)*(i+log10((double)j))/tics-ystart);
904 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
905 io->pixmap_width-io->right_x_border+1, ypos,
906 io->pixmap_width-io->right_x_border+1+xwidth, ypos);
908 ypos=io->pixmap_height-bottom_y_border-(draw_height-ystart)*i/tics-ystart;
910 /* all "main" logarithmic lines are slightly longer */
914 /* first, middle and last tick are slightly longer */
917 ypos=io->pixmap_height-bottom_y_border-draw_height*i/10;
920 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
921 io->pixmap_width-io->right_x_border+1, ypos,
922 io->pixmap_width-io->right_x_border+1+xwidth, ypos);
923 /* draw the labels */
926 if(io->max_y_units==LOGARITHMIC_YSCALE){
927 value=(guint32)(max_y/pow(10,tics-i));
929 print_time_scale_string(label_string, 15, value, value, TRUE);
931 g_snprintf(label_string, 15, "%d", value);
936 print_time_scale_string(label_string, 15, value, max_y, FALSE);
938 g_snprintf(label_string, 15, "%d", value);
942 pango_layout_set_text(layout, label_string, -1);
943 pango_layout_get_pixel_size(layout, &lwidth, NULL);
944 gdk_draw_layout(io->pixmap,
945 io->draw_area->style->black_gc,
946 io->pixmap_width-io->right_x_border+15+label_width-lwidth,
954 * if we have not specified the last_interval via the gui,
955 * then just pick the current end of the capture so that is scrolls
956 * nicely when doing live captures
958 if(io->last_interval==0xffffffff){
959 last_interval=io->max_interval;
961 last_interval=io->last_interval;
968 /* plot the x-scale */
969 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);
971 if((last_interval/io->interval)>=draw_width/io->pixels_per_tick){
972 first_interval=(last_interval/io->interval)-draw_width/io->pixels_per_tick+1;
973 first_interval*=io->interval;
978 interval_delta=(100/io->pixels_per_tick)*io->interval;
979 for(current_interval=last_interval;current_interval>=(gint32)first_interval;current_interval=current_interval-io->interval){
982 /* if pixels_per_tick is 1 or 2, only draw every 10 ticks */
983 /* if pixels_per_tick is 5, only draw every 5 ticks */
984 if(((io->pixels_per_tick<5) && (current_interval%(10*io->interval))) ||
985 ((io->pixels_per_tick==5) && (current_interval%(5*io->interval)))){
989 if(!(current_interval%interval_delta)){
991 } else if(!(current_interval%(interval_delta/2))){
997 x=draw_width+io->left_x_border-((last_interval-current_interval)/io->interval)*io->pixels_per_tick;
998 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
999 x-1-io->pixels_per_tick/2,
1000 io->pixmap_height-bottom_y_border+1,
1001 x-1-io->pixels_per_tick/2,
1002 io->pixmap_height-bottom_y_border+xlen+1);
1006 print_interval_string (label_string, 15, current_interval, io, TRUE);
1007 pango_layout_set_text(layout, label_string, -1);
1008 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1010 if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) {
1012 } else if ((x-1-io->pixels_per_tick/2+lwidth/2) > (io->pixmap_width-5)) {
1013 x_pos=io->pixmap_width-lwidth-5;
1015 x_pos=x-1-io->pixels_per_tick/2-lwidth/2;
1018 gdk_draw_layout(io->pixmap,
1019 io->draw_area->style->black_gc,
1021 io->pixmap_height-bottom_y_border+15,
1026 g_object_unref(G_OBJECT(layout));
1030 * Loop over all graphs and draw them
1032 for(i=MAX_GRAPHS-1;i>=0;i--){
1034 guint32 interval, x_pos, y_pos, prev_x_pos, prev_y_pos;
1036 if(!io->graphs[i].display){
1040 /* initialize prev x/y to the value of the first interval */
1041 prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval)+io->left_x_border;
1042 val=get_it_value(io, i, first_interval/io->interval);
1045 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1047 prev_y_pos=(guint32)(draw_height-1+top_y_border);
1049 prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1052 prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1055 for(interval=first_interval;interval<last_interval;interval+=io->interval){
1056 x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval)+io->left_x_border;
1058 val=get_it_value(io, i, interval/io->interval);
1061 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
1063 y_pos=(guint32)(draw_height-1+top_y_border);
1065 y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
1068 y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
1071 switch(io->graphs[i].plot_style){
1072 case PLOT_STYLE_LINE:
1073 /* dont need to draw anything if the segment
1074 * is entirely above the top of the graph
1076 if( (prev_y_pos!=0) || (y_pos!=0) ){
1077 gdk_draw_line(io->pixmap, io->graphs[i].gc,
1078 prev_x_pos, prev_y_pos,
1082 case PLOT_STYLE_IMPULSE:
1084 gdk_draw_line(io->pixmap, io->graphs[i].gc,
1085 x_pos, draw_height-1+top_y_border,
1089 case PLOT_STYLE_FILLED_BAR:
1091 gdk_draw_rectangle(io->pixmap,
1092 io->graphs[i].gc, TRUE,
1093 x_pos-io->pixels_per_tick/2,
1095 io->pixels_per_tick,
1096 draw_height-1+top_y_border-y_pos);
1099 case PLOT_STYLE_DOT:
1101 gdk_draw_rectangle(io->pixmap,
1102 io->graphs[i].gc, TRUE,
1103 x_pos-io->pixels_per_tick/2,
1104 y_pos-io->pixels_per_tick/2,
1105 io->pixels_per_tick,
1106 io->pixels_per_tick);
1118 gdk_draw_pixmap(io->draw_area->window,
1119 io->draw_area->style->fg_gc[GTK_WIDGET_STATE(io->draw_area)],
1123 io->pixmap_width, io->pixmap_height);
1126 /* update the scrollbar */
1127 if (io->max_interval == 0) {
1128 io->scrollbar_adjustment->upper=(gfloat) io->interval;
1129 io->scrollbar_adjustment->step_increment=(gfloat) (io->interval/10);
1130 io->scrollbar_adjustment->page_increment=(gfloat) io->interval;
1132 io->scrollbar_adjustment->upper=(gfloat) io->max_interval;
1133 io->scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1134 io->scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1136 io->scrollbar_adjustment->page_size=io->scrollbar_adjustment->page_increment;
1137 io->scrollbar_adjustment->value=(gfloat)first_interval;
1138 gtk_adjustment_changed(io->scrollbar_adjustment);
1139 gtk_adjustment_value_changed(io->scrollbar_adjustment);
1144 io_stat_redraw(io_stat_t *io)
1146 io->needs_redraw=TRUE;
1151 tap_iostat_draw(void *g)
1153 io_stat_graph_t *git=g;
1155 io_stat_draw(git->io);
1158 /* ok we get called with both the filter and the field.
1159 make sure the field is part of the filter.
1160 (make sure and make sure just append it)
1161 the field MUST be part of the filter or else we wont
1162 be able to pick up the field values after the edt tree has been
1166 enable_graph(io_stat_graph_t *gio, const char *filter, const char *field)
1168 char real_filter[262];
1174 /* skip all whitespaces */
1187 g_snprintf(real_filter, 257, "(%s)", filter);
1192 /* skip all whitespaces */
1205 if(real_filter[0]!=0){
1206 g_strlcat(real_filter, " && ", 262);
1208 g_strlcat(real_filter, field, 262);
1211 return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
1212 TL_REQUIRES_PROTO_TREE,
1213 tap_iostat_reset, tap_iostat_packet, tap_iostat_draw);
1217 disable_graph(io_stat_graph_t *gio)
1221 protect_thread_critical_region();
1222 remove_tap_listener(gio);
1223 unprotect_thread_critical_region();
1224 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
1230 iostat_init(const char *optarg _U_, void* userdata _U_)
1234 static color_t col[MAX_GRAPHS] = {
1235 {0, 0x0000, 0x0000, 0x0000},
1236 {0, 0xffff, 0x0000, 0x0000},
1237 {0, 0x0000, 0xffff, 0x0000},
1238 {0, 0x0000, 0x0000, 0xffff},
1239 {0, 0xffff, 0x5000, 0xffff}
1241 GString *error_string;
1243 io=g_malloc(sizeof(io_stat_t));
1244 io->needs_redraw=TRUE;
1245 io->interval=tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
1250 io->scrollbar_adjustment=NULL;
1251 io->pixmap_width=500;
1252 io->pixmap_height=200;
1253 io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
1254 io->max_y_units=AUTO_MAX_YSCALE;
1256 io->last_interval=0xffffffff;
1259 io->left_x_border=0;
1260 io->right_x_border=500;
1261 io->view_as_time=FALSE;
1262 io->start_time.secs=0;
1263 io->start_time.nsecs=0;
1265 for(i=0;i<MAX_GRAPHS;i++){
1266 io->graphs[i].gc=NULL;
1267 io->graphs[i].color.pixel=col[i].pixel;
1268 io->graphs[i].color.red=col[i].red;
1269 io->graphs[i].color.green=col[i].green;
1270 io->graphs[i].color.blue=col[i].blue;
1271 io->graphs[i].display=0;
1272 io->graphs[i].display_button=NULL;
1273 io->graphs[i].filter_field=NULL;
1274 io->graphs[i].advanced_buttons=NULL;
1275 io->graphs[i].io=io;
1277 io->graphs[i].args=g_malloc(sizeof(construct_args_t));
1278 io->graphs[i].args->title = NULL;
1279 io->graphs[i].args->wants_apply_button=TRUE;
1280 io->graphs[i].args->activate_on_ok=TRUE;
1281 io->graphs[i].args->modal_and_transient=FALSE;
1283 io->graphs[i].filter_bt=NULL;
1287 error_string=enable_graph(&io->graphs[0], NULL, NULL);
1288 g_assert((error_string == NULL) && "Can't attach io_stat tap !");
1292 fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
1294 g_string_free(error_string, TRUE);
1295 io->graphs[0].display=0;
1296 io->graphs[0].display_button=NULL;
1297 io->graphs[0].filter_field=NULL;
1298 io->graphs[0].advanced_buttons=NULL;
1303 init_io_stat_window(io);
1305 cf_retap_packets(&cfile);
1306 gdk_window_raise(io->window->window);
1311 draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
1313 io_stat_t *io = user_data;
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 _U_, GdkEventButton *event, gpointer user_data)
1337 io_stat_t *io = user_data;
1338 guint32 draw_width, interval, last_interval;
1341 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
1343 if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) ||
1344 (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) {
1345 /* Outside draw area */
1349 if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
1351 * Button 1 selects the first package in the interval.
1352 * Button 3 selects the last package in the interval.
1354 if (io->last_interval==0xffffffff) {
1355 last_interval=io->max_interval;
1357 last_interval=io->last_interval;
1360 interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick);
1361 frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE);
1362 if (frame_num != 0) {
1363 cf_goto_frame(&cfile, frame_num);
1370 /* create a new backing pixmap of the appropriate size */
1372 draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
1374 io_stat_t *io = user_data;
1376 #if GTK_CHECK_VERSION(2,6,0)
1381 gdk_pixmap_unref(io->pixmap);
1385 io->pixmap=gdk_pixmap_new(widget->window,
1386 widget->allocation.width,
1387 widget->allocation.height,
1389 io->pixmap_width=widget->allocation.width;
1390 io->pixmap_height=widget->allocation.height;
1392 #if GTK_CHECK_VERSION(2,6,0)
1393 save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1394 g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
1395 gtk_widget_set_sensitive(save_bt, TRUE);
1398 gdk_draw_rectangle(io->pixmap,
1399 widget->style->white_gc,
1402 widget->allocation.width,
1403 widget->allocation.height);
1405 /* set up the colors and the GC structs for this pixmap */
1406 for(i=0;i<MAX_GRAPHS;i++){
1407 io->graphs[i].gc=gdk_gc_new(io->pixmap);
1408 gdk_gc_set_rgb_fg_color(io->graphs[i].gc, &io->graphs[i].color);
1416 scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
1418 io_stat_t *io = user_data;
1421 mi=(guint32) (io->scrollbar_adjustment->value+io->scrollbar_adjustment->page_size);
1422 if(io->last_interval==mi){
1425 if( (io->last_interval==0xffffffff)
1426 && (mi==io->max_interval) ){
1430 io->last_interval=(mi/io->interval)*io->interval;
1436 /* redraw the screen from the backing pixmap */
1438 draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
1440 io_stat_t *io = user_data;
1442 gdk_draw_pixmap(widget->window,
1443 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1445 event->area.x, event->area.y,
1446 event->area.x, event->area.y,
1447 event->area.width, event->area.height);
1453 create_draw_area(io_stat_t *io, GtkWidget *box)
1455 io->draw_area=gtk_drawing_area_new();
1456 g_signal_connect(io->draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), io);
1458 gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
1460 /* signals needed to handle backing pixmap */
1461 g_signal_connect(io->draw_area, "expose-event", G_CALLBACK(draw_area_expose_event), io);
1462 g_signal_connect(io->draw_area, "configure-event", G_CALLBACK(draw_area_configure_event), io);
1463 gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
1464 g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), io);
1466 gtk_widget_show(io->draw_area);
1467 gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1469 /* create the associated scrollbar */
1470 io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1471 io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
1472 gtk_widget_show(io->scrollbar);
1473 gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
1474 g_signal_connect(io->scrollbar_adjustment, "value-changed", G_CALLBACK(scrollbar_changed), io);
1478 tick_interval_select(GtkWidget *item, gpointer user_data)
1480 io_stat_t *io = user_data;
1483 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1485 io->interval=tick_interval_values[i];
1486 cf_retap_packets(&cfile);
1487 gdk_window_raise(io->window->window);
1492 pixels_per_tick_select(GtkWidget *item, gpointer user_data)
1494 io_stat_t *io = user_data;
1497 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1498 io->pixels_per_tick=pixels_per_tick[i];
1503 plot_style_select(GtkWidget *item, gpointer user_data)
1505 io_stat_graph_t *ppt = user_data;
1508 val=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1510 ppt->plot_style=val;
1512 io_stat_redraw(ppt->io);
1516 create_pixels_per_tick_menu_items(io_stat_t *io)
1519 GtkWidget *combo_box;
1522 combo_box = gtk_combo_box_new_text ();
1524 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1525 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1526 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1528 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX);
1529 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), io);
1535 yscale_select(GtkWidget *item, gpointer user_data)
1537 io_stat_t *io = user_data;
1540 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1542 io->max_y_units = yscale_max[i];
1547 create_tick_interval_menu_items(io_stat_t *io)
1549 GtkWidget *combo_box;
1553 combo_box = gtk_combo_box_new_text ();
1555 for(i=0;i<MAX_TICK_VALUES;i++){
1556 if(tick_interval_values[i]>=60000){
1557 g_snprintf(str, sizeof(str), "%u min", tick_interval_values[i]/60000);
1558 } else if(tick_interval_values[i]>=1000){
1559 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1560 } else if(tick_interval_values[i]>=100){
1561 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1562 } else if(tick_interval_values[i]>=10){
1563 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1565 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1567 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1569 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX);
1570 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), io);
1576 create_yscale_max_menu_items(io_stat_t *io)
1579 GtkWidget *combo_box;
1582 combo_box = gtk_combo_box_new_text ();
1584 for(i=0;i<MAX_YSCALE;i++){
1585 if(yscale_max[i]==LOGARITHMIC_YSCALE){
1586 g_strlcpy(str, "Logarithmic", 15);
1587 } else if(yscale_max[i]==AUTO_MAX_YSCALE){
1588 g_strlcpy(str, "Auto", 15);
1590 g_snprintf(str, 15, "%u", yscale_max[i]);
1592 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1594 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_YSCALE_INDEX);
1595 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), io);
1600 count_type_select(GtkWidget *item, gpointer user_data)
1602 io_stat_t *io = user_data;
1603 static gboolean advanced_visible=FALSE;
1606 io->count_type = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1608 if(io->count_type==COUNT_TYPE_ADVANCED){
1609 for(i=0;i<MAX_GRAPHS;i++){
1610 disable_graph(&io->graphs[i]);
1611 gtk_widget_show(io->graphs[i].advanced_buttons);
1612 /* redraw the entire window so the unhidden widgets show up, hopefully */
1613 gtk_widget_queue_draw_area(io->window,
1616 io->window->allocation.width,
1617 io->window->allocation.height);
1619 advanced_visible=TRUE;
1621 } else if (advanced_visible) {
1622 for(i=0;i<MAX_GRAPHS;i++){
1623 gtk_widget_hide(io->graphs[i].advanced_buttons);
1624 filter_callback(item, &io->graphs[i]);
1626 advanced_visible=FALSE;
1633 create_frames_or_bytes_menu_items(io_stat_t *io)
1635 GtkWidget *combo_box;
1638 combo_box = gtk_combo_box_new_text ();
1640 for(i=0;i<MAX_COUNT_TYPES;i++){
1641 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), count_type_names[i]);
1643 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_COUNT_TYPE);
1644 g_signal_connect(combo_box, "changed", G_CALLBACK(count_type_select), io);
1649 create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, GtkWidget * (*func)(io_stat_t *io))
1653 GtkWidget *combo_box;
1655 hbox=gtk_hbox_new(FALSE, 0);
1656 gtk_container_add(GTK_CONTAINER(box), hbox);
1657 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1658 gtk_widget_show(hbox);
1660 label=gtk_label_new(name);
1661 gtk_widget_show(label);
1662 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1664 combo_box = (*func)(io);
1665 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1666 gtk_widget_show(combo_box);
1670 view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer user_data)
1672 io_stat_t *io = user_data;
1674 io->view_as_time = io->view_as_time ? FALSE : TRUE;
1680 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1682 GtkWidget *frame_vbox;
1687 frame_vbox=gtk_vbox_new(FALSE, 0);
1688 gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0);
1689 gtk_widget_show(frame_vbox);
1691 frame = gtk_frame_new("X Axis");
1692 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1693 gtk_widget_show(frame);
1695 vbox=gtk_vbox_new(FALSE, 0);
1696 gtk_container_add(GTK_CONTAINER(frame), vbox);
1697 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1698 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1699 gtk_widget_show(vbox);
1701 create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
1702 create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1704 view_cb = gtk_check_button_new_with_mnemonic("_View as time of day");
1705 gtk_container_add(GTK_CONTAINER(vbox), view_cb);
1706 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time);
1707 g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io);
1708 gtk_widget_show(view_cb);
1710 frame = gtk_frame_new("Y Axis");
1711 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1712 gtk_widget_show(frame);
1714 vbox=gtk_vbox_new(FALSE, 0);
1715 gtk_container_add(GTK_CONTAINER(frame), vbox);
1716 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1717 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1718 gtk_widget_show(vbox);
1720 create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
1721 create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
1727 filter_callback(GtkWidget *widget _U_, gpointer user_data)
1729 io_stat_graph_t *gio = user_data;
1731 const char *field=NULL;
1732 header_field_info *hfi;
1735 /* this graph is not active, just update display and redraw */
1736 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1738 io_stat_redraw(gio->io);
1742 /* first check if the field string is valid */
1743 if(gio->io->count_type==COUNT_TYPE_ADVANCED){
1744 field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
1746 /* warn and bail out if there was no field specified */
1747 if(field==NULL || field[0]==0){
1748 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
1750 io_stat_redraw(gio->io);
1753 /* warn and bail out if the field could not be found */
1754 hfi=proto_registrar_get_byname(field);
1756 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
1758 io_stat_redraw(gio->io);
1761 gio->hf_index=hfi->id;
1762 /* check that the type is compatible */
1774 /* these values support all calculations except LOAD */
1775 switch(gio->calc_type){
1776 case CALC_TYPE_LOAD:
1777 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1778 "LOAD(*) is only supported for relative-time fields.");
1780 io_stat_redraw(gio->io);
1783 /* these types support all calculations */
1785 case FT_RELATIVE_TIME:
1786 /* this type only supports COUNT, MAX, MIN, AVG */
1787 switch(gio->calc_type){
1789 case CALC_TYPE_COUNT:
1793 case CALC_TYPE_LOAD:
1796 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1797 "%s is a relative-time field, so %s calculations are not supported on it.",
1799 calc_type_names[gio->calc_type]);
1801 io_stat_redraw(gio->io);
1808 * XXX - support this if gint64/guint64 are
1811 if(gio->calc_type!=CALC_TYPE_COUNT){
1812 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1813 "%s is a 64-bit integer, so %s calculations are not supported on it.",
1815 calc_type_names[gio->calc_type]);
1817 io_stat_redraw(gio->io);
1822 if(gio->calc_type!=CALC_TYPE_COUNT){
1823 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1824 "%s doesn't have integral or float values, so %s calculations are not supported on it.",
1826 calc_type_names[gio->calc_type]);
1828 io_stat_redraw(gio->io);
1835 /* first check if the filter string is valid. */
1836 filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
1837 if(!dfilter_compile(filter, &dfilter)) {
1838 bad_dfilter_alert_box(filter);
1840 io_stat_redraw(gio->io);
1843 if (dfilter != NULL)
1844 dfilter_free(dfilter);
1846 /* ok, we have a valid filter and the graph is active.
1847 first just try to delete any previous settings and then apply
1850 protect_thread_critical_region();
1851 remove_tap_listener(gio);
1852 unprotect_thread_critical_region();
1854 io_stat_reset(gio->io);
1855 enable_graph(gio, filter, field);
1856 cf_retap_packets(&cfile);
1857 gdk_window_raise(gio->io->window->window);
1858 io_stat_redraw(gio->io);
1864 calc_type_select(GtkWidget *item, gpointer user_data)
1866 io_stat_graph_t *gio = user_data;
1868 gio->calc_type=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1870 /* disable the graph */
1872 io_stat_redraw(gio->io);
1876 create_calc_types_menu_items(io_stat_graph_t *gio)
1878 GtkWidget *combo_box;
1881 combo_box = gtk_combo_box_new_text ();
1883 for(i=0;i<MAX_CALC_TYPES;i++){
1884 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), calc_type_names[i]);
1886 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_CALC_TYPE);
1887 g_signal_connect(combo_box, "changed", G_CALLBACK(calc_type_select), gio);
1892 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name, GtkWidget *(*func)(io_stat_graph_t *io))
1896 GtkWidget *combo_box;
1898 hbox=gtk_hbox_new(FALSE, 0);
1899 gtk_container_add(GTK_CONTAINER(box), hbox);
1900 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1901 gtk_widget_show(hbox);
1903 label=gtk_label_new(name);
1904 gtk_widget_show(label);
1905 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1907 combo_box = (*func)(gio);
1908 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1909 gtk_widget_show(combo_box);
1913 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
1916 gio->calc_field=gtk_entry_new();
1917 gtk_entry_set_max_length(GTK_ENTRY(gio->calc_field),50);
1918 gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0);
1919 gtk_widget_show(gio->calc_field);
1920 g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio);
1921 g_object_set_data (G_OBJECT(gio->calc_field), E_FILT_FIELD_NAME_ONLY_KEY, "");
1922 g_signal_connect(gio->calc_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
1923 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
1924 g_signal_connect(gio->calc_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
1925 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
1926 colorize_filter_te_as_empty(gio->calc_field);
1930 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
1934 hbox=gtk_hbox_new(FALSE, 0);
1935 gio->advanced_buttons=hbox;
1936 gtk_container_add(GTK_CONTAINER(box), hbox);
1937 gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START);
1938 gtk_widget_hide(hbox);
1940 gio->calc_type=CALC_TYPE_SUM;
1941 create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
1942 create_advanced_field(gio, hbox);
1946 filter_button_clicked(GtkWidget *w, gpointer user_data)
1948 io_stat_graph_t *gio = user_data;
1950 display_filter_construct_cb(w, gio->args);
1955 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
1957 GtkWidget *combo_box;
1963 hbox=gtk_hbox_new(FALSE, 3);
1964 gtk_container_add(GTK_CONTAINER(box), hbox);
1965 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1966 gtk_widget_show(hbox);
1968 g_snprintf(str, 256, "Graph %d", num);
1969 gio->display_button=gtk_toggle_button_new_with_label(str);
1970 gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
1971 gtk_widget_show(gio->display_button);
1972 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
1973 g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);
1975 label=gtk_label_new("Color");
1976 gtk_widget_show(label);
1977 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1979 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color);
1980 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color);
1981 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color);
1982 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color);
1983 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color);
1984 /* g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);*/
1987 /* filter prefs dialog */
1988 gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
1990 g_snprintf(str, 256, "Wireshark: Display Filter IO-Stat (Filter:%d)", num);
1991 g_free( (gpointer) (gio->args->title) );
1992 gio->args->title=g_strdup(str);
1994 g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio);
1995 g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
1997 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
1998 gtk_widget_show(gio->filter_bt);
2000 gio->filter_field=gtk_entry_new();
2001 gtk_entry_set_max_length(GTK_ENTRY(gio->filter_field),256);
2002 /* filter prefs dialog */
2003 g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field);
2004 /* filter prefs dialog */
2006 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0);
2007 gtk_widget_show(gio->filter_field);
2008 g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio);
2009 g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
2010 g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
2011 g_signal_connect(gio->filter_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
2012 g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
2013 colorize_filter_te_as_empty(gio->filter_field);
2015 create_advanced_box(gio, hbox);
2018 * create PlotStyle menu
2020 g_snprintf(str, 256, " Style:");
2021 label=gtk_label_new(str);
2022 gtk_widget_show(label);
2023 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2025 combo_box = gtk_combo_box_new_text ();
2026 for(i=0;i<MAX_PLOT_STYLES;i++){
2027 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), plot_style_name[i]);
2029 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PLOT_STYLE);
2030 g_signal_connect(combo_box, "changed", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]);
2032 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
2033 gtk_widget_show(combo_box);
2039 create_filter_area(io_stat_t *io, GtkWidget *box)
2045 frame=gtk_frame_new("Graphs");
2046 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2047 gtk_widget_show(frame);
2049 vbox=gtk_vbox_new(FALSE, 1);
2050 gtk_container_add(GTK_CONTAINER(frame), vbox);
2051 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
2052 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
2053 gtk_widget_show(vbox);
2055 for(i=0;i<MAX_GRAPHS;i++){
2056 create_filter_box(&io->graphs[i], vbox, i+1);
2063 copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer user_data)
2065 guint32 i, interval, val;
2068 GString *CSV_str=g_string_new("");
2069 io_stat_t *io = user_data;
2071 g_string_append(CSV_str, "\"Interval start\"");
2072 for(i=0;i<MAX_GRAPHS;i++) {
2073 if (io->graphs[i].display) {
2074 g_string_append_printf(CSV_str, ",\"Graph %d\"", i+1);
2077 g_string_append(CSV_str,"\n");
2079 for(interval=0; interval<io->max_interval; interval+=io->interval) {
2080 print_interval_string (string, 15, interval, io, FALSE);
2081 g_string_append_printf(CSV_str, "\"%s\"", string);
2082 for(i=0;i<MAX_GRAPHS;i++) {
2083 if (io->graphs[i].display) {
2084 val=get_it_value(io, i, interval/io->interval);
2085 g_string_append_printf(CSV_str, ",\"%d\"", val);
2088 g_string_append(CSV_str,"\n");
2091 /* Now that we have the CSV data, copy it into the default clipboard */
2092 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
2093 gtk_clipboard_set_text(cb, CSV_str->str, -1); /* Copy the CSV data into the clipboard */
2094 g_string_free(CSV_str, TRUE); /* Free the memory */
2098 init_io_stat_window(io_stat_t *io)
2103 GtkWidget *close_bt, *help_bt;
2104 GtkTooltips *tooltips = gtk_tooltips_new();
2106 #if GTK_CHECK_VERSION(2,6,0)
2110 /* create the main window, transient_for top_level */
2111 io->window = dlg_window_new("I/O Graphs");
2112 gtk_window_set_destroy_with_parent (GTK_WINDOW(io->window), TRUE);
2114 vbox=gtk_vbox_new(FALSE, 0);
2115 gtk_container_add(GTK_CONTAINER(io->window), vbox);
2116 gtk_widget_show(vbox);
2118 create_draw_area(io, vbox);
2120 hbox=gtk_hbox_new(FALSE, 3);
2121 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2122 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
2123 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2124 gtk_widget_show(hbox);
2126 create_filter_area(io, hbox);
2127 create_ctrl_area(io, hbox);
2129 io_stat_set_title(io);
2131 #if GTK_CHECK_VERSION(2,6,0)
2132 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE,
2133 GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
2135 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY,
2136 GTK_STOCK_HELP, NULL);
2138 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
2139 gtk_widget_show(bbox);
2141 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
2142 window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
2143 gtk_tooltips_set_tip(tooltips, close_bt, "Close this dialog", NULL);
2145 #if GTK_CHECK_VERSION(2,6,0)
2146 save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
2147 gtk_widget_set_sensitive(save_bt, FALSE);
2148 gtk_tooltips_set_tip(tooltips, save_bt, "Save the displayed graph to a file", NULL);
2149 g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
2150 g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt);
2153 copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
2154 gtk_tooltips_set_tip(tooltips, copy_bt,
2155 "Copy values from selected graphs to the clipboard in CSV (Comma Separated Values) format", NULL);
2156 g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io);
2158 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
2159 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG);
2160 gtk_tooltips_set_tip (tooltips, help_bt, "Show topic specific help", NULL);
2162 g_signal_connect(io->window, "delete-event", G_CALLBACK(window_delete_event_cb), NULL);
2164 gtk_widget_show(io->window);
2165 window_present(io->window);
2168 #ifdef MAIN_MENU_USE_UIMANAGER
2170 gui_iostat_cb(GtkAction *action _U_, gpointer user_data _U_)
2172 iostat_init(NULL,NULL);
2176 gui_iostat_cb(GtkWidget *w _U_, gpointer d _U_)
2178 iostat_init(NULL,NULL);
2183 register_tap_listener_gtk_iostat(void)
2185 register_stat_cmd_arg("io,stat", iostat_init,NULL);
2187 #ifdef MAIN_MENU_USE_UIMANAGER
2189 register_stat_menu_item_stock("_IO Graphs",
2190 REGISTER_STAT_GROUP_GENERIC, WIRESHARK_STOCK_GRAPHS,
2191 gui_iostat_cb, NULL, NULL, NULL);