2 * rpcstat 2002 Ronnie Sahlberg
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
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 SRT statistics to tshark.
26 * It is only used by tshark and not wireshark.
28 * It serves as an example on how to use the tap api.
37 #ifdef HAVE_SYS_TYPES_H
38 # include <sys/types.h>
42 #include "epan/packet_info.h"
44 #include <epan/stat_cmd_args.h>
45 #include <epan/dissectors/packet-rpc.h>
47 #define MICROSECS_PER_SEC 1000000
48 #define NANOSECS_PER_SEC 1000000000
50 /* used to keep track of statistics for a specific procedure */
51 typedef struct _rpc_procedure_t {
59 /* used to keep track of the statistics for an entire program interface */
60 typedef struct _rpcstat_t {
65 guint32 num_procedures;
66 rpc_procedure_t *procedures;
71 /* This callback is never used by tshark but it is here for completeness.
72 * When registering below, we could just have left this function as NULL.
74 * When used by wireshark, this function will be called whenever we would need
75 * to reset all state, such as when wireshark opens a new file, when it
76 * starts a new capture, when it rescans the packetlist after some prefs have
79 * So if your application 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 we might
101 * be interested in. The function is to be used to only update internal state
102 * information in the *tapdata structure, and if there were state changes which
103 * requires the window to be redrawn, return 1 and (*draw) will be called
106 * This function should be as lightweight as possible since it executes
107 * together with the normal wireshark dissectors. Try to push as much
108 * processing as possible into (*draw) instead since that function executes
109 * asynchronously and does not affect the main thread's performance.
111 * If it is possible, try to do all "filtering" explicitly as we do below in
112 * this example since you will get MUCH better performance than applying
113 * a similar display-filter in the register call.
115 * The third parameter is tap dependent. Since we register this one to the
116 * "rpc" tap, the third parameter type is rpc_call_info_value.
118 * The filtering we do is just to check the rpc_call_info_value struct that we
119 * were called for the proper program and version. We didn't apply a filter
120 * when we registered so we will be called for ALL rpc packets and not just
121 * the ones we are collecting stats for.
124 * 0: no updates, no need to call (*draw) later
125 * !0: state has changed, call (*draw) sometime later
128 rpcstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri)
131 const rpc_call_info_value *ri=pri;
135 if(ri->proc>=rs->num_procedures){
136 /* dont handle this since its outside of known table */
139 /* we are only interested in reply packets */
143 /* we are only interested in certain program/versions */
144 if( (ri->prog!=rs->program) || (ri->vers!=rs->version) ){
148 rp=&(rs->procedures[ri->proc]);
150 /* calculate time delta between request and reply */
151 nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->req_time);
154 rp->max.secs=delta.secs;
155 rp->max.nsecs=delta.nsecs;
159 rp->min.secs=delta.secs;
160 rp->min.nsecs=delta.nsecs;
163 if( (delta.secs<rp->min.secs)
164 ||( (delta.secs==rp->min.secs)
165 &&(delta.nsecs<rp->min.nsecs) ) ){
166 rp->min.secs=delta.secs;
167 rp->min.nsecs=delta.nsecs;
170 if( (delta.secs>rp->max.secs)
171 ||( (delta.secs==rp->max.secs)
172 &&(delta.nsecs>rp->max.nsecs) ) ){
173 rp->max.secs=delta.secs;
174 rp->max.nsecs=delta.nsecs;
177 rp->tot.secs += delta.secs;
178 rp->tot.nsecs += delta.nsecs;
179 if(rp->tot.nsecs > NANOSECS_PER_SEC){
180 rp->tot.nsecs -= NANOSECS_PER_SEC;
189 /* This callback is used when tshark wants us to draw/update our data to the
190 * output device. Since this is tshark, the only output is stdout.
191 * TShark will only call this callback once, which is when tshark has finished
192 * reading all packets and exits.
193 * If used with wireshark this may be called any time, perhaps once every 3
195 * This function may even be called in parallel with (*reset) or (*draw), so
196 * make sure there are no races. The data in the rpcstat_t can thus change
197 * beneath us. Beware!
200 rpcstat_draw(void *prs)
206 printf("=======================================================\n");
207 printf("%s Version %d SRT Statistics:\n", rs->prog, rs->version);
208 printf("Filter: %s\n",rs->filter?rs->filter:"");
209 printf("Procedure Calls Min SRT Max SRT Avg SRT\n");
210 for(i=0;i<rs->num_procedures;i++){
211 if(rs->procedures[i].num==0){
214 /* Scale the average SRT in units of 1us and round to the nearest us. */
215 td = ((guint64)(rs->procedures[i].tot.secs)) * NANOSECS_PER_SEC + rs->procedures[i].tot.nsecs;
216 td = ((td / rs->procedures[i].num) + 500) / 1000;
218 printf("%-15s %6d %3d.%06d %3d.%06d %3" G_GINT64_MODIFIER "u.%06" G_GINT64_MODIFIER "u\n",
219 rs->procedures[i].proc,
220 rs->procedures[i].num,
221 (int)(rs->procedures[i].min.secs),(rs->procedures[i].min.nsecs+500)/1000,
222 (int)(rs->procedures[i].max.secs),(rs->procedures[i].max.nsecs+500)/1000,
223 td/MICROSECS_PER_SEC, td%MICROSECS_PER_SEC
226 printf("=======================================================\n");
229 static guint32 rpc_program=0;
230 static guint32 rpc_version=0;
231 static gint32 rpc_min_proc=-1;
232 static gint32 rpc_max_proc=-1;
235 rpcstat_find_procs(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
237 rpc_proc_info_key *k=(rpc_proc_info_key *)key;
239 if(k->prog!=rpc_program){
242 if(k->vers!=rpc_version){
245 if(rpc_min_proc==-1){
246 rpc_min_proc=k->proc;
247 rpc_max_proc=k->proc;
249 if((gint32)k->proc<rpc_min_proc){
250 rpc_min_proc=k->proc;
252 if((gint32)k->proc>rpc_max_proc){
253 rpc_max_proc=k->proc;
260 /* When called, this function will create a new instance of rpcstat.
262 * program and version are which onc-rpc program/version we want to collect
265 * This function is called from tshark when it parses the -z rpc, arguments and
266 * it creates a new instance to store statistics in and registers this new
267 * instance for the rpc tap.
270 rpcstat_init(const char *optarg, void* userdata _U_)
274 int program, version;
276 const char *filter=NULL;
277 GString *error_string;
279 if(sscanf(optarg,"rpc,srt,%d,%d,%n",&program,&version,&pos)==2){
286 fprintf(stderr, "tshark: invalid \"-z rpc,srt,<program>,<version>[,<filter>]\" argument\n");
290 rs=g_malloc(sizeof(rpcstat_t));
291 rs->prog=rpc_prog_name(program);
295 rs->filter=g_strdup(filter);
303 g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_procs, NULL);
304 if(rpc_min_proc==-1){
305 fprintf(stderr,"tshark: Invalid -z rpc,srt,%d,%d\n",rpc_program,rpc_version);
306 fprintf(stderr," Program:%d version:%d isn't supported by tshark.\n", rpc_program, rpc_version);
311 rs->num_procedures=rpc_max_proc+1;
312 rs->procedures=g_malloc(sizeof(rpc_procedure_t)*(rs->num_procedures+1));
313 for(i=0;i<rs->num_procedures;i++){
314 rs->procedures[i].proc=rpc_proc_name(program, version, i);
315 rs->procedures[i].num=0;
316 rs->procedures[i].min.secs=0;
317 rs->procedures[i].min.nsecs=0;
318 rs->procedures[i].max.secs=0;
319 rs->procedures[i].max.nsecs=0;
320 rs->procedures[i].tot.secs=0;
321 rs->procedures[i].tot.nsecs=0;
324 /* It is possible to create a filter and attach it to the callbacks. Then the
325 * callbacks would only be invoked if the filter matched.
327 * Evaluating filters is expensive and if we can avoid it and not use them,
328 * then we gain performance.
330 * In this case, we do the filtering for protocol and version inside the
331 * callback itself but use whatever filter the user provided.
332 * (Perhaps the user only wants the stats for nis+ traffic for certain objects?)
335 error_string=register_tap_listener("rpc", rs, filter, 0, rpcstat_reset, rpcstat_packet, rpcstat_draw);
337 /* error, we failed to attach to the tap. clean up */
338 g_free(rs->procedures);
342 fprintf(stderr, "tshark: Couldn't register rpc,srt tap: %s\n",
344 g_string_free(error_string, TRUE);
351 register_tap_listener_rpcstat(void)
353 register_stat_cmd_arg("rpc,srt,", rpcstat_init,NULL);