From Lars Roland:
[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.6 2003/04/25 20:54:18 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 "../timestats.h"
44 #include "compat_macros.h"
45 #include "../simple_dialog.h"
46 #include "../file.h"
47 #include "../globals.h"
48
49 /* used to keep track of the statistics for an entire program interface */
50 typedef struct _smbstat_t {
51         GtkWidget *win;
52         GtkWidget *vbox;
53         char *filter;
54         GtkWidget *table;
55         int table_height;
56         GtkWidget *table_widgets[768];
57         timestat_t proc[256];
58         timestat_t trans2[256];
59         timestat_t nt_trans[256];
60 } smbstat_t;
61
62
63
64
65 static void
66 add_table_entry(smbstat_t *ss, char *str, int x, int y)
67 {
68         GtkWidget *tmp;
69
70         if(y>=ss->table_height){
71                 ss->table_height=y+1;
72                 gtk_table_resize(GTK_TABLE(ss->table), ss->table_height, 5);
73         }
74         tmp=gtk_label_new(str);
75         gtk_table_attach_defaults(GTK_TABLE(ss->table), tmp, x, x+1, y, y+1);
76         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
77         gtk_widget_show(tmp);
78 }
79
80
81 static void
82 smbstat_reset(void *pss)
83 {
84         smbstat_t *ss=(smbstat_t *)pss;
85         guint32 i;
86
87         for(i=0;i<256;i++){
88                 ss->proc[i].num=0;
89                 ss->proc[i].min_num=0;
90                 ss->proc[i].max_num=0;
91                 ss->proc[i].min.secs=0;
92                 ss->proc[i].min.nsecs=0;
93                 ss->proc[i].max.secs=0;
94                 ss->proc[i].max.nsecs=0;
95                 ss->proc[i].tot.secs=0;
96                 ss->proc[i].tot.nsecs=0;
97
98                 ss->trans2[i].num=0;
99                 ss->trans2[i].min_num=0;
100                 ss->trans2[i].max_num=0;
101                 ss->trans2[i].min.secs=0;
102                 ss->trans2[i].min.nsecs=0;
103                 ss->trans2[i].max.secs=0;
104                 ss->trans2[i].max.nsecs=0;
105                 ss->trans2[i].tot.secs=0;
106                 ss->trans2[i].tot.nsecs=0;
107
108                 ss->nt_trans[i].num=0;
109                 ss->nt_trans[i].min_num=0;
110                 ss->nt_trans[i].max_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         timestat_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         time_stat_update(sp,&delta, pinfo);
160
161         return 1;
162 }
163
164 static void
165 smbstat_draw(void *pss)
166 {
167         smbstat_t *ss=(smbstat_t *)pss;
168         guint32 i;
169         int pos;
170         char str[256];
171 #ifdef G_HAVE_UINT64
172         guint64 td;
173 #else
174         guint32 td;
175 #endif
176
177         gtk_widget_destroy(ss->table);
178         ss->table_height=5;
179         ss->table=gtk_table_new(ss->table_height, 5, TRUE);
180         gtk_container_add(GTK_CONTAINER(ss->vbox), ss->table);
181
182         pos=0;
183         add_table_entry(ss, "Command", 0, pos);
184         add_table_entry(ss, "Calls", 1, pos);
185         add_table_entry(ss, "Min RTT", 2, pos);
186         add_table_entry(ss, "Max RTT", 3, pos);
187         add_table_entry(ss, "Avg RTT", 4, pos);
188         pos++;
189
190         for(i=0;i<256;i++){
191                 /* nothing seen, nothing to do */
192                 if(ss->proc[i].num==0){
193                         continue;
194                 }
195
196                 /* we deal with transaction2 later */
197                 if(i==0x32){
198                         continue;
199                 }
200
201                 /* we deal with nt transaction later */
202                 if(i==0xA0){
203                         continue;
204                 }
205
206                 /* scale it to units of 10us.*/
207                 /* for long captures with a large tot time, this can overflow on 32bit */
208                 td=(int)ss->proc[i].tot.secs;
209                 td=td*100000+(int)ss->proc[i].tot.nsecs/10000;
210                 if(ss->proc[i].num){
211                         td/=ss->proc[i].num;
212                 } else {
213                         td=0;
214                 }
215
216                 sprintf(str, "%s", val_to_str(i, smb_cmd_vals, "Unknown (0x%02x)"));
217                 add_table_entry(ss, str, 0, pos);
218                 sprintf(str, "%d", ss->proc[i].num);
219                 add_table_entry(ss, str, 1, pos);
220                 sprintf(str, "%3d.%05d", (int)ss->proc[i].min.secs,ss->proc[i].min.nsecs/10000);
221                 add_table_entry(ss, str, 2, pos);
222                 sprintf(str, "%3d.%05d", (int)ss->proc[i].max.secs,ss->proc[i].max.nsecs/10000);
223                 add_table_entry(ss, str, 3, pos);
224                 sprintf(str, "%3d.%05d", td/100000, td%100000);
225                 add_table_entry(ss, str, 4, pos);
226                 pos++;
227         }
228
229
230         add_table_entry(ss, "", 0, pos);
231         add_table_entry(ss, "", 1, pos);
232         add_table_entry(ss, "", 2, pos);
233         add_table_entry(ss, "", 3, pos);
234         add_table_entry(ss, "", 4, pos);
235         pos++;
236
237         add_table_entry(ss, "Transaction2 Command", 0, pos);
238         add_table_entry(ss, "Calls", 1, pos);
239         add_table_entry(ss, "Min RTT", 2, pos);
240         add_table_entry(ss, "Max RTT", 3, pos);
241         add_table_entry(ss, "Avg RTT", 4, pos);
242         pos++;
243
244         for(i=0;i<256;i++){
245                 /* nothing seen, nothing to do */
246                 if(ss->trans2[i].num==0){
247                         continue;
248                 }
249
250                 /* scale it to units of 10us.*/
251                 /* for long captures with a large tot time, this can overflow on 32bit */
252                 td=(int)ss->trans2[i].tot.secs;
253                 td=td*100000+(int)ss->trans2[i].tot.nsecs/10000;
254                 if(ss->trans2[i].num){
255                         td/=ss->trans2[i].num;
256                 } else {
257                         td=0;
258                 }
259
260                 sprintf(str, "%s", val_to_str(i, trans2_cmd_vals, "Unknown (0x%02x)"));
261                 add_table_entry(ss, str, 0, pos);
262                 sprintf(str, "%d", ss->trans2[i].num);
263                 add_table_entry(ss, str, 1, pos);
264                 sprintf(str, "%3d.%05d", (int)ss->trans2[i].min.secs,ss->trans2[i].min.nsecs/10000);
265                 add_table_entry(ss, str, 2, pos);
266                 sprintf(str, "%3d.%05d", (int)ss->trans2[i].max.secs,ss->trans2[i].max.nsecs/10000);
267                 add_table_entry(ss, str, 3, pos);
268                 sprintf(str, "%3d.%05d", td/100000, td%100000);
269                 add_table_entry(ss, str, 4, pos);
270                 pos++;
271         }
272
273         add_table_entry(ss, "", 0, pos);
274         add_table_entry(ss, "", 1, pos);
275         add_table_entry(ss, "", 2, pos);
276         add_table_entry(ss, "", 3, pos);
277         add_table_entry(ss, "", 4, pos);
278         pos++;
279
280         add_table_entry(ss, "NT Transaction Command", 0, pos);
281         add_table_entry(ss, "Calls", 1, pos);
282         add_table_entry(ss, "Min RTT", 2, pos);
283         add_table_entry(ss, "Max RTT", 3, pos);
284         add_table_entry(ss, "Avg RTT", 4, pos);
285         pos++;
286
287         for(i=0;i<256;i++){
288                 /* nothing seen, nothing to do */
289                 if(ss->nt_trans[i].num==0){
290                         continue;
291                 }
292
293                 /* scale it to units of 10us.*/
294                 /* for long captures with a large tot time, this can overflow on 32bit */
295                 td=(int)ss->nt_trans[i].tot.secs;
296                 td=td*100000+(int)ss->nt_trans[i].tot.nsecs/10000;
297                 if(ss->nt_trans[i].num){
298                         td/=ss->nt_trans[i].num;
299                 } else {
300                         td=0;
301                 }
302
303                 sprintf(str, "%s", val_to_str(i, nt_cmd_vals, "Unknown (0x%02x)"));
304                 add_table_entry(ss, str, 0, pos);
305                 sprintf(str, "%d", ss->nt_trans[i].num);
306                 add_table_entry(ss, str, 1, pos);
307                 sprintf(str, "%3d.%05d", (int)ss->nt_trans[i].min.secs,ss->nt_trans[i].min.nsecs/10000);
308                 add_table_entry(ss, str, 2, pos);
309                 sprintf(str, "%3d.%05d", (int)ss->nt_trans[i].max.secs,ss->nt_trans[i].max.nsecs/10000);
310                 add_table_entry(ss, str, 3, pos);
311                 sprintf(str, "%3d.%05d", td/100000, td%100000);
312                 add_table_entry(ss, str, 4, pos);
313                 pos++;
314         }
315         gtk_widget_show(ss->table);
316 }
317
318
319 void protect_thread_critical_region(void);
320 void unprotect_thread_critical_region(void);
321 static void
322 win_destroy_cb(GtkWindow *win _U_, gpointer data)
323 {
324         smbstat_t *ss=(smbstat_t *)data;
325
326         protect_thread_critical_region();
327         remove_tap_listener(ss);
328         unprotect_thread_critical_region();
329
330         if(ss->filter){
331                 g_free(ss->filter);
332                 ss->filter=NULL;
333         }
334         g_free(ss);
335 }
336
337
338 static void
339 gtk_smbstat_init(char *optarg)
340 {
341         smbstat_t *ss;
342         char *filter=NULL;
343         GtkWidget *stat_label;
344         GtkWidget *filter_label;
345         char filter_string[256];
346         GString *error_string;
347
348         if(!strncmp(optarg,"smb,rtt,",8)){
349                 filter=optarg+8;
350         } else {
351                 filter=NULL;
352         }
353
354         ss=g_malloc(sizeof(smbstat_t));
355         if(filter){
356                 ss->filter=g_malloc(strlen(filter)+1);
357                 strcpy(ss->filter, filter);
358         } else {
359                 ss->filter=NULL;
360         }
361
362         smbstat_reset(ss);
363
364         ss->win=gtk_window_new(GTK_WINDOW_TOPLEVEL);
365         gtk_window_set_title(GTK_WINDOW(ss->win), "SMB RTT Statistics");
366         SIGNAL_CONNECT(ss->win, "destroy", win_destroy_cb, ss);
367
368         ss->vbox=gtk_vbox_new(FALSE, 0);
369         gtk_container_add(GTK_CONTAINER(ss->win), ss->vbox);
370         gtk_container_set_border_width(GTK_CONTAINER(ss->vbox), 10);
371         gtk_widget_show(ss->vbox);
372
373         stat_label=gtk_label_new("SMB RTT Statistics");
374         gtk_box_pack_start(GTK_BOX(ss->vbox), stat_label, FALSE, FALSE, 0);
375         gtk_widget_show(stat_label);
376
377         snprintf(filter_string,255,"Filter:%s",filter?filter:"");
378         filter_label=gtk_label_new(filter_string);
379         gtk_box_pack_start(GTK_BOX(ss->vbox), filter_label, FALSE, FALSE, 0);
380         gtk_widget_show(filter_label);
381
382
383         ss->table_height=5;
384         ss->table=gtk_table_new(ss->table_height, 5, TRUE);
385         gtk_container_add(GTK_CONTAINER(ss->vbox), ss->table);
386
387         add_table_entry(ss, "Command", 0, 0);
388         add_table_entry(ss, "Calls", 1, 0);
389         add_table_entry(ss, "Min RTT", 2, 0);
390         add_table_entry(ss, "Max RTT", 3, 0);
391         add_table_entry(ss, "Avg RTT", 4, 0);
392
393         add_table_entry(ss, "", 0, 1);
394         add_table_entry(ss, "", 1, 1);
395         add_table_entry(ss, "", 2, 1);
396         add_table_entry(ss, "", 3, 1);
397         add_table_entry(ss, "", 4, 1);
398
399         add_table_entry(ss, "Transaction2 Commands", 0, 2);
400         add_table_entry(ss, "Calls", 1, 2);
401         add_table_entry(ss, "Min RTT", 2, 2);
402         add_table_entry(ss, "Max RTT", 3, 2);
403         add_table_entry(ss, "Avg RTT", 4, 2);
404
405         add_table_entry(ss, "", 0, 3);
406         add_table_entry(ss, "", 1, 3);
407         add_table_entry(ss, "", 2, 3);
408         add_table_entry(ss, "", 3, 3);
409         add_table_entry(ss, "", 4, 3);
410
411         add_table_entry(ss, "NT Transaction Commands", 0, 4);
412         add_table_entry(ss, "Calls", 1, 4);
413         add_table_entry(ss, "Min RTT", 2, 4);
414         add_table_entry(ss, "Max RTT", 3, 4);
415         add_table_entry(ss, "Avg RTT", 4, 4);
416
417         gtk_widget_show(ss->table);
418
419         error_string=register_tap_listener("smb", ss, filter, smbstat_reset, smbstat_packet, smbstat_draw);
420         if(error_string){
421                 simple_dialog(ESD_TYPE_WARN, NULL, error_string->str);
422                 g_string_free(error_string, TRUE);
423                 g_free(ss->filter);
424                 g_free(ss);
425                 return;
426         }
427
428         gtk_widget_show_all(ss->win);
429         redissect_packets(&cfile);
430 }
431
432
433
434 static GtkWidget *dlg=NULL, *dlg_box;
435 static GtkWidget *filter_box;
436 static GtkWidget *filter_label, *filter_entry;
437 static GtkWidget *start_button;
438
439 static void
440 dlg_destroy_cb(void)
441 {
442         dlg=NULL;
443 }
444
445 static void
446 smbstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
447 {
448         char *filter;
449         char str[256];
450
451         filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
452         if(filter[0]==0){
453                 gtk_smbstat_init("smb,rtt");
454         } else {
455                 sprintf(str,"smb,rtt,%s", filter);
456                 gtk_smbstat_init(str);
457         }
458 }
459
460 static void
461 gtk_smbstat_cb(GtkWidget *w _U_, gpointer d _U_)
462 {
463         /* if the window is already open, bring it to front */
464         if(dlg){
465                 gdk_window_raise(dlg->window);
466                 return;
467         }
468
469         dlg=gtk_window_new(GTK_WINDOW_TOPLEVEL);
470         gtk_window_set_title(GTK_WINDOW(dlg), "SMB RTT Statistics");
471         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
472         dlg_box=gtk_vbox_new(FALSE, 0);
473         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
474         gtk_widget_show(dlg_box);
475
476
477         /* filter box */
478         filter_box=gtk_hbox_new(FALSE, 10);
479         /* Filter label */
480         gtk_container_set_border_width(GTK_CONTAINER(filter_box), 10);
481         filter_label=gtk_label_new("Filter:");
482         gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
483         gtk_widget_show(filter_label);
484
485         filter_entry=gtk_entry_new_with_max_length(250);
486         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, FALSE, FALSE, 0);
487         gtk_widget_show(filter_entry);
488
489         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
490         gtk_widget_show(filter_box);
491
492
493         /* the start button */
494         start_button=gtk_button_new_with_label("Create Stat");
495         SIGNAL_CONNECT_OBJECT(start_button, "clicked",
496                               smbstat_start_button_clicked, NULL);
497         gtk_box_pack_start(GTK_BOX(dlg_box), start_button, TRUE, TRUE, 0);
498         gtk_widget_show(start_button);
499
500         gtk_widget_show_all(dlg);
501 }
502
503 void
504 register_tap_listener_gtksmbstat(void)
505 {
506         register_ethereal_tap("smb,rtt", gtk_smbstat_init);
507 }
508
509 void
510 register_tap_menu_gtksmbstat(void)
511 {
512         register_tap_menu_item("SMB/RTT", gtk_smbstat_cb);
513 }