2 * rpcstat 2002 Ronnie Sahlberg
4 * $Id: tap-rpcstat.c,v 1.12 2004/05/09 10:03:38 guy Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@ethereal.com>
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.
25 /* This module provides rpc call/reply RTT statistics to tethereal.
26 * It is only used by tethereal and not ethereal
28 * It serves as an example on how to use the tap api.
31 /* With MSVC and a libethereal.dll this file needs to import some variables
32 in a special way. Therefore _NEED_VAR_IMPORT_ is defined. */
33 #define _NEED_VAR_IMPORT_
41 #ifdef HAVE_SYS_TYPES_H
42 # include <sys/types.h>
46 #include "epan/packet_info.h"
48 #include "packet-rpc.h"
51 /* used to keep track of statistics for a specific procedure */
52 typedef struct _rpc_procedure_t {
60 /* used to keep track of the statistics for an entire program interface */
61 typedef struct _rpcstat_t {
66 guint32 num_procedures;
67 rpc_procedure_t *procedures;
72 /* This callback is never used by tethereal but it is here for completeness.
73 * When registering below, we could just have left this function as NULL.
75 * When used by ethereal, this function will be called whenever we would need
76 * to reset all state. Such as when ethereal opens a new file, when it
77 * starts a new capture, when it rescans the packetlist after some prefs have
79 * So if your aplication has some state it needs to clean up in those
80 * situations, here is a good place to put that code.
83 rpcstat_reset(void *prs)
88 for(i=0;i<rs->num_procedures;i++){
89 rs->procedures[i].num=0;
90 rs->procedures[i].min.secs=0;
91 rs->procedures[i].min.nsecs=0;
92 rs->procedures[i].max.secs=0;
93 rs->procedures[i].max.nsecs=0;
94 rs->procedures[i].tot.secs=0;
95 rs->procedures[i].tot.nsecs=0;
100 /* This callback is invoked whenever the tap system has seen a packet
101 * we might be interested in.
102 * The function is to be used to only update internal state information
103 * in the *tapdata structure, and if there were state changes which requires
104 * the window to be redrawn, return 1 and (*draw) will be called sometime
107 * This function should be as lightweight as possible since it executes together
108 * with the normal ethereal dissectors. Try to push as much processing as
109 * possible into (*draw) instead since that function executes asynchronously
110 * and does not affect the main threads performance.
112 * If it is possible, try to do all "filtering" explicitely as we do below in
113 * this example since you will get MUCH better performance than applying
114 * a similar display-filter in the register call.
116 * The third parameter is tap dependant. Since we register this one to the "rpc"
117 * tap the third parameters type is rpc_call_info_value.
120 * The filtering we do is just to check the rpc_call_info_value struct that
121 * we were called for the proper program and version. We didnt apply a filter
122 * when we registered so we will be called for ALL rpc packets and not just
123 * the ones we are collecting stats for.
127 * 0: no updates, no need to call (*draw) later
128 * !0: state has changed, call (*draw) sometime later
131 rpcstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, void *pri)
134 rpc_call_info_value *ri=pri;
138 if(ri->proc>=rs->num_procedures){
139 /* dont handle this since its outside of known table */
142 /* we are only interested in reply packets */
146 /* we are only interested in certain program/versions */
147 if( (ri->prog!=rs->program) || (ri->vers!=rs->version) ){
151 rp=&(rs->procedures[ri->proc]);
153 /* calculate time delta between request and reply */
154 delta.secs=pinfo->fd->abs_secs-ri->req_time.secs;
155 delta.nsecs=pinfo->fd->abs_usecs*1000-ri->req_time.nsecs;
157 delta.nsecs+=1000000000;
162 rp->max.secs=delta.secs;
163 rp->max.nsecs=delta.nsecs;
167 rp->min.secs=delta.secs;
168 rp->min.nsecs=delta.nsecs;
171 if( (delta.secs<rp->min.secs)
172 ||( (delta.secs==rp->min.secs)
173 &&(delta.nsecs<rp->min.nsecs) ) ){
174 rp->min.secs=delta.secs;
175 rp->min.nsecs=delta.nsecs;
178 if( (delta.secs>rp->max.secs)
179 ||( (delta.secs==rp->max.secs)
180 &&(delta.nsecs>rp->max.nsecs) ) ){
181 rp->max.secs=delta.secs;
182 rp->max.nsecs=delta.nsecs;
185 rp->tot.secs += delta.secs;
186 rp->tot.nsecs += delta.nsecs;
187 if(rp->tot.nsecs>1000000000){
188 rp->tot.nsecs-=1000000000;
197 /* This callback is used when tethereal wants us to draw/update our
198 * data to the output device. Since this is tethereal only output is
200 * Tethereal will only call this callback once, which is when tethereal has
201 * finished reading all packets and exists.
202 * If used with ethereal this may be called any time, perhaps once every 3
204 * This function may even be called in parallell with (*reset) or (*draw)
205 * so make sure there are no races. The data in the rpcstat_t can thus change
206 * beneath us. Beware.
209 rpcstat_draw(void *prs)
219 printf("===================================================================\n");
220 printf("%s Version %d RTT Statistics:\n", rs->prog, rs->version);
221 printf("Filter: %s\n",rs->filter?rs->filter:"");
222 printf("Procedure Calls Min RTT Max RTT Avg RTT\n");
223 for(i=0;i<rs->num_procedures;i++){
224 /* scale it to units of 10us.*/
225 /* for long captures with a large tot time, this can overflow on 32bit */
226 td=(int)rs->procedures[i].tot.secs;
227 td=td*100000+(int)rs->procedures[i].tot.nsecs/10000;
228 if(rs->procedures[i].num){
229 td/=rs->procedures[i].num;
234 printf("%-15s %6d %3d.%05d %3d.%05d %3d.%05d\n",
235 rs->procedures[i].proc,
236 rs->procedures[i].num,
237 (int)rs->procedures[i].min.secs,rs->procedures[i].min.nsecs/10000,
238 (int)rs->procedures[i].max.secs,rs->procedures[i].max.nsecs/10000,
242 printf("===================================================================\n");
245 static guint32 rpc_program=0;
246 static guint32 rpc_version=0;
247 static gint32 rpc_min_proc=-1;
248 static gint32 rpc_max_proc=-1;
251 rpcstat_find_procs(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
253 rpc_proc_info_key *k=(rpc_proc_info_key *)key;
255 if(k->prog!=rpc_program){
258 if(k->vers!=rpc_version){
261 if(rpc_min_proc==-1){
262 rpc_min_proc=k->proc;
263 rpc_max_proc=k->proc;
265 if((gint32)k->proc<rpc_min_proc){
266 rpc_min_proc=k->proc;
268 if((gint32)k->proc>rpc_max_proc){
269 rpc_max_proc=k->proc;
276 /* When called, this function will create a new instance of rpcstat.
277 * program and version are whick onc-rpc program/version we want to
278 * collect statistics for.
279 * This function is called from tethereal when it parses the -z rpc, arguments
280 * and it creates a new instance to store statistics in and registers this
281 * new instance for the rpc tap.
284 rpcstat_init(char *optarg)
288 int program, version;
291 GString *error_string;
293 if(sscanf(optarg,"rpc,rtt,%d,%d,%n",&program,&version,&pos)==2){
300 fprintf(stderr, "tethereal: invalid \"-z rpc,rtt,<program>,<version>[,<filter>]\" argument\n");
304 rs=g_malloc(sizeof(rpcstat_t));
305 rs->prog=rpc_prog_name(program);
309 rs->filter=g_malloc(strlen(filter)+1);
310 strcpy(rs->filter, filter);
318 g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_procs, NULL);
319 if(rpc_min_proc==-1){
320 fprintf(stderr,"tethereal: Invalid -z rpc,rrt,%d,%d\n",rpc_program,rpc_version);
321 fprintf(stderr," Program:%d version:%d is not supported by tethereal.\n", rpc_program, rpc_version);
326 rs->num_procedures=rpc_max_proc+1;
327 rs->procedures=g_malloc(sizeof(rpc_procedure_t)*(rs->num_procedures+1));
328 for(i=0;i<rs->num_procedures;i++){
329 rs->procedures[i].proc=rpc_proc_name(program, version, i);
330 rs->procedures[i].num=0;
331 rs->procedures[i].min.secs=0;
332 rs->procedures[i].min.nsecs=0;
333 rs->procedures[i].max.secs=0;
334 rs->procedures[i].max.nsecs=0;
335 rs->procedures[i].tot.secs=0;
336 rs->procedures[i].tot.nsecs=0;
339 /* It is possible to create a filter and attach it to the callbacks. Then the
340 * callbacks would only be invoked if the filter matched.
341 * Evaluating filters is expensive and if we can avoid it and not use them
342 * we gain performance.
343 * In this case we do the filtering for protocol and version inside the
344 * callback itself but use whatever filter the user provided.
345 * (Perhaps the user only want the stats for nis+ traffic for certain objects?)
349 error_string=register_tap_listener("rpc", rs, filter, rpcstat_reset, rpcstat_packet, rpcstat_draw);
351 /* error, we failed to attach to the tap. clean up */
352 g_free(rs->procedures);
356 fprintf(stderr, "tethereal: Couldn't register rpc,rtt tap: %s\n",
358 g_string_free(error_string, TRUE);
365 register_tap_listener_rpcstat(void)
367 register_ethereal_tap("rpc,rtt,", rpcstat_init);