From Harald Welte:
[obnox/wireshark/wip.git] / tap-rpcstat.c
index cf80ef73e74b761ec09729ffbd82572a34243de3..3f7245f425a37300d633f84dac6717c66349268d 100644 (file)
@@ -1,29 +1,29 @@
 /* tap-rpcstat.c
  * rpcstat   2002 Ronnie Sahlberg
  *
- * $Id: tap-rpcstat.c,v 1.1 2002/09/04 09:40:24 sahlberg Exp $
+ * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
- * 
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
-/* This module provides rpc call/reply RTT statistics to tethereal.
- * It is only used by tethereal and not ethereal
+/* This module provides rpc call/reply SRT statistics to tshark.
+ * It is only used by tshark and not wireshark.
  *
  * It serves as an example on how to use the tap api.
  */
 
 #include <string.h>
 #include "epan/packet_info.h"
-#include "tap.h"
-#include "tap-rpcstat.h"
-#include "packet-rpc.h"
+#include <epan/tap.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/dissectors/packet-rpc.h>
 
+#define MICROSECS_PER_SEC   1000000
+#define NANOSECS_PER_SEC    1000000000
 
 /* used to keep track of statistics for a specific procedure */
 typedef struct _rpc_procedure_t {
-       char *proc;
+       const char *proc;
        int num;
        nstime_t min;
        nstime_t max;
@@ -56,7 +58,8 @@ typedef struct _rpc_procedure_t {
 
 /* used to keep track of the statistics for an entire program interface */
 typedef struct _rpcstat_t {
-       char *prog;
+       const char *prog;
+       char *filter;
        guint32 program;
        guint32 version;
        guint32 num_procedures;
@@ -65,23 +68,25 @@ typedef struct _rpcstat_t {
 
 
 
-/* This callback is never used by tethereal but it is here for completeness.
+/* This callback is never used by tshark but it is here for completeness.
  * When registering below, we could just have left this function as NULL.
  *
- * When used by ethereal, this function will be called whenever we would need
- * to reset all state. Such as when ethereal opens a new file, when it
+ * When used by wireshark, this function will be called whenever we would need
+ * to reset all state, such as when wireshark opens a new file, when it
  * starts a new capture, when it rescans the packetlist after some prefs have
  * changed etc.
- * So if your aplication has some state it needs to clean up in those
+ *
+ * So if your application has some state it needs to clean up in those
  * situations, here is a good place to put that code.
  */
 static void
-rpcstat_reset(rpcstat_t *rs)
+rpcstat_reset(void *prs)
 {
+       rpcstat_t *rs=prs;
        guint32 i;
 
        for(i=0;i<rs->num_procedures;i++){
-               rs->procedures[i].num=0;        
+               rs->procedures[i].num=0;
                rs->procedures[i].min.secs=0;
                rs->procedures[i].min.nsecs=0;
                rs->procedures[i].max.secs=0;
@@ -92,39 +97,38 @@ rpcstat_reset(rpcstat_t *rs)
 }
 
 
-/* This callback is invoked whenever the tap system has seen a packet
- * we might be interested in.
- * The function is to be used to only update internal state information
- * in the *tapdata structure, and if there were state changes which requires
- * the window to be redrawn, return 1 and (*draw) will be called sometime
- * later.
+/* This callback is invoked whenever the tap system has seen a packet we might
+ * be interested in.  The function is to be used to only update internal state
+ * information in the *tapdata structure, and if there were state changes which
+ * requires the window to be redrawn, return 1 and (*draw) will be called
+ * sometime later.
  *
- * This function should be as lightweight as possible since it executes together
- * with the normal ethereal dissectors. Try to push as much processing as
- * possible into (*draw) instead since that function executes asynchronously
- * and does not affect the main threads performance.
+ * This function should be as lightweight as possible since it executes
+ * together with the normal wireshark dissectors.  Try to push as much
+ * processing as possible into (*draw) instead since that function executes
+ * asynchronously and does not affect the main thread's performance.
  *
- * If it is possible, try to do all "filtering" explicitely as we do below in 
+ * If it is possible, try to do all "filtering" explicitly as we do below in
  * this example since you will get MUCH better performance than applying
  * a similar display-filter in the register call.
  *
- * The third parameter is tap dependant. Since we register this one to the "rpc"
- * tap the third parameters type is rpc_call_info_value.
+ * The third parameter is tap dependent.  Since we register this one to the
+ * "rpc" tap, the third parameter type is rpc_call_info_value.
  *
- *
- * The filtering we do is just to check the rpc_call_info_value struct that
- * we were called for the proper program and version. We didnt apply a filter
+ * The filtering we do is just to check the rpc_call_info_value struct that we
+ * were called for the proper program and version.  We didn't apply a filter
  * when we registered so we will be called for ALL rpc packets and not just
  * the ones we are collecting stats for.
- * 
  *
  * function returns :
  *  0: no updates, no need to call (*draw) later
  * !0: state has changed, call (*draw) sometime later
  */
 static int
-rpcstat_packet(rpcstat_t *rs, packet_info *pinfo, rpc_call_info_value *ri)
+rpcstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri)
 {
+       rpcstat_t *rs=prs;
+       const rpc_call_info_value *ri=pri;
        nstime_t delta;
        rpc_procedure_t *rp;
 
@@ -144,21 +148,14 @@ rpcstat_packet(rpcstat_t *rs, packet_info *pinfo, rpc_call_info_value *ri)
        rp=&(rs->procedures[ri->proc]);
 
        /* calculate time delta between request and reply */
-       delta.secs=pinfo->fd->abs_secs-ri->req_time.secs;
-       delta.nsecs=pinfo->fd->abs_usecs*1000-ri->req_time.nsecs;
-       if(delta.nsecs<0){
-               delta.nsecs+=1000000000;
-               delta.secs--;
-       }
+       nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->req_time);
 
-       if((rp->max.secs==0)
-       && (rp->max.nsecs==0) ){
+       if(rp->num==0){
                rp->max.secs=delta.secs;
                rp->max.nsecs=delta.nsecs;
        }
 
-       if((rp->min.secs==0)
-       && (rp->min.nsecs==0) ){
+       if(rp->num==0){
                rp->min.secs=delta.secs;
                rp->min.nsecs=delta.nsecs;
        }
@@ -176,62 +173,57 @@ rpcstat_packet(rpcstat_t *rs, packet_info *pinfo, rpc_call_info_value *ri)
                rp->max.secs=delta.secs;
                rp->max.nsecs=delta.nsecs;
        }
-       
+
        rp->tot.secs += delta.secs;
        rp->tot.nsecs += delta.nsecs;
-       if(rp->tot.nsecs>1000000000){
-               rp->tot.nsecs-=1000000000;
+       if(rp->tot.nsecs > NANOSECS_PER_SEC){
+               rp->tot.nsecs -= NANOSECS_PER_SEC;
                rp->tot.secs++;
        }
+
        rp->num++;
 
        return 1;
 }
 
-/* This callback is used when tethereal wants us to draw/update our
- * data to the output device. Since this is tethereal only output is
- * stdout.
- * Tethereal will only call this callback once, which is when tethereal has
- * finished reading all packets and exists.
- * If used with ethereal this may be called any time, perhaps once every 3 
+/* This callback is used when tshark wants us to draw/update our data to the
+ * output device.  Since this is tshark, the only output is stdout.
+ * TShark will only call this callback once, which is when tshark has finished
+ * reading all packets and exits.
+ * If used with wireshark this may be called any time, perhaps once every 3
  * seconds or so.
- * This function may even be called in parallell with (*reset) or (*draw)
- * so make sure there are no races. The data in the rpcstat_t can thus change
- * beneath us. Beware.
+ * This function may even be called in parallel with (*reset) or (*draw), so
+ * make sure there are no races.  The data in the rpcstat_t can thus change
+ * beneath us.  Beware!
  */
 static void
-rpcstat_draw(rpcstat_t *rs)
+rpcstat_draw(void *prs)
 {
+       rpcstat_t *rs=prs;
        guint32 i;
-#ifdef G_HAVE_UINT64
        guint64 td;
-#else
-       guint32 td;
-#endif
        printf("\n");
-       printf("===================================================================\n");
-       printf("%s Version %d RTT Statistics:\n", rs->prog, rs->version);
-       printf("Procedure        Calls   Min RTT   Max RTT   Avg RTT\n");
+       printf("=======================================================\n");
+       printf("%s Version %d SRT Statistics:\n", rs->prog, rs->version);
+       printf("Filter: %s\n",rs->filter?rs->filter:"");
+       printf("Procedure        Calls    Min SRT    Max SRT    Avg SRT\n");
        for(i=0;i<rs->num_procedures;i++){
-               /* scale it to units of 10us.*/
-               /* for long captures with a large tot time, this can overflow on 32bit */
-               td=(int)rs->procedures[i].tot.secs;
-               td=td*100000+(int)rs->procedures[i].tot.nsecs/10000;
-               if(rs->procedures[i].num){
-                       td/=rs->procedures[i].num;
-               } else {
-                       td=0;
+               if(rs->procedures[i].num==0){
+                       continue;
                }
+               /* Scale the average SRT in units of 1us and round to the nearest us. */
+               td = ((guint64)(rs->procedures[i].tot.secs)) * NANOSECS_PER_SEC + rs->procedures[i].tot.nsecs;
+               td = ((td / rs->procedures[i].num) + 500) / 1000;
 
-               printf("%-15s %6d %3d.%05d %3d.%05d %3d.%05d\n",
+               printf("%-15s %6d %3d.%06d %3d.%06d %3" G_GINT64_MODIFIER "u.%06" G_GINT64_MODIFIER "u\n",
                        rs->procedures[i].proc,
                        rs->procedures[i].num,
-                       (int)rs->procedures[i].min.secs,rs->procedures[i].min.nsecs/10000,
-                       (int)rs->procedures[i].max.secs,rs->procedures[i].max.nsecs/10000,
-                       td/100000, td%100000
+                       (int)(rs->procedures[i].min.secs),(rs->procedures[i].min.nsecs+500)/1000,
+                       (int)(rs->procedures[i].max.secs),(rs->procedures[i].max.nsecs+500)/1000,
+                       td/MICROSECS_PER_SEC, td%MICROSECS_PER_SEC
                );
        }
-       printf("===================================================================\n");
+       printf("=======================================================\n");
 }
 
 static guint32 rpc_program=0;
@@ -266,33 +258,52 @@ rpcstat_find_procs(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
 
 
 /* When called, this function will create a new instance of rpcstat.
- * program and version are whick onc-rpc program/version we want to
- * collect statistics for.
- * This function is called from tethereal when it parses the -Z rpc, arguments
- * and it creates a new instance to store statistics in and registers this
- * new instance for the rpc tap.
+ *
+ * program and version are which onc-rpc program/version we want to collect
+ * statistics for.
+ *
+ * This function is called from tshark when it parses the -z rpc, arguments and
+ * it creates a new instance to store statistics in and registers this new
+ * instance for the rpc tap.
  */
-void
-rpcstat_init(guint32 program, guint32 version)
+static void
+rpcstat_init(const char *optarg, void* userdata _U_)
 {
        rpcstat_t *rs;
        guint32 i;
-/*     char filter[256];*/
-
+       int program, version;
+       int pos=0;
+       const char *filter=NULL;
+       GString *error_string;
+
+       if(sscanf(optarg,"rpc,srt,%d,%d,%n",&program,&version,&pos)==2){
+               if(pos){
+                       filter=optarg+pos;
+               } else {
+                       filter=NULL;
+               }
+       } else {
+               fprintf(stderr, "tshark: invalid \"-z rpc,srt,<program>,<version>[,<filter>]\" argument\n");
+               exit(1);
+       }
 
        rs=g_malloc(sizeof(rpcstat_t));
        rs->prog=rpc_prog_name(program);
        rs->program=program;
        rs->version=version;
-
+       if(filter){
+               rs->filter=g_strdup(filter);
+       } else {
+               rs->filter=NULL;
+       }
        rpc_program=program;
        rpc_version=version;
        rpc_min_proc=-1;
        rpc_max_proc=-1;
        g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_procs, NULL);
        if(rpc_min_proc==-1){
-               fprintf(stderr,"tethereal: Invalid -Z rpc,rrt,%d,%d\n",rpc_program,rpc_version);
-               fprintf(stderr,"   Program:%d version:%d is not supported by tethereal.\n", rpc_program, rpc_version);
+               fprintf(stderr,"tshark: Invalid -z rpc,srt,%d,%d\n",rpc_program,rpc_version);
+               fprintf(stderr,"   Program:%d version:%d isn't supported by tshark.\n", rpc_program, rpc_version);
                exit(1);
        }
 
@@ -301,7 +312,7 @@ rpcstat_init(guint32 program, guint32 version)
        rs->procedures=g_malloc(sizeof(rpc_procedure_t)*(rs->num_procedures+1));
        for(i=0;i<rs->num_procedures;i++){
                rs->procedures[i].proc=rpc_proc_name(program, version, i);
-               rs->procedures[i].num=0;        
+               rs->procedures[i].num=0;
                rs->procedures[i].min.secs=0;
                rs->procedures[i].min.nsecs=0;
                rs->procedures[i].max.secs=0;
@@ -310,29 +321,35 @@ rpcstat_init(guint32 program, guint32 version)
                rs->procedures[i].tot.nsecs=0;
        }
 
-/* It is possible to create a filter and attach it to the callbacks. Then the
+/* It is possible to create a filter and attach it to the callbacks.  Then the
  * callbacks would only be invoked if the filter matched.
- * Evaluating filters is expensive and if we can avoid it and not use them
- * we gain performance. Here we just tap everything from rpc without
- * filtering and we do the filtering we need explicitely inside the callback
- * itself (*packet) instead with a few if()s.
  *
- * This is what it would look like IF we wanted to use a filter when attaching
- * it to the tap.
-       snprintf(filter, 255, "(rpc.msgtyp==1) && (rpc.program==%d) && (rpc.programversion==%d)",program,version);
-       register_tap_listener("rpc", rs, filter, (void*)rpcstat_reset, (void*)rpcstat_packet, (void*)rpcstat_draw);
+ * Evaluating filters is expensive and if we can avoid it and not use them,
+ * then we gain performance.
+ *
+ * In this case, we do the filtering for protocol and version inside the
+ * callback itself but use whatever filter the user provided.
+ * (Perhaps the user only wants the stats for nis+ traffic for certain objects?)
  */
 
-       if(register_tap_listener("rpc", rs, NULL, (void*)rpcstat_reset, (void*)rpcstat_packet, (void*)rpcstat_draw)){
+       error_string=register_tap_listener("rpc", rs, filter, 0, rpcstat_reset, rpcstat_packet, rpcstat_draw);
+       if(error_string){
                /* error, we failed to attach to the tap. clean up */
                g_free(rs->procedures);
+               g_free(rs->filter);
                g_free(rs);
 
-               fprintf(stderr,"tethereal: rpcstat_init() failed to attach to tap.\n");
+               fprintf(stderr, "tshark: Couldn't register rpc,srt tap: %s\n",
+                   error_string->str);
+               g_string_free(error_string, TRUE);
                exit(1);
        }
 }
 
 
-
+void
+register_tap_listener_rpcstat(void)
+{
+       register_stat_cmd_arg("rpc,srt,", rpcstat_init,NULL);
+}