Make "register_tap_listener()" return NULL on success and a "GString *"
[obnox/wireshark/wip.git] / gtk / rpc_stat.c
1 /* rpc_stat.c
2  * rpc_stat   2002 Ronnie Sahlberg
3  *
4  * $Id: rpc_stat.c,v 1.9 2003/04/23 08:20:05 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  * 
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.
14  * 
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.
19  * 
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.
23  */
24
25 /* This module provides rpc call/reply RTT statistics to tethereal.
26  * It is only used by tethereal and not ethereal
27  *
28  * It serves as an example on how to use the tap api.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <gtk/gtk.h>
36 #include "menu.h"
37 #include "epan/packet_info.h"
38 #include "simple_dialog.h"
39 #include "tap.h"
40 #include "../register.h"
41 #include "packet-rpc.h"
42 #include "../globals.h"
43 #include "compat_macros.h"
44
45 /* used to keep track of statistics for a specific procedure */
46 typedef struct _rpc_procedure_t {
47         GtkWidget *wnum;
48         GtkWidget *wmin;
49         GtkWidget *wmax;
50         GtkWidget *wavg;
51         gchar snum[8];
52         gchar smin[16];
53         gchar smax[16];
54         gchar savg[16];
55         int num;
56         nstime_t min;
57         nstime_t max;
58         nstime_t tot;
59 } rpc_procedure_t;
60
61 /* used to keep track of the statistics for an entire program interface */
62 typedef struct _rpcstat_t {
63         GtkWidget *win;
64         GtkWidget *table;
65         char *prog;
66         guint32 program;
67         guint32 version;
68         guint32 num_procedures;
69         rpc_procedure_t *procedures;
70 } rpcstat_t;
71
72
73
74
75 static void
76 rpcstat_reset(rpcstat_t *rs)
77 {
78         guint32 i;
79
80         for(i=0;i<rs->num_procedures;i++){
81                 rs->procedures[i].num=0;        
82                 rs->procedures[i].min.secs=0;
83                 rs->procedures[i].min.nsecs=0;
84                 rs->procedures[i].max.secs=0;
85                 rs->procedures[i].max.nsecs=0;
86                 rs->procedures[i].tot.secs=0;
87                 rs->procedures[i].tot.nsecs=0;
88         }
89 }
90
91
92 static int
93 rpcstat_packet(rpcstat_t *rs, packet_info *pinfo, epan_dissect_t *edt _U_, rpc_call_info_value *ri)
94 {
95         nstime_t delta;
96         rpc_procedure_t *rp;
97
98         if(ri->proc>=rs->num_procedures){
99                 /* dont handle this since its outside of known table */
100                 return 0;
101         }
102         /* we are only interested in reply packets */
103         if(ri->request){
104                 return 0;
105         }
106         /* we are only interested in certain program/versions */
107         if( (ri->prog!=rs->program) || (ri->vers!=rs->version) ){
108                 return 0;
109         }
110
111         rp=&(rs->procedures[ri->proc]);
112
113         /* calculate time delta between request and reply */
114         delta.secs=pinfo->fd->abs_secs-ri->req_time.secs;
115         delta.nsecs=pinfo->fd->abs_usecs*1000-ri->req_time.nsecs;
116         if(delta.nsecs<0){
117                 delta.nsecs+=1000000000;
118                 delta.secs--;
119         }
120
121         if((rp->max.secs==0)
122         && (rp->max.nsecs==0) ){
123                 rp->max.secs=delta.secs;
124                 rp->max.nsecs=delta.nsecs;
125         }
126
127         if((rp->min.secs==0)
128         && (rp->min.nsecs==0) ){
129                 rp->min.secs=delta.secs;
130                 rp->min.nsecs=delta.nsecs;
131         }
132
133         if( (delta.secs<rp->min.secs)
134         ||( (delta.secs==rp->min.secs)
135           &&(delta.nsecs<rp->min.nsecs) ) ){
136                 rp->min.secs=delta.secs;
137                 rp->min.nsecs=delta.nsecs;
138         }
139
140         if( (delta.secs>rp->max.secs)
141         ||( (delta.secs==rp->max.secs)
142           &&(delta.nsecs>rp->max.nsecs) ) ){
143                 rp->max.secs=delta.secs;
144                 rp->max.nsecs=delta.nsecs;
145         }
146         
147         rp->tot.secs += delta.secs;
148         rp->tot.nsecs += delta.nsecs;
149         if(rp->tot.nsecs>1000000000){
150                 rp->tot.nsecs-=1000000000;
151                 rp->tot.secs++;
152         }
153         rp->num++;
154
155         return 1;
156 }
157
158 static void
159 rpcstat_draw(rpcstat_t *rs)
160 {
161         guint32 i;
162 #ifdef G_HAVE_UINT64
163         guint64 td;
164 #else
165         guint32 td;
166 #endif
167
168         for(i=0;i<rs->num_procedures;i++){
169                 /* scale it to units of 10us.*/
170                 /* for long captures with a large tot time, this can overflow on 32bit */
171                 td=(int)rs->procedures[i].tot.secs;
172                 td=td*100000+(int)rs->procedures[i].tot.nsecs/10000;
173                 if(rs->procedures[i].num){
174                         td/=rs->procedures[i].num;
175                 } else {
176                         td=0;
177                 }
178
179                 sprintf(rs->procedures[i].snum,"%d", rs->procedures[i].num);
180                 gtk_label_set_text(GTK_LABEL(rs->procedures[i].wnum), rs->procedures[i].snum);
181
182                 sprintf(rs->procedures[i].smin,"%3d.%05d", (int)rs->procedures[i].min.secs,rs->procedures[i].min.nsecs/10000);
183                 gtk_label_set_text(GTK_LABEL(rs->procedures[i].wmin), rs->procedures[i].smin);
184
185                 sprintf(rs->procedures[i].smax,"%3d.%05d", (int)rs->procedures[i].max.secs,rs->procedures[i].max.nsecs/10000);
186                 gtk_label_set_text(GTK_LABEL(rs->procedures[i].wmax), rs->procedures[i].smax);
187
188                 sprintf(rs->procedures[i].savg,"%3d.%05d", td/100000, td%100000);
189                 gtk_label_set_text(GTK_LABEL(rs->procedures[i].wavg), rs->procedures[i].savg);
190
191         }
192 }
193
194
195
196 static guint32 rpc_program=0;
197 static guint32 rpc_version=0;
198 static gint32 rpc_min_vers=-1;
199 static gint32 rpc_max_vers=-1;
200 static gint32 rpc_min_proc=-1;
201 static gint32 rpc_max_proc=-1;
202
203 static void *
204 rpcstat_find_procs(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
205 {
206         rpc_proc_info_key *k=(rpc_proc_info_key *)key;
207
208         if(k->prog!=rpc_program){
209                 return NULL;
210         }
211         if(k->vers!=rpc_version){
212                 return NULL;
213         }
214         if(rpc_min_proc==-1){
215                 rpc_min_proc=k->proc;
216                 rpc_max_proc=k->proc;
217         }
218         if((gint32)k->proc<rpc_min_proc){
219                 rpc_min_proc=k->proc;
220         }
221         if((gint32)k->proc>rpc_max_proc){
222                 rpc_max_proc=k->proc;
223         }
224
225         return NULL;
226 }
227
228 static void *
229 rpcstat_find_vers(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
230 {
231         rpc_proc_info_key *k=(rpc_proc_info_key *)key;
232
233         if(k->prog!=rpc_program){
234                 return NULL;
235         }
236         if(rpc_min_vers==-1){
237                 rpc_min_vers=k->vers;
238                 rpc_max_vers=k->vers;
239         }
240         if((gint32)k->vers<rpc_min_vers){
241                 rpc_min_vers=k->vers;
242         }
243         if((gint32)k->vers>rpc_max_vers){
244                 rpc_max_vers=k->vers;
245         }
246
247         return NULL;
248 }
249
250 /* since the gtk2 implementation of tap is multithreaded we must protect
251  * remove_tap_listener() from modifying the list while draw_tap_listener()
252  * is running.  the other protected block is in main.c
253  *
254  * there should not be any other critical regions in gtk2
255  */
256 void protect_thread_critical_region(void);
257 void unprotect_thread_critical_region(void);
258 static void
259 win_destroy_cb(GtkWindow *win _U_, gpointer data)
260 {
261         rpcstat_t *rs=(rpcstat_t *)data;
262
263         protect_thread_critical_region();
264         remove_tap_listener(rs);
265         unprotect_thread_critical_region();
266
267         g_free(rs->procedures);
268         g_free(rs);
269 }
270
271 /* When called, this function will create a new instance of gtk2-rpcstat.
272  */
273 static void
274 gtk_rpcstat_init(char *optarg)
275 {
276         rpcstat_t *rs;
277         guint32 i;
278         char title_string[60];
279         char filter_string[256];
280         GtkWidget *vbox;
281         GtkWidget *stat_label;
282         GtkWidget *filter_label;
283         GtkWidget *tmp;
284         int program, version, pos;
285         char *filter=NULL;
286         GString *error_string;
287
288         pos=0;
289         if(sscanf(optarg,"rpc,rtt,%d,%d,%n",&program,&version,&pos)==2){
290                 if(pos){
291                         filter=optarg+pos;
292                 } else {
293                         filter=NULL;
294                 }
295         } else {
296                 fprintf(stderr, "ethereal: invalid \"-z rpc,rtt,<program>,<version>[,<filter>]\" argument\n");
297                 exit(1);
298         }
299
300         rpc_program=program;
301         rpc_version=version;
302         rs=g_malloc(sizeof(rpcstat_t));
303         rs->prog=rpc_prog_name(rpc_program);
304         rs->program=rpc_program;
305         rs->version=rpc_version;
306
307         rs->win=gtk_window_new(GTK_WINDOW_TOPLEVEL);
308         sprintf(title_string,"ONC-RPC RTT Stat for %s version %d", rs->prog, rs->version);
309         gtk_window_set_title(GTK_WINDOW(rs->win), title_string);
310         SIGNAL_CONNECT(rs->win, "destroy", win_destroy_cb, rs);
311
312         vbox=gtk_vbox_new(FALSE, 0);
313         gtk_container_add(GTK_CONTAINER(rs->win), vbox);
314         gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
315         gtk_widget_show(vbox);
316
317         stat_label=gtk_label_new(title_string);
318         gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
319         gtk_widget_show(stat_label);
320
321         snprintf(filter_string,255,"Filter:%s",filter?filter:"");
322         filter_label=gtk_label_new(filter_string);
323         gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
324         gtk_widget_show(filter_label);
325
326
327         rpc_min_proc=-1;
328         rpc_max_proc=-1;
329         g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_procs, NULL);
330         rs->num_procedures=rpc_max_proc+1;
331
332         rs->table=gtk_table_new(rs->num_procedures+1, 5, TRUE);
333         gtk_container_add(GTK_CONTAINER(vbox), rs->table);
334
335         tmp=gtk_label_new("Procedure");
336         gtk_table_attach_defaults(GTK_TABLE(rs->table), tmp, 0,1,0,1);
337         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
338         gtk_widget_show(tmp);
339
340         tmp=gtk_label_new("Calls");
341         gtk_table_attach_defaults(GTK_TABLE(rs->table), tmp, 1,2,0,1);
342         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_RIGHT);
343         gtk_widget_show(tmp);
344
345         tmp=gtk_label_new("Min RTT");
346         gtk_table_attach_defaults(GTK_TABLE(rs->table), tmp, 2,3,0,1);
347         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_RIGHT);
348         gtk_widget_show(tmp);
349
350         tmp=gtk_label_new("Max RTT");
351         gtk_table_attach_defaults(GTK_TABLE(rs->table), tmp, 3,4,0,1);
352         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_RIGHT);
353         gtk_widget_show(tmp);
354
355         tmp=gtk_label_new("Avg RTT");
356         gtk_table_attach_defaults(GTK_TABLE(rs->table), tmp, 4,5,0,1);
357         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_RIGHT);
358         gtk_widget_show(tmp);
359
360         
361         rs->procedures=g_malloc(sizeof(rpc_procedure_t)*(rs->num_procedures+1));
362         for(i=0;i<rs->num_procedures;i++){
363                 GtkWidget *tmp;
364                 
365                 tmp=gtk_label_new(rpc_proc_name(rpc_program, rpc_version, i));
366                 gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
367                 gtk_table_attach_defaults(GTK_TABLE(rs->table), tmp, 0,1,i+1,i+2);
368                 gtk_widget_show(tmp);
369
370                 rs->procedures[i].wnum=gtk_label_new("0");
371                 gtk_table_attach_defaults(GTK_TABLE(rs->table), rs->procedures[i].wnum, 1,2,i+1,i+2);
372                 gtk_label_set_justify(GTK_LABEL(rs->procedures[i].wnum), GTK_JUSTIFY_RIGHT);
373                 gtk_widget_show(rs->procedures[i].wnum);
374
375                 rs->procedures[i].wmin=gtk_label_new("0");
376                 gtk_table_attach_defaults(GTK_TABLE(rs->table), rs->procedures[i].wmin, 2,3,i+1,i+2);
377                 gtk_label_set_justify(GTK_LABEL(rs->procedures[i].wmin), GTK_JUSTIFY_RIGHT);
378                 gtk_widget_show(rs->procedures[i].wmin);
379
380                 rs->procedures[i].wmax=gtk_label_new("0");
381                 gtk_table_attach_defaults(GTK_TABLE(rs->table), rs->procedures[i].wmax, 3,4,i+1,i+2);
382                 gtk_label_set_justify(GTK_LABEL(rs->procedures[i].wmax), GTK_JUSTIFY_RIGHT);
383                 gtk_widget_show(rs->procedures[i].wmax);
384
385                 rs->procedures[i].wavg=gtk_label_new("0");
386                 gtk_table_attach_defaults(GTK_TABLE(rs->table), rs->procedures[i].wavg, 4,5,i+1,i+2);
387                 gtk_label_set_justify(GTK_LABEL(rs->procedures[i].wavg), GTK_JUSTIFY_RIGHT);
388                 gtk_widget_show(rs->procedures[i].wavg);
389
390                 rs->procedures[i].num=0;        
391                 rs->procedures[i].min.secs=0;
392                 rs->procedures[i].min.nsecs=0;
393                 rs->procedures[i].max.secs=0;
394                 rs->procedures[i].max.nsecs=0;
395                 rs->procedures[i].tot.secs=0;
396                 rs->procedures[i].tot.nsecs=0;
397         }
398
399         gtk_widget_show(rs->table);
400
401         error_string=register_tap_listener("rpc", rs, filter, (void*)rpcstat_reset, (void*)rpcstat_packet, (void*)rpcstat_draw);
402         if(error_string){
403                 simple_dialog(ESD_TYPE_WARN, NULL, error_string->str);
404                 g_string_free(error_string, TRUE);
405                 g_free(rs->procedures);
406                 g_free(rs);
407                 return;
408         }
409
410
411         gtk_widget_show_all(rs->win);
412         redissect_packets(&cfile);
413 }
414
415
416
417
418 static GtkWidget *dlg=NULL, *dlg_box;
419 static GtkWidget *prog_box;
420 static GtkWidget *prog_label, *prog_opt, *prog_menu;
421 static GtkWidget *vers_label, *vers_opt, *vers_menu;
422 static GtkWidget *filter_box;
423 static GtkWidget *filter_label, *filter_entry;
424 static GtkWidget *start_button;
425
426
427 static void
428 rpcstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
429 {
430         char *filter;
431         char str[256];
432
433         filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
434         if(filter[0]==0){
435                 sprintf(str, "rpc,rtt,%d,%d", rpc_program, rpc_version);
436                 filter="";
437         } else {
438                 sprintf(str, "rpc,rtt,%d,%d,%s", rpc_program, rpc_version, filter);
439         }
440         gtk_rpcstat_init(str);
441 }
442
443
444 static void
445 rpcstat_version_select(GtkWidget *item _U_, gpointer key)
446 {
447         int vers=(int)key;
448
449         rpc_version=vers;
450 }
451
452
453
454 static void
455 rpcstat_program_select(GtkWidget *item _U_, gpointer key)
456 {
457         rpc_prog_info_key *k=(rpc_prog_info_key *)key;
458         int i;
459
460         rpc_program=k->prog;
461
462         /* change version menu */
463         rpc_version=0;
464         gtk_object_destroy(GTK_OBJECT(vers_menu));
465         vers_menu=gtk_menu_new();
466         rpc_min_vers=-1;
467         rpc_max_vers=-1;
468         g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_vers, NULL);
469         rpc_version=rpc_min_vers;
470         for(i=rpc_min_vers;i<=rpc_max_vers;i++){
471                 GtkWidget *menu_item;
472                 char vs[5];
473                 sprintf(vs,"%d",i);
474                 menu_item=gtk_menu_item_new_with_label(vs);
475                 SIGNAL_CONNECT(menu_item, "activate", rpcstat_version_select,
476                                i);
477
478                 gtk_widget_show(menu_item);
479                 gtk_menu_append(GTK_MENU(vers_menu), menu_item);
480         }
481         gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
482 }
483
484 static void *
485 rpcstat_list_programs(gpointer *key, gpointer *value, gpointer *user_data _U_)
486 {
487         rpc_prog_info_key *k=(rpc_prog_info_key *)key;
488         rpc_prog_info_value *v=(rpc_prog_info_value *)value;
489         GtkWidget *menu_item;
490
491         menu_item=gtk_menu_item_new_with_label(v->progname);
492         SIGNAL_CONNECT(menu_item, "activate", rpcstat_program_select, k);
493
494         gtk_widget_show(menu_item);
495         gtk_menu_append(GTK_MENU(prog_menu), menu_item);
496
497         if(!rpc_program){
498                 rpc_program=k->prog;
499         }
500
501         return NULL;
502 }
503
504 static void
505 dlg_destroy_cb(void)
506 {
507         dlg=NULL;
508 }
509
510 static void
511 gtk_rpcstat_cb(GtkWidget *w _U_, gpointer d _U_)
512 {
513         int i;
514
515         /* if the window is already open, bring it to front */
516         if(dlg){
517                 gdk_window_raise(dlg->window);
518                 return;
519         }
520
521         dlg=gtk_window_new(GTK_WINDOW_TOPLEVEL);
522         gtk_window_set_title(GTK_WINDOW(dlg), "ONC-RPC RTT Statistics");
523         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
524         dlg_box=gtk_vbox_new(FALSE, 0);
525         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
526         gtk_widget_show(dlg_box);
527
528
529         prog_box=gtk_hbox_new(FALSE, 10);
530         /* Program label */
531         gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
532         prog_label=gtk_label_new("Program:");
533         gtk_box_pack_start(GTK_BOX(prog_box), prog_label, FALSE, FALSE, 0);
534         gtk_widget_show(prog_label);
535
536         /* Program menu */
537         prog_opt=gtk_option_menu_new();
538         prog_menu=gtk_menu_new();
539         g_hash_table_foreach(rpc_progs, (GHFunc)rpcstat_list_programs, NULL);
540         gtk_option_menu_set_menu(GTK_OPTION_MENU(prog_opt), prog_menu);
541         gtk_box_pack_start(GTK_BOX(prog_box), prog_opt, TRUE, TRUE, 0);
542         gtk_widget_show(prog_opt);
543
544         /* Version label */
545         gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
546         vers_label=gtk_label_new("Version:");
547         gtk_box_pack_start(GTK_BOX(prog_box), vers_label, FALSE, FALSE, 0);
548         gtk_widget_show(vers_label);
549
550         /* Version menu */
551         vers_opt=gtk_option_menu_new();
552         vers_menu=gtk_menu_new();
553         rpc_min_vers=-1;
554         rpc_max_vers=-1;
555         g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_vers, NULL);
556         rpc_version=rpc_min_vers;
557         for(i=rpc_min_vers;i<=rpc_max_vers;i++){
558                 GtkWidget *menu_item;
559                 char vs[5];
560                 sprintf(vs,"%d",i);
561                 menu_item=gtk_menu_item_new_with_label(vs);
562                 SIGNAL_CONNECT(menu_item, "activate", rpcstat_version_select,
563                                i);
564
565                 gtk_widget_show(menu_item);
566                 gtk_menu_append(GTK_MENU(vers_menu), menu_item);
567         }
568
569         gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
570         gtk_box_pack_start(GTK_BOX(prog_box), vers_opt, TRUE, TRUE, 0);
571         gtk_widget_show(vers_opt);
572
573         gtk_box_pack_start(GTK_BOX(dlg_box), prog_box, TRUE, TRUE, 0);
574         gtk_widget_show(prog_box);
575
576
577         /* filter box */
578         filter_box=gtk_hbox_new(FALSE, 10);
579         /* Filter label */
580         gtk_container_set_border_width(GTK_CONTAINER(filter_box), 10);
581         filter_label=gtk_label_new("Filter:");
582         gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
583         gtk_widget_show(filter_label);
584
585         filter_entry=gtk_entry_new_with_max_length(250);
586         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, FALSE, FALSE, 0);
587         gtk_widget_show(filter_entry);
588         
589         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
590         gtk_widget_show(filter_box);
591
592
593         /* the start button */
594         start_button=gtk_button_new_with_label("Create Stat");
595         SIGNAL_CONNECT_OBJECT(start_button, "clicked",
596                               rpcstat_start_button_clicked, NULL);
597         gtk_box_pack_start(GTK_BOX(dlg_box), start_button, TRUE, TRUE, 0);
598         gtk_widget_show(start_button);
599
600         gtk_widget_show_all(dlg);
601 }
602
603
604 void
605 register_tap_listener_gtkrpcstat(void)
606 {
607         register_ethereal_tap("rpc,rtt,", gtk_rpcstat_init);
608 }
609
610 void
611 register_tap_menu_gtkrpcstat(void)
612 {
613         register_tap_menu_item("ONC-RPC/RTT", gtk_rpcstat_cb);
614 }