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"
63 void protect_thread_critical_region(void);
64 void unprotect_thread_critical_region(void);
69 #define LOGARITHMIC_YSCALE 0
70 #define AUTO_MAX_YSCALE 1
71 #define DEFAULT_YSCALE 1
72 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};
74 #define MAX_PIXELS_PER_TICK 4
75 #define DEFAULT_PIXELS_PER_TICK 2
76 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
79 #define DEFAULT_PLOT_STYLE 0
80 #define PLOT_STYLE_LINE 0
81 #define PLOT_STYLE_IMPULSE 1
82 #define PLOT_STYLE_FILLED_BAR 2
83 #define PLOT_STYLE_DOT 3
84 #define MAX_PLOT_STYLES 4
85 static const char *plot_style_name[MAX_PLOT_STYLES] = {
93 #define COUNT_TYPE_FRAMES 0
94 #define COUNT_TYPE_BYTES 1
95 #define COUNT_TYPE_BITS 2
96 #define COUNT_TYPE_ADVANCED 3
97 #define MAX_COUNT_TYPES 4
98 static const char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Bits/Tick", "Advanced..."};
101 #define MAX_TICK_VALUES 7
102 #define DEFAULT_TICK_VALUE 3
103 static const guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000, 60000, 600000 };
105 #define CALC_TYPE_SUM 0
106 #define CALC_TYPE_COUNT 1
107 #define CALC_TYPE_MAX 2
108 #define CALC_TYPE_MIN 3
109 #define CALC_TYPE_AVG 4
110 #define CALC_TYPE_LOAD 5
111 #define MAX_CALC_TYPES 6
112 static const char *calc_type_names[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)", "LOAD(*)"};
115 typedef struct _io_stat_calc_type_t {
116 struct _io_stat_graph_t *gio;
118 } io_stat_calc_type_t;
120 #define NUM_IO_ITEMS 100000
121 typedef struct _io_item_t {
122 guint32 frames; /* always calculated, will hold number of frames*/
123 guint32 bytes; /* always calculated, will hold number of bytes*/
133 typedef struct _io_stat_graph_t {
134 struct _io_stat_t *io;
135 io_item_t items[NUM_IO_ITEMS];
138 GtkWidget *display_button;
139 GtkWidget *filter_field;
140 GtkWidget *advanced_buttons;
142 io_stat_calc_type_t calc_types[MAX_CALC_TYPES];
144 GtkWidget *calc_field;
147 construct_args_t *args;
148 GtkWidget *filter_bt;
152 typedef struct _io_stat_t {
153 gboolean needs_redraw;
154 gint32 interval; /* measurement interval in ms */
155 guint32 last_interval;
156 guint32 max_interval; /* XXX max_interval and num_items are redundant */
158 guint32 left_x_border;
159 guint32 right_x_border;
160 gboolean view_as_time;
163 struct _io_stat_graph_t graphs[MAX_GRAPHS];
165 GtkWidget *draw_area;
167 GtkAdjustment *scrollbar_adjustment;
168 GtkWidget *scrollbar;
169 guint first_frame_num[NUM_IO_ITEMS];
170 guint last_frame_num;
179 static void init_io_stat_window(io_stat_t *io);
180 static gint filter_callback(GtkWidget *widget _U_, io_stat_graph_t *gio);
183 io_stat_set_title(io_stat_t *io)
190 title = g_strdup_printf("Wireshark IO Graphs: %s", cf_get_display_name(&cfile));
191 gtk_window_set_title(GTK_WINDOW(io->window), title);
196 io_stat_reset(io_stat_t *io)
200 io->needs_redraw=TRUE;
201 for(i=0;i<MAX_GRAPHS;i++){
202 for(j=0;j<NUM_IO_ITEMS;j++){
204 ioi=&io->graphs[i].items[j];
212 nstime_set_zero(&ioi->time_max);
213 nstime_set_zero(&ioi->time_min);
214 nstime_set_zero(&ioi->time_tot);
217 io->last_interval=0xffffffff;
220 io->start_time.secs=0;
221 io->start_time.nsecs=0;
222 for(j=0;j<NUM_IO_ITEMS;j++) {
223 io->first_frame_num[j]=0;
225 io->last_frame_num=0;
227 io_stat_set_title(io);
231 gtk_iostat_reset(void *g)
233 io_stat_graph_t *gio=g;
235 io_stat_reset(gio->io);
239 gtk_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
241 io_stat_graph_t *git=g;
246 /* we sometimes get called when git is disabled.
247 this is a bug since the tap listener should be removed first */
252 git->io->needs_redraw=TRUE;
255 * Find which interval this is supposed to go in and store the
256 * interval index as idx
258 time_delta=pinfo->fd->rel_ts;
259 if(time_delta.nsecs<0){
261 time_delta.nsecs+=1000000000;
263 if(time_delta.secs<0){
266 idx=(int) ((time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval);
268 /* some sanity checks */
269 if((idx<0)||(idx>=NUM_IO_ITEMS)){
270 git->io->num_items = NUM_IO_ITEMS-1;
274 /* update num_items */
275 if((guint32)idx > git->io->num_items){
276 git->io->num_items=idx;
277 git->io->max_interval=(idx+1)*git->io->interval;
281 if(git->io->start_time.secs == 0 && git->io->start_time.nsecs == 0) {
282 git->io->start_time = pinfo->fd->abs_ts;
285 /* set first and last frame num in current interval */
286 if (git->io->first_frame_num[idx] == 0) {
287 git->io->first_frame_num[idx]=pinfo->fd->num;
289 git->io->last_frame_num=pinfo->fd->num;
292 * Find the appropriate io_item_t structure
298 * For ADVANCED mode we need to keep track of some more stuff
299 * than just frame and byte counts
301 if(git->io->count_type==COUNT_TYPE_ADVANCED){
305 gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index);
310 /* update the appropriate counters, make sure that if
311 * fields==0 then this is the first seen value so
312 * set any min/max values accordingly
314 for(i=0;i<gp->len;i++){
318 switch(proto_registrar_get_ftype(git->hf_index)){
323 new_int=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
325 if((new_int>it->int_max)||(it->fields==0)){
328 if((new_int<it->int_min)||(it->fields==0)){
331 it->int_tot+=new_int;
338 new_int=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
339 if((new_int>it->int_max)||(it->fields==0)){
342 if((new_int<it->int_min)||(it->fields==0)){
345 it->int_tot+=new_int;
348 case FT_RELATIVE_TIME:
349 new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
351 switch(git->calc_type){
352 guint64 t, pt; /* time in us */
355 /* it is a LOAD calculation of a relative time field.
356 * add the time this call spanned to each
357 * interval it spanned according to its contribution
361 t=t*1000000+new_time->nsecs/1000;
363 /* handle current interval */
364 pt=pinfo->fd->rel_ts.secs*1000000+pinfo->fd->rel_ts.nsecs/1000;
365 pt=pt%(git->io->interval*1000);
370 git->items[i].time_tot.nsecs+=(int) (pt*1000);
371 if(git->items[i].time_tot.nsecs>1000000000){
372 git->items[i].time_tot.secs++;
373 git->items[i].time_tot.nsecs-=1000000000;
381 if(t > (guint32) (git->io->interval*1000)){
382 pt=git->io->interval*1000;
389 if( (new_time->secs>it->time_max.secs)
390 ||( (new_time->secs==it->time_max.secs)
391 &&(new_time->nsecs>it->time_max.nsecs))
393 it->time_max=*new_time;
395 if( (new_time->secs<it->time_min.secs)
396 ||( (new_time->secs==it->time_min.secs)
397 &&(new_time->nsecs<it->time_min.nsecs))
399 it->time_min=*new_time;
401 nstime_add(&it->time_tot, new_time);
409 it->bytes+=pinfo->fd->pkt_len;
415 get_frame_num(io_stat_t *io, guint32 idx, gboolean first)
417 guint i, frame_num=0;
419 if (idx>io->num_items) {
424 frame_num=io->first_frame_num[idx];
429 * If first frame not found we select the last
430 * frame in the previous interval
432 * If selecting the last frame we select the frame
433 * before the first frame in the next interval
435 for(i=idx+1;i<=io->num_items;i++) {
436 frame_num=io->first_frame_num[i];
437 if (frame_num != 0) {
443 * If not found we select the last frame
445 frame_num=io->last_frame_num;
452 get_it_value(io_stat_t *io, int graph_id, int idx)
458 it=&io->graphs[graph_id].items[idx];
460 switch(io->count_type){
461 case COUNT_TYPE_FRAMES:
463 case COUNT_TYPE_BYTES:
465 case COUNT_TYPE_BITS:
466 return (it->bytes * 8);
470 adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
473 switch(io->graphs[graph_id].calc_type){
474 case CALC_TYPE_COUNT:
489 switch(io->graphs[graph_id].calc_type){
493 case CALC_TYPE_COUNT:
504 value=it->int_tot/it->fields;
513 case FT_RELATIVE_TIME:
514 switch(io->graphs[graph_id].calc_type){
515 case CALC_TYPE_COUNT:
519 value=(guint32) (it->time_max.secs*1000000+it->time_max.nsecs/1000);
522 value=(guint32) (it->time_min.secs*1000000+it->time_min.nsecs/1000);
525 value=(guint32) (it->time_tot.secs*1000000+it->time_tot.nsecs/1000);
529 guint64 t; /* time in us */
532 t=t*1000000+it->time_tot.nsecs/1000;
533 value=(guint32) (t/it->fields);
539 value=(guint32) ((it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval);
553 print_time_scale_string(char *buf, int buf_len, guint32 t, guint32 t_max, gboolean log)
555 if(t_max>=10000000 || (log && t_max>=1000000)){
556 g_snprintf(buf, buf_len, "%ds",t/1000000);
557 } else if(t_max>=1000000){
558 g_snprintf(buf, buf_len, "%d.%1ds",t/1000000,(t%1000000)/100000);
559 } else if(t_max>=10000 || (log && t_max>=1000)){
560 g_snprintf(buf, buf_len, "%dms",t/1000);
561 } else if(t_max>=1000){
562 g_snprintf(buf, buf_len, "%d.%1dms",t/1000,(t%1000)/100);
564 g_snprintf(buf, buf_len, "%dus",t);
570 print_interval_string(char *buf, int buf_len, guint32 interval, io_stat_t *io,
573 if (io->view_as_time) {
575 time_t sec_val = interval/1000 + io->start_time.secs;
576 gint32 nsec_val = interval%1000 + io->start_time.nsecs/1000000;
578 if(nsec_val >= 1000) {
582 tmp = localtime (&sec_val);
583 if(io->interval>=1000){
584 g_snprintf(buf, buf_len, "%02d:%02d:%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
585 } else if(io->interval>=100){
586 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%1d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/100);
587 } else if(io->interval>=10){
588 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/10);
590 g_snprintf(buf, buf_len, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val);
593 if(io->interval>=60000 && ext){
594 g_snprintf(buf, buf_len, "%d%s", interval/60000, ext?"m":"");
595 } else if(io->interval>=1000){
596 g_snprintf(buf, buf_len, "%d%s", interval/1000, ext?"s":"");
597 } else if(io->interval>=100){
598 g_snprintf(buf, buf_len, "%d.%1d%s", interval/1000,(interval/100)%10, ext?"s":"");
599 } else if(io->interval>=10){
600 g_snprintf(buf, buf_len, "%d.%02d%s", interval/1000,(interval/10)%100, ext?"s":"");
602 g_snprintf(buf, buf_len, "%d.%03d%s", interval/1000,interval%1000, ext?"s":"");
608 io_stat_draw(io_stat_t *io)
610 int i, tics, ystart, ys;
611 guint32 last_interval, first_interval, interval_delta;
612 gint32 current_interval;
613 guint32 top_y_border;
614 guint32 bottom_y_border;
616 int label_width, label_height;
617 guint32 draw_width, draw_height;
618 char label_string[45];
621 guint32 num_time_intervals;
622 guint32 max_value; /* max value of seen data */
623 guint32 max_y; /* max value of the Y scale */
624 gboolean draw_y_as_time;
626 if(!io->needs_redraw){
629 io->needs_redraw=FALSE;
633 * Find the length of the intervals we have data for
634 * so we know how large arrays we need to malloc()
636 num_time_intervals=io->num_items+1;
638 /* XXX move this check to _packet() */
639 if(num_time_intervals>NUM_IO_ITEMS){
640 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
646 * find the max value so we can autoscale the y axis
649 for(i=0;i<MAX_GRAPHS;i++){
652 if(!io->graphs[i].display){
655 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
658 val=get_it_value(io, i, idx);
660 /* keep track of the max value we have encountered */
672 gdk_draw_rectangle(io->pixmap,
673 io->draw_area->style->white_gc,
676 io->draw_area->allocation.width,
677 io->draw_area->allocation.height);
680 * Calculate the y scale we should use
682 if(io->max_y_units==AUTO_MAX_YSCALE){
683 max_y=yscale_max[MAX_YSCALE-1];
684 for(i=MAX_YSCALE-1;i>1;i--){
685 if(max_value<yscale_max[i]){
689 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
691 for(i=1000000000;i>1;i/=10){
692 if(max_value<(guint32)i){
697 /* the user had specified an explicit y scale to use */
698 max_y=io->max_y_units;
703 * If we use ADVANCED and all the graphs are plotting
704 * either MIN/MAX/AVG of an FT_RELATIVE_TIME field
705 * then we will do some some special processing for the
706 * labels for the Y axis below:
707 * we will append the time unit " s" " ms" or " us"
708 * and we will present the unit in decimal
710 draw_y_as_time=FALSE;
711 if(io->count_type==COUNT_TYPE_ADVANCED){
713 for(i=0;i<MAX_GRAPHS;i++){
716 if(!io->graphs[i].display){
719 adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
721 case FT_RELATIVE_TIME:
722 switch(io->graphs[i].calc_type){
729 draw_y_as_time=FALSE;
733 draw_y_as_time=FALSE;
741 * Calculate size of borders surrounding the plot
742 * The border on the right side needs to be adjusted depending
743 * on the width of the text labels. For simplicity we assume that the
744 * top y scale label will be the widest one
747 if(io->max_y_units==LOGARITHMIC_YSCALE){
748 print_time_scale_string(label_string, 15, 100000, 100000, TRUE); /* 100 ms */
750 print_time_scale_string(label_string, 15, max_y, max_y, FALSE);
753 g_snprintf(label_string, 15, "%d", max_y);
756 layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
757 pango_layout_get_pixel_size(layout, &label_width, &label_height);
759 io->left_x_border=10;
760 io->right_x_border=label_width+20;
762 bottom_y_border=label_height+20;
766 * Calculate the size of the drawing area for the actual plot
768 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
769 draw_height=io->pixmap_height-top_y_border-bottom_y_border;
773 * Add a warning if too many entries
775 if (num_time_intervals == NUM_IO_ITEMS) {
776 g_snprintf (label_string, 45, "Warning: Graph limited to %d entries", NUM_IO_ITEMS);
777 pango_layout_set_text(layout, label_string, -1);
778 gdk_draw_layout(io->pixmap,
779 io->draw_area->style->black_gc, 5,
780 io->pixmap_height-bottom_y_border-draw_height-label_height/2,
785 * Draw the y axis and labels
786 * (we always draw the y scale with 11 ticks along the axis)
788 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
789 io->pixmap_width-io->right_x_border+1,
791 io->pixmap_width-io->right_x_border+1,
792 io->pixmap_height-bottom_y_border);
794 if(io->max_y_units==LOGARITHMIC_YSCALE){
795 tics=(int)log10((double)max_y);
796 ystart=draw_height/10;
803 for(i=ys;i<=tics;i++){
804 int xwidth, lwidth, ypos;
807 if(io->max_y_units==LOGARITHMIC_YSCALE){
809 /* position for the 0 value */
810 ypos=io->pixmap_height-bottom_y_border;
812 /* position for the top value, do not draw logarithmic tics above graph */
813 ypos=io->pixmap_height-bottom_y_border-draw_height;
816 /* draw the logarithmic tics */
818 ypos=(int)(io->pixmap_height-bottom_y_border-(draw_height-ystart)*(i+log10((double)j))/tics-ystart);
820 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
821 io->pixmap_width-io->right_x_border+1, ypos,
822 io->pixmap_width-io->right_x_border+1+xwidth, ypos);
824 ypos=io->pixmap_height-bottom_y_border-(draw_height-ystart)*i/tics-ystart;
826 /* all "main" logarithmic lines are slightly longer */
830 /* first, middle and last tick are slightly longer */
833 ypos=io->pixmap_height-bottom_y_border-draw_height*i/10;
836 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
837 io->pixmap_width-io->right_x_border+1, ypos,
838 io->pixmap_width-io->right_x_border+1+xwidth, ypos);
839 /* draw the labels */
842 if(io->max_y_units==LOGARITHMIC_YSCALE){
843 value=(guint32)(max_y/pow(10,tics-i));
845 print_time_scale_string(label_string, 15, value, value, TRUE);
847 g_snprintf(label_string, 15, "%d", value);
852 print_time_scale_string(label_string, 15, value, max_y, FALSE);
854 g_snprintf(label_string, 15, "%d", value);
858 pango_layout_set_text(layout, label_string, -1);
859 pango_layout_get_pixel_size(layout, &lwidth, NULL);
860 gdk_draw_layout(io->pixmap,
861 io->draw_area->style->black_gc,
862 io->pixmap_width-io->right_x_border+15+label_width-lwidth,
870 * if we have not specified the last_interval via the gui,
871 * then just pick the current end of the capture so that is scrolls
872 * nicely when doing live captures
874 if(io->last_interval==0xffffffff){
875 last_interval=io->max_interval;
877 last_interval=io->last_interval;
884 /* plot the x-scale */
885 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);
887 if((last_interval/io->interval)>=draw_width/io->pixels_per_tick){
888 first_interval=(last_interval/io->interval)-draw_width/io->pixels_per_tick+1;
889 first_interval*=io->interval;
894 interval_delta=(100/io->pixels_per_tick)*io->interval;
895 for(current_interval=last_interval;current_interval>=(gint32)first_interval;current_interval=current_interval-io->interval){
898 /* if pixels_per_tick is 1 or 2, only draw every 10 ticks */
899 /* if pixels_per_tick is 5, only draw every 5 ticks */
900 if(((io->pixels_per_tick<5) && (current_interval%(10*io->interval))) ||
901 ((io->pixels_per_tick==5) && (current_interval%(5*io->interval)))){
905 if(!(current_interval%interval_delta)){
907 } else if(!(current_interval%(interval_delta/2))){
913 x=draw_width+io->left_x_border-((last_interval-current_interval)/io->interval)*io->pixels_per_tick;
914 gdk_draw_line(io->pixmap, io->draw_area->style->black_gc,
915 x-1-io->pixels_per_tick/2,
916 io->pixmap_height-bottom_y_border+1,
917 x-1-io->pixels_per_tick/2,
918 io->pixmap_height-bottom_y_border+xlen+1);
922 print_interval_string (label_string, 15, current_interval, io, TRUE);
923 pango_layout_set_text(layout, label_string, -1);
924 pango_layout_get_pixel_size(layout, &lwidth, NULL);
926 if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) {
928 } else if ((x-1-io->pixels_per_tick/2+lwidth/2) > (io->pixmap_width-5)) {
929 x_pos=io->pixmap_width-lwidth-5;
931 x_pos=x-1-io->pixels_per_tick/2-lwidth/2;
934 gdk_draw_layout(io->pixmap,
935 io->draw_area->style->black_gc,
937 io->pixmap_height-bottom_y_border+15,
942 g_object_unref(G_OBJECT(layout));
946 * Loop over all graphs and draw them
948 for(i=MAX_GRAPHS-1;i>=0;i--){
950 guint32 interval, x_pos, y_pos, prev_x_pos, prev_y_pos;
952 if(!io->graphs[i].display){
956 /* initialize prev x/y to the value of the first interval */
957 prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval)+io->left_x_border;
958 val=get_it_value(io, i, first_interval/io->interval);
961 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
963 prev_y_pos=(guint32)(draw_height-1+top_y_border);
965 prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
968 prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
971 for(interval=first_interval;interval<last_interval;interval+=io->interval){
972 x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval)+io->left_x_border;
974 val=get_it_value(io, i, interval/io->interval);
977 } else if(io->max_y_units==LOGARITHMIC_YSCALE){
979 y_pos=(guint32)(draw_height-1+top_y_border);
981 y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
984 y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
987 switch(io->graphs[i].plot_style){
988 case PLOT_STYLE_LINE:
989 /* dont need to draw anything if the segment
990 * is entirely above the top of the graph
992 if( (prev_y_pos!=0) || (y_pos!=0) ){
993 gdk_draw_line(io->pixmap, io->graphs[i].gc,
994 prev_x_pos, prev_y_pos,
998 case PLOT_STYLE_IMPULSE:
1000 gdk_draw_line(io->pixmap, io->graphs[i].gc,
1001 x_pos, draw_height-1+top_y_border,
1005 case PLOT_STYLE_FILLED_BAR:
1007 gdk_draw_rectangle(io->pixmap,
1008 io->graphs[i].gc, TRUE,
1009 x_pos-io->pixels_per_tick/2,
1011 io->pixels_per_tick,
1012 draw_height-1+top_y_border-y_pos);
1015 case PLOT_STYLE_DOT:
1017 gdk_draw_rectangle(io->pixmap,
1018 io->graphs[i].gc, TRUE,
1019 x_pos-io->pixels_per_tick/2,
1020 y_pos-io->pixels_per_tick/2,
1021 io->pixels_per_tick,
1022 io->pixels_per_tick);
1034 gdk_draw_pixmap(io->draw_area->window,
1035 io->draw_area->style->fg_gc[GTK_WIDGET_STATE(io->draw_area)],
1039 io->pixmap_width, io->pixmap_height);
1042 /* update the scrollbar */
1043 io->scrollbar_adjustment->upper=(gfloat) io->max_interval;
1044 io->scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1045 io->scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1046 io->scrollbar_adjustment->page_size=io->scrollbar_adjustment->page_increment;
1047 io->scrollbar_adjustment->value=(gfloat)first_interval;
1048 gtk_adjustment_changed(io->scrollbar_adjustment);
1049 gtk_adjustment_value_changed(io->scrollbar_adjustment);
1054 io_stat_redraw(io_stat_t *io)
1056 io->needs_redraw=TRUE;
1061 gtk_iostat_draw(void *g)
1063 io_stat_graph_t *git=g;
1065 io_stat_draw(git->io);
1069 /* ok we get called with both the filter and the field.
1070 make sure the field is part of the filter.
1071 (make sure and make sure just append it)
1072 the field MUST be part of the filter or else we wont
1073 be able to pick up the field values after the edt tree has been
1077 enable_graph(io_stat_graph_t *gio, const char *filter, const char *field)
1079 char real_filter[262];
1085 /* skip all whitespaces */
1098 g_snprintf(real_filter, 257, "(%s)", filter);
1103 /* skip all whitespaces */
1116 if(real_filter[0]!=0){
1117 g_strlcat(real_filter, " && ", 262);
1119 g_strlcat(real_filter, field, 262);
1122 return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
1123 gtk_iostat_reset, gtk_iostat_packet, gtk_iostat_draw);
1127 disable_graph(io_stat_graph_t *gio)
1131 protect_thread_critical_region();
1132 remove_tap_listener(gio);
1133 unprotect_thread_critical_region();
1134 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
1140 gtk_iostat_init(const char *optarg _U_, void* userdata _U_)
1144 static color_t col[MAX_GRAPHS] = {
1145 {0, 0x0000, 0x0000, 0x0000},
1146 {0, 0xffff, 0x0000, 0x0000},
1147 {0, 0x0000, 0xffff, 0x0000},
1148 {0, 0x0000, 0x0000, 0xffff},
1149 {0, 0xffff, 0x5000, 0xffff}
1151 GString *error_string;
1153 io=g_malloc(sizeof(io_stat_t));
1154 io->needs_redraw=TRUE;
1160 io->scrollbar_adjustment=NULL;
1161 io->pixmap_width=500;
1162 io->pixmap_height=200;
1163 io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
1164 io->max_y_units=AUTO_MAX_YSCALE;
1166 io->last_interval=0xffffffff;
1169 io->left_x_border=0;
1170 io->right_x_border=500;
1171 io->view_as_time=FALSE;
1172 io->start_time.secs=0;
1173 io->start_time.nsecs=0;
1175 for(i=0;i<MAX_GRAPHS;i++){
1176 io->graphs[i].gc=NULL;
1177 io->graphs[i].color.pixel=col[i].pixel;
1178 io->graphs[i].color.red=col[i].red;
1179 io->graphs[i].color.green=col[i].green;
1180 io->graphs[i].color.blue=col[i].blue;
1181 io->graphs[i].display=0;
1182 io->graphs[i].display_button=NULL;
1183 io->graphs[i].filter_field=NULL;
1184 io->graphs[i].advanced_buttons=NULL;
1185 io->graphs[i].io=io;
1187 io->graphs[i].args=g_malloc(sizeof(construct_args_t));
1188 io->graphs[i].args->title = NULL;
1189 io->graphs[i].args->wants_apply_button=TRUE;
1190 io->graphs[i].args->activate_on_ok=TRUE;
1191 io->graphs[i].args->modal_and_transient=FALSE;
1193 io->graphs[i].filter_bt=NULL;
1197 error_string=enable_graph(&io->graphs[0], NULL, NULL);
1199 fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
1201 g_string_free(error_string, TRUE);
1202 io->graphs[0].display=0;
1203 io->graphs[0].display_button=NULL;
1204 io->graphs[0].filter_field=NULL;
1205 io->graphs[0].advanced_buttons=NULL;
1210 init_io_stat_window(io);
1212 cf_retap_packets(&cfile, FALSE);
1217 quit(GtkWidget *widget, GdkEventExpose *event _U_)
1222 io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1224 for(i=0;i<MAX_GRAPHS;i++){
1225 if(io->graphs[i].display){
1226 protect_thread_critical_region();
1227 remove_tap_listener(&io->graphs[i]);
1228 unprotect_thread_critical_region();
1230 g_free( (gpointer) (io->graphs[i].args->title) );
1231 io->graphs[i].args->title=NULL;
1233 g_free(io->graphs[i].args);
1234 io->graphs[i].args=NULL;
1243 pixmap_clicked_event(GtkWidget *widget, GdkEventButton *event)
1245 io_stat_t *io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1246 guint32 draw_width, interval, last_interval;
1253 draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
1255 if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) ||
1256 (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) {
1257 /* Outside draw area */
1261 if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
1263 * Button 1 selects the first package in the interval.
1264 * Button 3 selects the last package in the interval.
1266 if (io->last_interval==0xffffffff) {
1267 last_interval=io->max_interval;
1269 last_interval=io->last_interval;
1272 interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick);
1273 frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE);
1274 if (frame_num != 0) {
1275 cf_goto_frame(&cfile, frame_num);
1282 /* create a new backing pixmap of the appropriate size */
1284 configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1288 #if GTK_CHECK_VERSION(2,6,0)
1292 io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1298 gdk_pixmap_unref(io->pixmap);
1302 io->pixmap=gdk_pixmap_new(widget->window,
1303 widget->allocation.width,
1304 widget->allocation.height,
1306 io->pixmap_width=widget->allocation.width;
1307 io->pixmap_height=widget->allocation.height;
1309 #if GTK_CHECK_VERSION(2,6,0)
1310 save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
1311 g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
1312 gtk_widget_set_sensitive(save_bt, TRUE);
1315 gdk_draw_rectangle(io->pixmap,
1316 widget->style->white_gc,
1319 widget->allocation.width,
1320 widget->allocation.height);
1322 /* set up the colors and the GC structs for this pixmap */
1323 for(i=0;i<MAX_GRAPHS;i++){
1324 io->graphs[i].gc=gdk_gc_new(io->pixmap);
1325 gdk_gc_set_rgb_fg_color(io->graphs[i].gc, &io->graphs[i].color);
1333 scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1335 io_stat_t *io=(io_stat_t *)data;
1338 mi=(guint32) (io->scrollbar_adjustment->value+io->scrollbar_adjustment->page_size);
1339 if(io->last_interval==mi){
1342 if( (io->last_interval==0xffffffff)
1343 && (mi==io->max_interval) ){
1347 io->last_interval=(mi/io->interval)*io->interval;
1353 /* redraw the screen from the backing pixmap */
1355 expose_event(GtkWidget *widget, GdkEventExpose *event)
1359 io=(io_stat_t *)g_object_get_data(G_OBJECT(widget), "io_stat_t");
1365 gdk_draw_pixmap(widget->window,
1366 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1368 event->area.x, event->area.y,
1369 event->area.x, event->area.y,
1370 event->area.width, event->area.height);
1377 create_draw_area(io_stat_t *io, GtkWidget *box)
1379 io->draw_area=gtk_drawing_area_new();
1380 g_signal_connect(io->draw_area, "destroy", G_CALLBACK(quit), io);
1381 g_object_set_data(G_OBJECT(io->draw_area), "io_stat_t", io);
1383 gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
1385 /* signals needed to handle backing pixmap */
1386 g_signal_connect(io->draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1387 g_signal_connect(io->draw_area, "configure_event", G_CALLBACK(configure_event), io);
1388 gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
1389 g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), NULL);
1391 gtk_widget_show(io->draw_area);
1392 gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
1394 /* create the associated scrollbar */
1395 io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1396 io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
1397 gtk_widget_show(io->scrollbar);
1398 gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
1399 g_signal_connect(io->scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), io);
1404 tick_interval_select(GtkWidget *item, gpointer key)
1409 io=(io_stat_t *)key;
1410 val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1413 cf_retap_packets(&cfile, FALSE);
1418 pixels_per_tick_select(GtkWidget *item, gpointer key)
1423 io=(io_stat_t *)key;
1424 val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1425 io->pixels_per_tick=val;
1430 plot_style_select(GtkWidget *item, gpointer key)
1433 io_stat_graph_t *ppt;
1435 ppt=(io_stat_graph_t *)key;
1436 val=(long)g_object_get_data(G_OBJECT(item), "plot_style");
1438 ppt->plot_style=val;
1440 io_stat_redraw(ppt->io);
1444 create_pixels_per_tick_menu_items(io_stat_t *io, GtkWidget *menu)
1447 GtkWidget *menu_item;
1450 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1451 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1452 menu_item=gtk_menu_item_new_with_label(str);
1454 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1455 GUINT_TO_POINTER(pixels_per_tick[i]));
1456 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), io);
1457 gtk_widget_show(menu_item);
1458 gtk_menu_append(GTK_MENU(menu), menu_item);
1460 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1466 yscale_select(GtkWidget *item, gpointer key)
1471 io=(io_stat_t *)key;
1472 val=(long)g_object_get_data(G_OBJECT(item), "yscale_max");
1474 io->max_y_units=val;
1479 create_tick_interval_menu_items(io_stat_t *io, GtkWidget *menu)
1482 GtkWidget *menu_item;
1485 for(i=0;i<MAX_TICK_VALUES;i++){
1486 if(tick_interval_values[i]>=60000){
1487 g_snprintf(str, 15, "%u min", tick_interval_values[i]/60000);
1488 } else if(tick_interval_values[i]>=1000){
1489 g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
1490 } else if(tick_interval_values[i]>=100){
1491 g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
1492 } else if(tick_interval_values[i]>=10){
1493 g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
1495 g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
1498 menu_item=gtk_menu_item_new_with_label(str);
1499 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1500 GUINT_TO_POINTER(tick_interval_values[i]));
1501 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)io);
1502 gtk_widget_show(menu_item);
1503 gtk_menu_append(GTK_MENU(menu), menu_item);
1505 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1510 create_yscale_max_menu_items(io_stat_t *io, GtkWidget *menu)
1513 GtkWidget *menu_item;
1516 for(i=0;i<MAX_YSCALE;i++){
1517 if(yscale_max[i]==LOGARITHMIC_YSCALE){
1518 g_strlcpy(str, "Logarithmic", 15);
1519 } else if(yscale_max[i]==AUTO_MAX_YSCALE){
1520 g_strlcpy(str, "Auto", 15);
1522 g_snprintf(str, 15, "%u", yscale_max[i]);
1524 menu_item=gtk_menu_item_new_with_label(str);
1525 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1526 GUINT_TO_POINTER(yscale_max[i]));
1527 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), io);
1528 gtk_widget_show(menu_item);
1529 gtk_menu_append(GTK_MENU(menu), menu_item);
1531 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_YSCALE);
1536 count_type_select(GtkWidget *item, gpointer key)
1538 static gboolean advanced_visible=FALSE;
1542 io=(io_stat_t *)key;
1543 val=(long)g_object_get_data(G_OBJECT(item), "count_type");
1547 if(io->count_type==COUNT_TYPE_ADVANCED){
1548 for(i=0;i<MAX_GRAPHS;i++){
1549 disable_graph(&io->graphs[i]);
1550 gtk_widget_show(io->graphs[i].advanced_buttons);
1551 /* redraw the entire window so the unhidden widgets show up, hopefully */
1552 {GdkRectangle update_rect;
1555 update_rect.width=io->window->allocation.width;
1556 update_rect.height=io->window->allocation.height;
1557 gtk_widget_draw(io->window, &update_rect);
1560 advanced_visible=TRUE;
1562 } else if (advanced_visible) {
1563 for(i=0;i<MAX_GRAPHS;i++){
1564 gtk_widget_hide(io->graphs[i].advanced_buttons);
1565 filter_callback(item, &io->graphs[i]);
1567 advanced_visible=FALSE;
1574 create_frames_or_bytes_menu_items(io_stat_t *io, GtkWidget *menu)
1576 GtkWidget *menu_item;
1579 for(i=0;i<MAX_COUNT_TYPES;i++){
1580 menu_item=gtk_menu_item_new_with_label(count_type_names[i]);
1581 g_object_set_data(G_OBJECT(menu_item), "count_type", GINT_TO_POINTER(i));
1582 g_signal_connect(menu_item, "activate", G_CALLBACK(count_type_select), io);
1583 gtk_widget_show(menu_item);
1584 gtk_menu_append(GTK_MENU(menu), menu_item);
1590 create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, void (*func)(io_stat_t *io, GtkWidget *menu))
1594 GtkWidget *option_menu;
1597 hbox=gtk_hbox_new(FALSE, 0);
1598 gtk_container_add(GTK_CONTAINER(box), hbox);
1599 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1600 gtk_widget_show(hbox);
1602 label=gtk_label_new(name);
1603 gtk_widget_show(label);
1604 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1606 option_menu=gtk_option_menu_new();
1607 menu=gtk_menu_new();
1609 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1610 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1611 gtk_widget_show(option_menu);
1615 view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer key)
1619 io=(io_stat_t *)key;
1620 io->view_as_time = io->view_as_time ? FALSE : TRUE;
1626 create_ctrl_area(io_stat_t *io, GtkWidget *box)
1628 GtkWidget *frame_vbox;
1633 frame_vbox=gtk_vbox_new(FALSE, 0);
1634 gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0);
1635 gtk_widget_show(frame_vbox);
1637 frame = gtk_frame_new("X Axis");
1638 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1639 gtk_widget_show(frame);
1641 vbox=gtk_vbox_new(FALSE, 0);
1642 gtk_container_add(GTK_CONTAINER(frame), vbox);
1643 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1644 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1645 gtk_widget_show(vbox);
1647 create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
1648 create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1650 view_cb = gtk_check_button_new_with_mnemonic("_View as time of day");
1651 gtk_container_add(GTK_CONTAINER(vbox), view_cb);
1652 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time);
1653 g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io);
1654 gtk_widget_show(view_cb);
1656 frame = gtk_frame_new("Y Axis");
1657 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1658 gtk_widget_show(frame);
1660 vbox=gtk_vbox_new(FALSE, 0);
1661 gtk_container_add(GTK_CONTAINER(frame), vbox);
1662 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1663 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1664 gtk_widget_show(vbox);
1666 create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
1667 create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
1674 filter_callback(GtkWidget *widget _U_, io_stat_graph_t *gio)
1677 const char *field=NULL;
1678 header_field_info *hfi;
1681 /* this graph is not active, just update display and redraw */
1682 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
1684 io_stat_redraw(gio->io);
1688 /* first check if the field string is valid */
1689 if(gio->io->count_type==COUNT_TYPE_ADVANCED){
1690 field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
1692 /* warn and bail out if there was no field specified */
1693 if(field==NULL || field[0]==0){
1694 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
1696 io_stat_redraw(gio->io);
1699 /* warn and bail out if the field could not be found */
1700 hfi=proto_registrar_get_byname(field);
1702 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
1704 io_stat_redraw(gio->io);
1707 gio->hf_index=hfi->id;
1708 /* check that the type is compatible */
1718 /* these values support all calculations except LOAD */
1719 switch(gio->calc_type){
1720 case CALC_TYPE_LOAD:
1721 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1722 "LOAD(*) is only supported for relative-time fields.");
1724 io_stat_redraw(gio->io);
1727 /* these types support all calculations */
1729 case FT_RELATIVE_TIME:
1730 /* this type only supports COUNT, MAX, MIN, AVG */
1731 switch(gio->calc_type){
1733 case CALC_TYPE_COUNT:
1737 case CALC_TYPE_LOAD:
1740 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1741 "%s is a relative-time field, so %s calculations are not supported on it.",
1743 calc_type_names[gio->calc_type]);
1745 io_stat_redraw(gio->io);
1752 * XXX - support this if gint64/guint64 are
1755 if(gio->calc_type!=CALC_TYPE_COUNT){
1756 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1757 "%s is a 64-bit integer, so %s calculations are not supported on it.",
1759 calc_type_names[gio->calc_type]);
1761 io_stat_redraw(gio->io);
1767 * XXX - support all operations on floating-point
1770 if(gio->calc_type!=CALC_TYPE_COUNT){
1771 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1772 "%s doesn't have integral values, so %s calculations are not supported on it.",
1774 calc_type_names[gio->calc_type]);
1776 io_stat_redraw(gio->io);
1783 /* first check if the filter string is valid. */
1784 filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
1785 if(!dfilter_compile(filter, &dfilter)) {
1786 bad_dfilter_alert_box(filter);
1788 io_stat_redraw(gio->io);
1791 if (dfilter != NULL)
1792 dfilter_free(dfilter);
1794 /* ok, we have a valid filter and the graph is active.
1795 first just try to delete any previous settings and then apply
1798 protect_thread_critical_region();
1799 remove_tap_listener(gio);
1800 unprotect_thread_critical_region();
1802 io_stat_reset(gio->io);
1803 enable_graph(gio, filter, field);
1804 cf_retap_packets(&cfile, FALSE);
1805 io_stat_redraw(gio->io);
1812 calc_type_select(GtkWidget *item _U_, gpointer key)
1814 io_stat_calc_type_t *ct=(io_stat_calc_type_t *)key;
1816 ct->gio->calc_type=ct->calc_type;
1818 /* disable the graph */
1819 disable_graph(ct->gio);
1820 io_stat_redraw(ct->gio->io);
1825 create_calc_types_menu_items(io_stat_graph_t *gio, GtkWidget *menu)
1827 GtkWidget *menu_item;
1830 for(i=0;i<MAX_CALC_TYPES;i++){
1831 gio->calc_types[i].gio=gio;
1832 gio->calc_types[i].calc_type=i;
1833 menu_item=gtk_menu_item_new_with_label(calc_type_names[i]);
1834 g_signal_connect(menu_item, "activate", G_CALLBACK(calc_type_select), &gio->calc_types[i]);
1835 gtk_widget_show(menu_item);
1836 gtk_menu_append(GTK_MENU(menu), menu_item);
1843 create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name, void (*func)(io_stat_graph_t *io, GtkWidget *menu))
1847 GtkWidget *option_menu;
1850 hbox=gtk_hbox_new(FALSE, 0);
1851 gtk_container_add(GTK_CONTAINER(box), hbox);
1852 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1853 gtk_widget_show(hbox);
1855 label=gtk_label_new(name);
1856 gtk_widget_show(label);
1857 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1859 option_menu=gtk_option_menu_new();
1860 menu=gtk_menu_new();
1862 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1863 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1864 gtk_widget_show(option_menu);
1868 create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
1871 gio->calc_field=gtk_entry_new_with_max_length(50);
1872 gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0);
1873 gtk_widget_show(gio->calc_field);
1874 g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio);
1879 create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
1883 hbox=gtk_hbox_new(FALSE, 0);
1884 gio->advanced_buttons=hbox;
1885 gtk_container_add(GTK_CONTAINER(box), hbox);
1886 gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START);
1887 gtk_widget_hide(hbox);
1889 gio->calc_type=CALC_TYPE_SUM;
1890 create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
1891 create_advanced_field(gio, hbox);
1896 filter_button_clicked(GtkWidget *w, gpointer uio)
1898 io_stat_graph_t *gio=(io_stat_graph_t *)uio;
1900 display_filter_construct_cb(w, gio->args);
1905 create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
1907 GtkWidget *option_menu;
1909 GtkWidget *menu_item;
1915 hbox=gtk_hbox_new(FALSE, 3);
1916 gtk_container_add(GTK_CONTAINER(box), hbox);
1917 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1918 gtk_widget_show(hbox);
1920 g_snprintf(str, 256, "Graph %d", num);
1921 gio->display_button=gtk_toggle_button_new_with_label(str);
1922 gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
1923 gtk_widget_show(gio->display_button);
1924 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
1925 g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);
1927 label=gtk_label_new("Color");
1928 gtk_widget_show(label);
1929 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1931 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color);
1932 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color);
1933 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color);
1934 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color);
1935 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color);
1936 /* gtk_signal_connect(GTK_OBJECT(gio->display_button), "toggled", GTK_SIGNAL_FUNC(filter_callback), gio);*/
1939 /* filter prefs dialog */
1940 gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
1942 g_snprintf(str, 256, "Wireshark: Display Filter IO-Stat (Filter:%d)", num);
1943 if(gio->args->title){
1944 g_free( (gpointer) (gio->args->title) );
1946 gio->args->title=g_strdup(str);
1948 g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio);
1949 g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
1951 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
1952 gtk_widget_show(gio->filter_bt);
1954 gio->filter_field=gtk_entry_new_with_max_length(256);
1956 /* filter prefs dialog */
1957 g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field);
1958 /* filter prefs dialog */
1960 gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0);
1961 gtk_widget_show(gio->filter_field);
1962 g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio);
1963 g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
1965 create_advanced_box(gio, hbox);
1969 * create PlotStyle menu
1971 g_snprintf(str, 256, " Style:");
1972 label=gtk_label_new(str);
1973 gtk_widget_show(label);
1974 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1976 option_menu=gtk_option_menu_new();
1977 menu=gtk_menu_new();
1978 for(i=0;i<MAX_PLOT_STYLES;i++){
1979 menu_item=gtk_menu_item_new_with_label(plot_style_name[i]);
1980 g_object_set_data(G_OBJECT(menu_item), "plot_style", GINT_TO_POINTER(i));
1981 g_signal_connect(menu_item, "activate", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]);
1982 gtk_widget_show(menu_item);
1983 gtk_menu_append(GTK_MENU(menu), menu_item);
1985 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PLOT_STYLE);
1987 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1988 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1989 gtk_widget_show(option_menu);
1996 create_filter_area(io_stat_t *io, GtkWidget *box)
2002 frame=gtk_frame_new("Graphs");
2003 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2004 gtk_widget_show(frame);
2006 vbox=gtk_vbox_new(FALSE, 1);
2007 gtk_container_add(GTK_CONTAINER(frame), vbox);
2008 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2009 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
2010 gtk_widget_show(vbox);
2012 for(i=0;i<MAX_GRAPHS;i++){
2013 create_filter_box(&io->graphs[i], vbox, i+1);
2021 copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer data)
2023 guint32 i, interval, val;
2026 GString *CSV_str=g_string_new("");
2027 io_stat_t *io=(io_stat_t *)data;
2029 g_string_append(CSV_str, "Interval start");
2030 for(i=0;i<MAX_GRAPHS;i++) {
2031 if (io->graphs[i].display) {
2032 g_string_append_printf(CSV_str, ",Graph %d", i+1);
2035 g_string_append(CSV_str,"\n");
2037 for(interval=0; interval<io->max_interval; interval+=io->interval) {
2038 print_interval_string (string, 15, interval, io, FALSE);
2039 g_string_append(CSV_str, string);
2040 for(i=0;i<MAX_GRAPHS;i++) {
2041 if (io->graphs[i].display) {
2042 val=get_it_value(io, i, interval/io->interval);
2043 g_string_append_printf(CSV_str, ",%d", val);
2046 g_string_append(CSV_str,"\n");
2049 /* Now that we have the CSV data, copy it into the default clipboard */
2050 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
2051 gtk_clipboard_set_text(cb, CSV_str->str, -1); /* Copy the CSV data into the clipboard */
2052 g_string_free(CSV_str, TRUE); /* Free the memory */
2057 init_io_stat_window(io_stat_t *io)
2062 GtkWidget *close_bt, *help_bt;
2063 GtkTooltips *tooltips = gtk_tooltips_new();
2065 #if GTK_CHECK_VERSION(2,6,0)
2069 /* create the main window */
2070 io->window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
2072 vbox=gtk_vbox_new(FALSE, 0);
2073 gtk_container_add(GTK_CONTAINER(io->window), vbox);
2074 gtk_widget_show(vbox);
2076 create_draw_area(io, vbox);
2078 hbox=gtk_hbox_new(FALSE, 3);
2079 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2080 gtk_container_border_width(GTK_CONTAINER(hbox), 3);
2081 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2082 gtk_widget_show(hbox);
2084 create_filter_area(io, hbox);
2085 create_ctrl_area(io, hbox);
2087 io_stat_set_title(io);
2089 #if GTK_CHECK_VERSION(2,6,0)
2090 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE,
2091 GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
2093 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY,
2094 GTK_STOCK_HELP, NULL);
2096 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
2097 gtk_widget_show(bbox);
2099 close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
2100 window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
2101 gtk_tooltips_set_tip(tooltips, close_bt, "Close this dialog", NULL);
2103 #if GTK_CHECK_VERSION(2,6,0)
2104 save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
2105 gtk_widget_set_sensitive(save_bt, FALSE);
2106 gtk_tooltips_set_tip(tooltips, save_bt, "Save the displayed graph to a file", NULL);
2107 g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
2108 g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt);
2111 copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
2112 gtk_tooltips_set_tip(tooltips, copy_bt,
2113 "Copy values from selected graphs to the clipboard in CSV (Comma Seperated Values) format", NULL);
2114 g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io);
2116 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
2117 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG);
2118 gtk_tooltips_set_tip (tooltips, help_bt, "Show topic specific help", NULL);
2120 g_signal_connect(io->window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
2122 gtk_widget_show(io->window);
2123 window_present(io->window);
2128 gtk_iostat_cb(GtkWidget *w _U_, gpointer d _U_)
2130 gtk_iostat_init(NULL,NULL);
2137 register_tap_listener_gtk_iostat(void)
2139 register_stat_cmd_arg("io,stat", gtk_iostat_init,NULL);
2141 register_stat_menu_item_stock("_IO Graphs",
2142 REGISTER_STAT_GROUP_GENERIC, WIRESHARK_STOCK_GRAPHS,
2143 gtk_iostat_cb, NULL, NULL, NULL);