29ff8e5832f87abe0fdc217d11ba53192105a5a7
[obnox/wireshark/wip.git] / gtk / smb_stat.c
1 /* smb_stat.c
2  * smb_stat   2003 Ronnie Sahlberg
3  *
4  * $Id: smb_stat.c,v 1.5 2003/04/23 08:20:06 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 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stdio.h>
30
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif
34
35 #include <gtk/gtk.h>
36 #include <string.h>
37 #include "menu.h"
38 #include "../epan/packet_info.h"
39 #include "../tap.h"
40 #include "../epan/value_string.h"
41 #include "../smb.h"
42 #include "../register.h"
43 #include "compat_macros.h"
44 #include "../simple_dialog.h"
45 #include "../file.h"
46 #include "../globals.h"
47
48 typedef struct _smb_procedure_t {
49         int num;
50         nstime_t min;
51         nstime_t max;
52         nstime_t tot;
53 } smb_procedure_t;
54
55 /* used to keep track of the statistics for an entire program interface */
56 typedef struct _smbstat_t {
57         GtkWidget *win;
58         GtkWidget *vbox;
59         char *filter;
60         GtkWidget *table;
61         int table_height;
62         GtkWidget *table_widgets[768];
63         smb_procedure_t proc[256];
64         smb_procedure_t trans2[256];
65         smb_procedure_t nt_trans[256];
66 } smbstat_t;
67
68
69
70
71 static void
72 add_table_entry(smbstat_t *ss, char *str, int x, int y)
73 {
74         GtkWidget *tmp;
75
76         if(y>=ss->table_height){
77                 ss->table_height=y+1;
78                 gtk_table_resize(GTK_TABLE(ss->table), ss->table_height, 5);
79         }
80         tmp=gtk_label_new(str);
81         gtk_table_attach_defaults(GTK_TABLE(ss->table), tmp, x, x+1, y, y+1);
82         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
83         gtk_widget_show(tmp);
84 }
85
86
87 static void
88 smbstat_reset(void *pss)
89 {
90         smbstat_t *ss=(smbstat_t *)pss;
91         guint32 i;
92
93         for(i=0;i<256;i++){
94                 ss->proc[i].num=0;      
95                 ss->proc[i].min.secs=0;
96                 ss->proc[i].min.nsecs=0;
97                 ss->proc[i].max.secs=0;
98                 ss->proc[i].max.nsecs=0;
99                 ss->proc[i].tot.secs=0;
100                 ss->proc[i].tot.nsecs=0;
101                 
102                 ss->trans2[i].num=0;    
103                 ss->trans2[i].min.secs=0;
104                 ss->trans2[i].min.nsecs=0;
105                 ss->trans2[i].max.secs=0;
106                 ss->trans2[i].max.nsecs=0;
107                 ss->trans2[i].tot.secs=0;
108                 ss->trans2[i].tot.nsecs=0;
109
110                 ss->nt_trans[i].num=0;  
111                 ss->nt_trans[i].min.secs=0;
112                 ss->nt_trans[i].min.nsecs=0;
113                 ss->nt_trans[i].max.secs=0;
114                 ss->nt_trans[i].max.nsecs=0;
115                 ss->nt_trans[i].tot.secs=0;
116                 ss->nt_trans[i].tot.nsecs=0;
117         }
118 }
119
120 static int
121 smbstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, void *psi)
122 {
123         smbstat_t *ss=(smbstat_t *)pss;
124         smb_info_t *si=psi;
125         nstime_t delta;
126         smb_procedure_t *sp;
127
128         /* we are only interested in reply packets */
129         if(si->request){
130                 return 0;
131         }
132         /* if we havnt seen the request, just ignore it */
133         if(!si->sip){
134                 return 0;
135         }
136
137         if(si->cmd==0xA0){
138                 smb_nt_transact_info_t *sti=(smb_nt_transact_info_t *)si->sip->extra_info;
139
140                 /*nt transaction*/
141                 sp=&(ss->nt_trans[sti->subcmd]);
142         } else if(si->cmd==0x32){
143                 smb_transact2_info_t *st2i=(smb_transact2_info_t *)si->sip->extra_info;
144
145                 /*transaction2*/
146                 sp=&(ss->trans2[st2i->subcmd]);
147         } else {
148                 sp=&(ss->proc[si->cmd]);
149         }
150
151         /* calculate time delta between request and reply */
152         delta.secs=pinfo->fd->abs_secs-si->sip->req_time.secs;
153         delta.nsecs=pinfo->fd->abs_usecs*1000-si->sip->req_time.nsecs;
154         if(delta.nsecs<0){
155                 delta.nsecs+=1000000000;
156                 delta.secs--;
157         }
158
159         if((sp->max.secs==0)
160         && (sp->max.nsecs==0) ){
161                 sp->max.secs=delta.secs;
162                 sp->max.nsecs=delta.nsecs;
163         }
164
165         if((sp->min.secs==0)
166         && (sp->min.nsecs==0) ){
167                 sp->min.secs=delta.secs;
168                 sp->min.nsecs=delta.nsecs;
169         }
170
171         if( (delta.secs<sp->min.secs)
172         ||( (delta.secs==sp->min.secs)
173           &&(delta.nsecs<sp->min.nsecs) ) ){
174                 sp->min.secs=delta.secs;
175                 sp->min.nsecs=delta.nsecs;
176         }
177
178         if( (delta.secs>sp->max.secs)
179         ||( (delta.secs==sp->max.secs)
180           &&(delta.nsecs>sp->max.nsecs) ) ){
181                 sp->max.secs=delta.secs;
182                 sp->max.nsecs=delta.nsecs;
183         }
184         
185         sp->tot.secs += delta.secs;
186         sp->tot.nsecs += delta.nsecs;
187         if(sp->tot.nsecs>1000000000){
188                 sp->tot.nsecs-=1000000000;
189                 sp->tot.secs++;
190         }
191         sp->num++;
192
193         return 1;
194 }
195
196 static void
197 smbstat_draw(void *pss)
198 {
199         smbstat_t *ss=(smbstat_t *)pss;
200         guint32 i;
201         int pos;
202         char str[256];
203 #ifdef G_HAVE_UINT64
204         guint64 td;
205 #else
206         guint32 td;
207 #endif
208
209         gtk_widget_destroy(ss->table);
210         ss->table_height=5;
211         ss->table=gtk_table_new(ss->table_height, 5, TRUE);
212         gtk_container_add(GTK_CONTAINER(ss->vbox), ss->table);
213
214         pos=0;
215         add_table_entry(ss, "Command", 0, pos);
216         add_table_entry(ss, "Calls", 1, pos);
217         add_table_entry(ss, "Min RTT", 2, pos);
218         add_table_entry(ss, "Max RTT", 3, pos);
219         add_table_entry(ss, "Avg RTT", 4, pos);
220         pos++;
221
222         for(i=0;i<256;i++){
223                 /* nothing seen, nothing to do */
224                 if(ss->proc[i].num==0){
225                         continue;
226                 }
227
228                 /* we deal with transaction2 later */
229                 if(i==0x32){
230                         continue;
231                 }
232
233                 /* we deal with nt transaction later */
234                 if(i==0xA0){
235                         continue;
236                 }
237
238                 /* scale it to units of 10us.*/
239                 /* for long captures with a large tot time, this can overflow on 32bit */
240                 td=(int)ss->proc[i].tot.secs;
241                 td=td*100000+(int)ss->proc[i].tot.nsecs/10000;
242                 if(ss->proc[i].num){
243                         td/=ss->proc[i].num;
244                 } else {
245                         td=0;
246                 }
247
248                 sprintf(str, "%s", val_to_str(i, smb_cmd_vals, "Unknown (0x%02x)"));
249                 add_table_entry(ss, str, 0, pos);
250                 sprintf(str, "%d", ss->proc[i].num);
251                 add_table_entry(ss, str, 1, pos);
252                 sprintf(str, "%3d.%05d", (int)ss->proc[i].min.secs,ss->proc[i].min.nsecs/10000);
253                 add_table_entry(ss, str, 2, pos);
254                 sprintf(str, "%3d.%05d", (int)ss->proc[i].max.secs,ss->proc[i].max.nsecs/10000);
255                 add_table_entry(ss, str, 3, pos);
256                 sprintf(str, "%3d.%05d", td/100000, td%100000);
257                 add_table_entry(ss, str, 4, pos);
258                 pos++;
259         }
260
261
262         add_table_entry(ss, "", 0, pos);
263         add_table_entry(ss, "", 1, pos);
264         add_table_entry(ss, "", 2, pos);
265         add_table_entry(ss, "", 3, pos);
266         add_table_entry(ss, "", 4, pos);
267         pos++;
268
269         add_table_entry(ss, "Transaction2 Command", 0, pos);
270         add_table_entry(ss, "Calls", 1, pos);
271         add_table_entry(ss, "Min RTT", 2, pos);
272         add_table_entry(ss, "Max RTT", 3, pos);
273         add_table_entry(ss, "Avg RTT", 4, pos);
274         pos++;
275
276         for(i=0;i<256;i++){
277                 /* nothing seen, nothing to do */
278                 if(ss->trans2[i].num==0){
279                         continue;
280                 }
281
282                 /* scale it to units of 10us.*/
283                 /* for long captures with a large tot time, this can overflow on 32bit */
284                 td=(int)ss->trans2[i].tot.secs;
285                 td=td*100000+(int)ss->trans2[i].tot.nsecs/10000;
286                 if(ss->trans2[i].num){
287                         td/=ss->trans2[i].num;
288                 } else {
289                         td=0;
290                 }
291
292                 sprintf(str, "%s", val_to_str(i, trans2_cmd_vals, "Unknown (0x%02x)"));
293                 add_table_entry(ss, str, 0, pos);
294                 sprintf(str, "%d", ss->trans2[i].num);
295                 add_table_entry(ss, str, 1, pos);
296                 sprintf(str, "%3d.%05d", (int)ss->trans2[i].min.secs,ss->trans2[i].min.nsecs/10000);
297                 add_table_entry(ss, str, 2, pos);
298                 sprintf(str, "%3d.%05d", (int)ss->trans2[i].max.secs,ss->trans2[i].max.nsecs/10000);
299                 add_table_entry(ss, str, 3, pos);
300                 sprintf(str, "%3d.%05d", td/100000, td%100000);
301                 add_table_entry(ss, str, 4, pos);
302                 pos++;
303         }
304
305         add_table_entry(ss, "", 0, pos);
306         add_table_entry(ss, "", 1, pos);
307         add_table_entry(ss, "", 2, pos);
308         add_table_entry(ss, "", 3, pos);
309         add_table_entry(ss, "", 4, pos);
310         pos++;
311
312         add_table_entry(ss, "NT Transaction Command", 0, pos);
313         add_table_entry(ss, "Calls", 1, pos);
314         add_table_entry(ss, "Min RTT", 2, pos);
315         add_table_entry(ss, "Max RTT", 3, pos);
316         add_table_entry(ss, "Avg RTT", 4, pos);
317         pos++;
318
319         for(i=0;i<256;i++){
320                 /* nothing seen, nothing to do */
321                 if(ss->nt_trans[i].num==0){
322                         continue;
323                 }
324
325                 /* scale it to units of 10us.*/
326                 /* for long captures with a large tot time, this can overflow on 32bit */
327                 td=(int)ss->nt_trans[i].tot.secs;
328                 td=td*100000+(int)ss->nt_trans[i].tot.nsecs/10000;
329                 if(ss->nt_trans[i].num){
330                         td/=ss->nt_trans[i].num;
331                 } else {
332                         td=0;
333                 }
334
335                 sprintf(str, "%s", val_to_str(i, nt_cmd_vals, "Unknown (0x%02x)"));
336                 add_table_entry(ss, str, 0, pos);
337                 sprintf(str, "%d", ss->nt_trans[i].num);
338                 add_table_entry(ss, str, 1, pos);
339                 sprintf(str, "%3d.%05d", (int)ss->nt_trans[i].min.secs,ss->nt_trans[i].min.nsecs/10000);
340                 add_table_entry(ss, str, 2, pos);
341                 sprintf(str, "%3d.%05d", (int)ss->nt_trans[i].max.secs,ss->nt_trans[i].max.nsecs/10000);
342                 add_table_entry(ss, str, 3, pos);
343                 sprintf(str, "%3d.%05d", td/100000, td%100000);
344                 add_table_entry(ss, str, 4, pos);
345                 pos++;
346         }
347         gtk_widget_show(ss->table);
348 }
349
350
351 void protect_thread_critical_region(void);
352 void unprotect_thread_critical_region(void);
353 static void
354 win_destroy_cb(GtkWindow *win _U_, gpointer data)
355 {
356         smbstat_t *ss=(smbstat_t *)data;
357
358         protect_thread_critical_region();
359         remove_tap_listener(ss);
360         unprotect_thread_critical_region();
361
362         if(ss->filter){
363                 g_free(ss->filter);
364                 ss->filter=NULL;
365         }
366         g_free(ss);
367 }
368
369
370 static void
371 gtk_smbstat_init(char *optarg)
372 {
373         smbstat_t *ss;
374         guint32 i;
375         char *filter=NULL;
376         GtkWidget *stat_label;
377         GtkWidget *filter_label;
378         char filter_string[256];
379         GString *error_string;
380
381         if(!strncmp(optarg,"smb,rtt,",8)){
382                 filter=optarg+8;
383         } else {
384                 filter=NULL;
385         }
386
387         ss=g_malloc(sizeof(smbstat_t));
388         if(filter){
389                 ss->filter=g_malloc(strlen(filter)+1);
390                 strcpy(ss->filter, filter);
391         } else {
392                 ss->filter=NULL;
393         }
394
395         for(i=0;i<256;i++){
396                 ss->proc[i].num=0;      
397                 ss->proc[i].min.secs=0;
398                 ss->proc[i].min.nsecs=0;
399                 ss->proc[i].max.secs=0;
400                 ss->proc[i].max.nsecs=0;
401                 ss->proc[i].tot.secs=0;
402                 ss->proc[i].tot.nsecs=0;
403                 
404                 ss->trans2[i].num=0;    
405                 ss->trans2[i].min.secs=0;
406                 ss->trans2[i].min.nsecs=0;
407                 ss->trans2[i].max.secs=0;
408                 ss->trans2[i].max.nsecs=0;
409                 ss->trans2[i].tot.secs=0;
410                 ss->trans2[i].tot.nsecs=0;
411
412                 ss->nt_trans[i].num=0;  
413                 ss->nt_trans[i].min.secs=0;
414                 ss->nt_trans[i].min.nsecs=0;
415                 ss->nt_trans[i].max.secs=0;
416                 ss->nt_trans[i].max.nsecs=0;
417                 ss->nt_trans[i].tot.secs=0;
418                 ss->nt_trans[i].tot.nsecs=0;
419         }
420
421         ss->win=gtk_window_new(GTK_WINDOW_TOPLEVEL);
422         gtk_window_set_title(GTK_WINDOW(ss->win), "SMB RTT Statistics");
423         SIGNAL_CONNECT(ss->win, "destroy", win_destroy_cb, ss);
424
425         ss->vbox=gtk_vbox_new(FALSE, 0);
426         gtk_container_add(GTK_CONTAINER(ss->win), ss->vbox);
427         gtk_container_set_border_width(GTK_CONTAINER(ss->vbox), 10);
428         gtk_widget_show(ss->vbox);
429
430         stat_label=gtk_label_new("SMB RTT Statistics");
431         gtk_box_pack_start(GTK_BOX(ss->vbox), stat_label, FALSE, FALSE, 0);
432         gtk_widget_show(stat_label);
433
434         snprintf(filter_string,255,"Filter:%s",filter?filter:"");
435         filter_label=gtk_label_new(filter_string);
436         gtk_box_pack_start(GTK_BOX(ss->vbox), filter_label, FALSE, FALSE, 0);
437         gtk_widget_show(filter_label);
438
439
440         ss->table_height=5;
441         ss->table=gtk_table_new(ss->table_height, 5, TRUE);
442         gtk_container_add(GTK_CONTAINER(ss->vbox), ss->table);
443
444         add_table_entry(ss, "Command", 0, 0);
445         add_table_entry(ss, "Calls", 1, 0);
446         add_table_entry(ss, "Min RTT", 2, 0);
447         add_table_entry(ss, "Max RTT", 3, 0);
448         add_table_entry(ss, "Avg RTT", 4, 0);
449
450         add_table_entry(ss, "", 0, 1);
451         add_table_entry(ss, "", 1, 1);
452         add_table_entry(ss, "", 2, 1);
453         add_table_entry(ss, "", 3, 1);
454         add_table_entry(ss, "", 4, 1);
455
456         add_table_entry(ss, "Transaction2 Commands", 0, 2);
457         add_table_entry(ss, "Calls", 1, 2);
458         add_table_entry(ss, "Min RTT", 2, 2);
459         add_table_entry(ss, "Max RTT", 3, 2);
460         add_table_entry(ss, "Avg RTT", 4, 2);
461
462         add_table_entry(ss, "", 0, 3);
463         add_table_entry(ss, "", 1, 3);
464         add_table_entry(ss, "", 2, 3);
465         add_table_entry(ss, "", 3, 3);
466         add_table_entry(ss, "", 4, 3);
467
468         add_table_entry(ss, "NT Transaction Commands", 0, 4);
469         add_table_entry(ss, "Calls", 1, 4);
470         add_table_entry(ss, "Min RTT", 2, 4);
471         add_table_entry(ss, "Max RTT", 3, 4);
472         add_table_entry(ss, "Avg RTT", 4, 4);
473
474         gtk_widget_show(ss->table);
475
476         error_string=register_tap_listener("smb", ss, filter, smbstat_reset, smbstat_packet, smbstat_draw);
477         if(error_string){
478                 simple_dialog(ESD_TYPE_WARN, NULL, error_string->str);
479                 g_string_free(error_string, TRUE);
480                 g_free(ss->filter);
481                 g_free(ss);
482                 return;
483         }
484
485         gtk_widget_show_all(ss->win);
486         redissect_packets(&cfile);
487 }
488
489
490
491 static GtkWidget *dlg=NULL, *dlg_box;
492 static GtkWidget *filter_box;
493 static GtkWidget *filter_label, *filter_entry;
494 static GtkWidget *start_button;
495
496 static void
497 dlg_destroy_cb(void)
498 {
499         dlg=NULL;
500 }
501
502 static void
503 smbstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
504 {
505         char *filter;
506         char str[256];
507
508         filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
509         if(filter[0]==0){
510                 gtk_smbstat_init("smb,rtt");
511         } else {
512                 sprintf(str,"smb,rtt,%s", filter);
513                 gtk_smbstat_init(str);
514         }
515 }
516
517 static void
518 gtk_smbstat_cb(GtkWidget *w _U_, gpointer d _U_)
519 {
520         /* if the window is already open, bring it to front */
521         if(dlg){
522                 gdk_window_raise(dlg->window);
523                 return;
524         }
525
526         dlg=gtk_window_new(GTK_WINDOW_TOPLEVEL);
527         gtk_window_set_title(GTK_WINDOW(dlg), "SMB RTT Statistics");
528         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
529         dlg_box=gtk_vbox_new(FALSE, 0);
530         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
531         gtk_widget_show(dlg_box);
532
533
534         /* filter box */
535         filter_box=gtk_hbox_new(FALSE, 10);
536         /* Filter label */
537         gtk_container_set_border_width(GTK_CONTAINER(filter_box), 10);
538         filter_label=gtk_label_new("Filter:");
539         gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
540         gtk_widget_show(filter_label);
541
542         filter_entry=gtk_entry_new_with_max_length(250);
543         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, FALSE, FALSE, 0);
544         gtk_widget_show(filter_entry);
545         
546         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
547         gtk_widget_show(filter_box);
548
549
550         /* the start button */
551         start_button=gtk_button_new_with_label("Create Stat");
552         SIGNAL_CONNECT_OBJECT(start_button, "clicked",
553                               smbstat_start_button_clicked, NULL);
554         gtk_box_pack_start(GTK_BOX(dlg_box), start_button, TRUE, TRUE, 0);
555         gtk_widget_show(start_button);
556
557         gtk_widget_show_all(dlg);
558 }
559
560 void
561 register_tap_listener_gtksmbstat(void)
562 {
563         register_ethereal_tap("smb,rtt", gtk_smbstat_init);
564 }
565
566 void
567 register_tap_menu_gtksmbstat(void)
568 {
569         register_tap_menu_item("SMB/RTT", gtk_smbstat_cb);
570 }