From adc8816a09e5b6be2e58f4a7c28d2418a74cce9c Mon Sep 17 00:00:00 2001 From: Michael Vrable <mvrable@cs.ucsd.edu> Date: Fri, 23 Apr 2010 12:48:14 -0700 Subject: [PATCH] Import TBBT (NFS trace replay). Code downloaded from http://www.ecsl.cs.sunysb.edu/TBBT/. --- TBBT/README | 50 + TBBT/trace_init/EXAMPLES | 170 ++ TBBT/trace_init/INSTALL | 61 + TBBT/trace_init/NOTES.TXT | 17 + TBBT/trace_init/commands | 20 + TBBT/trace_init/common.pl | 61 + TBBT/trace_init/counts.pl | 103 + TBBT/trace_init/extract-hierarchy | 4 + TBBT/trace_init/hier.pl | 715 +++++ TBBT/trace_init/hier.pl.old | 550 ++++ TBBT/trace_init/key.pl | 122 + TBBT/trace_init/latency.pl | 159 + TBBT/trace_init/nfsdump.pl | 338 +++ TBBT/trace_init/nfsscan.pl | 682 +++++ TBBT/trace_init/nfsscan.txt | 230 ++ TBBT/trace_init/ns_loc2gmt.pl | 108 + TBBT/trace_init/ns_quickview.pl | 245 ++ TBBT/trace_init/ns_split.pl | 153 + TBBT/trace_init/ns_timeagg.pl | 218 ++ TBBT/trace_init/ns_tsplit.pl | 219 ++ TBBT/trace_init/rfs.pl | 243 ++ TBBT/trace_init/rfs.pl.old | 138 + TBBT/trace_init/rfs.stub.pl | 213 ++ TBBT/trace_init/test.cnt | 2 + TBBT/trace_init/test.fil | 1 + TBBT/trace_init/userUtils.pl | 87 + TBBT/trace_play/Makefile.org | 328 ++ TBBT/trace_play/README | 10 + TBBT/trace_play/README.old | 140 + TBBT/trace_play/agefs | Bin 0 -> 83979 bytes TBBT/trace_play/agefs.log | 36 + TBBT/trace_play/frag_collect | 15 + TBBT/trace_play/frag_collect_cust | 14 + TBBT/trace_play/frag_count | Bin 0 -> 26012 bytes TBBT/trace_play/frag_count.c | 311 ++ TBBT/trace_play/fragment_collect | 43 + TBBT/trace_play/generate_xmgr | Bin 0 -> 26083 bytes TBBT/trace_play/generate_xmgr.c | 226 ++ TBBT/trace_play/generate_xmgr.c.old | 70 + TBBT/trace_play/generic_hash.c | 230 ++ TBBT/trace_play/generic_hash.h | 24 + TBBT/trace_play/hash.h | 87 + TBBT/trace_play/init_holder.c | 57 + TBBT/trace_play/nfsd_nfsfh_cust.h | 114 + TBBT/trace_play/profile.c | 215 ++ TBBT/trace_play/profile.h | 27 + TBBT/trace_play/rfs_3_ops.c | 827 +++++ TBBT/trace_play/rfs_3_ops.c.old | 739 +++++ TBBT/trace_play/rfs_assert.h | 13 + TBBT/trace_play/rfs_c_age.c | 1393 +++++++++ TBBT/trace_play/rfs_c_age.c.trace_base | 551 ++++ TBBT/trace_play/rfs_c_age.c.unit_base | 915 ++++++ TBBT/trace_play/rfs_c_age.obsolete.c | 194 ++ TBBT/trace_play/rfs_c_dat.c | 188 ++ TBBT/trace_play/rfs_c_def.h | 357 +++ TBBT/trace_play/rpc/auth.h | 185 ++ TBBT/trace_play/rpc/auth_none.c | 154 + TBBT/trace_play/rpc/auth_unix.c | 345 +++ TBBT/trace_play/rpc/auth_unix.h | 95 + TBBT/trace_play/rpc/authunix_prot.c | 86 + TBBT/trace_play/rpc/bindresvport.c | 103 + TBBT/trace_play/rpc/clnt.h | 384 +++ TBBT/trace_play/rpc/clnt_generic.c | 133 + TBBT/trace_play/rpc/clnt_perror.c | 316 ++ TBBT/trace_play/rpc/clnt_simple.c | 141 + TBBT/trace_play/rpc/clnt_tcp.c | 522 ++++ TBBT/trace_play/rpc/clnt_udp.c | 510 ++++ TBBT/trace_play/rpc/get_myaddress.c | 169 ++ TBBT/trace_play/rpc/getrpcent.c | 250 ++ TBBT/trace_play/rpc/getrpcport.c | 77 + TBBT/trace_play/rpc/netdb.h | 63 + TBBT/trace_play/rpc/osdep.h | 218 ++ TBBT/trace_play/rpc/pmap_clnt.c | 138 + TBBT/trace_play/rpc/pmap_clnt.h | 97 + TBBT/trace_play/rpc/pmap_getmaps.c | 105 + TBBT/trace_play/rpc/pmap_getport.c | 110 + TBBT/trace_play/rpc/pmap_prot.c | 78 + TBBT/trace_play/rpc/pmap_prot.h | 114 + TBBT/trace_play/rpc/pmap_prot2.c | 138 + TBBT/trace_play/rpc/pmap_rmt.c | 419 +++ TBBT/trace_play/rpc/pmap_rmt.h | 74 + TBBT/trace_play/rpc/rpc.h | 93 + TBBT/trace_play/rpc/rpc_callmsg.c | 214 ++ TBBT/trace_play/rpc/rpc_commondata.c | 62 + TBBT/trace_play/rpc/rpc_dtablesize.c | 114 + TBBT/trace_play/rpc/rpc_msg.h | 215 ++ TBBT/trace_play/rpc/rpc_prot.c | 307 ++ TBBT/trace_play/rpc/sfs_ctcp.c | 727 +++++ TBBT/trace_play/rpc/sfs_cudp.c | 604 ++++ TBBT/trace_play/rpc/svc.c | 479 +++ TBBT/trace_play/rpc/svc.h | 314 ++ TBBT/trace_play/rpc/svc_auth.c | 136 + TBBT/trace_play/rpc/svc_auth.h | 62 + TBBT/trace_play/rpc/svc_auth_unix.c | 156 + TBBT/trace_play/rpc/svc_raw.c | 196 ++ TBBT/trace_play/rpc/svc_run.c | 93 + TBBT/trace_play/rpc/svc_simple.c | 171 ++ TBBT/trace_play/rpc/svc_tcp.c | 481 +++ TBBT/trace_play/rpc/svc_udp.c | 507 ++++ TBBT/trace_play/rpc/types.h | 111 + TBBT/trace_play/rpc/xdr.c | 685 +++++ TBBT/trace_play/rpc/xdr.h | 308 ++ TBBT/trace_play/rpc/xdr_array.c | 175 ++ TBBT/trace_play/rpc/xdr_float.c | 288 ++ TBBT/trace_play/rpc/xdr_mem.c | 205 ++ TBBT/trace_play/rpc/xdr_rec.c | 608 ++++ TBBT/trace_play/rpc/xdr_reference.c | 156 + TBBT/trace_play/rpc/xdr_stdio.c | 210 ++ TBBT/trace_play/sem.c | 149 + TBBT/trace_play/sfs.1 | 842 ++++++ TBBT/trace_play/sfs3.full_speed | Bin 0 -> 1004158 bytes TBBT/trace_play/sfs_2_ops.c | 2131 +++++++++++++ TBBT/trace_play/sfs_2_vld.c | 1747 +++++++++++ TBBT/trace_play/sfs_2_xdr.c | 735 +++++ TBBT/trace_play/sfs_3_ops.c | 2746 +++++++++++++++++ TBBT/trace_play/sfs_3_ops.c.org | 2701 +++++++++++++++++ TBBT/trace_play/sfs_3_vld.c | 2126 +++++++++++++ TBBT/trace_play/sfs_3_xdr.c | 1720 +++++++++++ TBBT/trace_play/sfs_c_bio.c | 1023 +++++++ TBBT/trace_play/sfs_c_bio.org | 1012 +++++++ TBBT/trace_play/sfs_c_chd.2thread.c | 3509 ++++++++++++++++++++++ TBBT/trace_play/sfs_c_chd.3thread.c | 3815 ++++++++++++++++++++++++ TBBT/trace_play/sfs_c_chd.c | 1 + TBBT/trace_play/sfs_c_chd.c.old | 2457 +++++++++++++++ TBBT/trace_play/sfs_c_chd.c.old.2 | 2294 ++++++++++++++ TBBT/trace_play/sfs_c_chd.c.org | 2866 ++++++++++++++++++ TBBT/trace_play/sfs_c_chd.work | 3815 ++++++++++++++++++++++++ TBBT/trace_play/sfs_c_clk.c | 309 ++ TBBT/trace_play/sfs_c_clnt.c | 108 + TBBT/trace_play/sfs_c_dat.c | 339 +++ TBBT/trace_play/sfs_c_def.h | 634 ++++ TBBT/trace_play/sfs_c_dmp.c | 298 ++ TBBT/trace_play/sfs_c_man.c | 1998 +++++++++++++ TBBT/trace_play/sfs_c_mnt.c | 573 ++++ TBBT/trace_play/sfs_c_nfs.h | 1470 +++++++++ TBBT/trace_play/sfs_c_pnt.c | 1163 ++++++++ TBBT/trace_play/sfs_c_rnd.c | 247 ++ TBBT/trace_play/sfs_c_sig.c | 170 ++ TBBT/trace_play/sfs_c_sub.c | 198 ++ TBBT/trace_play/sfs_ext_mon | 58 + TBBT/trace_play/sfs_m_def.h | 59 + TBBT/trace_play/sfs_m_msg.c | 103 + TBBT/trace_play/sfs_m_prm.c | 1545 ++++++++++ TBBT/trace_play/sfs_m_snc.c | 456 +++ TBBT/trace_play/sfs_m_xdr.c | 103 + TBBT/trace_play/sfs_mcr | 170 ++ TBBT/trace_play/sfs_mgr | 1093 +++++++ TBBT/trace_play/sfs_rc | 149 + TBBT/trace_play/sfs_suchown | 71 + TBBT/trace_play/sfs_sync.x | 25 + TBBT/trace_play/t | Bin 0 -> 11546 bytes TBBT/trace_play/t.c | 6 + 152 files changed, 71768 insertions(+) create mode 100644 TBBT/README create mode 100755 TBBT/trace_init/EXAMPLES create mode 100755 TBBT/trace_init/INSTALL create mode 100755 TBBT/trace_init/NOTES.TXT create mode 100755 TBBT/trace_init/commands create mode 100755 TBBT/trace_init/common.pl create mode 100755 TBBT/trace_init/counts.pl create mode 100755 TBBT/trace_init/extract-hierarchy create mode 100755 TBBT/trace_init/hier.pl create mode 100755 TBBT/trace_init/hier.pl.old create mode 100755 TBBT/trace_init/key.pl create mode 100755 TBBT/trace_init/latency.pl create mode 100755 TBBT/trace_init/nfsdump.pl create mode 100755 TBBT/trace_init/nfsscan.pl create mode 100755 TBBT/trace_init/nfsscan.txt create mode 100755 TBBT/trace_init/ns_loc2gmt.pl create mode 100755 TBBT/trace_init/ns_quickview.pl create mode 100755 TBBT/trace_init/ns_split.pl create mode 100755 TBBT/trace_init/ns_timeagg.pl create mode 100755 TBBT/trace_init/ns_tsplit.pl create mode 100755 TBBT/trace_init/rfs.pl create mode 100755 TBBT/trace_init/rfs.pl.old create mode 100755 TBBT/trace_init/rfs.stub.pl create mode 100644 TBBT/trace_init/test.cnt create mode 100755 TBBT/trace_init/test.fil create mode 100755 TBBT/trace_init/userUtils.pl create mode 100644 TBBT/trace_play/Makefile.org create mode 100644 TBBT/trace_play/README create mode 100644 TBBT/trace_play/README.old create mode 100755 TBBT/trace_play/agefs create mode 100644 TBBT/trace_play/agefs.log create mode 100755 TBBT/trace_play/frag_collect create mode 100755 TBBT/trace_play/frag_collect_cust create mode 100755 TBBT/trace_play/frag_count create mode 100644 TBBT/trace_play/frag_count.c create mode 100755 TBBT/trace_play/fragment_collect create mode 100755 TBBT/trace_play/generate_xmgr create mode 100644 TBBT/trace_play/generate_xmgr.c create mode 100644 TBBT/trace_play/generate_xmgr.c.old create mode 100644 TBBT/trace_play/generic_hash.c create mode 100644 TBBT/trace_play/generic_hash.h create mode 100644 TBBT/trace_play/hash.h create mode 100644 TBBT/trace_play/init_holder.c create mode 100644 TBBT/trace_play/nfsd_nfsfh_cust.h create mode 100755 TBBT/trace_play/profile.c create mode 100755 TBBT/trace_play/profile.h create mode 100644 TBBT/trace_play/rfs_3_ops.c create mode 100644 TBBT/trace_play/rfs_3_ops.c.old create mode 100644 TBBT/trace_play/rfs_assert.h create mode 100644 TBBT/trace_play/rfs_c_age.c create mode 100644 TBBT/trace_play/rfs_c_age.c.trace_base create mode 100644 TBBT/trace_play/rfs_c_age.c.unit_base create mode 100644 TBBT/trace_play/rfs_c_age.obsolete.c create mode 100644 TBBT/trace_play/rfs_c_dat.c create mode 100644 TBBT/trace_play/rfs_c_def.h create mode 100755 TBBT/trace_play/rpc/auth.h create mode 100755 TBBT/trace_play/rpc/auth_none.c create mode 100755 TBBT/trace_play/rpc/auth_unix.c create mode 100755 TBBT/trace_play/rpc/auth_unix.h create mode 100755 TBBT/trace_play/rpc/authunix_prot.c create mode 100755 TBBT/trace_play/rpc/bindresvport.c create mode 100755 TBBT/trace_play/rpc/clnt.h create mode 100755 TBBT/trace_play/rpc/clnt_generic.c create mode 100755 TBBT/trace_play/rpc/clnt_perror.c create mode 100755 TBBT/trace_play/rpc/clnt_simple.c create mode 100755 TBBT/trace_play/rpc/clnt_tcp.c create mode 100755 TBBT/trace_play/rpc/clnt_udp.c create mode 100755 TBBT/trace_play/rpc/get_myaddress.c create mode 100755 TBBT/trace_play/rpc/getrpcent.c create mode 100755 TBBT/trace_play/rpc/getrpcport.c create mode 100755 TBBT/trace_play/rpc/netdb.h create mode 100755 TBBT/trace_play/rpc/osdep.h create mode 100755 TBBT/trace_play/rpc/pmap_clnt.c create mode 100755 TBBT/trace_play/rpc/pmap_clnt.h create mode 100755 TBBT/trace_play/rpc/pmap_getmaps.c create mode 100755 TBBT/trace_play/rpc/pmap_getport.c create mode 100755 TBBT/trace_play/rpc/pmap_prot.c create mode 100755 TBBT/trace_play/rpc/pmap_prot.h create mode 100755 TBBT/trace_play/rpc/pmap_prot2.c create mode 100755 TBBT/trace_play/rpc/pmap_rmt.c create mode 100755 TBBT/trace_play/rpc/pmap_rmt.h create mode 100755 TBBT/trace_play/rpc/rpc.h create mode 100755 TBBT/trace_play/rpc/rpc_callmsg.c create mode 100755 TBBT/trace_play/rpc/rpc_commondata.c create mode 100755 TBBT/trace_play/rpc/rpc_dtablesize.c create mode 100755 TBBT/trace_play/rpc/rpc_msg.h create mode 100755 TBBT/trace_play/rpc/rpc_prot.c create mode 100755 TBBT/trace_play/rpc/sfs_ctcp.c create mode 100755 TBBT/trace_play/rpc/sfs_cudp.c create mode 100755 TBBT/trace_play/rpc/svc.c create mode 100755 TBBT/trace_play/rpc/svc.h create mode 100755 TBBT/trace_play/rpc/svc_auth.c create mode 100755 TBBT/trace_play/rpc/svc_auth.h create mode 100755 TBBT/trace_play/rpc/svc_auth_unix.c create mode 100755 TBBT/trace_play/rpc/svc_raw.c create mode 100755 TBBT/trace_play/rpc/svc_run.c create mode 100755 TBBT/trace_play/rpc/svc_simple.c create mode 100755 TBBT/trace_play/rpc/svc_tcp.c create mode 100755 TBBT/trace_play/rpc/svc_udp.c create mode 100755 TBBT/trace_play/rpc/types.h create mode 100755 TBBT/trace_play/rpc/xdr.c create mode 100755 TBBT/trace_play/rpc/xdr.h create mode 100755 TBBT/trace_play/rpc/xdr_array.c create mode 100755 TBBT/trace_play/rpc/xdr_float.c create mode 100755 TBBT/trace_play/rpc/xdr_mem.c create mode 100755 TBBT/trace_play/rpc/xdr_rec.c create mode 100755 TBBT/trace_play/rpc/xdr_reference.c create mode 100755 TBBT/trace_play/rpc/xdr_stdio.c create mode 100644 TBBT/trace_play/sem.c create mode 100755 TBBT/trace_play/sfs.1 create mode 100755 TBBT/trace_play/sfs3.full_speed create mode 100644 TBBT/trace_play/sfs_2_ops.c create mode 100644 TBBT/trace_play/sfs_2_vld.c create mode 100644 TBBT/trace_play/sfs_2_xdr.c create mode 100644 TBBT/trace_play/sfs_3_ops.c create mode 100644 TBBT/trace_play/sfs_3_ops.c.org create mode 100644 TBBT/trace_play/sfs_3_vld.c create mode 100644 TBBT/trace_play/sfs_3_xdr.c create mode 100644 TBBT/trace_play/sfs_c_bio.c create mode 100644 TBBT/trace_play/sfs_c_bio.org create mode 100644 TBBT/trace_play/sfs_c_chd.2thread.c create mode 100644 TBBT/trace_play/sfs_c_chd.3thread.c create mode 120000 TBBT/trace_play/sfs_c_chd.c create mode 100644 TBBT/trace_play/sfs_c_chd.c.old create mode 100644 TBBT/trace_play/sfs_c_chd.c.old.2 create mode 100644 TBBT/trace_play/sfs_c_chd.c.org create mode 100644 TBBT/trace_play/sfs_c_chd.work create mode 100644 TBBT/trace_play/sfs_c_clk.c create mode 100644 TBBT/trace_play/sfs_c_clnt.c create mode 100644 TBBT/trace_play/sfs_c_dat.c create mode 100644 TBBT/trace_play/sfs_c_def.h create mode 100644 TBBT/trace_play/sfs_c_dmp.c create mode 100644 TBBT/trace_play/sfs_c_man.c create mode 100644 TBBT/trace_play/sfs_c_mnt.c create mode 100644 TBBT/trace_play/sfs_c_nfs.h create mode 100644 TBBT/trace_play/sfs_c_pnt.c create mode 100644 TBBT/trace_play/sfs_c_rnd.c create mode 100644 TBBT/trace_play/sfs_c_sig.c create mode 100644 TBBT/trace_play/sfs_c_sub.c create mode 100755 TBBT/trace_play/sfs_ext_mon create mode 100644 TBBT/trace_play/sfs_m_def.h create mode 100644 TBBT/trace_play/sfs_m_msg.c create mode 100644 TBBT/trace_play/sfs_m_prm.c create mode 100644 TBBT/trace_play/sfs_m_snc.c create mode 100644 TBBT/trace_play/sfs_m_xdr.c create mode 100755 TBBT/trace_play/sfs_mcr create mode 100755 TBBT/trace_play/sfs_mgr create mode 100755 TBBT/trace_play/sfs_rc create mode 100755 TBBT/trace_play/sfs_suchown create mode 100755 TBBT/trace_play/sfs_sync.x create mode 100755 TBBT/trace_play/t create mode 100644 TBBT/trace_play/t.c diff --git a/TBBT/README b/TBBT/README new file mode 100644 index 0000000..d2886cf --- /dev/null +++ b/TBBT/README @@ -0,0 +1,50 @@ +1. TBBT Directory Structure + +# README: this file. Introduce the TBBT directory and how to use TBBT. +# trace_init: source code for extract file system hierarchy from the trace +# trace_play: source code for trace player +# test: example of play the EECS Oct 21 1 hour trace. It contains the +# original trace file: anon-lair62-011021-0000.txt +# file system hierarchy map: fh-path-map-play +# file system hierarchy: RFSFS +# + +2. How to use TBBT? + +#STEP1: compile the TBBT player, change the executable "sfs3" to be owned +# by root + +cd ~nzhu/TBBT/trace_play +make sfs3 +su - root; chown root:root tplay; exit + +#STEP2: copy or link trace file in a directory for trace play + +cd TESTDIR +ln -s ../test/anon-lair62-011021-0000.txt anon-lair62-011021-0000.txt + +#STEP3: extract the file system hierarchy from the trace file +# There are two outputs: fh-path-map-play and RFSFS +# fh-path-map-play is the file system hierarchy map +# RFSFS is the actually file system hierarchy, instead of writing +# each file to the full length, -S option creates a file system +# hierarchy where all files are of 0 length. This is useful when +# for experimental test run or debug run because writing all files +# to the full length could be time consuming. + +~nzhu/TBBT/trace_init/extract-hierarchy anon-lair62-011021-0000.txt [-S] + +#STEP4: copy RFSFS to an exported directory on NFS file server + +scp -r RFSFS server:/export_dir/ + +#STEP5: pair-up the request and reply packets in the trace. +# The output file name is based on input file with suffix ".pair" +# in this example, anon-lair62-011021-0000.txt.pair + +sfs3 -pair_trace anon-lair62-011021-0000.txt + + +#STEP6: play the trace against initial file system hierarchy (RFSFS) on server + +sfs3 hostname:/export_dir/RFSFS anon-lair62-011021-0000.txt.pair fh-path-map-play 1 0 diff --git a/TBBT/trace_init/EXAMPLES b/TBBT/trace_init/EXAMPLES new file mode 100755 index 0000000..2ed5187 --- /dev/null +++ b/TBBT/trace_init/EXAMPLES @@ -0,0 +1,170 @@ +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# $Id: EXAMPLES,v 1.3 2003/07/28 14:27:16 ellard Exp $ +# +# For nfsscan version 0.10a (dated 7/25/2003). + +INTRODUCTION + +The usual procedure for analyzing a trace is the following: + + 1. Use nfsscan to produce a tabularized summary of each + 300-second segment of the trace. For these examples, + we'll call this DEFAULT_TABLE. + + Depending on what you're looking for, the default + settings of nfsscan might not provide all the info + you're going to want in the next step. The default is + to omit per-client, per-user, per-group, and per-file + stats and only tally total operation counts. [THE + DEFAULTS MAY CHANGE IN A FUTURE RELEASE.] + + 2. Use ns_timeagg to create a summary of activity in the + entire trace named SUMMARY_TABLE from DEFAULT_TABLE. + + Note that almost anything ns_timeagg and ns_split can + do can also be done directly with nfsscan. However, + the implicit goal of ns_timeagg and ns_split is to + AVOID re-running nfsscan. It is much faster to + re-process a table created by nfsscan than it is to + re-create the table -- the input to nfsscan is + typically several million (or billion) lines of trace + data, while the output is usually only a few thousand + table rows. + + 3. Use ns_quickview to plot interesting aspects of the + DEFAULT_TABLE and/or SUMMARY_TABLE. + + 4. [optional] Use ns_split and/or ns_tsplit to isolate + interesting parts of DEFAULT_TABLE (such as per-client + or per-user counts). Repeat steps 2 and 3 with the + results. + + 5. [optional] If steps 2-4 found anything interesting, re-run + nfsscan with new parameters to take a closer look at + the trace. + +Examples and discussion of these steps and related topics is given +below. + +For these examples, TRACE is a trace file gathered by nfsdump (or +another tool that creates traces files in the same format), and TABLE.ns +is a file created by nfsscan from TRACE. The suffix ".ns" is also +used to denote files that contain tables created by nfsscan, +ns_timeagg, ns_split, and ns_tsplit. Example commandlines always +begin with "%". + +1. RUNNING NFSSCAN + +2. CREATING A SUMMARY TABLE + + To compute a table contsisting of a single row with counts for + each operation tallied by the nfsscan run, aggregate over time + with a time length of zero. (Zero is treated as a special + time length that includes the entire input table.) + + % ns_timeagg -t0 TABLE.ns > SUMMARY.ns + + Note that timeagg will always aggregate over every (except + time) attribute, so it does not matter whether or not the + TABLE.ns contains per-client, per-user, per-group, or per-file + data. The sum will always be the same. + + On the other hand, if you want to prevent ns_timeagg from + aggregating over a particular attribute, specify that + attribute in the same manner as with nfsscan. For example, to + create a table with a single row containing the operation + count per user: + + % ns_timeagg -t0 -BU TABLE.ns > SUMMARY.ns + + Of course, ns_timeagg cannot create data out of thin air. If + TABLE.ns does not contain per-user information then -BU will + have no effect. + +3. PLOTTING THE DATA + + To simply plot the total operation count: + + % ns_quickview TABLE.ns + % gv qv.ps + +WHICH CLIENT REQUESTS THE MOST OPERATIONS? + +Method: use nfsscan to tally the per-client operation counts for the + entire trace file (by using -t0), and then sort by the TOTAL + op count fields: + + If TABLE contains per-client information, then this is easy: + + % ns_timeagg -t0 -BC TABLE | grep -v '^#' \ + | awk '{print $7, $3}' | sort -nr + + If TABLE does not contain per-client info, then it's necessary + to re-run nfsscan: + + % nfsscan -t0 -BC TRACE | grep -v '^#' \ + | awk '{print $7, $3}' | sort -nr + + The output from either command is a two-column table. The + first column is the total operation count of each client, and + the second column is the ID of each client. + +WHICH CLIENT DOES THE MOST READING? + + If we've already got TABLE, and it contains per-client info, + then the easiest way is to simply use extract the read count + column (instead of the TOTAL column) from TABLE: + + % ns_timeagg -t0 -BC TABLE | grep -v '^#' \ + | awk '{print $9, $3}' | sort -nr + + Or, we can nfsscan. Because we're not interested in anything + except the read count, we can change the list of operations + that nfsscan tabulates so that it only counts reads. (Of course, + the resulting table is useless for anything except answering + this particular question, and since nfsscan is expensive to run + this is probably wasteful.) + + % nfsscan -t0 -BC -Oread -i TRACE | grep -v '^#' \ + | awk '{print $7, $3}' | sort -nr + +WHICH CLIENT DOES THE MOST FSSTATS? + + fsstat is not ordinarily tabulated by nfsscan. To tell nfsscan + to keep track of it, we can change the list of operations to consist + only of fsstat: + + % nfsscan -t0 -BC -Ofsstat -i TRACE | ... + + As mentioned in the previous example, it is often wasteful to + run nfsscan just to get one number. Another approach is to + add fsstat to the default list of "interesting" operations, by + using "+" at the start of the operation list. This tells nfsscan + to append the given list of operations to the default list: + + % nfsscan -t0 -BC -O+fsstat -i TRACE | ... + + An implication of this is that it's impossible to know what + each column in the table represents unless you know what + operations were considered "interesting" for each run of + nfsscan. To help with this, nfsscan includes the commandline + and column titles at the start of each file it creates. + +WHICH USER DOES THE MOST READING? + + This is exactly like the previous example, except that we use + -BU instead -BC, to do everything per-user instead of + per-client. + +WHAT DIRECTORIES ARE HOTTEST? + + Use the -d option to find the cummulative number of operations + per directory, then sort the results by operation count. In + order to avoid drowning in data you might choose to print + print only the top 100: + + % nfsscan -i TRACE -t0 -d | grep '^D' \ + | awk '{print $7, $5}' | sort -nr | head -100 + diff --git a/TBBT/trace_init/INSTALL b/TBBT/trace_init/INSTALL new file mode 100755 index 0000000..6849497 --- /dev/null +++ b/TBBT/trace_init/INSTALL @@ -0,0 +1,61 @@ +# $Id: INSTALL,v 1.4 2003/07/28 14:27:16 ellard Exp $ + +NFSSCAN INSTALLATION INSTRUCTIONS + +This is version 0.10a of nfsscan, dated 7/25/2003. + +THIS IS A PRELIMINARY RELEASE OF NEW SOFTWARE: + +- THE COMMANDLINE FORMATS MAY EVOLVE RAPIDLY OVER THE NEXT SEVERAL + RELEASES. + +- DEBUGGING MESSAGES AND WARNINGS MAY APPEAR ON STDERR. MOST OF THESE + ARE MEANINGLESS, BUT IF YOU ENCOUNTER A PROBLEM WITH THE + PROGRAM PLEASE INCLUDE THEM IN YOUR BUG REPORT. + +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +System Requirements: + + - You must have PERL 5 (5.005 or later) installed in order to + use any of these tools. (None of these tools have + been tested with PERL 6 or later.) + + - If you plan to use ns_quickview, make sure that gnuplot is + installed on your system. + + ns_quickview has been tested with gnuplot 3.7 and + should work with later versions. If you have problems + with ns_quickview, please include any diagnostics + messages from gnuplot and the version of gnuplot with + your bug report. + + If gnuplot is not in your path, you must edit + ns_quickview to set GNUPLOT_PATH to the appropriate + path after step 2 in the installation instructions. + +Installation: + +1. Un-tar the distribution into an empty directory. + +2. Edit the first line of nfsscan and ns_* to point to wherever your + favorite version of PERL 5 is installed. If you don't like + looking at debugging messages, remove the -w from the + invocation of PERL. + +3. Make sure that nfsscan and ns_* are executable and that + all the files are publically readable: + + chmod 444 * + chmod 555 nfsscan ns_* + +4. Read NOTES.TXT for last-minute info or errata for this release, + and changes from previous releases. + +5. Read nfsscan.txt and EXAMPLES for more information about how to + use the tools. + +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +PLEASE report bugs, problems or suggestions for improvements to +ellard@eecs.harvard.edu. diff --git a/TBBT/trace_init/NOTES.TXT b/TBBT/trace_init/NOTES.TXT new file mode 100755 index 0000000..040500d --- /dev/null +++ b/TBBT/trace_init/NOTES.TXT @@ -0,0 +1,17 @@ +# $Id: NOTES.TXT,v 1.2 2003/07/28 14:27:16 ellard Exp $ + +NFSSCAN BUGS + +Known bugs for version 0.10a: + +- Debugging messages and warnings may appear on stderr. Most of these + are meaningless, but if you encounter a problem with any of + the programs program please include them in your bug report. + + Remove the -w from the invocation of PERL (on the first line + of each script) if you want to get rid of the PERL warnings. + +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +PLEASE report bugs, problems or suggestions for improvements to +ellard@eecs.harvard.edu. diff --git a/TBBT/trace_init/commands b/TBBT/trace_init/commands new file mode 100755 index 0000000..e71f0b1 --- /dev/null +++ b/TBBT/trace_init/commands @@ -0,0 +1,20 @@ +#!/bin/bash -x + +# to locate a program +GUNZIP=`whereis gunzip | gawk '{print $2}'` +echo $GUNZIP + +# to run nfsscan +gunzip -c traces/* | ./nfsscan -f -t0 -o result-total/test > result-total/output 2>&1 +gunzip -c ~traces/anon-lair62-011130-1100.txt.gz | ../nfsscan.pl > output 2>&1 + + + +# post processing of nfsscan +gawk '{if ($1=="F") print $2, $3, $5, $6 }' output > output-filename +gawk '{if ($3!=".") print $0 }' output-filename > output-filename-known + +echo file#; gawk '{if ($1=="F") print $0}' output-filename-known | wc +echo dir#; gawk '{if ($1=="D") print $0}' output-filename-known | wc +echo active#; gawk '{if ($2=="A") print $0}' output-filename-known | wc +echo dead#; gawk '{if ($2=="D") print $0}' output-filename-known | wc diff --git a/TBBT/trace_init/common.pl b/TBBT/trace_init/common.pl new file mode 100755 index 0000000..5959ee1 --- /dev/null +++ b/TBBT/trace_init/common.pl @@ -0,0 +1,61 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_timeagg,v 1.4 2003/07/10 20:13:31 ellard Exp $ + +# Allows rounding of the start time, so that minor clock drift doesn't +# make trace periods appear to begin at funny times (i.e. one or two +# seconds after midnight, instead of midnight). +# +# This is only meant to help with SMALL amounts of drift. Trying to +# use this to correct for anything more than a small fraction of the +# sample interval is asking for trouble. + +require 'timelocal.pl'; + +sub findStartTime { + my ($time, $rounding) = @_; + + my $new_time = int ($time); + + if (defined $rounding && $rounding != 0) { + $rounding = int ($rounding); + + my $leftover = $new_time % $rounding; + + if ($leftover > $rounding / 2) { + $new_time += $rounding; + } + + $new_time -= ($new_time % $rounding); + } + + return $new_time; +} + +1; diff --git a/TBBT/trace_init/counts.pl b/TBBT/trace_init/counts.pl new file mode 100755 index 0000000..33d7ad9 --- /dev/null +++ b/TBBT/trace_init/counts.pl @@ -0,0 +1,103 @@ +# +# Copyright (c) 2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: counts.pl,v 1.9 2003/07/28 14:27:16 ellard Exp $ + +package counts; + +@OpList = (); +%KeysSeen = (); + +%OpCounts = (); + +sub init { + my (@oplist) = @_; + + @OpList = @oplist; +} + +sub printTitle { + my ($out) = @_; + + my $str = "#C time client fh euid egid"; + + foreach my $op ( @OpList ) { + $str .= " $op"; + } + $str .= "\n"; + + print $out $str; +} + +sub printOps { + my ($start_time, $out) = @_; + my ($k, $str, $op, $nk); + + my @allkeys = sort keys %KeysSeen; + + foreach $k ( @allkeys ) { + my $tot = "$k,TOTAL"; + + if ($main::OMIT_ZEROS && + (! exists $OpCounts{$tot} || $OpCounts{$tot} == 0)) { + next; + } + + $str = sprintf ("C %s %s", $start_time, &key::key2str ($k)); + + foreach $op ( @OpList ) { + $nk = "$k,$op"; + if (exists $OpCounts{$nk}) { + if ($op eq 'readM' || $op eq 'writeM') { + $str .= sprintf (" %.3f", + $OpCounts{$nk} / (1024 * 1024)); + } + else { + $str .= " $OpCounts{$nk}" + } + } + else { + $str .= " 0"; + } + } + $str .= "\n"; + print $out $str; + } +} + +sub resetOpCounts { + + # Clear the counts on everything we've seen. + + foreach my $op ( keys %OpCounts ) { + $OpCounts{$op} = 0; + } +} + +1; + diff --git a/TBBT/trace_init/extract-hierarchy b/TBBT/trace_init/extract-hierarchy new file mode 100755 index 0000000..e6b2b9f --- /dev/null +++ b/TBBT/trace_init/extract-hierarchy @@ -0,0 +1,4 @@ +#Usage extract-hierarchy trace_file [-S] +#nfsscan $1 +echo nfsscan $1 +rfs.pl $2 diff --git a/TBBT/trace_init/hier.pl b/TBBT/trace_init/hier.pl new file mode 100755 index 0000000..3e2ebfc --- /dev/null +++ b/TBBT/trace_init/hier.pl @@ -0,0 +1,715 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: hier.pl,v 1.14 2003/07/26 20:52:03 ellard Exp $ +# +# hier.pl - Tools to map out the file system hierarchy. This is +# accomplished by snooping out the lookup calls. +# +# This is expensive because the hierarchy can require a LOT of space +# to store for a large system with lots of files (especially if files +# come and go). Don't construct the hierarchy unless you want it -- +# and be prepared to prune it from time to time. + +package hier; + +# Tables used by the outside world: + +%fh2Parent = (); +%fh2Name = (); +%fh2Attr = (); +%fh2AttrOrig = (); +%parent2fh = (); + +#RFS: init FS +%rootsName = (); +%discardFHs = (); +%rootsFHs = (); +#RFS: dependency table +%fhCreate = (); + +%rfsAllFHs = (); +%fhType = (); # we use %fhIsDir instead +$rfsLineNum = 0; + + + + + + +# Library-private tables and variables. + +%pendingCallsXIDnow = (); +%pendingCallsXIDfh = (); +%pendingCallsXIDname = (); + +$nextPruneTime = -1; +$PRUNE_INTERVAL = 5 * 60; # Five minutes. + +sub processLine { + my ($line, $proto, $op, $xid, $client, $now, $response, $fh_type) = @_; + + &addRfsAllFHs($line, $proto, $op, $uxid, + $now, $response, $fh_type); + + if ($now > $nextPruneTime) { + &prunePending ($now - $PRUNE_INTERVAL); + $nextPruneTime = $now + $PRUNE_INTERVAL; + } + + my $uxid = "$client-$xid"; + + # 'lookup', 'create', 'rename', 'delete', + # 'getattr', 'setattr' + + #RFS: add mkdir/rmdir/symlink + if ( $op eq 'lookup' || $op eq 'create' || $op eq 'mkdir' || + ($op eq 'symlink' && ($proto eq 'C3' || $proto eq 'R3' ) ) ) { + return (&doLookup ($line, $proto, $op, $uxid, + $now, $response, $fh_type)); + } + elsif ($op eq 'rename') { + } + elsif ($op eq 'remove' || $op eq 'rmdir') { + # RFS: why remove these entries? Just let them exist since + # there is generation number available to distinguish btw removed dir/file + # and new dir/file with the same inode number. + #return (&doRemove ($line, $proto, $op, $uxid, + # $now, $response, $fh_type)); + } + elsif ($op eq 'getattr' || $op eq 'read' || $op eq 'write' || + ($op eq 'readlink' && ($proto eq 'C3' || $proto eq 'R3' ) ) ) { + return (&doGetAttr ($line, $proto, $op, $uxid, + $now, $response, $fh_type)); + } + elsif ($op eq 'setattr') { + } +} + + +# get time stamp + +sub getTimeStamp{ + my ($line) = @_; + + if (! ($line =~ /^[0-9]/)) { + print "getTimeStamp return undef\n"; + return undef; + } + else { + my @l = split (' ', $line, 2); + return $l[0]; + } +} + +sub addRfsAllFHs { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + my $fh = undef; + + my $checkfh = undef; + $checkfh = nfsd::nfsDumpParseLineField ($line, 'fh'); + if (defined $checkfh) { + $fh = nfsd::nfsDumpCompressFH ($fh_type, $checkfh); + } + + my $fh2 = undef; + + if ($op eq 'rename' || $op eq 'link') { + $checkfh = nfsd::nfsDumpParseLineField ($line, 'fh2'); + if (defined $checkfh) { + $fh2 = nfsd::nfsDumpCompressFH ($fh_type, $checkfh); + } + } + + if (defined $fh) { + # record the first appearance of the fh + if ( !exists $rfsAllFHs{$fh} ) { + $rfsAllFHs{$fh} = $rfsLineNum ; + } + } + + if (defined $fh2) { + if ( !exists $rfsAllFHs{$fh2} ) { + $rfsAllFHs{$fh2} = $rfsLineNum; + } + } + + return ; +} + + +sub doLookup { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $tag = ($proto eq 'C3') ? 'name' : 'fn'; + my $name = nfsd::nfsDumpParseLineField ($line, $tag); + + # All directories have (at least) three names: the + # given name, and "." and "..". We're only interested + # in the given name. + + if ($name eq '"."' || $name eq '".."') { + return ; + } + + my $fh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $fh; + $pendingCallsXIDname{$uxid} = $name; + } + elsif ($proto eq 'R3' || $proto eq 'R2') { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $pfh = $pendingCallsXIDfh{$uxid}; + my $name = $pendingCallsXIDname{$uxid}; + + delete $pendingCallsXIDnow{$uxid}; + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDname{$uxid}; + + if ($response eq 'OK') { + my $cfh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + my $type = nfsd::nfsDumpParseLineField ($line, 'ftype'); + + #if ($type == 2) + { + $fhIsDir{$cfh} = $type; + } + + # Original code + # $fh2Parent{$cfh} = $pfh; + # $fh2Name{$cfh} = $name; + # $parent2fh{"$pfh,$name"} = $cfh; + # RFS code: in case of the rename, we will record the name of the old name + $fh2Parent{$cfh} = $pfh; + if (! exists $fh2Name{$cfh}) { + $fh2Name{$cfh} = $name; + $parent2fh{"$pfh,$name"} = $cfh; + } else { + # keep the old name in the fh2Name{$cfh} + # and we also add the newname and pfh mapping + #$fh2Name{$cfh} = $name; + $parent2fh{"$pfh,$name"} = $cfh; + } + + my ($size, $mode, $atime, $mtime, $ctime, $nlink) = + nfsd::nfsDumpParseLineFields ($line, + 'size', 'mode', + 'atime', 'mtime', 'ctime', 'nlink'); + my $ts = getTimeStamp($line); + + # RFS: modify here to get/maintain more file attributes + # we can just check the ctime and compare it with trace-start-time + # to decide whether to create a file/diretory. + # atime - last access time of the file + # mtime - last modification time of the file + # ctime - last file status change time + + #$fh2Attr{$cfh} = "$size $mode $atime $mtime $ctime"; + if (! exists $fh2AttrOrig{$cfh} ) { + $fh2AttrOrig{$cfh} = "$size $mode $op $atime $mtime $ctime $nlink $ts"; + } + $fh2Attr{$cfh} = "$size $mode $op $atime $mtime $ctime $nlink $ts"; + } + + } + + return ; +} + +sub doRemove { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $tag = ($proto eq 'C3') ? 'name' : 'fn'; + my $name = nfsd::nfsDumpParseLineField ($line, $tag); + + # All directories have (at least) three names: the + # given name, and "." and "..". We're only interested + # in the given name. + + if ($name eq '"."' || $name eq '".."') { + return ; + } + + my $pfh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + if (! exists $parent2fh{"$pfh,$name"}) { + return ; + } + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $pfh; + $pendingCallsXIDname{$uxid} = $name; + } + elsif ($proto eq 'R3' || $proto eq 'R2') { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $pfh = $pendingCallsXIDfh{$uxid}; + my $name = $pendingCallsXIDname{$uxid}; + + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDname{$uxid}; + delete $pendingCallsXIDnow{$uxid}; + + if (! exists $parent2fh{"$pfh,$name"}) { + return ; + } + + my $cfh = $parent2fh{"$pfh,$name"}; + + if ($response eq 'OK') { + if ($op eq 'remove') { + printFileInfo ($cfh, 'D'); + + delete $fh2Parent{$cfh}; + delete $fh2Name{$cfh}; + delete $fh2Attr{$cfh}; + delete $fhs2AttrOrig{$cfg}; + delete $parent2fh{"$pfh,$name"}; + } + } + } + + return ; +} + +sub doGetAttr { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $fh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + #if (nfsd::nfsDumpParseLineField ($line, 'fh') + # eq '00018961-57570100-d2440000-61890100') { + # printf STDERR "Seen it ($op)\n"; + #} + + if (! defined $fh) { + return ; + } + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $fh; +# RFS debug code +#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +#if ($fh eq $wantfh) { +# print "JIAWU: doGetAttr call $wantfh\n"; +#} + } + else { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $fh = $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDnow{$uxid}; +# RFS debug code +#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +#if ($fh eq $wantfh) { +# print "JIAWU: doGetAttr response $wantfh\n"; +#} + + if ($response ne 'OK') { + return ; + } + + my ($ftype) = nfsd::nfsDumpParseLineFields ($line, 'ftype'); + if (!defined $ftype) { + print STDERR "BAD $line"; + return ; + } + + #if ($ftype == 2) + { + $fhIsDir{$fh} = $ftype; + } + + #RFS comment: here if fh is a directory, then it will not be add + # in the two hash table %fh2Attr(%fh2AttrOrig) and %fh2Name + # if ($ftype != 1) { + # return ; + #} + if ($ftype != 1) { + #return ; + } + + + my ($mode, $size, $atime, $mtime, $ctime, $nlink) = + nfsd::nfsDumpParseLineFields ($line, + 'mode', 'size', 'atime', 'mtime', 'ctime', 'nlink'); + my $ts = getTimeStamp($line); + + # RFS: modify here to get/maintain more file attributes + # we can just check the ctime and compare it with trace-start-time + # to decide whether to create a file/diretory. + # atime - last access time of the file + # mtime - last modification time of the file + # ctime - last file status change time + + # $fh2Attr{$fh} = "$size $mode $atime $mtime $ctime"; + + if (! exists $fh2AttrOrig{$fh} ) { + $fh2AttrOrig{$fh} = "$size $mode $op $atime $mtime $ctime $nlink $ts"; + } + $fh2Attr{$fh} = "$size $mode $op $atime $mtime $ctime $nlink $ts"; + } +} + +# Purge all the pending XID records dated earlier than $when (which is +# typically at least $PRUNE_INTERVAL seconds ago). This is important +# because otherwise missing XID records can pile up, eating a lot of +# memory. + +sub prunePending { + my ($when) = @_; + + foreach my $uxid ( keys %pendingCallsXIDnow ) { + if ($pendingCallsXIDnow{$uxid} < $when) { +# RFS debug code +my $fh = $pendingCallsXIDfh{$uxid}; +my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +if ($fh eq $wantfh) { + print "JIAWU: prunePending $wantfh\n"; +} +#enf RFS + delete $pendingCallsXIDnow{$uxid}; + } + } + + return ; +} + +# Return as much of the path for the given fh as possible. It may or +# may not reach the root (or the mount point of the file system), but +# right now we don't check. Usually on busy systems the data is +# complete enough so that most paths are complete back to the mount +# point. + +sub findPath { + my ($fh) = @_; + my $isdir = 0; + my $cnt = 0; + my $MaxPathLen = 40; + + if (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) { + $isdir = 1; + } + + my @path = (); + while ($fh && exists $fh2Name{$fh}) { + unshift (@path, $fh2Name{$fh}); + + if ( ($fh2Name{$fh} ne '"RFSNN0"' ) ) { + if (! exists $fh2Parent{$fh}) { + print STDERR "$fh2Name{$fh} "; + if ( ($fh2Name{$fh} eq '"RFSNN0"' ) ) { + print STDERR "eq RFSNN0\n"; + } else { + print STDERR "NOT eq RFSNN0\n"; + } + } + if ($fh eq $fh2Parent{$fh}) { + unshift (@path, '(LOOP)'); + last; + } + } + + if ($cnt++ > $MaxPathLen) { + print STDERR "findPath: path too long (> $MaxPathLen)\n"; + unshift (@path, '(TOO-LONG)'); + last; + } + + $fh = $fh2Parent{$fh}; + } + + # RFS: append the ~user (fh and !exists $fh2Name{$fh} and type is Directory) + if ($fh && !exists $fh2Name{$fh} && (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ) { + if (exists $rootsName{$fh}) { + #print "JIAWU: $rootsName{$fh}\n"; + unshift(@path, $rootsName{$fh}); + } else { + print "JIAWU: WARNING! No rootsName for this fh: $fh\n"; + unshift(@path, $fh); + } + } else { + if ($fh && !exists $fh2Name{$fh} && (!exists $fhIsDir{$fh} || (exists $fhIsDir{$fh} && $fhIsDir{$fh}!=2)) ) { + if (exists $discardFHs{$fh}) { + open NOATTRDIR, ">>noattrdirdiscard" || die "open noattrdirdiscard failed\n"; + print NOATTRDIR "$fh DISCARD\n"; + close NOATTRDIR; + } else { + # RFS: if a possible fh without attr and name, then regard it as a special root ~/RFSNN0 + unshift(@path, '"RFSNN0"'); + $fhIsDir{$fh}=2; + $fh2Name{$fh} = '"RFSNN0"'; + $rootsName{$fh} = '"RFSNN0"'; + open NOATTRDIR, ">>noattrdir-root"; + print NOATTRDIR "$fh /RFSNN0/\n"; + close NOATTRDIR; + } + } + } + + + my $str = ''; + $cnt = 0; + foreach my $p ( @path ) { + $p =~ s/^.//; + $p =~ s/.$//; + $str .= "/$p"; + $cnt++; + } + + if ($isdir) { + $str .= '/'; + } + + if ($cnt == 0) { + $str = '.'; + } + + return ($str, $cnt); +} + + +$total_unknown_fh = 0; +$total_known_fh = 0; + +sub printAll { + my ($start_time, $out) = @_; + + my %allfh = (); + my $fh; + my $u = 0; + my $k = 0; + + # RFS print more information here + open (OUT_RFS, ">rfsinfo") || + die "Can't create $OutFileBaseName.rfs."; + + foreach $fh ( keys %fh2Attr ) { + $allfh{$fh} = 1; + } + foreach $fh ( keys %fh2Name ) { + $allfh{$fh} = 1; + } + + #RFS: before printFileInfo, name those roots' name + + #RFS there are three kind of fh + # 1. fh/name paired (fh/attr must) + # 2. fh/attr but no fh/name: type file (discard related operations) + # 3. fh/attr but no fh/name: type dir (keep as persuedo root) + $u = $k = 0; + my $sn=1; + foreach $fh ( keys %allfh ) { + if (exists $fh2Parent{$fh} ) { + $k++; + } + else { + $u++; + my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F'; + if ($type eq 'D') { + $rootsName{$fh} = sprintf("\"RFSNN%d\"", $sn++); + $rootsFHs{$fh} = 2; + } + else { + $discardFHs{$fh} = 1; + } + } + } + print OUT_RFS "#stat: fh with parent = $k, fh without parent = $u\n"; + $u = keys %rootsFHs; + print OUT_RFS "#RFS: root fh list($u)\n"; + foreach $fh (keys %rootsName) { + print OUT_RFS "#RFS: $rootsName{$fh} $fh\n"; + } + $u = keys %discardFHs; + print OUT_RFS "#RFS: discard fh list($u)\n"; + print OUT_RFS join("\n", keys %discardFHs, ""); + + + print $out "#F type state fh path pathcount attrOrig(size,mode,op,atime,mt,ct) attrLast(size,mode,op,at,mt,ct)\n"; + + print $out "#T starttime = $start_time\n"; + foreach $fh ( keys %allfh ) { + printFileInfoOutputFile ($fh, 'A', $out); + } + + my $numfh2Name = keys %fh2Name; + my $numfh2Attr = keys %fh2Attr; + print OUT_RFS "fh2name has $numfh2Name, fh2Attr has $numfh2Attr\n"; + + + $u = $k = 0; + foreach $fh ( keys %allfh ) { + if ( exists $fh2Name{$fh} ) {$k++;} + else {$u++;} + } + print OUT_RFS "#stat: total fh with name = $k, without name = $u\n"; + + print OUT_RFS "#stat: finally, total known fh = $total_known_fh, unknown = $total_unknown_fh\n"; + +# Note: fh with name (8303), fh without name (103) +# root fh list: 18 +# discard fh list: 85 +# known fh (8321): ( fh with name(8303) + root fh list (18) = 8321) +# unknown fh (85) +# +# All fh from the those data structures: 8321 + 85 = 8303+103 +# Or, in keys %allfh +# +# + print OUT_RFS "#RFS\n"; + close OUT_RFS; + + open (MISSED, ">missdiscardfh") || + die "Can't create missdiscardfh."; + foreach $fh (keys %rfsAllFHs) { + if ( !exists $allfh{$fh} && + ( (defined $fh2Name{$fh}) && ($fh2Name{$fh} ne '"RFSNN0"')) ) { + print MISSED "$fh LN: $rfsAllFHs{$fh}\n" + } + } + close MISSED; + +# check for a special fh +#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +#if ($allfh{$wantfh} == 1) { +# print OUT_RFS "JIAWU: found $wantfh\n"; +#} else { +# print OUT_RFS "JIAWU: NOT found $wantfh\n"; +#} +#foreach $fh ( keys %allfh ) { +# if ( $fh eq $wantfh ) { +# print OUT_RFS "JIAWU: found $wantfh\n"; +# printFileInfoOutputFile ($fh, 'JIAWU', *OUT_RFS); +# last; +# } +#} +#print OUT_RFS "JIAWU: after \n"; + + +} + +sub printFileInfoOutputFile { + my ($fh, $state, $out) = @_; + + my ($p, $c) = findPath ($fh); + + if ($c == 0) {$total_unknown_fh++;} + else {$total_known_fh++;} + + #my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F'; + my $type = $fhIsDir{$fh}; + if (!defined $type) + { + print STDERR "unknown ftype(U) for fh: $fh\n"; + $type = 'U'; + } + my $attr = (exists $fh2Attr{$fh}) ? + $fh2Attr{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1"; + my $attrOrig = (exists $fh2AttrOrig{$fh}) ? + $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1"; + + print $out "F $type $state $fh $p $c $attrOrig $attr\n"; +} + +sub printFileInfo { + my ($fh, $state) = @_; + + my ($p, $c) = findPath ($fh); + + if ($c == 0) {$total_unknown_fh++;} + else {$total_known_fh++;} + + my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F'; + my $attr = (exists $fh2Attr{$fh}) ? + $fh2Attr{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1"; + my $attrOrig = (exists $fh2AttrOrig{$fh}) ? + $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1"; + + print "F $type $state $fh $p $c $attrOrig $attr\n"; +} + +# +# The flow to create the dependency table +# +# create(dirfh, name, attr) -->newfh, new attr +# mkdir(dirfh, name, attr) -> newfh, new attr +# +# remove(dirfh, name) --> status +# rmdir(dirfh, name) --> status +# rename(dirfh, name, todirfh, toname) --> status +# +# link(newdirfh, newname, dirfh, name) --> status (newdir/newname=>dir/name) +# syslink(newdirfh, newname, string) --> status (newdir/newname=>"string") +# readlink(fh) --> string +# lookup(dirfh, name) --> fh, attr +# getattr(fh) --> attr +# setattr(fh, attr) --> attr +# read(fh, offset, count) -> attr, data +# write(fh, offset, count, data) --> attr +# readdir(dirfh, cookie, count) --> entries +# statfs(fh) --> status +# +# +# +# +# for each line the trace file: +# if (op == R2 or R3) continue; #skip the response line +# switch (the op) +# { +# # CREATION OPs: +# case create: +# case remove: +# # DELETE OPs: +# case mkdir: +# case rmdir: +# # other OPs +# +# +# +# +# +1; diff --git a/TBBT/trace_init/hier.pl.old b/TBBT/trace_init/hier.pl.old new file mode 100755 index 0000000..b7f0cf4 --- /dev/null +++ b/TBBT/trace_init/hier.pl.old @@ -0,0 +1,550 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: hier.pl,v 1.14 2003/07/26 20:52:03 ellard Exp $ +# +# hier.pl - Tools to map out the file system hierarchy. This is +# accomplished by snooping out the lookup calls. +# +# This is expensive because the hierarchy can require a LOT of space +# to store for a large system with lots of files (especially if files +# come and go). Don't construct the hierarchy unless you want it -- +# and be prepared to prune it from time to time. + +package hier; + +# Tables used by the outside world: + +%fh2Parent = (); +%fh2Name = (); +%fh2Attr = (); +%fh2AttrOrig = (); +%parent2fh = (); +#RFS +%rootsName = (); +%discardFHs = (); +%rootsFHs = (); + + +# Library-private tables and variables. + +%pendingCallsXIDnow = (); +%pendingCallsXIDfh = (); +%pendingCallsXIDname = (); + +$nextPruneTime = -1; +$PRUNE_INTERVAL = 5 * 60; # Five minutes. + +sub processLine { + my ($line, $proto, $op, $xid, $client, $now, $response, $fh_type) = @_; + + if ($now > $nextPruneTime) { + &prunePending ($now - $PRUNE_INTERVAL); + $nextPruneTime = $now + $PRUNE_INTERVAL; + } + + my $uxid = "$client-$xid"; + + # 'lookup', 'create', 'rename', 'delete', + # 'getattr', 'setattr' + + #RFS: add mkdir/rmdir + if ($op eq 'lookup' || $op eq 'create' || $op eq 'mkdir') { + return (&doLookup ($line, $proto, $op, $uxid, + $now, $response, $fh_type)); + } + elsif ($op eq 'rename') { + } + elsif ($op eq 'remove' || $op eq 'rmdir') { + # RFS: why remove these entries? Just let them exist since + # there is generation number available to distinguish btw removed dir/file + # and new dir/file with the same inode number. + #return (&doRemove ($line, $proto, $op, $uxid, + # $now, $response, $fh_type)); + } + elsif ($op eq 'getattr' || $op eq 'read' || $op eq 'write' ) { + return (&doGetAttr ($line, $proto, $op, $uxid, + $now, $response, $fh_type)); + } + elsif ($op eq 'setattr') { + } +} + +sub doLookup { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $tag = ($proto eq 'C3') ? 'name' : 'fn'; + my $name = nfsd::nfsDumpParseLineField ($line, $tag); + + # All directories have (at least) three names: the + # given name, and "." and "..". We're only interested + # in the given name. + + if ($name eq '"."' || $name eq '".."') { + return ; + } + + my $fh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $fh; + $pendingCallsXIDname{$uxid} = $name; + } + elsif ($proto eq 'R3' || $proto eq 'R2') { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $pfh = $pendingCallsXIDfh{$uxid}; + my $name = $pendingCallsXIDname{$uxid}; + + delete $pendingCallsXIDnow{$uxid}; + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDname{$uxid}; + + if ($response eq 'OK') { + my $cfh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + my $type = nfsd::nfsDumpParseLineField ($line, 'ftype'); + + if ($type == 2) { + $fhIsDir{$cfh} = 1; + } + + $fh2Parent{$cfh} = $pfh; + $fh2Name{$cfh} = $name; + $parent2fh{"$pfh,$name"} = $cfh; + + my ($size, $mode, $atime, $mtime, $ctime) = + nfsd::nfsDumpParseLineFields ($line, + 'size', 'mode', + 'atime', 'mtime', 'ctime'); + + # RFS: modify here to get/maintain more file attributes + # we can just check the ctime and compare it with trace-start-time + # to decide whether to create a file/diretory. + # atime - last access time of the file + # mtime - last modification time of the file + # ctime - last file status change time + + #$fh2Attr{$cfh} = "$size $mode $atime $mtime $ctime"; + if (! exists $fh2AttrOrig{$cfh} ) { + $fh2AttrOrig{$cfh} = "$size $mode $op $atime $mtime $ctime"; + } + $fh2Attr{$cfh} = "$size $mode $op $atime $mtime $ctime"; + } + + } + + return ; +} + +sub doRemove { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $tag = ($proto eq 'C3') ? 'name' : 'fn'; + my $name = nfsd::nfsDumpParseLineField ($line, $tag); + + # All directories have (at least) three names: the + # given name, and "." and "..". We're only interested + # in the given name. + + if ($name eq '"."' || $name eq '".."') { + return ; + } + + my $pfh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + if (! exists $parent2fh{"$pfh,$name"}) { + return ; + } + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $pfh; + $pendingCallsXIDname{$uxid} = $name; + } + elsif ($proto eq 'R3' || $proto eq 'R2') { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $pfh = $pendingCallsXIDfh{$uxid}; + my $name = $pendingCallsXIDname{$uxid}; + + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDname{$uxid}; + delete $pendingCallsXIDnow{$uxid}; + + if (! exists $parent2fh{"$pfh,$name"}) { + return ; + } + + my $cfh = $parent2fh{"$pfh,$name"}; + + if ($response eq 'OK') { + if ($op eq 'remove') { + printFileInfo ($cfh, 'D'); + + delete $fh2Parent{$cfh}; + delete $fh2Name{$cfh}; + delete $fh2Attr{$cfh}; + delete $fhs2AttrOrig{$cfg}; + delete $parent2fh{"$pfh,$name"}; + } + } + } + + return ; +} + +sub doGetAttr { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $fh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + #if (nfsd::nfsDumpParseLineField ($line, 'fh') + # eq '00018961-57570100-d2440000-61890100') { + # printf STDERR "Seen it ($op)\n"; + #} + + if (! defined $fh) { + return ; + } + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $fh; +# RFS debug code +my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +if ($fh eq $wantfh) { + print "JIAWU: doGetAttr call $wantfh\n"; +} + } + else { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $fh = $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDnow{$uxid}; +# RFS debug code +my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +if ($fh eq $wantfh) { + print "JIAWU: doGetAttr response $wantfh\n"; +} + + if ($response ne 'OK') { + return ; + } + + my ($ftype) = nfsd::nfsDumpParseLineFields ($line, 'ftype'); + if (!defined $ftype) { + print STDERR "BAD $line"; + return ; + } + + if ($ftype == 2) { + $fhIsDir{$fh} = 1; + } + + #RFS comment: here if fh is a directory, then it will not be add + # in the two hash table %fh2Attr(%fh2AttrOrig) and %fh2Name + # if ($ftype != 1) { + # return ; + #} + if ($ftype != 1) { + #return ; + } + + + my ($mode, $size, $atime, $mtime, $ctime) = + nfsd::nfsDumpParseLineFields ($line, + 'mode', 'size', 'atime', 'mtime', 'ctime'); + + # RFS: modify here to get/maintain more file attributes + # we can just check the ctime and compare it with trace-start-time + # to decide whether to create a file/diretory. + # atime - last access time of the file + # mtime - last modification time of the file + # ctime - last file status change time + + # $fh2Attr{$fh} = "$size $mode $atime $mtime $ctime"; + + if (! exists $fh2AttrOrig{$fh} ) { + $fh2AttrOrig{$fh} = "$size $mode $op $atime $mtime $ctime"; + } + $fh2Attr{$fh} = "$size $mode $op $atime $mtime $ctime"; + } +} + +# Purge all the pending XID records dated earlier than $when (which is +# typically at least $PRUNE_INTERVAL seconds ago). This is important +# because otherwise missing XID records can pile up, eating a lot of +# memory. + +sub prunePending { + my ($when) = @_; + + foreach my $uxid ( keys %pendingCallsXIDnow ) { + if ($pendingCallsXIDnow{$uxid} < $when) { +# RFS debug code +my $fh = $pendingCallsXIDfh{$uxid}; +my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +if ($fh eq $wantfh) { + print "JIAWU: prunePending $wantfh\n"; +} +#enf RFS + delete $pendingCallsXIDnow{$uxid}; + } + } + + return ; +} + +# Return as much of the path for the given fh as possible. It may or +# may not reach the root (or the mount point of the file system), but +# right now we don't check. Usually on busy systems the data is +# complete enough so that most paths are complete back to the mount +# point. + +sub findPath { + my ($fh) = @_; + my $isdir = 0; + my $cnt = 0; + my $MaxPathLen = 40; + + if (exists $fhIsDir{$fh}) { + $isdir = 1; + } + + my @path = (); + while ($fh && exists $fh2Name{$fh}) { + unshift (@path, $fh2Name{$fh}); + if ($fh eq $fh2Parent{$fh}) { + unshift (@path, '(LOOP)'); + last; + } + + if ($cnt++ > $MaxPathLen) { + print STDERR "findPath: path too long (> $MaxPathLen)\n"; + unshift (@path, '(TOO-LONG)'); + last; + } + + $fh = $fh2Parent{$fh}; + } + + # RFS: append the ~user (fh and !exists $fh2Name{$fh} and type is Directory) + if ($fh && !exists $fh2Name{$fh} && exists $fhIsDir{$fh}) { + if (exists $rootsName{$fh}) { + print "JIAWU: $rootsName{$fh}\n"; + unshift(@path, $rootsName{$fh}); + } else { + print "JIAWU: WARNING! No rootsName for this fh: $fh\n"; + unshift(@path, $fh); + } + } else { + if ($fh && !exists $fh2Name{$fh} && !exists $fhIsDir{$fh}) { + if (exists $discardFHs{$fh}) { + open NOATTRDIR, ">>noattrdirdiscard"; + print NOATTRDIR "$fh DISCARD\n"; + close NOATTRDIR; + } else { + # RFS: if a possible fh without attr and name, then regard it as a special root ~/RFSNN0 + unshift(@path, '"RFSNN0"'); + $fhIsDir{$fh}=1; + $fh2Name{$fh} = '"RFSNN0"'; + $rootsName{$fh} = '"RFSNN0"'; + open NOATTRDIR, ">>noattrdir-root"; + print NOATTRDIR "$fh RFSNN0\n"; + close NOATTRDIR; + } + } + } + + + my $str = ''; + $cnt = 0; + foreach my $p ( @path ) { + $p =~ s/^.//; + $p =~ s/.$//; + $str .= "/$p"; + $cnt++; + } + + if ($isdir) { + $str .= '/'; + } + + if ($cnt == 0) { + $str = '.'; + } + + return ($str, $cnt); +} + + +$total_unknown_fh = 0; +$total_known_fh = 0; + +sub printAll { + my ($start_time, $out) = @_; + + my %allfh = (); + my $fh; + my $u = 0; + my $k = 0; + + # RFS print more information here + open (OUT_RFS, ">rfsinfo") || + die "Can't create $OutFileBaseName.rfs."; + + foreach $fh ( keys %fh2Attr ) { + $allfh{$fh} = 1; + } + foreach $fh ( keys %fh2Name ) { + $allfh{$fh} = 1; + } + + #RFS: before printFileInfo, name those roots' name + + #RFS there are three kind of fh + # 1. fh/name paired (fh/attr must) + # 2. fh/attr but no fh/name: type file (discard related operations) + # 3. fh/attr but no fh/name: type dir (keep as persuedo root) + $u = $k = 0; + my $sn=1; + foreach $fh ( keys %allfh ) { + if (exists $fh2Parent{$fh} ) { + $k++; + } + else { + $u++; + my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F'; + if ($type eq 'D') { + $rootsName{$fh} = sprintf("\"RFSNN%d\"", $sn++); + $rootsFHs{$fh} = 1; + } + else { + $discardFHs{$fh} = 1; + } + } + } + print OUT_RFS "#stat: fh with parent = $k, fh without parent = $u\n"; + $u = keys %rootsFHs; + print OUT_RFS "#RFS: root fh list($u)\n"; + foreach $fh (keys %rootsName) { + print OUT_RFS "#RFS: $rootsName{$fh} $fh\n"; + } + $u = keys %discardFHs; + print OUT_RFS "#RFS: discard fh list($u)\n"; + print OUT_RFS join("\n", keys %discardFHs, ""); + + + print $out "#F type state fh path pathcount attrOrig(size,mode,op,atime,mt,ct) attrLast(size,mode,op,at,mt,ct)\n"; + + print $out "#T starttime = $start_time\n"; + foreach $fh ( keys %allfh ) { + printFileInfoOutputFile ($fh, 'A', $out); + } + + + my $numfh2Name = keys %fh2Name; + my $numfh2Attr = keys %fh2Attr; + print OUT_RFS "fh2name has $numfh2Name, fh2Attr has $numfh2Attr\n"; + my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; + if ($allfh{$wantfh} == 1) { + print OUT_RFS "JIAWU: found $wantfh\n"; + } else { + print OUT_RFS "JIAWU: NOT found $wantfh\n"; + } + foreach $fh ( keys %allfh ) { + if ( $fh eq $wantfh ) { + print OUT_RFS "JIAWU: found $wantfh\n"; + printFileInfoOutputFile ($fh, 'JIAWU', *OUT_RFS); + last; + } + } + print OUT_RFS "JIAWU: after \n"; + + + $u = $k = 0; + foreach $fh ( keys %allfh ) { + if ( exists $fh2Name{$fh} ) {$k++;} + else {$u++;} + } + print OUT_RFS "#stat: total known fh = $total_known_fh, unknown = $total_unknown_fh\n"; + print OUT_RFS "#stat: total fh with name = $k, without name = $u\n"; + + print OUT_RFS "#RFS\n"; + close OUT_RFS; + +} + +sub printFileInfoOutputFile { + my ($fh, $state, $out) = @_; + + my ($p, $c) = findPath ($fh); + + if ($c == 0) {$total_unknown_fh++;} + else {$total_known_fh++;} + + my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F'; + my $attr = (exists $fh2Attr{$fh}) ? + $fh2Attr{$fh} : "-1 -1 -1 -1 -1"; + my $attrOrig = (exists $fh2AttrOrig{$fh}) ? + $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1"; + + print $out "F $type $state $fh $p $c $attrOrig $attr\n"; +} + +sub printFileInfo { + my ($fh, $state) = @_; + + my ($p, $c) = findPath ($fh); + + if ($c == 0) {$total_unknown_fh++;} + else {$total_known_fh++;} + + my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F'; + my $attr = (exists $fh2Attr{$fh}) ? + $fh2Attr{$fh} : "-1 -1 -1 -1 -1"; + my $attrOrig = (exists $fh2AttrOrig{$fh}) ? + $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1"; + + print "F $type $state $fh $p $c $attrOrig $attr\n"; +} + +1; diff --git a/TBBT/trace_init/key.pl b/TBBT/trace_init/key.pl new file mode 100755 index 0000000..b4eacb0 --- /dev/null +++ b/TBBT/trace_init/key.pl @@ -0,0 +1,122 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: key.pl,v 1.11 2003/07/26 20:52:04 ellard Exp $ + +package key; + +sub makeKey { + my ($line, $proto, $op, $xid, $client, $now) = @_; + my ($client_id, $fh, $euid, $egid) = ('u', 'u', 'u', 'u'); + my ($uxid) = "$client-$xid"; + + if ($proto eq 'R3' || $proto eq 'R2') { + if (exists $PendingKeyStr{$uxid}) { + return ($PendingKeyStr{$uxid}); + } + else { + return 'u,u,u,u'; + } + } + + if ($main::UseClient) { + $client_id = $client; + $client_id =~ s/\..*//g + } + if ($main::UseFH && $op ne 'null') { + my $tag = ($op eq 'commit') ? 'file' : 'fh'; + + $fh = nfsd::nfsDumpParseLineField ($line, $tag); + if (! defined $fh) { + print STDERR "undefined fh ($line)\n"; + } + + $fh = nfsd::nfsDumpCompressFH ($main::FH_TYPE, $fh); + + } + if ($main::UseUID && $op ne 'null') { + $euid = nfsd::nfsDumpParseLineField ($line, 'euid'); + } + if ($main::UseGID && $op ne 'null') { + $egid = nfsd::nfsDumpParseLineField ($line, 'egid'); + } + + my $key = "$client_id,$fh,$euid,$egid"; + $KeysSeen{$key} = 1; + + $PendingKeyStr{$uxid} = $key; + $PendingKeyTime{$uxid} = $now; + + return ($key); +} + +sub key2str { + my ($key) = @_; + + my ($client_id, $fh, $euid, $egid) = split (/,/, $key); + + if ($client_id ne 'u') { + + # just for aesthetics: + $client_id = sprintf ("%.8x", hex ($client_id)); + $client_id =~ /^(..)(..)(..)(..)/; + $client_id = sprintf ("%d.%d.%d.%d", + hex ($1), hex ($2), + hex ($3), hex ($4)); + $client_id = sprintf ("%-15s", $client_id); + } + + if ($euid ne 'u') { + $euid = hex ($euid); + } + if ($egid ne 'u') { + $egid = hex ($egid); + } + + return ("$client_id $fh $euid $egid"); +} + +# Purge all the pending XID records dated earlier than $when (which is +# typically at least $PRUNE_INTERVAL seconds ago). This is important +# because otherwise missing XID records can pile up, eating a lot of +# memory. + +sub prunePending { + my ($when) = @_; + + foreach my $uxid ( keys %PendingKeyTime ) { + if ($PendingKeyTime{$uxid} < $when) { + delete $PendingKeyTime{$uxid}; + delete $PendingKeyStr{$uxid}; + } + } + + return ; +} + +1; diff --git a/TBBT/trace_init/latency.pl b/TBBT/trace_init/latency.pl new file mode 100755 index 0000000..0ba1ecd --- /dev/null +++ b/TBBT/trace_init/latency.pl @@ -0,0 +1,159 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: latency.pl,v 1.8 2003/07/28 14:27:16 ellard Exp $ +# +# latency.pl - + +package latency; + +%PendingOps = (); +%OpCount = (); +%OpTime = (); +%KeysSeen = (); + +@OpList = (); + +sub init { + my (@oplist) = @_; + + @OpList = @oplist; +} + +# Bugs: might not recognize the actual response packets. It's an +# approximation. + +sub update { + my ($key, $proto, $op, $xid, $client, $now) = @_; + + my $uxid = "$client-$xid"; + + if ($proto eq 'C3' || $proto eq 'C2') { + $PendingOps{$uxid} = $now; + } + elsif (exists $PendingOps{$uxid}) { + my $elapsed = $now - $PendingOps{$uxid}; + + $KeysSeen{$key} = 1; + + $OpTime{"$key,$op"} += $elapsed; + $OpCount{"$key,$op"}++; + + $OpTime{"$key,TOTAL"} += $elapsed; + $OpCount{"$key,TOTAL"}++; + + $OpTime{"$key,INTERESTING"} += $elapsed; + $OpCount{"$key,INTERESTING"}++; + + delete $PendingOps{$uxid}; + } +} + +sub resetOpCounts { + + my $k; + + foreach $k ( keys %OpTime ) { + $OpTime{$k} = 0.0; + } + foreach $k ( keys %OpCount ) { + $OpCount{$k} = 0; + } + + return ; +} + +sub printTitle { + my $str = "#L time client euid egid fh"; + + foreach my $op ( @OpList ) { + $str .= " $op-cnt $op-lat"; + } + $str .= "\n"; + + print $str; +} + +sub printOps { + my ($start_time, $out) = @_; + my ($k, $str, $op, $nk, $latms, $cnt); + + my @allkeys = sort keys %KeysSeen; + + foreach $k ( @allkeys ) { + my $tot = "$k,TOTAL"; + + if ($main::OMIT_ZEROS && + (! exists $OpCounts{$tot} || $OpCounts{$tot} == 0)) { + next; + } + + $str = sprintf ("L %s %s", $start_time, &key::key2str ($k)); + + foreach $op ( @OpList ) { + $nk = "$k,$op"; + + if (exists $OpCount{$nk}) { + $cnt = $OpCount{"$k,$op"}; + } + else { + $cnt = 0; + } + + if ($cnt > 0) { + $latms = 1000 * $OpTime{$nk} / $cnt; + } + else { + $latms = -1; + } + + $str .= sprintf (" %d %.4f", $cnt, $latms); + } + + print $out "$str\n"; + } +} + +# Purge all the pending XID records dated earlier than $when (which is +# typically at least $PRUNE_INTERVAL seconds ago). This is important +# because otherwise missing XID records can pile up, eating a lot of +# memory. + +sub prunePending { + my ($when) = @_; + + foreach my $uxid ( keys %PendingOps ) { + if ($PendingOps{$uxid} < $when) { + delete $PendingOps{$uxid}; + } + } + + return ; +} + +1; diff --git a/TBBT/trace_init/nfsdump.pl b/TBBT/trace_init/nfsdump.pl new file mode 100755 index 0000000..69eca84 --- /dev/null +++ b/TBBT/trace_init/nfsdump.pl @@ -0,0 +1,338 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: nfsdump.pl,v 1.5 2003/07/26 20:52:04 ellard Exp $ +# +# Utility for dealing with raw nfsdump records. + +package nfsd; + +# If $AllowRisky is set, then allow some optimizations that might be +# "risky" in bizarre situations (but have never been observed to +# actually break anything). By default, no riskiness is permitted. + +$AllowRisky = 0; + +# nfsDumpParseLine -- initializes the global associative array +# %nfsd'nfsDumpLine with information about a record from nfsdump. +# Returns an empty list if anything goes wrong. Otherwise, returns a +# list of the protocol (eg R2, C2, R3, C3), the name of the operation, +# and the xid, the client host ID, the time, and for responses, the +# status (via nfsDumpParseLineHeader). The reason for this particular +# return list is that these are very frequently-accessed values, so it +# can save time to avoid going through the associative array to access +# them. +# +# All records begin with several fixed fields, and then are followed +# by some number of name/value pairs, and finally some diagnostic +# fields (which are mostly ignored by this routine-- the only +# diagnostic this routine cares about is whether the packet as part of +# a jumbo packet or not. If so, then 'truncated' is set.) + +sub nfsDumpParseLine { + my ($line, $total) = @_; + + my (@rl) = &nfsDumpParseLineHeader ($line); + + if (@rl && $total) { + &nfsDumpParseLineBody ($line); + } + + return @rl; +} + +sub nfsDumpParseLineBody { + my ($line) = @_; + my $i; + my $client_id; + my $reseen; + + undef %nfsDumpLine; + + # If the line doesn't start with a digit, then it's certainly + # not properly formed, so bail out immediately. + + if (! ($line =~ /^[0-9]/)) { + return undef; + } + + my @l = split (' ', $line); + my $lineLen = @l; + if ($l[$lineLen - 1] eq 'LONGPKT') { + splice (@l, $lineLen - 1); + $nfsDumpLine{'truncated'} = 1; + $lineLen--; + } + + $nfsDumpLine{'time'} = $l[0]; + $nfsDumpLine{'srchost'} = $l[1]; + $nfsDumpLine{'deshost'} = $l[2]; + $nfsDumpLine{'proto'} = $l[4]; + $nfsDumpLine{'xid'} = $l[5]; + $nfsDumpLine{'opcode'} = $l[6]; + $nfsDumpLine{'opname'} = $l[7]; + + if (($l[4] eq 'R3') || ($l[4] eq 'R2')) { + $nfsDumpLine{'status'} = $l[8]; + + $client_id = $l[2]; + $reseen = 0; + for ($i = 9; $i < $lineLen - 10; $i += 2) { + if (defined $nfsDumpLine{$l[$i]}) { + $reseen = 1; + $nfsDumpLine{"$l[$i]-2"} = $l[$i + 1]; + } + else { + $nfsDumpLine{$l[$i]} = $l[$i + 1]; + } + } + } + else { + $client_id = $l[1]; + $reseen = 0; + for ($i = 8; $i < $lineLen - 6; $i += 2) { + if (defined $nfsDumpLine{$l[$i]}) { + $nfsDumpLine{"$l[$i]-2"} = $l[$i + 1]; + $reseen = 1; + } + else { + $nfsDumpLine{$l[$i]} = $l[$i + 1]; + } + } + } +} + +# Returns an empty list if anything goes wrong. Otherwise, returns a +# list of the protocol (eg R2, C2, R3, C3), the name of the operation, +# and the xid, the client host ID, and time, and the response status. +# (For call messages, the status is 'na'.) + +sub nfsDumpParseLineHeader { + my ($line) = @_; + + # If the line doesn't start with a digit, then it's certainly + # not properly formed, so bail out immediately. + + if (! ($line =~ /^[0-9]/)) { + return (); + } + else { + my $client_id; + my $status; + + my @l = split (' ', $line, 10); + + + if (($l[4] eq 'R3') || ($l[4] eq 'R2')) { + $client_id = $l[2]; + $status = $l[8]; + } + else { + $client_id = $l[1]; + $status = 'na'; + } + + return ($l[4], $l[7], $l[5], $client_id, $l[0], $status); + } +} + +# nfsDumpParseLineFields -- Just return a subset of the fields, +# without parsing the entire line. + +sub nfsDumpParseLineFields { + my ($line, @fields) = @_; + my $i; + + # If the line doesn't start with a digit, then + # it's certainly not properly formed, so bail out + # immediately. + + if (! ($line =~ /^[0-9]/)) { + return (); + } + + my $rest; + if ($AllowRisky) { + $rest = $line; + } + else { + my @foo = split (' ', $line, 9); + $rest = ' ' . $foo[8]; + } + + my $fl = @fields; + my @l = (); + for ($i = 0; $i < $fl; $i++) { + my $field = $fields[$i]; + + $rest =~ /\ $field\ +([^\ ]+)/; + $l[$i] = $1; + } + + return (@l); +} + +# nfsDumpParseLineField -- Just return ONE of the fields, +# without parsing the entire line. + +sub nfsDumpParseLineField { + my ($line, $field) = @_; + + # If the line doesn't start with a digit, then + # it's certainly not properly formed, so bail out + # immediately. + + if (! ($line =~ /^[0-9]/)) { + return undef; + } + + my $rest; + if ($AllowRisky) { + $rest = $line; + } + else { + my @foo = split (' ', $line, 9); + $rest = ' ' . $foo[8]; + } + + $rest =~ /\ $field\ +([^\ ]+)/; + return $1; +} + +# Returns a new file handle that has all the "useful" information as +# the original, but requires less storage space. File handles +# typically contain quite a bit of redundancy or unused bytes. +# +# This routine only knows about the advfs and netapp formats. If +# you're using anything else, just use anything else as the mode, and +# the original file handle will be returned. +# +# If you extend this to handle more file handles, please send the new +# code to me (ellard@eecs.harvard.edu) so I can add it to the +# distribution. + +sub nfsDumpCompressFH { + my ($mode, $fh) = @_; + + if ($mode eq 'advfs') { + + # The fh is a long hex string: + # 8 chars: file system ID + # 8 chars: apparently unused. + # 8 chars: unused. + # 8 chars: inode + # 8 chars: generation + # rest of string: mount point (not interesting). + # So all we do is pluck out the fsid, inode, + # and generation number, and throw the rest away. + + $fh =~ /^(........)(........)(........)(........)(........)/; + + return ("$1-$4-$5"); + } + elsif ($mode eq 'netapp') { + + # Here's the netapp format (from Shane Owara): + # + # 4 bytes mount point file inode number + # 4 bytes mount point file generation number + # + # 2 bytes flags + # 1 byte snapshot id + # 1 byte unused + # + # 4 bytes file inode number + # 4 bytes file generation number + # 4 bytes volume identifier + # + # 4 bytes export point fileid + # 1 byte export point snapshot id + # 3 bytes export point snapshot generation number + # + # The only parts of this that are interesting are + # inode, generation, and volume identifier (and probably + # a lot of the bits of the volume identifier could be + # tossed, since we don't have many volumes...). + + $fh =~ /^(........)(........)(........)(........)(........)(........)(........)/; + + return ("$4-$5-$6-$1"); + } + elsif ($mode eq 'RFSNN') { + + # Here's the netapp format (from Shane Owara): + # + # 4 bytes mount point file inode number + # 4 bytes mount point file generation number + # + # 2 bytes flags + # 1 byte snapshot id + # 1 byte unused + # + # 4 bytes file inode number + # 4 bytes file generation number + # 4 bytes volume identifier + # + # 4 bytes export point fileid + # 1 byte export point snapshot id + # 3 bytes export point snapshot generation number + # + # The only parts of this that are interesting are + # inode, generation, and volume identifier (and probably + # a lot of the bits of the volume identifier could be + # tossed, since we don't have many volumes...). + + # 61890100575701002000000 0009ac710e9ea381 0d24400006189010057570100 + # 61890100575701002000000 0009ac70ed2ea381 0d24400006189010057570100 + # 61890100575701002000000 000479a1e008d782 0d24400006189010057570100 + # Ningning need only 24-39 (or 12-19 bytes) + + $fh =~ /^(........)(........)(........)(........)(........)(........)/; + + return ("$4$5"); + }else { + + return ($fh); + } +} + +sub testMain { + $lineNum = 0; + + while (<STDIN>) { + $line = $_; + $lineNum++; + + &nfsDumpParseLine ($line); + } +} + +1; + +# end of nfsdump.pl diff --git a/TBBT/trace_init/nfsscan.pl b/TBBT/trace_init/nfsscan.pl new file mode 100755 index 0000000..fad5216 --- /dev/null +++ b/TBBT/trace_init/nfsscan.pl @@ -0,0 +1,682 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: nfsscan,v 1.18 2003/07/28 14:27:16 ellard Exp $ + +$ProgDir = $0; +$ProgDir =~ /(^.*)\//; +$ProgDir = $1; +if (!$ProgDir) { + $ProgDir = "."; +} + +require "$ProgDir/nfsdump.pl"; +require "$ProgDir/userUtils.pl"; +require "$ProgDir/hier.pl"; +require "$ProgDir/counts.pl"; +require "$ProgDir/latency.pl"; +require "$ProgDir/key.pl"; +require "$ProgDir/common.pl"; + +use Getopt::Std; + +$INTERVAL = 5 * 60; # in seconds (5 minutes) + +%KeysSeen = (); + +@ADD_USERS = (); +@DEL_USERS = (); +@ADD_GROUPS = (); +@DEL_GROUPS = (); +@ADD_CLIENTS = (); +@DEL_CLIENTS = (); + +$DO_COUNTS = 1; +$DO_LATENCY = 0; +$DO_FILES = 0; +$DO_PATHS = 0; +$DO_SQUEEZE = 0; + +$FH_TYPE = 'unknown'; + +$END_TIME = -1; +$START_TIME = -1; +$NOW = -1; +$UseClient = 0; +$UseFH = 0; +$UseUID = 0; +$UseGID = 0; +$OMIT_ZEROS = 0; + +$OutFileBaseName = undef; + +$nextPruneTime = -1; +$PRUNE_INTERVAL = 1 * 60; # One minute. + +# &&& +# Is this really the right default set of operations? + +$DEF_OPLIST = 'read,write,lookup,getattr,access,create,remove'; +@OPLIST = ('TOTAL', 'INTERESTING', + split (/,/, $DEF_OPLIST)); +%OPARRAY = (); + +$Usage =<< "."; + +Usage: $0 [options] [trace1 [trace2 ...]] + +If no trace files are specified, then the trace is read from stdin. + +Command line options: + +-h Print usage message and exit. + +-B [CFUG] Compute per-Client, per-File, per-User, or per-Group info. + +-c c1[,c2]* Include only activity performed by the specified clients. + +-C c1[,c2]* Exclude activity performed by the specified clients. + +-d Compute per-directory statistics. This implicitly + enables -BF so that per-file info is computed. + +-f Do file info tracking. This implicitly enables -BF so + that per-File info is computed. + +-F fhtype Specify the file handle type used by the server. + (advfs or netapp) + +-g g1[,g2]* Include only activity performed by the specified groups. + +-G g1[,g2]* Exclude activity performed by the specified groups. + +-l Record average operation latency. + +-o basename Write output to files starting with the specified + basename. The "Count" table goes to basename.cnt, + "Latency" to basename.lat, and "File" to basename.fil. + The default is to write all output to stdout. + +-O op[,op]* Specify the list of "interesting" operations. + The default list is: + + read,write,lookup,getattr,access,create,remove + + If the first op starts with +, then the specified list + of ops is appended to the default list. The special + pseudo-ops readM and writeM represent the number of + bytes read and written, expressed in MB. + +-t interval Time interval for cummulative statistics (such as + operation count). The default is $INTERVAL seconds. + If set to 0, then the entire trace is processed. By + default, time is specified in seconds, but if the last + character of the interval is any of s, m, h, or d, + then the interval is interpreted as seconds, minutes, + hours, or days. + +-u u1[,u2]* Include only activity performed by the specified users. + +-U u1[,u2]* Exclude activity performed by the specified users. + +-Z Omit count and latency lines that have a zero total + operation count. +. + + +main (); + +sub main { + + parseArgs (); + + if ($DO_COUNTS) { + counts::printTitle (*OUT_COUNTS); + } + + if ($DO_LATENCY) { + latency::printTitle (*OUT_LATENCY); + } + + counts::resetOpCounts (); + + my $cmdbuf = 'rm -f noattrdirdiscard noattrdir-root'; + system($cmdbuf); + + readTrace (); +} + +sub parseArgs { + + my $cmdline = "$0 " . join (' ', @ARGV); + + my $Options = "B:dfF:g:G:hlO:o:t:u:U:SR:Z"; + if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); + } + if (defined $opt_h) { + print $Usage; + exit (0); + } + + #RFS: neednot input arguments + $opt_o = "test"; + $opt_f = 1; + $opt_t = 0; + #$opt_F = 'RFSNN'; # advfs or netapp + + if (defined $opt_B) { + $UseClient = ($opt_B =~ /C/); + $UseFH = ($opt_B =~ /F/); + $UseUID = ($opt_B =~ /U/); + $UseGID = ($opt_B =~ /G/); + } + + if (defined $opt_o) { + $OutFileBaseName = $opt_o; + } + + if (defined $opt_O) { + if ($opt_O =~ /^\+(.*)/) { + @OPLIST = (@OPLIST, split (/,/, $1)); + } + else { + @OPLIST = ('TOTAL', 'INTERESTING', split (/,/, $opt_O)); + } + # Error checking? + } + + if (defined $opt_l) { + $DO_LATENCY = 1; + } + + if (defined $opt_t) { + if ($INTERVAL =~ /([0-9]*)([smhd])/) { + my $n = $1; + my $unit = $2; + + if ($unit eq 's') { + $INTERVAL = $opt_t; + } + elsif ($unit eq 'm') { + $INTERVAL = $opt_t * 60; + } + elsif ($unit eq 'h') { + $INTERVAL = $opt_t * 60 * 60; + } + elsif ($unit eq 'd') { + $INTERVAL = $opt_t * 24 * 60 * 60; + } + } + else { + $INTERVAL = $opt_t; + } + } + + $DO_PATHS = (defined $opt_d); + $DO_FILES = (defined $opt_f); + $DO_SQUEEZE = (defined $opt_S); + $OMIT_ZEROS = (defined $opt_Z); + + $TIME_ROUNDING = (defined $opt_R) ? $opt_R : 0; + + if (defined $opt_F) { + $FH_TYPE = $opt_F; + } + + if (defined $opt_c) { + @ADD_CLIENTS = split (/,/, $opt_c); + } + if (defined $opt_C) { + @DEL_CLIENTS = split (/,/, $opt_c); + } + + if (defined $opt_g) { + @ADD_GROUPS = groups2gids (split (/,/, $opt_g)); + } + if (defined $opt_G) { + @DEL_GROUPS = groups2gids (split (/,/, $opt_G)); + } + + if (defined $opt_u) { + @ADD_USERS = logins2uids (split (/,/, $opt_u)); + } + if (defined $opt_U) { + @DEL_USERS = logins2uids (split (/,/, $opt_U)); + } + + + # Now that we know what options the user asked for, initialize + # things accordingly. + + if ($DO_PATHS || $DO_FILES) { + $UseFH = 1; + } + + if ($DO_LATENCY) { + latency::init (@OPLIST); + } + + if ($DO_COUNTS) { + counts::init (@OPLIST); + } + + if (defined $OutFileBaseName) { + if ($DO_COUNTS) { + open (OUT_COUNTS, ">$OutFileBaseName.cnt") || + die "Can't create $OutFileBaseName.cnt."; + print OUT_COUNTS "#cmdline $cmdline\n"; + } + if ($DO_LATENCY) { + open (OUT_LATENCY, ">$OutFileBaseName.lat") || + die "Can't create $OutFileBaseName.lat."; + print OUT_LATENCY "#cmdline $cmdline\n"; + } + if ($DO_FILES) { + open (OUT_FILES, ">$OutFileBaseName.fil") || + die "Can't create $OutFileBaseName.fil."; + print OUT_FILES "#cmdline $cmdline\n"; + } + if ($DO_PATHS) { + open (OUT_PATHS, ">$OutFileBaseName.pat") || + die "Can't create $OutFileBaseName.pat."; + print OUT_PATHS "#cmdline $cmdline\n"; + } + } + else { + *OUT_COUNTS = STDOUT; + *OUT_LATENCY = STDOUT; + *OUT_FILES = STDOUT; + *OUT_PATHS = STDOUT; + + print STDOUT "#cmdline $cmdline\n"; + } + + foreach my $op ( @OPLIST ) { + $OPARRAY{$op} = 1; + } + + return ; +} + +sub readTrace { + my (@args) = @_; + + while (my $line = <>) { + + $hier::rfsLineNum++; + if ( ($hier::rfsLineNum % 1000) eq 0) { + print STDERR "$hier::rfsLineNum\n"; + } + + + if ($line =~ /SHORT\ PACKET/) { + next; + } + + my ($proto, $op, $xid, $client, $now, $response) = + nfsd::nfsDumpParseLineHeader ($line); + $NOW = $now; + + # NOTE: This next bit of logic requires a little + # extra attention. We want to discard lines as + # quickly as we can if they're not "interesting". + # However, different lines are interesting in + # different contexts, so the order of the tests and + # the manner in which they are interspersed with + # subroutine calls to pluck info from the lines is + # very important. + + # Check whether it is a line that we should prune and + # ignore, because of the filters. + + next if (($op eq 'C3' || $op eq 'C2') && + ! pruneCall ($line, $client)); + + if ($DO_PATHS || $DO_FILES) { + hier::processLine ($line, + $proto, $op, $xid, $client, + $now, $response, $FH_TYPE); + } + + my $key = key::makeKey ($line, $proto, $op, + $xid, $client, $now, + $UseClient, $UseFH, $UseUID, $UseGID, + $FH_TYPE); + if (! defined $key) { + next ; + } + $KeysSeen{$key} = 1; + + # Count everything towards the total, but only + # do the rest of the processing for things + # that are "interesting". + + if ($proto eq 'C3' || $proto eq 'C2') { + $counts::OpCounts{"$key,TOTAL"}++; + $counts::KeysSeen{$key} = 1; + + next if (! exists $OPARRAY{$op}); + + $counts::OpCounts{"$key,$op"}++; + $counts::OpCounts{"$key,INTERESTING"}++; + } + + if ($op eq 'read' && exists $OPARRAY{'readM'}) { + doReadSize ($line, $proto, $op, $key, $client, $xid, $response, $now); + } + + if ($op eq 'write' && exists $OPARRAY{'writeM'}) { + doWriteSize ($line, $proto, $op, $key, $client, $xid, $response, $now); + } + + if ($DO_LATENCY) { + latency::update ($key, $proto, $op, + $xid, $client, $now); + } + + if ($END_TIME < 0) { + $START_TIME = findStartTime ($NOW, $TIME_ROUNDING); + $END_TIME = $START_TIME + $INTERVAL; + } + + # Note that this is a loop, because if the interval is + # short enough, or the system is very idle (or there's + # a filter in place that makes it look idle), entire + # intervals can go by without anything happening at + # all. Some tools can get confused if intervals are + # missing from the table, so we emit them anyway. + + while (($INTERVAL > 0) && ($NOW >= $END_TIME)) { + printAll ($START_TIME); + + counts::resetOpCounts (); + latency::resetOpCounts (); + + $START_TIME += $INTERVAL; + $END_TIME = $START_TIME + $INTERVAL; + } + + if ($now > $nextPruneTime) { + key::prunePending ($now - $PRUNE_INTERVAL); + latency::prunePending ($now - $PRUNE_INTERVAL); + + prunePending ($now - $PRUNE_INTERVAL); + + $nextPruneTime = $now + $PRUNE_INTERVAL; + } + } + + # Squeeze out the last little bit, if there's anything that we + # counted but did not emit. If DO_SQUEEZE is true, then + # always do this. Otherwise, only squeeze out the results of + # the last interval if the interval is "almost" complete (ie + # within 10 seconds of the end). + + if (($NOW > $START_TIME) && ($DO_SQUEEZE || (($END_TIME - $NOW) < 10))) { + printAll ($START_TIME); + counts::resetOpCounts (); + } + + print "#T endtime = $NOW\n"; + +} + +sub printAll { + my ($start_time) = @_; + + if ($DO_COUNTS) { + counts::printOps ($start_time, *OUT_COUNTS); + } + + if ($DO_LATENCY) { + latency::printOps ($start_time, *OUT_LATENCY); + } + + if ($DO_FILES) { + hier::printAll ($start_time, *OUT_FILES); + } + + if ($DO_PATHS) { + printPaths ($start_time, *OUT_PATHS); + } +} + +sub pruneCall { + my ($line, $client) = @_; + + if (@ADD_USERS > 0 || @DEL_USERS > 0) { + my $c_uid = nfsd::nfsDumpParseLineField ($line, 'euid'); + if (! defined ($c_uid)) { + return 0; + } + $c_uid = hex ($c_uid); + + if (@ADD_USERS && !grep (/^$c_uid$/, @ADD_USERS)) { + return 0; + } + if (@DEL_USERS && grep (/^$c_uid$/, @DEL_USERS)) { + return 0; + } + } + + if (@ADD_GROUPS > 0 || @DEL_GROUPS > 0) { + my $c_gid = nfsd::nfsDumpParseLineField ($line, 'egid'); + if (! defined ($c_gid)) { + return 0; + } + $c_gid = hex ($c_gid); + + if (@ADD_GROUPS && !grep (/^$c_gid$/, @ADD_GROUPS)) { + return 0; + } + if (@DEL_GROUPS && grep (/^$c_gid$/, @DEL_GROUPS)) { + return 0; + } + } + + if (@ADD_CLIENTS > 0 || @DEL_CLIENTS > 0) { + if (@ADD_CLIENTS && !grep (/^$client$/, @ADD_CLIENTS)) { + return 0; + } + if (@DEL_CLIENTS && grep (/^$client$/, @DEL_CLIENTS)) { + return 0; + } + } + + return 1; +} + +%PathOpCounts = (); +%PathsSeen = (); + +sub buildDirPath { + my ($fh, $key) = @_; + my $pfh; + my $cnt; + + foreach my $op ( @OPLIST ) { + if (exists $counts::OpCounts{"$key,$op"}) { + $cnt = $counts::OpCounts{"$key,$op"}; + } + else { + $cnt = 0; + } + $PathOpCounts{"$fh,$op"} = $cnt; + + $PathsSeen{$fh} = 1; + + $pfh = $fh; + my $len = 0; + while (defined ($pfh = $hier::fh2Parent{$pfh})) { + + if ($len++ > 20) { + print "Really long path ($fh)\n"; + last; + } + + if (exists $PathOpCounts{"$pfh,$op"}) { + $PathOpCounts{"$pfh,$op"} += $cnt; + } + else { + $PathOpCounts{"$pfh,$op"} = $cnt; + } + $PathsSeen{$pfh} = 1; + } + } + + return ; +} + +sub printPaths { + my ($start_time, $out) = @_; + + my $str = "#D time Dir/File dircnt path fh"; + foreach my $op ( @OPLIST ) { + $str .= " $op"; + } + $str .= "\n"; + + print $out $str; + + undef %PathsSeen; + + foreach my $key ( keys %KeysSeen ) { + my ($client_id, $fh, $euid, $egid) = split (/,/, $key); + + buildDirPath ($fh, $key); + } + + foreach my $fh ( keys %PathsSeen ) { + my ($path, $cnt) = hier::findPath ($fh); + + if ($cnt == 0) { + $path = "."; + } + + my $type = (exists $hier::fhIsDir{$fh} && $hier::fhIsDir{$fh}==2) ? 'D' : 'F'; + + my $str = "$cnt $type $path $fh "; + + foreach my $op ( @OPLIST ) { + my $cnt; + + if (exists $PathOpCounts{"$fh,$op"}) { + $cnt = $PathOpCounts{"$fh,$op"}; + } + else { + print "Missing $fh $op\n"; + $cnt = 0; + } + + $str .= " $cnt"; + + $PathOpCounts{"$fh,$op"} = 0; # &&& reset + } + + print $out "D $start_time $str\n"; + } +} + +%uxid2key = (); +%uxid2time = (); + +sub doReadSize { + my ($line, $proto, $op, $key, $client, $xid, $response, $time) = @_; + + my $uxid = "$client-$xid"; + + if ($proto eq 'C3' || $proto eq 'C2') { + $uxid2time{$uxid} = $time; + $uxid2key{$uxid} = $key; + } + else { + if (! exists $uxid2key{$uxid}) { + return ; + } + if ($response ne 'OK') { + return ; + } + + $key = $uxid2key{$uxid}; + my $count = nfsd::nfsDumpParseLineField ($line, 'count'); + $count = hex ($count); + + delete $uxid2key{$uxid}; + delete $uxid2time{$uxid}; + + $counts::OpCounts{"$key,readM"} += $count; + } +} + +# Note that we always just assume that writes succeed, because on most +# systems they virtually always do. If you're tracing a system where +# your users are constantly filling up the disk or exceeding their +# quotas, then you will need to fix this. + +sub doWriteSize { + my ($line, $proto, $op, $key, $client, $xid, $response, $time) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + + my $tag = ($proto eq 'C3') ? 'count' : 'tcount'; + + my $count = nfsd::nfsDumpParseLineField ($line, $tag); + + if (! $count) { + printf "WEIRD count $line\n"; + } + + $count = hex ($count); + + $counts::OpCounts{"$key,writeM"} += $count; + } +} + + +# Purge all the pending XID records dated earlier than $when (which is +# typically at least $PRUNE_INTERVAL seconds ago). This is important +# because otherwise missing XID records can pile up, eating a lot of +# memory. + +sub prunePending { + my ($when) = @_; + + foreach my $uxid ( keys %uxid2time ) { + if ($uxid2time{$uxid} < $when) { + delete $uxid2key{$uxid}; + delete $uxid2time{$uxid}; + } + } + + return ; +} + diff --git a/TBBT/trace_init/nfsscan.txt b/TBBT/trace_init/nfsscan.txt new file mode 100755 index 0000000..b004339 --- /dev/null +++ b/TBBT/trace_init/nfsscan.txt @@ -0,0 +1,230 @@ +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# $Id: nfsscan.txt,v 1.5 2003/07/28 14:27:16 ellard Exp $ + +NFSSCAN DOCUMENTATION + +This is version 0.10a of nfsscan, dated 7/25/2003. + +THIS IS A PRELIMINARY RELEASE OF A NEW UTILITY. YOU SHOULD ASSUME +THAT THE COMMANDLINE FORMATS WILL EVOLVE RAPIDLY OVER THE NEXT SEVERAL +WEEKS. + +Please report bugs, problems or suggestions for improvements to +ellard@eecs.harvard.edu. + +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +COMMANDLINE OPTIONS + +Usage: nfsscan [options] [trace1 [trace2 ...]] + +If no trace files are specified, then the trace is read from stdin. + +Command line options: + +-h Print usage message and exit. + +-B [CFUG] Compute per-Client, per-File, per-User, or per-Group info. + +-c c1[,c2]* Include only activity performed by the specified clients. + +-C c1[,c2]* Exclude activity performed by the specified clients. + +-d Compute per-directory statistics. This implicitly + enables -BF so that per-file info is computed. + +-f Do file info tracking. This implicitly enables -BF so + that per-File info is computed. + +-F fhtype Specify the file handle type used by the server. + (advfs or netapp) + +-g g1[,g2]* Include only activity performed by the specified groups. + +-G g1[,g2]* Exclude activity performed by the specified groups. + +-l Record average operation latency. + +-o basename Write output to files starting with the specified + basename. The "Count" table goes to basename.cnt, + "Latency" to basename.lat, and "File" to basename.fil. + The default is to write all output to stdout. + +-O op[,op]* Specify the list of "interesting" operations. + The default list is: + + read,write,lookup,getattr,access,create,remove + + If the first op starts with +, then the specified list + of ops is appended to the default list. The special + pseudo-ops readM and writeM represent the number of + bytes read and written, expressed in MB. + +-t interval Time interval for cummulative statistics (such as + operation count). The default is 300 seconds. + If set to 0, then the entire trace is processed. By + default, time is specified in seconds, but if the last + character of the interval is any of s, m, h, or d, + then the interval is interpreted as seconds, minutes, + hours, or days. + +-u u1[,u2]* Include only activity performed by the specified users. + +-U u1[,u2]* Exclude activity performed by the specified users. + +-Z Omit count and latency lines that have a zero total + operation count. + + +OUTPUT FORMATS + +Each line generated by nfsscan begins with a token that indicates the +table to which it belongs. + +COUNT LINES + +These lines all begin with 'C' (or #C, for comments), +and have the following format: + +C time client euid egid fh TOTAL INTERESTING <OPS> + +time - The time of the start of the measurement interval. + +client - The IP number of the client. If the user has not + requested per-client statistics, this field is 'u'. + +euid - The effective uid of the caller, in decimal. If the user has + not requested per-user statistics, this field is 'u'. + +egid - The effective gid of the caller, in decimal. If the user has + not requested per-group statistics, this field is 'u'. + +fh - The file handle used by the operation. If the user has not + requested per-file statistics, this field is 'u'. + +TOTAL - The total number of operations for the given client, euid, + egid, and fh, for all the operations in <OPS...>. Note that + this is NOT the same as the total of all operations! For + example, if you set <OPS> to the empty list, the TOTAL will + always be zero. + +INTERESTING - The total number of "interesting" operations. These are + either the default operations (listed below) or the whatever + set of operations the user specifies. + + +<OPS> - Following INTERESTING, the count for each "interesting" + operation observed during the measurement interval is printed. + The list of operations can be specified or extended on the + commandline. By default, the list of operations is: + + read, write, lookup, getattr, access, create, remove + +LATENCY LINES + +The format for the "latency" lines is like that of the "count" lines, +except the lines all begin with "L" (or "#L") and each count +(including the TOTAL and INTERESTING counts) is followed the average +latency for that operation. If the count for a particular operation +is 0, then the average latency is shown as -1. + +Note that the counts for the latency lines may be (and often are) +different than the counts for count lines. This is because the count +lines show the number of calls that were observed, and the latency +lines require observing both calls and reponses. + +FILE LINES + +These lines show information about files, rather than information +about calls. These lines all begin with "F" (or "#F"). The file +information (size, mode, etc) are currently only printed for files, +not directories. + +The format is: + +F type state fh path dirs mode size atime mtime ctime + +type - Either "F" for file, or "D" for directory. + +state - "A" if the file is still alive at the end of the + trace, or "D" if the file has been deleted. + +fh - The file handle. + +path - as much of the file path as can be reconstructed from the + observed traffic. + +dirs - the number of directories in the path. + +mode - the most recently observed permissions on the file, in hex. + +size - the most recently observed size of the file, in hex. + +atime - last access time of the file + +mtime - last modification time of the file + +ctime - last file status change time + +DIRECTORY LINES + +These lines show operation count information for files and +directories. These lines all begin with "D" (or "#D"). Each line +begins with the following fields: + +time - The time of the start of the measurement interval. + +Dir/File - D for directory, F for file. + +dircnt - The length of the path to that directory, or zero for files. + +path - The path from the mount point to the directory or file. + +fh - The file handle of the directory or file. + +TOTAL - The total number of operations for the given client, euid, + egid, and fh, for all the operations in <OPS...>. Note that + this is NOT the same as the total of all operations! For + example, if you set <OPS> to the empty list, the TOTAL will + always be zero. + +Following the TOTAL, the count for each "interesting" operation +observed during the measurement interval is printed. The list of +operations can be specified on the commandline. By default, the list +of operations is: + + read, write, lookup, getattr, access, create, remove + +The information for files is redundant because this information is +also reflected in the Count lines (if -BF is used), but it is +sometimes useful to have it in the same format as the directory info. + +The TOTAL and op counts for directories represent the total number of +ops for the files in that directory AND all of its subdirectories. + +NOTE: there are several potential problems with the directory information: + + 1. It is possible (and not uncommon) for some part of a path + to be missing from the traces. The path is + reconstructed as far back to the root as possible, but + this is not always successful. + + If no information about the name of a file is known, + then it is given the name ".". + + 2. If files are renamed during the trace, then the name shown + is the most recent name for the file. + + 3. If files are removed during the trace, they are still + reported in the summary. + + This can lead to an error: if a directory is deleted, + and then another directory is created elsewhere with a + new name but the same inode number, this new directory + can "inherit" all the info about the files in the old + directory, including parentage. In the worst case, + this can cause a loop. The program will detect such + loops, and therefore not get caught forever, but it + doesn't do anything clever about them. diff --git a/TBBT/trace_init/ns_loc2gmt.pl b/TBBT/trace_init/ns_loc2gmt.pl new file mode 100755 index 0000000..815f68f --- /dev/null +++ b/TBBT/trace_init/ns_loc2gmt.pl @@ -0,0 +1,108 @@ +#!/usr/bin/perl +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_loc2gmt,v 1.6 2003/07/28 14:27:16 ellard Exp $ +# +# A helper application used by ns_quickview to convert from local time +# to GMT. nfsdump records time as localtime, but gnuplot only know +# how to deal with dates expressed as Greenwich Mean Time (which it +# then displays using the local time, for some reason). +# +# This is a fragile tool -- it must be run in the same time zone in +# which the data was collected via nfsdump, or else it will not do the +# proper conversion. Improvements welcomed! +# +# The 'C' (count) and 'L' (latency) records use the second column for +# dates, expressed as seconds.microseconds in localtime. The seconds +# portion is the only part of the data modified by this program. +# Comment lines are passed through unaltered. +# +# There is no error checking. Garbage in, garbage out. +# +# Note - we're throwing the microseconds away. + + +use Getopt::Std; +require 'timelocal.pl'; + +$Usage =<< "."; + +Usage: $0 [options] [table1.ns [table2.ns ... ]] + +If no table files are specified, then the input is read from stdin. + +Command line options: + +-h Print usage message and exit. + +-d secs Time offset, in seconds, to subtract from each time + in the input tables. Note that all times are rounded + down to the nearest second. + +IMPORTANT NOTE: this must be run in the same time zone in which the +data was collected via nfsdump, or else it will not do the proper +conversion unless the -d option is used. + +. + +$Options = "d:"; +if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); +} +if (defined $opt_h) { + print $Usage; + exit (0); +} + +$TimeOffset = 0; +if (defined $opt_d) { + $TimeOffset = int ($opt_d); +} + +while ($line = <>) { + if ($line =~ /^#/) { + print $line; + next; + } + + @arr = split (' ', $line); + + ($secs, $usec) = split (/\./, $arr[1]); + $secs -= $TimeOffset; + + $arr[1] = &timegm (localtime ($secs)); + + print join (' ', @arr); + print "\n"; +} + +exit (0); + diff --git a/TBBT/trace_init/ns_quickview.pl b/TBBT/trace_init/ns_quickview.pl new file mode 100755 index 0000000..1460235 --- /dev/null +++ b/TBBT/trace_init/ns_quickview.pl @@ -0,0 +1,245 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_quickview,v 1.9 2003/07/28 14:27:17 ellard Exp $ + +use Getopt::Std; + +$ProgDir = $0; +$ProgDir =~ /(^.*)\//; +$ProgDir = $1; +if (!$ProgDir) { + $ProgDir = "."; +} + +$GNUPLOT_PATH = "gnuplot"; +$GNUPLOT_OPTS = "-persist"; +$LOC2GMT_PATH = "$ProgDir/ns_loc2gmt"; + +$OUTPUT = "qv.ps"; +$GP_OUTPUT = "qv.gp"; +$TERMINAL = "postscript"; +$SIZE = undef; +$EXTRA_CMD = undef; +$TITLE_TEXT = undef; +$PLOT_OPTS = ''; +@OPS = ( 7 ); +@SCALES = (); + +$Usage =<< "."; + +Usage: $0 [options] table1.ns [table2.ns ...] + +At least one table file must be specified. + +Command line options: + +-h Print usage message and exit. + +-c cmd Pass an extra command directly to gnuplot. + +-g Save the gnuplot commands to $GP_OUTPUT instead + of executing the immediately. + +-k Show the key for each line on the plot. The default + is not to show the key. + +-l If more than one input file is specified, superimpose + the plot for each so that they all appear to begin at + the same time as the first row in the first file. + +-o filename Save the plot to the specified file. The default + output file is \"$OUTPUT\". + +-O op1,...,opN Plot the specified columns from the table. The + default is $OPS[0], the total operation count. + Note that the operations must be specified by column + number, not by name. + +-t terminal Create a plot for the given gnuplot terminal specification. + The default is \"$TERMINAL\". See the gnuplot + documentation for more information. + +-T string Use the specified string as the title for the plot. + The default is no title. + +-s sizespec Set the output size specifier. See the gnuplot + documentation for more information. + +-S s1,..,sN Set the scaling factor for each file. This is useful + for plotting files aggregated across different time + intervals on the same scale. + +. + +$Options = "c:ghklo:O:P:t:T:s:S:"; +if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); +} +if (defined $opt_h) { + print $Usage; + exit (0); +} + +$EXTRA_CMD = $opt_c if (defined $opt_c); +$OUTPUT = $opt_o if (defined $opt_o); +$TERMINAL = $opt_t if (defined $opt_t); +$SIZE = $opt_s if (defined $opt_s); +$TITLE_TEXT = $opt_T if (defined $opt_T); +$PLOT_OPTS = $opt_P if (defined $opt_P); +@OPS = split (/,/, $opt_O) if (defined $opt_O); +@SCALES = split (/,/, $opt_S) if (defined $opt_S); + +$SAVE_GNUPLOT = defined $opt_g; +$SHOW_KEY = defined $opt_k; +$LAYER = defined $opt_l; + +@FILES = @ARGV; + +if (@FILES == 0) { + print STDERR "No tables to plot?\n"; + exit (1); +} + +&makePlot; +exit (0); + +sub makePlot { + my @starts = (); + my @ends = (); + + my $nfiles = @FILES; + my $nops = @OPS; + + if ($SAVE_GNUPLOT) { + die unless open (PLOT, ">$GP_OUTPUT"); + } + else { + die unless open (PLOT, "|$GNUPLOT_PATH $GNUPLOT_OPTS"); + } + + $preamble = `cat $ProgDir/template.gp`; + print PLOT $preamble; + + print PLOT "set terminal $TERMINAL\n"; + print PLOT "set output \'$OUTPUT\'\n"; + if (defined $SIZE) { + print PLOT "set size $SIZE\n"; + } + + if (defined $TITLE_TEXT) { + print PLOT "set title \"$TITLE_TEXT\"\n"; + } + + if (defined $EXTRA_CMD) { + print PLOT "$EXTRA_CMD\n"; + } + + print PLOT "set key below\n"; + + if ($LAYER) { + for ($i = 0; $i < $nfiles; $i++) { + ($starts [$i], $ends [$i]) = + findFileTimeSpan ($FILES [$i]); + } + } + + print PLOT "plot $PLOT_OPTS \\\n"; + for ($i = 0; $i < $nfiles; $i++) { + my $offset = 0; + my $scale = 1; + if (defined $SCALES[$i]) { + $scale = $SCALES[$i]; + } + + my $file = $FILES [$i]; + + if ($LAYER) { + $offset = $starts [$i] - $starts [0]; + } + + for ($j = 0; $j < @OPS; $j++) { + my $op = $OPS[$j]; + + print PLOT "\t\"< $LOC2GMT_PATH -d $offset $file\" \\\n"; + print PLOT "\t\tusing 2:(\$$op*$scale) \\\n"; + if ($SHOW_KEY) { + print PLOT "\t\ttitle \'$file:$op\'\\\n"; + } + print PLOT "\t\twith lines lw 2"; + if ($i != $nfiles - 1 || $j != $nops - 1) { + print PLOT ",\\\n"; + } + else { + print PLOT "\n"; + } + } + } + print PLOT "\n"; + close PLOT; +} + +# Find the earliest and latest times in a file created by nfsscan, and +# return them as a list. Returns the empty list if anything goes +# wrong. + +sub findFileTimeSpan { + my ($fname) = @_; + my ($smallest, $largest) = (-1, -1); + + if (! open (FF, "<$fname")) { + return (); + } + + while (my $l = <FF>) { + if ($l =~ /^#/) { + next; + } + + @a = split (' ', $l); + + if ($smallest < 0 || $smallest > $a[1]) { + $smallest = $a[1]; + } + if ($largest < 0 || $largest < $a[1]) { + $largest = $a[1]; + } + } + + close FF; + + if ((! defined $smallest) || (! defined $largest)) { + return (); + } + + return ($smallest, $largest); +} + diff --git a/TBBT/trace_init/ns_split.pl b/TBBT/trace_init/ns_split.pl new file mode 100755 index 0000000..b600448 --- /dev/null +++ b/TBBT/trace_init/ns_split.pl @@ -0,0 +1,153 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_split,v 1.4 2003/07/28 14:27:17 ellard Exp $ + +use Getopt::Std; + +$ProgDir = $0; +$ProgDir =~ /(^.*)\//; +$ProgDir = $1; +if (!$ProgDir) { + $ProgDir = "."; +} + +require "$ProgDir/userUtils.pl"; + +@ADD_USERS = (); +@DEL_USERS = (); +@ADD_GROUPS = (); +@DEL_GROUPS = (); +@ADD_CLIENTS = (); +@DEL_CLIENTS = (); + +$Usage =<< "."; + +Usage: $0 [options] [table1.ns [table2.ns ... ]] + +If no table files are specified, then the input is read from stdin. + +Command line options: + +-h Print usage message and exit. + +-c c1[,c2]* Include only activity performed by the specified clients. + +-C c1[,c2]* Exclude activity performed by the specified clients. + +-g g1[,g2]* Include only activity performed by the specified groups. + +-G g1[,g2]* Exclude activity performed by the specified groups. + +-u u1[,u2]* Include only activity performed by the specified users. + +-U u1[,u2]* Exclude activity performed by the specified users. + +. + +$cmdline = "$0 " . join (' ', @ARGV); +$Options = "c:C:g:G:u:U::"; +if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; +exit (1); +} +if (defined $opt_h) { + print $Usage; + exit (0); +} + +if (defined $opt_u) { + @ADD_USERS = &logins2uids ($opt_u); +} +if (defined $opt_U) { + @DEL_USERS = &logins2uids ($opt_U); +} +if (defined $opt_g) { + @ADD_GROUPS = &groups2gids ($opt_g); +} +if (defined $opt_G) { + @DEL_GROUPS = &groups2gids ($opt_G); +} +if (defined $opt_c) { + @ADD_CLIENTS = split (/,/, $opt_c); +} +if (defined $opt_C) { + @DEL_CLIENTS = split (/,/, $opt_C); +} + +print "#cmdline $cmdline\n"; + +while ($l = <>) { + if ($l =~ /^#/) { + print $l; + next; + } + + my ($type, $time, $client, $fh, $euid, $egid, @vals) = split (' ', $l); + + next unless ($type eq 'C'); + + # Something wrong with the input? + + next if (@vals == 0); + + if (saveRecord ($client, $fh, $euid, $egid)) { + print $l; + } +} + +sub saveRecord { + my ($client, $fh, $euid, $egid) = @_; + + if (@ADD_CLIENTS && !grep (/^$client$/, @ADD_CLIENTS)) { + return 0; + } + if (@DEL_CLIENTS && grep (/^$client$/, @DEL_CLIENTS)) { + return 0; + } + + if (@ADD_USERS && !grep (/^$euid$/, @ADD_USERS)) { + return 0; + } + if (@DEL_USERS && grep (/^$euid$/, @DEL_USERS)) { + return 0; + } + + if (@ADD_GROUPS && !grep (/^$egid$/, @ADD_GROUPS)) { + return 0; + } + if (@DEL_GROUPS && grep (/^$egid$/, @DEL_GROUPS)) { + return 0; + } + + return (1); +} + +exit 0; diff --git a/TBBT/trace_init/ns_timeagg.pl b/TBBT/trace_init/ns_timeagg.pl new file mode 100755 index 0000000..9e5e536 --- /dev/null +++ b/TBBT/trace_init/ns_timeagg.pl @@ -0,0 +1,218 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_timeagg,v 1.7 2003/07/28 14:27:17 ellard Exp $ + +use Getopt::Std; + +$ProgDir = $0; +$ProgDir =~ /(^.*)\//; +$ProgDir = $1; +if (!$ProgDir) { + $ProgDir = "."; +} + +require "$ProgDir/common.pl"; + +$UseClient = 0; +$UseFH = 0; +$UseUID = 0; +$UseGID = 0; +$NEW_INTERVAL = 300; +$OMIT_ZEROS = 0; + +$Usage =<< "."; + +Usage: $0 [options] [table1.ns [table2.ns ... ]] + +If no table files are specified, then the input is read from stdin. + +Command line options: + +-h Print usage message and exit. + +-B flags Choose fields to NOT aggregate. The fields are: + C - client host ID + U - Effective user ID + G - Effective group ID + F - File handle + +-R secs Round the start time to the closest multiple of + the given number of seconds. This is useful for + dealing with small amounts of clock drift. For example, + if the trace starts at 12:00:01 instead of 12:00:00, + it is probably convenient to pretend that it started + at 12:00:00. + +-t secs Aggregate over the given number of seconds. The default + is $NEW_INTERVAL. If the current interval does not + divide the new interval, warning messages are printed + and the output might not be meaningful. + + If secs is 0, then the total for the entire table is + computed. + +-Z Omit count lines that have a zero total operation count. +. + +$cmdline = "$0 " . join (' ', @ARGV); +$Options = "B:hR:t:Z"; +if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); +} +if (defined $opt_h) { + print $Usage; + exit (0); +} + +if (defined $opt_t) { + if ($opt_t <= 0) { + $EndTime = 0; + } + $NEW_INTERVAL = $opt_t; +} + +$TIME_ROUNDING = defined $opt_R ? $opt_R : 0; + +if (defined $opt_B) { + $UseClient = ($opt_B =~ /C/); + $UseUID = ($opt_B =~ /U/); + $UseGID = ($opt_B =~ /G/); + $UseFH = ($opt_B =~ /F/); +} + +# We need to let the measurement of time be a little bit inexact, so +# that a small rounding error or truncation doesn't throw everything +# off. + +$TimerInacc = 0.05; + +# Print out the commandline as a comment in the output. + +print "#cmdline $cmdline\n"; + +# It would be nice to make this do the right thing with Latency lines, +# and perhaps file op counts. + +while ($l = <>) { + if ($l =~ /^#C/) { + print $l; + next; + } + + next if ($l =~ /^#/); + + my ($type, $time, $client, $fh, $euid, $egid, @vals) = split (' ', $l); + + next unless ($type eq 'C'); + + # Something wrong with the input? + + next if (@vals == 0); + + if (!defined $StartTime) { + $StartTime = findStartTime ($time, $TIME_ROUNDING); + if ($NEW_INTERVAL > 0) { + $EndTime = $StartTime + $NEW_INTERVAL - $TimerInacc; + } + } + + if ($EndTime > 0 && $time >= $EndTime) { + my $diff = int ($time - $StartTime); + if ($diff > 0) { + if ($NEW_INTERVAL % $diff != 0) { + print STDERR "$0: time interval mismatch "; + print STDERR "(from $diff to $NEW_INTERVAL)\n"; + } + } + + dumpKeys ($StartTime); + + $StartTime += $NEW_INTERVAL; + $EndTime = $StartTime + $NEW_INTERVAL - $TimerInacc; + } + + + # Aggregate across clients, files, users, and groups unless + # specifically asked NOT to. + + $client = $UseClient ? $client : 'u'; + $fh = $UseFH ? $fh : 'u'; + $euid = $UseUID ? $euid : 'u'; + $egid = $UseGID ? $egid : 'u'; + + $key = "$client,$fh,$euid,$egid"; + + if (exists $Totals{$key}) { + my (@tots) = split (' ', $Totals{$key}); + for (my $i = 0; $i < @vals; $i++) { + $tots[$i] += $vals[$i]; + } + $Totals{$key} = join (' ', @tots); + } + else { + $Totals{$key} = join (' ', @vals); + } +} + +if ($EndTime <= 0) { + dumpKeys ($StartTime); +} + +sub dumpKeys { + my ($start) = @_; + my ($i, $k, $ks); + + foreach $k ( keys %Totals ) { + my (@v) = split (' ', $Totals{$k}); + + if ($OMIT_ZEROS && $v[0] == 0) { + next; + } + + $ks = $k; + $ks =~ s/,/\ /g; + + print "C $start $ks"; + + for ($i = 0; $i < @v; $i++) { + print " $v[$i]"; + } + print "\n"; + + for ($i = 0; $i < @v; $i++) { + $v[$i] = 0; + } + $Totals{$k} = join (' ', @v); + } +} + +exit 0; diff --git a/TBBT/trace_init/ns_tsplit.pl b/TBBT/trace_init/ns_tsplit.pl new file mode 100755 index 0000000..e3dd420 --- /dev/null +++ b/TBBT/trace_init/ns_tsplit.pl @@ -0,0 +1,219 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_tsplit,v 1.4 2003/07/26 20:52:04 ellard Exp $ + +use Getopt::Std; +use Time::Local; + +$ProgDir = $0; +$ProgDir =~ /(^.*)\//; +$ProgDir = $1; +if (!$ProgDir) { + $ProgDir = "."; +} + +# Set the default start and end times. + +$DEF_BEGIN_TIME = "20000101:00:00:00"; +$DEF_END_TIME = "20360101:00:00:00"; + +# The range includes everything from the start time up to and less +# than the end time (so if you want one day, it is correct to go +# midnight to midnight, instead of going until 23:59:59). + +$Usage =<< "."; + +Usage: $0 [options] rangeSpec... - [table.ns [table.ns...]] + +Command line options: + +-h Print usage message and exit. + +Any number of time range specifier can be provided. The special +symbol "-" is used to separated the time specifiers from the input +file names. If input is taken from stdin, then the "-" may be +omitted. + +The basic time range specification format is: StartTime-EndTime where +StartTime and EndTime have the form YYMMDD[:HH[:MM[:SS]]]. All +records from the input that have a timestamp greater than or equal to +StartTime and less than EndTime are printed to stdout. + +If StartTime is omitted, $DEF_BEGIN_TIME is used. +If EndTime is omitted, $DEF_END_TIME is used. + +Note that omitting both the StartTime and EndTime results in a rangeSpec +of "-", which is the special symbol that marks the end of the rangeSpecs. +. + +$cmdline = "$0 " . join (' ', @ARGV); +$Options = "h"; +if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); +} +if (defined $opt_h) { + print $Usage; + exit (0); +} + +@StartTimes = (); +@EndTimes = (); + +while ($ts = shift @ARGV) { + last if ($ts eq '-'); + + my ($ts_s, $ts_e) = split (/-/, $ts); + my $s; + + if ($ts_s eq '') { + $ts_s = $DEF_START_TIME; + } + $s = &ts2secs ($ts_s); + if (! defined $s) { + print STDERR "Failed to translate ($ts_s)\n"; + exit (1); + } + push @StartTimes, $s; + + if ($ts_e eq '') { + $ts_e = $DEF_END_TIME; + } + $s = &ts2secs ($ts_e); + if (! defined $s) { + print STDERR "Failed to translate ($ts_e)\n"; + exit (1); + } + + push @EndTimes, $s; +} + +print "#cmdline $cmdline\n"; + +while ($l = <>) { + if ($l =~ /^#/) { + print $l; + next; + } + + my ($type, $time, $client, $fh, $euid, $egid, @vals) = split (' ', $l); + + next unless ($type eq 'C'); + + # Something wrong with the input? + + next if (@vals == 0); + + if (saveRecord ($time)) { + print $l; + } +} + +exit 0; + +sub saveRecord { + my ($t) = @_; + + for (my $i = 0; $i < @StartTimes ; $i++) { + if (($StartTimes[$i] <= $t) && ($t < $EndTimes[$i])) { + return (1); + } + } + + return (0); +} + +# This is ugly! +# +# The basic time specification format is: YYMMDD[:HH[:MM[:SS]]]. +# +# To make a time range, join two time specs with a -. +# +# The data spec is Y2.01K compliant... I'm assuming that all the +# traces processed by this tool will be gathered before 2010. If +# anyone is still gathering NFSv3 traces in 2010, I'm sorry. + +sub ts2secs { + my ($ts) = @_; + my ($hour, $min, $sec) = (0, 0, 0); + + my (@d) = split (/:/, $ts); + + if ($d[0] =~ /([0-9][0-9])([0-2][0-9])([0-3][0-9])/) { + $year = $1 + 100; + $mon = $2 - 1; + $day = $3; + } + else { + return undef; + } + + if (@d > 1) { + if ($d[1] =~ /([0-2][0-9])/) { + $hour = $1; + } + else { + return undef; + } + } + + if (@d > 2) { + if ($d[2] =~ /([0-5][0-9])/) { + $min = $1; + } + else { + return undef; + } + } + + if (@d > 3) { + if ($d[3] =~ /([0-5][0-9])/) { + $sec = $1; + } + else { + return undef; + } + } + + my $s = timelocal ($sec, $min, $hour, $day, $mon, $year, + undef, undef, undef); + + my ($s2, $m2, $h2, $md2, $mon2, $y2, $isdst) = localtime ($s); + if (($s2 != $sec) || $min != $m2 || $hour != $h2 || $day != $md2) { + print "failed 1!\n"; + } + if (($mon != $mon2) || $year != $y2) { + print "failed 2!\n"; + } + + return ($s); +} + diff --git a/TBBT/trace_init/rfs.pl b/TBBT/trace_init/rfs.pl new file mode 100755 index 0000000..c167df7 --- /dev/null +++ b/TBBT/trace_init/rfs.pl @@ -0,0 +1,243 @@ +#!/usr/bin/perl -w + +use Getopt::Std; +$Usage =<< "EOF"; + +Usage: $0 [options] + +This program runs after nfsscan.pl. It outputs the file system hierarchy (RFSFS) as well as +the file system hierarchy map (stored in three files: fh-path-map, noattrdirdiscard, noattrdir-root). + +This perl program do post processing based on this file + +Command line options: + +-h Print Usage message and exit. + +-S The file in file system hierarchy are all of size 0. + Without -S, the files are written to it full length. + + +EOF + +$writebuf = "R" x 8193; +$writeBlockSize = 8192; + +main (); + +sub rfsCreateSymlink() +{ + my ($path, $pathcnt, $sizeHexStr) = @_; + $sizeHexStr = "0x".$sizeHexStr; + + my $dir = $path; + my $name = ''; + my $cmdbuf; + + $dir =~ /(^.*)\/(.*)/; + $dir = $1; + $name = $2; + if (!$dir) {$dir = ".";} + die "name is empty\n" if (!$name); + #print "path($path) dir($dir) name($name)\n"; + + if (! -e $dir) { + $cmdbuf = "mkdir -p $dir"; + system $cmdbuf; + print "RFS: Warning: the directory should be created already: $path\n"; + } else { + die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir)); + } + + my $size = hex($sizeHexStr); + #print "size($sizeHexStr) lp($lp) rem($remSize)\n"; + + my $pathnamebuf = $sizeHexStr; + symlink ($pathnamebuf, $path); + #if (! symlink ($pathnamebuf, $path) ) { + # print STDERR "JIAWU: WARNING: failed to create symlink: $path -> $pathnamebuf\n"; + #} +} + + +sub rfsCreateFile() +{ + my ($path, $pathcnt, $sizeHexStr) = @_; + $sizeHexStr = "0x".$sizeHexStr; + + my $dir = $path; + my $name = ''; + my $cmdbuf; + + $dir =~ /(^.*)\/(.*)/; + $dir = $1; + $name = $2; + if (!$dir) {$dir = ".";} + die "name is empty\n" if (!$name); + #print "path($path) dir($dir) name($name)\n"; + + if (! -e $dir) { + $cmdbuf = "mkdir -p $dir"; + system $cmdbuf; + print "RFS: Warning: the directory should be created already: $path\n"; + } else { + die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir)); + } + + my $size = hex($sizeHexStr); + my $remSize = $size % $writeBlockSize; + my $lp = ($size - $remSize) / $writeBlockSize; + #print "size($sizeHexStr) lp($lp) rem($remSize)\n"; + + open RFSTMPWRITE, ">$path" || die "RFS: can not open file for write"; + + # the eight lines below should not be commented out + if (!defined $opt_S) { + my $i = 0; + for ($i = 0; $i < $lp; $i++) { + syswrite(RFSTMPWRITE, $writebuf, $writeBlockSize); + } + if ($remSize) { + syswrite(RFSTMPWRITE, $writebuf, $remSize); + #print "write ($remSize) byte\n"; + } + close RFSTMPWRITE; + } +} + +# Useful commands: +# sort -n -k a,b -c -u +# +# *** WARNING *** The locale specified by the environment affects sort +# order. Set LC_ALL=C or LC_ALL=POSIX to get the traditional sort order that uses native +# byte values. + +sub parseArgs { + my $Options = "S"; + if (! getopts($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); + } +} + +sub main { + + parseArgs (); + + my $cmdbuf; + + # format of lines in test.fil: + # "F(1) $type(2) $state(3) $fh(4) $path(5) $pathcnt(6) $attrOrig(7-14) $attr() + # attrOrig: ($size(7) $mode(8) $op(9) $atime(10) $mtime(11) $ctime(12) $nlink(13) $ts(14)) + # attrLast: ($size(15) $mode(16) $op(17) $atime(18) $mtime(19) $ctime(20) $nlink(21) $ts(22)) + + # skip comment lines + # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file + + $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0") print $0 }\' test.fil > all.fil'; + print "$cmdbuf\n"; + system $cmdbuf; + + # sort the test.fil according to path name + $cmdbuf = 'export LC_ALL=C; sort -k 5,5 all.fil > all.fil.order; echo $LC_ALL'; + print "$cmdbuf\n"; + system $cmdbuf; + + # keep the interested field only + # 2(D/F) 4(FH) 5(path) 6(count) 15(size_last) 21(nlink_last) 14(ts_s) 22(ts_e) + $cmdbuf = 'gawk \' { print $14, $22, $4, $5, $15, $21}\' all.fil.order > fh-path-map-all'; + print "$cmdbuf\n"; + system $cmdbuf; + + # skip comment lines + # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file + + $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0" && $9 != "create" && $9 != "mkdir" && $9 != "symlink") print $0 }\' test.fil > active.fil'; + print "$cmdbuf\n"; + system $cmdbuf; + + # sort the active.fil according to path name + $cmdbuf = 'export LC_ALL=C; sort -k 5,5 active.fil > active.fil.order; echo $LC_ALL'; + print "$cmdbuf\n"; + system $cmdbuf; + + # keep the interested field only + # 2(D/F) 4(FH) 5(path) 6(count) 7(size) 8(mode) 13(nlink) + $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order > active'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'gawk \' { print $4, $5, $7, $13}\' active.fil.order > fh-path-map'; + print "$cmdbuf\n"; + system $cmdbuf; + + # format output files + $cmdbuf = 'export LC_ALL=C; mv noattrdirdiscard noattrdirdiscard-tmp; sort -u -k 1,1 noattrdirdiscard-tmp > noattrdirdiscard; echo $LC_ALL; rm -f noattrdirdiscard-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + $cmdbuf = 'export LC_ALL=C; mv noattrdir-root noattrdir-root-tmp; sort -u -k 1,1 noattrdir-root-tmp > noattrdir-root; echo $LC_ALL; rm -f noattrdir-root-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + #$cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp missdiscardfh'; + $cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + $cmdbuf = "cut -d ' ' -f '1 2' fh-path-map > fh-path-map-tmp"; + print "$cmdbuf\n"; + system $cmdbuf; + + $cmdbuf = 'cat noattrdirdiscard noattrdir-root fh-path-map-tmp > fh-path-map-play; rm -rf fh-path-map-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + #exit(0); + + # so far, you got the following information + # in active: all files/dirs active + # in noattrdir-root: a set of fhs pointing to RFSNN0 + # in rfsinfo: a set of dir fhs refer to RFSNNxxx(>0) + # a set of file fhs should be discard due to short of information + + # create the active fs + # 1. BASEDIR/RFSNN0 + # 2. BASEDIR/active + $cmdbuf = "mkdir -p RFSFS/RFSNN0"; + system $cmdbuf; + open RFS_ACTIVE, "active" || die "open active failed\n"; + while (<RFS_ACTIVE>) { + chop; + my ($type, $fh, $path, $pathcnt, $sizeHexStr, $mode) = split (' ', $_, 7); + if ($type==2) { + $cmdbuf = "mkdir -p RFSFS$path"; + system $cmdbuf; + }elsif ($type==1){ + &rfsCreateFile("RFSFS$path", $pathcnt, $sizeHexStr); + }elsif ($type==5){ + #print STDERR "SYMLINK: $type fh: $fh path: $path\n"; + &rfsCreateSymlink("RFSFS$path", $pathcnt, $sizeHexStr); + }else { + print STDERR "special file: $type fh: $fh path: $path\n"; + } + + my $line_num=0; + $line_num++; + if ( ($line_num %100)==0 ) { + print STDERR "$line_num\n"; + } + + } + + # create map table: key (fh), value (path/fn) + + # check whether there is fh that is not mapped to any path/fn in the trace + + # simulate a replay of trace + + return; + +} + +1; diff --git a/TBBT/trace_init/rfs.pl.old b/TBBT/trace_init/rfs.pl.old new file mode 100755 index 0000000..550f2c8 --- /dev/null +++ b/TBBT/trace_init/rfs.pl.old @@ -0,0 +1,138 @@ +#!/usr/bin/perl -w + +$Usage =<< "."; + +This is the rfs program + +After run nfsscan, you will get a file-list file in the output + +This perl program do post processing based on this file + + +. + +$writebuf = "R" x 8193; +$writeBlockSize = 8192; + + +main (); + +sub rfsCreateFile() +{ + my ($path, $pathcnt, $sizeHexStr) = @_; + $sizeHexStr = "0x".$sizeHexStr; + + my $dir = $path; + my $name = ''; + my $cmdbuf; + + $dir =~ /(^.*)\/(.*)/; + $dir = $1; + $name = $2; + if (!$dir) {$dir = ".";} + die "name is empty\n" if (!$name); + #print "path($path) dir($dir) name($name)\n"; + + if (! -e $dir) { + $cmdbuf = "mkdir -p $dir"; + system $cmdbuf; + print "RFS: Warning: the directory should be created already: $path\n"; + } else { + die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir)); + } + + my $size = hex($sizeHexStr); + my $remSize = $size % $writeBlockSize; + my $lp = ($size - $remSize) / $writeBlockSize; + #print "size($sizeHexStr) lp($lp) rem($remSize)\n"; + + open RFSTMPWRITE, ">$path" || die "RFS: can not open file for write"; + + my $i = 0; + for ($i = 0; $i < $lp; $i++) { + syswrite(RFSTMPWRITE, $writebuf, $writeBlockSize); + } + if ($remSize) { + syswrite(RFSTMPWRITE, $writebuf, $remSize); + #print "write ($remSize) byte\n"; + } + close RFSTMPWRITE; +} + +# Useful commands: +# sort -n -k a,b -c -u +# +# + +sub main { + + my $cmdbuf; + + # skip comment lines + # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir") skip the file + + $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0" && $9 != "create" && $9 != "mkdir") print $0 }\' test.fil > active.fil'; + print "$cmdbuf\n"; + system $cmdbuf; + + # sort the active.fil according to path_count + $cmdbuf = 'sort -k 5,5 active.fil > active.fil.order'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'sort -n -k 6,6 active.fil > active.fil.order-pathcnt'; + print "$cmdbuf\n"; + system $cmdbuf; + + # keep the interested field only + # 2(D/F) 4(FH) 5(path) 6(count) 7(size) 8(mode) + $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order > active'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'gawk \' { print $4, $5 }\' active.fil.order > fh-path-map'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order-pathcnt > active-pathcnt'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'gawk \' { print $4, $5 }\' active.fil.order-pathcnt > fh-path-map-pathcnt'; + print "$cmdbuf\n"; + system $cmdbuf; + + #exit(0); + + # so far, you got the following information + # in active: all files/dirs active + # in noattrdir-root: a set of fhs pointing to RFSNN0 + # in rfsinfo: a set of dir fhs refer to RFSNNxxx(>0) + # a set of file fhs should be discard due to short of information + + # create the active fs + # 1. BASEDIR/RFSNN0 + # 2. BASEDIR/active + $cmdbuf = "mkdir -p RFSFS/RFSNN0"; + system $cmdbuf; + open RFS_ACTIVE, "active"; + while (<RFS_ACTIVE>) { + chop; + my ($type, $fh, $path, $pathcnt, $sizeHexStr, $mode) = split (' ', $_, 7); + if ($type eq "D") { + $cmdbuf = "mkdir -p RFSFS$path"; + system $cmdbuf; + } + else { + &rfsCreateFile("RFSFS$path", $pathcnt, $sizeHexStr); + } + + } + + # create map table: key (fh), value (path/fn) + + # check whether there is fh that is not mapped to any path/fn in the trace + + # simulate a replay of trace + + return; + +} + +1; diff --git a/TBBT/trace_init/rfs.stub.pl b/TBBT/trace_init/rfs.stub.pl new file mode 100755 index 0000000..a0de62a --- /dev/null +++ b/TBBT/trace_init/rfs.stub.pl @@ -0,0 +1,213 @@ +#!/usr/bin/perl -w + +$Usage =<< "."; + +This is the rfs program + +After run nfsscan, you will get a file-list file in the output + +This perl program do post processing based on this file + + +. + +$writebuf = "R" x 8193; +$writeBlockSize = 8192; + +main (); + +sub rfsCreateSymlink() +{ + my ($path, $pathcnt, $sizeHexStr) = @_; + $sizeHexStr = "0x".$sizeHexStr; + + my $dir = $path; + my $name = ''; + my $cmdbuf; + + $dir =~ /(^.*)\/(.*)/; + $dir = $1; + $name = $2; + if (!$dir) {$dir = ".";} + die "name is empty\n" if (!$name); + #print "path($path) dir($dir) name($name)\n"; + + if (! -e $dir) { + $cmdbuf = "mkdir -p $dir"; + system $cmdbuf; + print "RFS: Warning: the directory should be created already: $path\n"; + } else { + die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir)); + } + + my $size = hex($sizeHexStr); + #print "size($sizeHexStr) lp($lp) rem($remSize)\n"; + + my $pathnamebuf = $sizeHexStr; + symlink ($pathnamebuf, $path); + #if (! symlink ($pathnamebuf, $path) ) { + # print STDERR "JIAWU: WARNING: failed to create symlink: $path -> $pathnamebuf\n"; + #} +} + + +sub rfsCreateFile() +{ + my ($path, $pathcnt, $sizeHexStr) = @_; + $sizeHexStr = "0x".$sizeHexStr; + + my $dir = $path; + my $name = ''; + my $cmdbuf; + + $dir =~ /(^.*)\/(.*)/; + $dir = $1; + $name = $2; + if (!$dir) {$dir = ".";} + die "name is empty\n" if (!$name); + #print "path($path) dir($dir) name($name)\n"; + + if (! -e $dir) { + $cmdbuf = "mkdir -p $dir"; + system $cmdbuf; + print "RFS: Warning: the directory should be created already: $path\n"; + } else { + die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir)); + } + + my $size = hex($sizeHexStr); + my $remSize = $size % $writeBlockSize; + my $lp = ($size - $remSize) / $writeBlockSize; + #print "size($sizeHexStr) lp($lp) rem($remSize)\n"; + + open RFSTMPWRITE, ">$path" || die "RFS: can not open file for write"; + + # the eight lines below should not be commented out + #my $i = 0; + #for ($i = 0; $i < $lp; $i++) { + # syswrite(RFSTMPWRITE, $writebuf, $writeBlockSize); + #} + #if ($remSize) { + # syswrite(RFSTMPWRITE, $writebuf, $remSize); + # #print "write ($remSize) byte\n"; + #} + close RFSTMPWRITE; +} + +# Useful commands: +# sort -n -k a,b -c -u +# +# *** WARNING *** The locale specified by the environment affects sort +# order. Set LC_ALL=C or LC_ALL=POSIX to get the traditional sort order that uses native +# byte values. + +sub main { + + my $cmdbuf; + + # format of lines in test.fil: + # "F(1) $type(2) $state(3) $fh(4) $path(5) $pathcnt(6) $attrOrig(7-14) $attr() + # attrOrig: ($size(7) $mode(8) $op(9) $atime(10) $mtime(11) $ctime(12) $nlink(13) $ts(14)) + # attrLast: ($size(15) $mode(16) $op(17) $atime(18) $mtime(19) $ctime(20) $nlink(21) $ts(22)) + + # skip comment lines + # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file + + $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0") print $0 }\' test.fil > all.fil'; + print "$cmdbuf\n"; + system $cmdbuf; + + # sort the test.fil according to path name + $cmdbuf = 'export LC_ALL=C; sort -k 5,5 all.fil > all.fil.order; echo $LC_ALL'; + print "$cmdbuf\n"; + system $cmdbuf; + + # keep the interested field only + # 2(D/F) 4(FH) 5(path) 6(count) 15(size_last) 21(nlink_last) 14(ts_s) 22(ts_e) + $cmdbuf = 'gawk \' { print $14, $22, $4, $5, $15, $21}\' all.fil.order > fh-path-map-all'; + print "$cmdbuf\n"; + system $cmdbuf; + + # skip comment lines + # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file + + $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0" && $9 != "create" && $9 != "mkdir" && $9 != "symlink") print $0 }\' test.fil > active.fil'; + print "$cmdbuf\n"; + system $cmdbuf; + + # sort the active.fil according to path name + $cmdbuf = 'export LC_ALL=C; sort -k 5,5 active.fil > active.fil.order; echo $LC_ALL'; + print "$cmdbuf\n"; + system $cmdbuf; + + # keep the interested field only + # 2(D/F) 4(FH) 5(path) 6(count) 7(size) 8(mode) 13(nlink) + $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order > active'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'gawk \' { print $4, $5, $7, $13}\' active.fil.order > fh-path-map'; + print "$cmdbuf\n"; + system $cmdbuf; + + # format output files + $cmdbuf = 'export LC_ALL=C; mv noattrdirdiscard noattrdirdiscard-tmp; sort -u -k 1,1 noattrdirdiscard-tmp > noattrdirdiscard; echo $LC_ALL; rm -f noattrdirdiscard-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + $cmdbuf = 'export LC_ALL=C; mv noattrdir-root noattrdir-root-tmp; sort -u -k 1,1 noattrdir-root-tmp > noattrdir-root; echo $LC_ALL; rm -f noattrdir-root-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + #$cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp missdiscardfh'; + $cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + #exit(0); + + # so far, you got the following information + # in active: all files/dirs active + # in noattrdir-root: a set of fhs pointing to RFSNN0 + # in rfsinfo: a set of dir fhs refer to RFSNNxxx(>0) + # a set of file fhs should be discard due to short of information + + # create the active fs + # 1. BASEDIR/RFSNN0 + # 2. BASEDIR/active + $cmdbuf = "mkdir -p RFSFS/RFSNN0"; + system $cmdbuf; + open RFS_ACTIVE, "active" || die "open active failed\n"; + while (<RFS_ACTIVE>) { + chop; + my ($type, $fh, $path, $pathcnt, $sizeHexStr, $mode) = split (' ', $_, 7); + if ($type==2) { + $cmdbuf = "mkdir -p RFSFS$path"; + system $cmdbuf; + }elsif ($type==1){ + &rfsCreateFile("RFSFS$path", $pathcnt, $sizeHexStr); + }elsif ($type==5){ + #print STDERR "SYMLINK: $type fh: $fh path: $path\n"; + &rfsCreateSymlink("RFSFS$path", $pathcnt, $sizeHexStr); + }else { + print STDERR "special file: $type fh: $fh path: $path\n"; + } + + my $line_num=0; + $line_num++; + if ( ($line_num %100)==0 ) { + print STDERR "$line_num\n"; + } + + } + + # create map table: key (fh), value (path/fn) + + # check whether there is fh that is not mapped to any path/fn in the trace + + # simulate a replay of trace + + return; + +} + +1; diff --git a/TBBT/trace_init/test.cnt b/TBBT/trace_init/test.cnt new file mode 100644 index 0000000..e78f658 --- /dev/null +++ b/TBBT/trace_init/test.cnt @@ -0,0 +1,2 @@ +#cmdline nfsscan.pl +#C time client fh euid egid TOTAL INTERESTING read write lookup getattr access create remove diff --git a/TBBT/trace_init/test.fil b/TBBT/trace_init/test.fil new file mode 100755 index 0000000..429a873 --- /dev/null +++ b/TBBT/trace_init/test.fil @@ -0,0 +1 @@ +#cmdline nfsscan.pl diff --git a/TBBT/trace_init/userUtils.pl b/TBBT/trace_init/userUtils.pl new file mode 100755 index 0000000..4019d5d --- /dev/null +++ b/TBBT/trace_init/userUtils.pl @@ -0,0 +1,87 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: userUtils.pl,v 1.5 2003/07/26 20:52:04 ellard Exp $ +# +# utils.pl - deal with details like turning login names into uids. +# + +sub logins2uids { + my ($str) = @_; + + my @uids = (); + my @logins = split (/,/, $str); + + my $j = 0; + for (my $i = 0; $i < @logins; $i++) { + if ($logins[$i] =~ /^[0-9]/) { + $uids[$j++] = $logins[$i]; + } + else { + my ($login, $pw, $uid, $gid) = getpwnam ($logins[$i]); + if (defined $uid) { + $uids[$j++] = $uid; + } + else { + # &&& Warning or error? + print STDERR "WARNING: Unrecognized login ($logins[$i])\n"; + $uids[$j++] = $logins[$i]; + } + } + } + + return (@uids); +} + +sub groups2gids { + my ($str) = @_; + + my @gids = (); + my @groups = split (/,/, $str); + + for (my $i = 0; $i < @groups; $i++) { + if ($groups[$i] =~ /^[0-9]/) { + $gids[$j++] = $groups[$i]; + } + else { + my $gid = getgrnam ($groups[$i]); + if (defined $gid) { + $gids[$j++] = $gid; + } + else { + # &&& Warning or error? + print STDERR "WARNING: Unrecognized group ($logins[$i])\n"; + $gids[$j++] = $groups[$i]; + } + } + } + + return (@gids); +} + +1; diff --git a/TBBT/trace_play/Makefile.org b/TBBT/trace_play/Makefile.org new file mode 100644 index 0000000..d019a0b --- /dev/null +++ b/TBBT/trace_play/Makefile.org @@ -0,0 +1,328 @@ +# +# @(#)Makefile 2.1 97/10/23 +# +# Makefile to build SFS benchmark suite +# +# +# Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation +# All rights reserved. +# Standard Performance Evaluation Corporation (SPEC) +# 6585 Merchant Place, Suite 100 +# Warrenton, VA 20187 +# +# This tape contains benchmarks acquired from several sources who +# understand and agree with SPEC's goal of creating fair and objective +# benchmarks to measure computer performance. +# +# This copyright notice is placed here only to protect SPEC in the +# event the source is misused in any manner that is contrary to the +# spirit, the goals and the intent of SPEC. +# +# The source code is provided to the user or company under the license +# agreement for the SPEC Benchmark Suite for this tape. +# +# +# ***************************************************************** +# * * +# * Copyright 1991,1992 Legato Systems, Inc. * +# * Copyright 1991,1992 Auspex Systems, Inc. * +# * Copyright 1991,1992 Data General Corporation * +# * Copyright 1991,1992 Digital Equipment Corporation * +# * Copyright 1991,1992 Interphase Corporation * +# * Copyright 1991,1992 Sun Microsystems, Inc. * +# * * +# ***************************************************************** + +# +# ----------------------- How to use this makefile -------------------- +# +# The makefile is divided into 10 sections: +# 1) basic information +# 2) default compiler definitions +# 3) vendor specific definitions +# 4) OS feature specific definitions +# 5) final compiler definitions (includes specific vendor and feature defs) +# 6) source file definitions +# 7) object file definitions +# 8) rules and targets for building sfs +# 9) lint, install, clean, misc rules + + +# ---------------------- default compiler definitions ----------------- +# +# For Solaris 2.X (SunOS 5.X) or later +MACHID = solaris2 +CC=cc +OPT = -O +CFLAGS = -v -Xc -D__sparc -D_POSIX_SOURCE +LDFLAGS = +EXTRA_CFLAGS = -DPORTMAP +EXTRA_LDFLAGS = +EXTRA_LINTFLAGS = +EXTRA_LIBS = +RESVPORT_MOUNT= +OSTYPE = -DSVR4 -DSOLARIS2 +INCDIR = -I. +RPCLIB = rpc/librpclib.a +LIBS = -lm -lsocket -lnsl + +# +# ---------------------- OS feature specific definitions ------------------ +# + +# +# A UNIX derivative that only supports the getfsent(3) family of calls +# should have the USEFSTAB variable set. +# A UNIX derivative that supports the getmntent(3) family of calls +# should have the line commented out. +#FSTAB = -DUSEFSTAB + +# SFS uses the setsid(2) system call to form a new proces group +# This prevents 'synchronization' signals from causing parent shell (sh) +# to exit. SETPGRP_CALL allows you to specify the alternative use +# of the setpgrp(2) and which variation you are using. + +# If you are linking with BSD libraries then uncomment the next line. +# SETPGRP_CALL = -DSETPGRP_BSD + +# If you are linking with SYSV type libraries then uncomment the next line. +#SETPGRP_CALL = -DSETPGRP_SYSV + +# if your client is failing the mount even when run as root then you should +# uncomment the RESVPORT_MOUNT to explicitly bind to a reserved port. +# RESVPORT_MOUNT = -DRESVPORT + +# +# ---------------------- final compiler definitions ----------------------- +# + +# DEBUG = -g +CFLAGS = +EXTRA_CFLAGS = + +LDFLAGS = $(OSTYPE) + + +# +# ---------------------- source file definitions ------------------ +# + +MAKEFILE = Makefile + +DOC = sfs.1 README sfs.1 + +SHELLFILES = sfs_mgr sfs_mcr sfs_rc + +V2_HDR = sfs_c_def.h sfs_c_nfs.h + +V3_HDR = sfs_c_def.h sfs_c_nfs.h + +V2_SRC = sfs_2_ops.c sfs_2_vld.c + +V3_SRC = sfs_3_ops.c sfs_3_vld.c + +C_SRC = sfs_c_clk.c sfs_c_dat.c sfs_c_dmp.c sfs_c_man.c \ + sfs_c_pnt.c sfs_c_rnd.c sfs_c_sig.c sfs_c_bio.c \ + sfs_2_xdr.c sfs_3_xdr.c sfs_c_mnt.c sfs_c_chd.c \ + sfs_c_clnt.c + +C_COMMON_SRC = sfs_c_sub.c + +M_HDR = sfs_m_def.h + +M_PRIME_SRC = sfs_m_prm.c + +M_SYNCD_SRC = sfs_m_snc.c + +M_MSG_SRC = sfs_m_msg.c + +M_COMMON_SRC = sfs_m_xdr.c + +# +# ---------------------- object file definitions ------------------- +# + +V2_OBJ = sfs_2_ops.o sfs_2_vld.o + +V3_OBJ = sfs_3_ops.o sfs_3_vld.o + +C_OBJ = sfs_c_clk.o sfs_c_dat.o sfs_c_dmp.o sfs_c_man.o \ + sfs_c_pnt.o sfs_c_rnd.o sfs_c_sig.o sfs_c_bio.o \ + sfs_2_xdr.o sfs_3_xdr.o sfs_c_mnt.o sfs_c_chd.o \ + sfs_c_clnt.o + +C_COMMON_OBJ = sfs_c_sub.o + + +M_PRIME_OBJ = sfs_m_prm.o + +M_SYNCD_OBJ = sfs_m_snc.o + +M_MSG_OBJ = sfs_m_msg.o + +M_COMMON_OBJ = sfs_m_xdr.o + +# +# ---------------------- rules and targets for building sfs --------- +# + +BENCH = /tmp/spec +all: fix_perm install + +compile: fix_perm install + +fix_perm: + chmod 755 $(BENCH)/src + chmod 755 $(BENCH)/src/rpc + chmod 755 $(BENCH)/bin + +sfs: $(V2_HDR) $(M_HDR) $(V2_OBJ) $(C_OBJ) \ + $(C_COMMON_OBJ) $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB) + $(CC) -o $@ $(LDFLAGS) $(V2_OBJ) $(C_OBJ) $(C_COMMON_OBJ) \ + $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB) $(LIBS) \ + $(EXTRA_LIBS) + +sfs3: $(V3_HDR) $(M_HDR) $(V3_OBJ) $(C_OBJ) \ + $(C_COMMON_OBJ) $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB) + $(CC) -o $@ $(LDFLAGS) $(V3_OBJ) $(C_OBJ) $(C_COMMON_OBJ) \ + $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB) $(LIBS) \ + $(EXTRA_LIBS) + +sfs_prime: $(V2_HDR) $(M_HDR) $(M_PRIME_OBJ) \ + $(C_COMMON_OBJ) $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB) + $(CC) -o $@ $(LDFLAGS) $(M_PRIME_OBJ) $(C_COMMON_OBJ) \ + $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB) $(LIBS) \ + $(EXTRA_LIBS) + +sfs_syncd: $(V2_HDR) $(M_HDR) $(M_SYNCD_OBJ) \ + $(C_COMMON_OBJ) $(M_COMMON_OBJ) $(RPCLIB) + $(CC) -o $@ $(LDFLAGS) $(M_SYNCD_OBJ) $(C_COMMON_OBJ) \ + $(M_COMMON_OBJ) $(RPCLIB) $(LIBS) $(EXTRA_LIBS) + +.c.o: + $(CC) -c $(INCDIR) $(CFLAGS) $(EXTRA_CFLAGS) $(DEBUG) \ + $(SETPGRP_CALL) $(FSTAB) $(OSTYPE) $(OPT) \ + $(RESVPORT_MOUNT) $*.c + +${RPCLIB}: FRC + @cd rpc; make MACHID="${MACHID}"\ + CC="${CC}" CFLAGS="${CFLAGS}" \ + LDFLAGS="${LDFLAGS}" EXTRA_CFLAGS="${EXTRA_CFLAGS}" \ + EXTRA_LDFLAGS="${EXTRA_LDFLAGS}" EXTRA_LIBS="${EXTRA_LIBS}" \ + LIBS="${LIBS}" OSTYPE="${OSTYPE}" OPT="${OPT}" \ + EXTRA_LINTFLAGS="${EXTRA_LINTFLAGS}" \ + RESVPORT_MOUNT="${RESVPORT_MOUNT}"\ + librpclib.a + +sfs_3_ops.o: sfs_c_nfs.h sfs_c_def.h sfs_3_ops.c + +sfs_3_vld.o: sfs_c_nfs.h sfs_c_def.h sfs_3_vld.c + +sfs_3_xdr.o: sfs_c_nfs.h sfs_c_def.h sfs_3_xdr.c + +sfs_c_bio.o: sfs_c_nfs.h sfs_c_def.h sfs_c_bio.c + +sfs_c_chd.o: sfs_c_nfs.h sfs_c_def.h sfs_m_def.h sfs_c_chd.c + +sfs_c_clk.o: sfs_c_nfs.h sfs_c_def.h sfs_c_clk.c + +sfs_c_clnt.o: sfs_c_nfs.h sfs_c_def.h sfs_c_clnt.c + +sfs_c_dat.o: sfs_c_def.h sfs_m_def.h sfs_c_nfs.h sfs_c_dat.c + +sfs_c_dmp.o: sfs_c_nfs.h sfs_c_def.h sfs_c_dmp.c + +sfs_c_man.o: sfs_c_nfs.h sfs_c_def.h \ + sfs_m_def.h sfs_c_man.c + +sfs_c_mnt.o: sfs_c_nfs.h sfs_c_def.h sfs_c_mnt.c + +sfs_2_ops.o: sfs_c_nfs.h sfs_c_def.h sfs_2_ops.c + +sfs_c_pnt.o: sfs_c_nfs.h sfs_c_def.h \ + sfs_m_def.h sfs_c_pnt.c + +sfs_c_rnd.o: sfs_c_rnd.c + +sfs_c_sig.o: sfs_c_nfs.h sfs_c_def.h sfs_c_sig.c + +sfs_c_sub.o: sfs_c_nfs.h sfs_c_def.h sfs_c_sub.c + +sfs_2_vld.o: sfs_c_nfs.h sfs_c_def.h sfs_2_vld.c + +sfs_2_xdr.o: sfs_c_nfs.h sfs_c_def.h sfs_2_xdr.c + +sfs_m_msg.o: sfs_c_nfs.h sfs_c_def.h \ + sfs_m_def.h sfs_m_msg.c + +sfs_m_prm.o: sfs_c_nfs.h sfs_c_def.h \ + sfs_m_def.h sfs_m_prm.c + +sfs_m_snc.o: sfs_c_nfs.h sfs_c_def.h \ + sfs_m_def.h sfs_m_snc.c + +sfs_m_xdr.o: sfs_m_def.h sfs_m_xdr.c + +# +# ---------------------- lint, install, clean, misc rules ----------------- +# + +lint: lint_sfs lint_sfs3 lint_prime lint_syncd + +lint_sfs: $(V2_HDR) $(M_HDR) $(V2_SRC) $(C_SRC) \ + $(C_COMMON_SRC) $(M_COMMON_SRC) $(M_MSG_SRC) + lint -Dlint $(INCDIR) $(CFLAGS) $(EXTRA_CFLAGS) $(DEBUG) \ + $(EXTRA_LINTFLAGS) \ + $(SETPGRP_CALL) $(FSTAB) $(OSTYPE) \ + $(RESVPORT_MOUNT) \ + $(V2_SRC) $(C_SRC) $(C_COMMON_SRC) \ + $(M_COMMON_SRC) $(M_MSG_SRC) \ + $(LIBS) $(EXTRA_LIBS) \ + > sfs.lint + +lint_prime: $(V2_HDR) $(M_HDR) $(M_PRIME_SRC) \ + $(C_COMMON_SRC) $(M_COMMON_SRC) $(M_MSG_SRC) + lint -Dlint $(INCDIR) $(CFLAGS) $(EXTRA_CFLAGS) $(DEBUG) \ + $(EXTRA_LINTFLAGS) \ + $(SETPGRP_CALL) $(FSTAB) $(OSTYPE) \ + $(RESVPORT_MOUNT) \ + $(M_PRIME_SRC) $(C_COMMON_SRC) \ + $(M_COMMON_SRC) $(M_MSG_SRC) \ + $(LIBS) $(EXTRA_LIBS) \ + > sfs_prime.lint + +lint_syncd: $(V2_HDR) $(M_HDR) $(M_SYNCD_SRC) \ + $(C_COMMON_SRC) $(M_COMMON_SRC) + lint -Dlint $(INCDIR) $(CFLAGS) $(EXTRA_CFLAGS) $(DEBUG) \ + $(EXTRA_LINTFLAGS) \ + $(SETPGRP_CALL) $(FSTAB) $(OSTYPE) \ + $(RESVPORT_MOUNT) \ + $(M_SYNCD_SRC) $(C_COMMON_SRC) $(M_COMMON_SRC) \ + $(LIBS) $(EXTRA_LIBS) \ + > sfs_syncd.lint + +lint_sfs3: $(V3_HDR) $(M_HDR) $(V3_SRC) $(C_SRC) \ + $(C_COMMON_SRC) $(M_COMMON_SRC) $(M_MSG_SRC) + lint -Dlint $(INCDIR) $(CFLAGS) $(EXTRA_CFLAGS) $(DEBUG) \ + $(EXTRA_LINTFLAGS) \ + $(SETPGRP_CALL) $(FSTAB) $(OSTYPE) \ + $(RESVPORT_MOUNT) \ + $(V3_SRC) $(C_SRC) $(C_COMMON_SRC) \ + $(M_COMMON_SRC) $(M_MSG_SRC) \ + $(LIBS) $(EXTRA_LIBS) \ + > sfs3.lint + +install: sfs sfs3 sfs_syncd sfs_prime + chmod +x sfs_syncd sfs_prime \ + sfs_mgr sfs_mcr sfs_ext_mon \ + sfs_suchown sfs sfs3 + ./sfs_suchown $(RESVPORT_MOUNT) sfs sfs3 + +clean clobber: + rm -f *.o sfs sfs_syncd sfs_prime \ + sfs3 sfs_prime \ + lint.out sfs_*.shr1 core *.lint + cd rpc; make clean + ./sfs_suchown clobber + +FRC: diff --git a/TBBT/trace_play/README b/TBBT/trace_play/README new file mode 100644 index 0000000..e340761 --- /dev/null +++ b/TBBT/trace_play/README @@ -0,0 +1,10 @@ + +The trace player executable is sfs3. For trace player to run, the owner of the +executable has to be root. + +To make the executable: + make sfs3 + +The change owner: + su - + chown root:root sfs3 diff --git a/TBBT/trace_play/README.old b/TBBT/trace_play/README.old new file mode 100644 index 0000000..3f31fff --- /dev/null +++ b/TBBT/trace_play/README.old @@ -0,0 +1,140 @@ +A few notes about the redesign of the file selection algorithm for +SFS 3.0, which may be incorporated into the inline comments in the +next maintenance release. + + +Q1. How is the Poisson Distribution used in SFS? + +A1. The probabilities from a Poisson-derived distribution are used +to select files from the I/O working-set in a non-uniform fashion. + +The load-generators are not Poisson Processes. In particular, their +inter-request times are not exponentially-distributed; they are programmed +to have uniformly distributed think-times, which are only approximately +achieved, due to limitations of the load-generating client and its +operating system. + +Q2. Why is a Poisson Distribution used in SFS, rather than, say a +Zipf distribution? + +A2. The reasoning behind the use of Poisson probabilities in SFS 2.0 +may be lost in the mists of time. Because SFS 2.0 was withdrawn by SPEC +before SFS 3.0 development had proceeded very far, there was considerable +time-pressure to release SFS 3.0 quickly. So we chose to patch the +existing Poisson probability calculations instead of implementing a new +distribution for file accesses. + +There is experimental data showing that a Zipf Distribution is a good +model for file access probabilities. The question of what distribution +to use in SFS 4.0 is open, so various distributions can be considered. + +Q3. Is it true that SFS 3.0 combines multiple Poisson cycles into a +single cycle that is also a Poisson Distribution? + +A3. The combination of the distributions follows the curve of a Poisson +Distribution only if you treat each of the twelve generations of +access-groups as a single outcome. + +Q4. Why did the calculation of "Io_working_set.access_group_cnt" change +between SFS 2.0 and SFS 3.0? + +A4. This calculation determines the number of access-groups in the I/O +working-set. The number of access-groups grows *roughly* in linear +proportion to the number of files, so that no access-group contains more +than 125 files. + +In SFS 2.0, 4 groups are added for every 500 files, but in SFS 3.0 +12 groups are added for every 1500 files, so the number of files per +access-group remains roughly the same. + +The main effect of the change is how the number of groups is rounded. +In SFS 2.0, the number of groups was rounded to a multiple of 4, +with a minimum of 8 groups. In SFS 3.0, the number of groups is +rounded to a multiple of 12 (the value of "generations") so that the +cycle of Poisson probabilities (for all 12 generations of access-groups) +will be completely repeated group_cnt/12 times with no groups left over. + +Q5. Why was the minimum number of access-groups changed from 8 to 12? +Why not, say, 24? + +A5. The choice was somewhat arbitrary. Here is some of the rationale: + +In order to get a complete set of Poisson probabilities, there must be +at least as many access-groups as generations. If we had generations=24 +instead of 12, then some of the group-probabilities would be zero due to +defect #2. (Recall that with 24 groups in SFS 2.0, 13% were inaccessible +due to rounding of the scaled probabilities.) In SFS 2.0, 12 was the +largest number of groups which did not trigger defect #2. Of course, +since we now know how to fix that defect, that wasn't the complete reason. + +Another consideration was that the number of access groups must be rounded +to a multiple of the number of generations. If we had used generations=24 +then the rounding would introduce a more radical change in the number +of groups, especially for large numbers of procs. + +On the other hand, if we had used generations=8 instead of 12, we would +have gotten very little variation in access probabilities, which seemed +against the intent of the benchmark design. + +Q6. Why is lambda being calculated differently in SFS 3.0? + +A6. Lambda is the parameter of the Poisson Distribution which determines +the shape of its probability density function, its mean, and other +characteristics. + +SFS 2.0 set lambda to half the number of groups, which in turn was a +function of the requested ops/sec/proc. That was defect #4, which caused +the shape of the access-distribution to change depending on how many +processes a vendor chose to use -- it would get narrower as the number +of processes was reduced. SFS 3.0 sets lambda based on "generations", +which is a constant. Thus the new distribution has the same shape no +matter how many load-generating processes are used. + +The key to defect #4 was that we *don't* want lambda to vary +with the number of access-groups. We want it to be a constant so that +for a given size fileset the number of files that fit into cache +depends very little on the number of load-generation processes used. + +Q7. Why was lambda set to half the number of groups in SFS 2.0 +instead of something else? + +A7. The SFS 2.0 rationale is not documented, as far as we know. The nice +thing about group_count/2 is that it puts the characteristic "hump" of the +probability density function (the mode) near group_count/2, in the middle +of the array of groups. Probably that was the reason. + +In SFS 3.0, each cycle of probabilities has 12 groups, so lambda=6 +to preserve this feature of the distribution. + +Q8. How is the SFS 3.0 approach differ from always using 12 access-groups +and allowing the number of files per access-group to be more than 125? + +A8. The end-result would have been roughly the same. The reason this +was not done was that SFS sometimes searches within an access-group, for +instance to find the best fit for an operation. The search is a linear +search. We were concerned that if the number of files in a group got +too large, the client could get bogged down searching for the best fit. +The search for the appropriate access-group is a binary search, which +scales better. + +Q9. Why not just limit the benchmark to 25 requested ops/second/process, +so that the number of groups would always be 12 or less? + +A9. Historically SPEC has given server vendors lots of leeway to +have as few processes as they wish, modulo the Uniform Access Rule and +the run-rule requirement for at least 8 processes per network. In the +interest of avoiding a long, drawn-out debate about adding a new run-rule +to arbitrarily limit ops/process, we decided to simply fix the code so +that it would work reasonably well for larger numbers of ops/process. + +Q10. Where did the new variable "cumulative_ratio" come from? + +A10. This variable is used in the loop that calculates Poisson distribution +for I/O working-set accesses. In 2.0, lambda^x and x! were calculated +separately. Both variables got very large and would overflow when 'x' got +into the hundreds. This was defect #3. The Poisson distribution really +only needs the ratio (lambda^x/x!), which is numerically more stable. +So that is what the SFS 3.0 code calculates. + +Since SFS 3.0 never uses values of lambda other than 6.0, the change +has proven to be moot. diff --git a/TBBT/trace_play/agefs b/TBBT/trace_play/agefs new file mode 100755 index 0000000000000000000000000000000000000000..bdebf34bc10dcc1235dc4fb5a3415a95b7d6ecbd GIT binary patch literal 83979 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%0DkCKlZX))p%JLRy7aofn{+!^Hg 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 literal 0 HcmV?d00001 diff --git a/TBBT/trace_play/agefs.log b/TBBT/trace_play/agefs.log new file mode 100644 index 0000000..d3fcb00 --- /dev/null +++ b/TBBT/trace_play/agefs.log @@ -0,0 +1,36 @@ + + +agefs Wed Apr 21 18:28:42 EDT 2004 + + +agefs Wed Apr 21 18:31:44 EDT 2004 +Wed Apr 21 18:31:44 EDT 2004 + + +agefs Wed Apr 21 18:32:06 EDT 2004 +Wed Apr 21 18:32:06 EDT 2004 + + +agefs Wed Apr 21 18:32:58 EDT 2004 +Wed Apr 21 18:32:58 EDT 2004 + + +agefs Wed Apr 21 18:33:15 EDT 2004 +Wed Apr 21 18:33:15 EDT 2004 +Wed Apr 21 23:18:15 EDT 2004 +Wed Apr 21 23:18:15 EDT 2004 +Thu Apr 22 00:08:24 EDT 2004 +Thu Apr 22 00:08:24 EDT 2004 +Thu Apr 22 00:33:15 EDT 2004 +Thu Apr 22 00:33:15 EDT 2004 +Thu Apr 22 00:33:33 EDT 2004 +Thu Apr 22 00:33:33 EDT 2004 +Thu Apr 22 00:35:06 EDT 2004 +Thu Apr 22 00:35:06 EDT 2004 +Thu Apr 22 00:35:16 EDT 2004 +Thu Apr 22 00:35:16 EDT 2004 +Thu Apr 22 00:36:25 EDT 2004 +Thu Apr 22 00:38:36 EDT 2004 +Thu Apr 22 00:39:17 EDT 2004 +Thu Apr 22 00:39:19 EDT 2004 +Thu Apr 22 00:47:43 EDT 2004 diff --git a/TBBT/trace_play/frag_collect b/TBBT/trace_play/frag_collect new file mode 100755 index 0000000..773e158 --- /dev/null +++ b/TBBT/trace_play/frag_collect @@ -0,0 +1,15 @@ +nfs stop +umount $1 +mount $1 +nfs start +cd $1 +echo "find test1 -print >$3.1" +find test1 -print > $3.1 +echo "sed -d s/^/show_inode_info / $3.1" +sed -e "s/^/show_inode_info /" $3.1 >$3.2 +echo "open $2" >$3.3 +cat $3.2 >> $3.3 +echo "debugfs -f $3.3" +debugfs -f $3.3 > $3.4 +#echo "frag_count $3.4 > $3.5" +#frag_count $3.4 > $3.5 diff --git a/TBBT/trace_play/frag_collect_cust b/TBBT/trace_play/frag_collect_cust new file mode 100755 index 0000000..24d9c62 --- /dev/null +++ b/TBBT/trace_play/frag_collect_cust @@ -0,0 +1,14 @@ +#This is to collect info for something other than test1 +#test1 is hard-coded in frag_collect +nfs stop +umount $1 +mount $1 +nfs start +echo "sed -d s/^/show_inode_info / $3" +sed -e "s/^/show_inode_info /" $3 >$3.2 +echo "open $2" >$3.3 +cat $3.2 >> $3.3 +echo "debugfs -f $3.3" +debugfs -f $3.3 > $3.4 +#echo "frag_count $3.4 > $3.5" +#frag_count $3.4 > $3.5 diff --git a/TBBT/trace_play/frag_count b/TBBT/trace_play/frag_count new file mode 100755 index 0000000000000000000000000000000000000000..e1cdae0c203dbc002dfef9101cd677150860f3ae GIT binary patch literal 26012 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 literal 0 HcmV?d00001 diff --git a/TBBT/trace_play/frag_count.c b/TBBT/trace_play/frag_count.c new file mode 100644 index 0000000..a8d8397 --- /dev/null +++ b/TBBT/trace_play/frag_count.c @@ -0,0 +1,311 @@ +#include <stdio.h> +#define RFS_ASSERT(condition) \ + if (!(condition)) { \ + fprintf(stderr, "Assertion failed: line %d, file \"%s\"\n", \ + __LINE__, __FILE__); \ + fflush(stdout); \ + fflush(stderr); \ + exit(-1); \ + } + +#define DISK_BLOCK_SIZE 4096 +#define SUB_DISTANCE +int f() +{} +char buf[8024000]; +main(int argc, char ** argv) +{ + FILE * fp; + int ret; + char * p; + int i; + int frag_num, distance, tfrag_num=0; + int td=0, td_MB=0; + int max_block_num = -1; + int block_num = -1; + int last_block_num; + int tblock_num =0; + int last_block = 0; + int block_range_start, block_range_stop; + int lineno = 0; + int format_version = 1; + char * p1=NULL, *p2=NULL, *p3=NULL; + int size = -1; + int max_block = 0; + FILE * fpout; + char name[1024]; + int file_num = 0; + unsigned long long distancell = 0; + int avg_frag_distance; + int avg_block_distance; +#ifdef SUB_DISTANCE +#define MAX_SUB_DISTANCE_NUM 50 + int sub_distance_index; + int SUB_DISTANCE_NUM = 1; + int SUB_DISTANCE_SIZE = 1000000; + unsigned long long sub_distance[MAX_SUB_DISTANCE_NUM]; + unsigned int sub_frag_num[MAX_SUB_DISTANCE_NUM], sub_block_num[MAX_SUB_DISTANCE_NUM]; + memset (&sub_distance, 0, sizeof(sub_distance)); + memset (&sub_frag_num, 0, sizeof(sub_frag_num)); + memset (&sub_block_num, 0, sizeof(sub_block_num)); + + if (argc == 3) { + SUB_DISTANCE_NUM = atoi(argv[2]); + RFS_ASSERT ((SUB_DISTANCE_NUM >=1) && (SUB_DISTANCE_NUM <= MAX_SUB_DISTANCE_NUM)); + } +#endif + + fp = fopen (argv[1], "r"); + if (!fp) { + printf ("can not opern %s\n", argv[1]); + perror("open"); + exit (0); + } + + + strcpy (name, argv[1]); + strcat (name, ".disk"); + + fpout = fopen(name, "w"); + if (!fpout) { + printf ("can not opern %s\n", name); + perror("open"); + exit (0); + } + + while (fgets(buf, sizeof(buf), fp)) { + lineno++; + if ((lineno%10000)==0) { // || (lineno >630000)) { + fprintf(stderr, "%d lines processed\n", lineno); + } + if (lineno==122165) + f(); + RFS_ASSERT (buf[strlen(buf)-1]=='\n'); + if (buf[0]=='U') { + p = strstr (buf, "Size"); + RFS_ASSERT (p); + if (size != -1) { + printf ("lineno %d size %d\n", lineno, size); + } + RFS_ASSERT (size == -1); + sscanf(p, "Size: %d", &size); + continue; + } + + + /* For now we ignore symbolic links */ + if (!strncmp(buf, "Fast_link_dest", strlen("Fast_link_dest"))) { + f(); + goto ENDLOOP; + } + + if (buf[0]!='B') + continue; + + RFS_ASSERT (!strcmp(buf, "BLOCKS:\n")); + fgets(buf, sizeof(buf), fp); + lineno++; + if (!(buf[strlen(buf)-1]=='\n')) { + printf ("line[%d] %s\n", lineno, buf); + }; + RFS_ASSERT (buf[strlen(buf)-1]=='\n'); + if (!strcmp(buf, "\n")) + goto ENDLOOP; + + + RFS_ASSERT (size >= 0); + RFS_ASSERT (block_num == -1); + RFS_ASSERT (max_block_num == -1); + block_num = 0; /* the block number of block_range_start of current fragment */ + last_block_num = 0; /* the block number of block_range_start of last fragment */ + max_block_num = 0; + if (size >0) { + char line[1024]; + int i; + fgets(line, sizeof(line), fp); + lineno++; + RFS_ASSERT (line[strlen(line)-1]=='\n'); + RFS_ASSERT (strstr(line, "TOTAL: ")); + max_block_num = atoi (line+strlen("TOTAL: ")); + i = ((size+DISK_BLOCK_SIZE-1)/DISK_BLOCK_SIZE); + RFS_ASSERT ((max_block_num >= i) && ((max_block_num*9/i)<10)); + } + tblock_num += max_block_num; + + p = buf; + frag_num = 0; + distance = 0; + last_block = 0; + //printf ("line %d %s", lineno, buf); + while (p && (*p!='\n')) { + if (format_version == 1) { + p1 = strchr (p, ')'); + if (p1) { + p2 = strchr (p, '-'); + p3 = strchr (p, ':'); + RFS_ASSERT (p3); + p3++; + } else { + format_version = 2; + p3 = p; + } + } else + p3 = p; + +#define checkit + /* single block range */ + if ((p2==NULL) || p2>p1) { +#ifdef checkit + char * pt2, a; + pt2 = strchr(p3, ' '); + if (!pt2) { + pt2 = strchr(p3, '\n'); + a = '\n'; + } else + a = ' '; + RFS_ASSERT (pt2); + RFS_ASSERT (pt2!=p3); + *pt2 = 0; + block_range_start = atoi (p3); + *pt2 = a ; +#else + sscanf(p3, "%d", &block_range_start); +#endif + block_range_stop = block_range_start; + } else { +#ifdef checkit + char * pt, * pt2, a; + pt = strchr(p3, '-'); + RFS_ASSERT (pt); + *pt = 0; + block_range_start = atoi (p3); + *pt = '-'; + pt2 = strchr(pt+1, ','); + if (!pt2) { + pt2 = strchr(pt+1, '\n'); + a = '\n'; + } else + a = ','; + RFS_ASSERT (pt2); + *pt2 = 0; + block_range_stop = atoi (pt+1); + *pt2 = a ; +#else + sscanf(p3, "%d-%d", &block_range_start, &block_range_stop); +#endif + } + + RFS_ASSERT (block_range_start >0); + RFS_ASSERT (block_range_stop >= block_range_start); + + block_num += (block_range_stop - block_range_start+1); + + if (block_range_start != (last_block+1)) { + frag_num ++; + +#ifdef SUB_DISTANCE + sub_distance_index = (block_num-1) * SUB_DISTANCE_NUM/max_block_num; + sub_block_num[sub_distance_index] += block_num - last_block_num; + last_block_num = block_num; + //printf ("block_num %d SUB_DISTANCE_NUM %d max_block_num %d index %d\n", block_num, SUB_DISTANCE_NUM, +//max_block_num, sub_distance_index); + RFS_ASSERT ((sub_distance_index>=0) && (sub_distance_index<SUB_DISTANCE_NUM)); +#endif + if (last_block!=0) { + if (block_range_start > last_block+1) { + distance += block_range_start - (last_block+1); +#ifdef SUB_DISTANCE + sub_distance[sub_distance_index] += block_range_start - (last_block+1); +#endif + } else { + distance += (last_block+1)-block_range_start; +#ifdef SUB_DISTANCE + sub_distance[sub_distance_index] += (last_block+1)-block_range_start ; +#endif + } + + if (distance >= 1000000000) { + printf ("line[%d] %s, block_range_start %d last_block %d\n", lineno, buf, block_range_start, last_block); + RFS_ASSERT (0); + } + fprintf(fpout, "%d %d\n", last_block, block_range_start); + }; + //printf ("range_start %d last_block %d distance %d\n", + //block_range_start, last_block, distance); +#ifdef SUB_DISTANCE + sub_frag_num[sub_distance_index] ++; +#endif + } + + last_block = block_range_stop; + if (last_block > max_block) + max_block = last_block; + if (p1) + p = strchr (p3, '('); + else { + p = strchr (p3, ' '); + p++; + } + } + //printf ("FRAG_NUM %d DISTANCE %d\n", frag_num, distance); + tfrag_num += frag_num; + distancell += distance; +ENDLOOP: + file_num ++; + size = -1; + block_num = -1; + max_block_num = -1; +/* + td += distance; + if (td > 1000000) { + td_MB += td/1000000; + td = td%1000000; + RFS_ASSERT (td_MB < 1000000000); + } +*/ + } + fclose (fpout); + fclose (fp); + + if (tfrag_num != file_num) { + RFS_ASSERT ((distancell /(tfrag_num-file_num)) < 1000000000); + RFS_ASSERT ((distancell /(tblock_num-file_num)) < 1000000000); + avg_frag_distance = distancell/(tfrag_num-file_num); + avg_block_distance = distancell/(tblock_num-file_num); + } else { + avg_frag_distance =0; + avg_block_distance =0; + } + RFS_ASSERT ((distancell /1000000) < 1000000000); + td_MB = distancell/1000000; + td = distancell%1000000; + +#ifdef SUB_DISTANCE + for (i=0; i<SUB_DISTANCE_NUM; i++) { + printf("sub[%d] block_num %d frag_num %d distance %d\n", i, sub_block_num[i], sub_frag_num[i], sub_distance[i]/1000000); + } +#endif +/* + distancell = td_MB; + distancell *=1000000; + distancell +=td; + distancell /= (tfrag_num-file_num); + if (distancell > 1000000000) { + printf ("error 4\n"); + exit(-1); + } + avg_frag_distance = distancell; + + distancell = td_MB; + distancell *=1000000; + distancell +=td; + distancell /= (tblock_num-file_num); + if (distancell > 1000000000) { + printf ("error 4\n"); + exit(-1); + } + avg_block_distance = distancell; +*/ + + printf("****total FRAG_NUM %d td_MB %d td %d tblock_num %d max_blockno %d file_num %d avg_frag_distance %d avg_block_distance %d\n", tfrag_num, td_MB, td, tblock_num, max_block, file_num, avg_frag_distance, avg_block_distance); +} diff --git a/TBBT/trace_play/fragment_collect b/TBBT/trace_play/fragment_collect new file mode 100755 index 0000000..9e5b8c9 --- /dev/null +++ b/TBBT/trace_play/fragment_collect @@ -0,0 +1,43 @@ +# Usage: fragment_collect dev mount_dir test_path output_file +# +# On linux system, this script collects the fragmentation status of a +# particular directory. The objects under this directory SHOULD NOT be +# used as mount point. If they do, please umount them first, but there +# is no danger even if you forget. The "output_file" is the one that I +# need, please gzip it and send it back to nzhu@cs.sunysb.edu +# +# The program needs to be run as ROOT, but it is SAFE and KEEPS PRIVACY. +# The pathname information on your system is completely filtered out +# from the final output. This program ONLY READ and DOES NOT WRITE any +# files EXCEPT "output_file*" +# +# Parameters: +# dev :the device of the directory to be measured +# mount_dir :the the mount point of the device +# test_path :the relative path of the directory under mount_dir +# output_file :where the result is stored. +# +# Example: fragment_collect /dev/hda7 /home ningning /tmp/home-ningning.frag +# +# This gives the fragmentation information (saved in "/tmp/home-ningning.frag") +# of the file system objects under the directory of "/home/ningning" on the +# disk partition of "/dev/hda7" mounted to "/home". +# +#nfs stop +#umount $2 +#mount $2 +#nfs start +cd $2 +echo "find $3 -print >$4.1" +find $3 -print > $4.1 +echo "sed -d s/^/show_inode_info / $4.1" +sed -e "s/^/show_inode_info /" $4.1 >$4.2 +echo "open $1" >$4.3 +cat $4.2 >> $4.3 +echo "debugfs -f $4.3" +debugfs -f $4.3 > $4.4 +grep -v debugfs $4.4 > $4 +#rm $4.1 +#rm $4.2 +#rm $4.3 +#rm $4.4 diff --git a/TBBT/trace_play/generate_xmgr b/TBBT/trace_play/generate_xmgr new file mode 100755 index 0000000000000000000000000000000000000000..a44ed3393dc5a21d850301df1506f676c35a7b2c GIT binary patch literal 26083 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%0A3-<nNr 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 literal 0 HcmV?d00001 diff --git a/TBBT/trace_play/generate_xmgr.c b/TBBT/trace_play/generate_xmgr.c new file mode 100644 index 0000000..354db18 --- /dev/null +++ b/TBBT/trace_play/generate_xmgr.c @@ -0,0 +1,226 @@ +#include <stdio.h> +#include "rfs_assert.h" + +#define EVEN_CHUNK_SIZE_MAX 2 +#define CHUNK_NUM_MAX 17 +#define FS_SIZE_MB_MAX 81 +#define STAGE_NUM_MAX 65 +typedef struct { + int flag; + int frag_num; + int avg_frag_distance; + int avg_block_distance; +} result_t; +result_t result[EVEN_CHUNK_SIZE_MAX][CHUNK_NUM_MAX][FS_SIZE_MB_MAX][STAGE_NUM_MAX]; + +int range_check (int i, int min, int max) +{ + if (i<min) + return 0; + if (i>max) + return 0; + return 1; +} + +int file_ratio, active_ratio, chunk_num, fs_size_MB, even_chunk_size, stage_num; +#define SEQ_NUM 11 +int frag_num[SEQ_NUM], td_MB[SEQ_NUM], td[SEQ_NUM], tblock_num[SEQ_NUM], file_num; +main(int argc, char ** argv) +{ + char line[1024]; + FILE * fp; + unsigned long long distance; + unsigned long long tll; + int avg_frag_distance = 0; + int max_blockno=0, avg_block_distance=0; + char * p; + int sequence[SEQ_NUM]={1,2,5,10,20,30,50,100,200,250,500}; + int i; + char fhmap[1024]; + result_t * resultp= NULL; + + + memset (result, 0, sizeof(result)); + + fp = fopen (argv[1], "r"); + if (!fp) { + perror("open"); + exit(-1); + } + i=-1; + while (fgets(line, sizeof(line), fp)) { + if (feof(fp)) + break; + if (strstr(line, "==>")) { + RFS_ASSERT (resultp == NULL); + + p = strrchr (line, '/'); + if (p==NULL) + p=line; + p = strchr (p, '_'); + if (p==NULL) { + printf("error 2\n"); + exit(-1); + } + sscanf (p, "_%d_%d_%d_%d_%d_%d_%s.5 <==\n", &even_chunk_size, &file_ratio, &active_ratio, &chunk_num, &fs_size_MB, &stage_num, fhmap); + i = chunk_num; + RFS_ASSERT (range_check (even_chunk_size, 0, EVEN_CHUNK_SIZE_MAX-1)); + RFS_ASSERT (range_check (chunk_num, 0, CHUNK_NUM_MAX-1)); + RFS_ASSERT (range_check (fs_size_MB/100, 0, FS_SIZE_MB_MAX-1)); + RFS_ASSERT (range_check (stage_num, 0, STAGE_NUM_MAX-1)); + resultp = & (result[even_chunk_size][chunk_num][fs_size_MB/100][stage_num]); + RFS_ASSERT (resultp->flag == 0); + } + if (strstr(line, "****")) { + + RFS_ASSERT (resultp); + + sscanf(line, "****total FRAG_NUM %d td_MB %d td %d tblock_num %d max_blockno %d file_num %d avg_frag_distance %d avg_block_distance %d", &frag_num[0], &td_MB[0], &td[0], &tblock_num[0], &max_blockno, &file_num, &avg_frag_distance, &avg_block_distance); + sscanf(line, "****total FRAG_NUM %d td_MB %d td %d tblock_num %d max_blockno %d file_num %d avg_frag_distance %d avg_block_distance %d", &(resultp->frag_num), &td_MB[0], &td[0], &tblock_num[0], &max_blockno, &file_num, &(resultp->avg_frag_distance), &(resultp->avg_block_distance)); + resultp ->flag = 1; + + //printf("%d %d %d %d %d %d\n", chunk_num, frag_num[0], avg_frag_distance, td_MB[0], tblock_num[0], avg_block_distance); + + resultp = NULL; + } + } + + print_xmgr ("avg_block_distance"); + print_xmgr ("frag_num"); + print_xmgr ("frag_size"); + print_xmgr ("avg_frag_distance"); + print_xmgr_chunk_stage ("avg_block_distance"); +} + +int print_y(FILE * fp, result_t * resultp, char * y_axis) +{ + if (!strcmp (y_axis, "avg_block_distance")) { + fprintf (fp, "%d ", resultp->avg_block_distance); + } else if (!strcmp (y_axis, "frag_num")) { + fprintf (fp, "%d ", resultp->frag_num); + } else if (!strcmp (y_axis, "frag_size")) { + fprintf (fp, "%f ", (float)tblock_num[0]/(float)resultp->frag_num); + } else if (!strcmp (y_axis, "avg_frag_distance")) { + fprintf (fp, "%d ", resultp->avg_frag_distance); + } else { + RFS_ASSERT (0); + } +} + /* statistics for avg_block_distance */ +int print_xmgr (char * y_axis) +{ + int flag1, flag2; + result_t * resultp; + char name[1024]; + + FILE * fp = NULL; + + for (even_chunk_size = 0; even_chunk_size < EVEN_CHUNK_SIZE_MAX; even_chunk_size ++) { + for (fs_size_MB = 0; fs_size_MB < FS_SIZE_MB_MAX; fs_size_MB ++) { + + sprintf (name, "xmgr.%d_%d.stage.%s", even_chunk_size, fs_size_MB*100, y_axis); + fp = NULL; + flag1 = 1; + for (stage_num = 0; stage_num < STAGE_NUM_MAX; stage_num++) { + flag2 = 1; + for (chunk_num = 0; chunk_num < CHUNK_NUM_MAX; chunk_num++) { + resultp = &(result[even_chunk_size][chunk_num][fs_size_MB][stage_num]); + if (resultp->flag) { + if (flag1) { + printf ("*** even %d fs_size_MB %d X: stage_num Y: %s Lines: different chunk_num ***\n", even_chunk_size, fs_size_MB*100, y_axis); + flag1 = 0; + if (!fp) { + fp = fopen (name, "w"); + if (!fp) + RFS_ASSERT (0); + } + } + if (flag2) { + //fprintf (fp, "%d ", file_num/stage_num); + fprintf (fp, "%f ", 1/((float)stage_num)); + flag2 = 0; + } + print_y (fp, resultp, y_axis); + } + } + if (!flag2) + fprintf (fp, "\n"); + } + if (fp) + fclose (fp); + } + } + + for (even_chunk_size = 0; even_chunk_size < EVEN_CHUNK_SIZE_MAX; even_chunk_size ++) { + for (fs_size_MB = 0; fs_size_MB < FS_SIZE_MB_MAX; fs_size_MB ++) { + sprintf (name, "xmgr.%d_%d.chunk.%s", even_chunk_size, fs_size_MB*100, y_axis); + fp = NULL; + flag1 = 1; + for (chunk_num = 0; chunk_num < CHUNK_NUM_MAX; chunk_num++) { + flag2 = 1; + for (stage_num = 0; stage_num < STAGE_NUM_MAX; stage_num++) { + resultp = &(result[even_chunk_size][chunk_num][fs_size_MB][stage_num]); + if (resultp->flag) { + if (flag1) { + printf ("*** even %d fs_size_MB %d X: chunk_num Y: %s Lines: different stage_num ***\n", even_chunk_size, fs_size_MB*100, y_axis); + flag1 = 0; + if (!fp) { + fp = fopen (name, "w"); + if (!fp) + RFS_ASSERT (0); + } + } + if (flag2) { + fprintf (fp, "%d ", chunk_num); + flag2 = 0; + } + print_y (fp, resultp, y_axis); + } + } + if (!flag2) + fprintf (fp, "\n"); + } + if (fp) + fclose (fp); + } + } +} +int print_xmgr_chunk_stage (char * y_axis) +{ + int flag1, flag2; + result_t * resultp; + char name[1024]; + + FILE * fp = NULL; + + for (even_chunk_size = 0; even_chunk_size < EVEN_CHUNK_SIZE_MAX; even_chunk_size ++) { + for (fs_size_MB = 0; fs_size_MB < FS_SIZE_MB_MAX; fs_size_MB ++) { + + sprintf (name, "xmgr.%d_%d.stage_chunk.%s", even_chunk_size, fs_size_MB*100, y_axis); + fp = NULL; + flag1 = 1; + for (stage_num = STAGE_NUM_MAX-1; stage_num>=0; stage_num--) { + flag2 = 1; + for (chunk_num = 0; chunk_num < CHUNK_NUM_MAX; chunk_num++) { + resultp = &(result[even_chunk_size][chunk_num][fs_size_MB][stage_num]); + if (resultp->flag) { + if (flag1) { + printf ("*** even %d fs_size_MB %d X: stage_num Y: %s Lines: different chunk_num ***\n", even_chunk_size, fs_size_MB*100, y_axis); + flag1 = 0; + if (!fp) { + fp = fopen (name, "w"); + if (!fp) + RFS_ASSERT (0); + } + } + fprintf (fp, "%f ", chunk_num/((float)stage_num)); + print_y (fp, resultp, y_axis); + fprintf (fp, "\n"); + } + } + } + if (fp) + fclose (fp); + } + } +} diff --git a/TBBT/trace_play/generate_xmgr.c.old b/TBBT/trace_play/generate_xmgr.c.old new file mode 100644 index 0000000..4a12ce2 --- /dev/null +++ b/TBBT/trace_play/generate_xmgr.c.old @@ -0,0 +1,70 @@ +#include <stdio.h> +main(int argc, char ** argv) +{ + char line[1024]; + FILE * fp; + unsigned long long distance; + unsigned long long tll; + int avg_frag_distance = 0; +#define SEQ_NUM 11 + int frag_num[SEQ_NUM], td_MB[SEQ_NUM], td[SEQ_NUM], tblock_num[SEQ_NUM], file_num; + int max_blockno=0, avg_block_distance=0; + char * p; + int sequence[SEQ_NUM]={1,2,5,10,20,30,50,100,200,250,500}; + int i; + int file_ratio, active_ratio, chunk_num, fs_size_MB; + char fhmap[1024]; + + fp = fopen (argv[1], "r"); + if (!fp) { + perror("open"); + exit(-1); + } + i=-1; + while (fgets(line, sizeof(line), fp)) { + if (feof(fp)) + break; + if (strstr(line, "==>")) { + if (i!=-1) { + printf("error 1\n"); + exit(-1); + } + p = strrchr (line, '/'); + if (p==NULL) + p=line; + p = strchr (p, '_'); + if (p==NULL) { + printf("error 2\n"); + exit(-1); + } + sscanf (p, "_%d_%d_%d_%d_%s.5 <==\n", &file_ratio, &active_ratio, &chunk_num, &fs_size_MB, fhmap); + i = chunk_num; + } + if (strstr(line, "****")) { + if (i==-1) { + printf("error 3\n"); + exit(-1); + } + sscanf(line, "****total FRAG_NUM %d td_MB %d td %d tblock_num %d max_blockno %d file_num %d avg_frag_distance %d avg_block_distance %d", &frag_num[0], &td_MB[0], &td[0], &tblock_num[0], &max_blockno, &file_num, &avg_frag_distance, &avg_block_distance); + +#ifdef checkit + distance = td_MB[0]; + distance *=1000000; + distance +=td[0]; + tll = frag_num[0]; + distance /= frag_num[0]; + + if (distance > 1000000000) { + printf ("error 4\n"); + exit(-1); + } + avg_frag_distance = distance; + printf("%d %d %d %d %d\n", chunk_num, frag_num[0], avg_frag_distance, td_MB[0], tblock_num[0]); +#else + printf("%d %d %d %d %d %d\n", chunk_num, frag_num[0], avg_frag_distance, td_MB[0], tblock_num[0], avg_block_distance); +#endif + i = -1; + } + } +} + diff --git a/TBBT/trace_play/generic_hash.c b/TBBT/trace_play/generic_hash.c new file mode 100644 index 0000000..1d73a34 --- /dev/null +++ b/TBBT/trace_play/generic_hash.c @@ -0,0 +1,230 @@ +/* generic_hash.c: Generic hash function that hashes on a maximum of three keys + * $Id: generic_hash.c,v 1.5 2002/08/21 22:08:01 ningning Exp $ + * Changes: + * + * $Log: generic_hash.c,v $ + * Revision 1.5 2002/08/21 22:08:01 ningning + * *** empty log message *** + * + */ + +#include <stdio.h> +#include "generic_hash.h" + +/* create an entry for a block */ +void generic_insert(char * key1, unsigned int key1_size, unsigned int key3, struct generic_entry **htable, int hsize) +{ +unsigned int index; +struct generic_entry *p,*q; + +unsigned int * tmp =(unsigned int *)key1; +unsigned int i; +index = 0; +for (i=0; i<(key1_size/4); i++) + index += *(tmp+i); +index = index%hsize; +{ +//printf ("generic_insert index %d key %s\n", index, key1); +} +p = q = htable[index]; +while (p) { + if ((!memcmp(p->key1, key1, key1_size)) + && (p->key3==key3)) { + return; + } + q = p; + p = p->next; +} +if (!p) { + p = (struct generic_entry *)malloc(sizeof(struct generic_entry)); + memcpy(p->key1, key1, key1_size); + p->key3 = key3; + p->next = NULL; +} +if (!q) + htable[index] = p; +else + q->next = p; +return; +} + +void generic_delete(char * key1, unsigned int key1_size, unsigned int key3, struct generic_entry **htable, int hsize) +{ +unsigned int index; +int found; +struct generic_entry *p,*q; + +unsigned int * tmp =(unsigned int *)key1; +unsigned int i; +index = 0; +for (i=0; i<(key1_size/4); i++) + index += *(tmp+i); +index = index%hsize; + +p = q = htable[index]; +found = 0; +while (p) { + if ((!memcmp(p->key1, key1, key1_size)) + && (p->key3==key3)) { + found = 1; + break; + } + q = p; + p = p->next; +} +RFS_ASSERT(found==1); +if (p==htable[index]) + htable[index] = p->next; +else + q->next = p->next; + +/* free hash entry */ + +free(p); + +return; +} + +struct generic_entry *generic_lookup(char * key1, unsigned int key1_size, unsigned int key3, struct generic_entry **htable, int hsize) +{ +unsigned int index; +struct generic_entry *p; + +unsigned int * tmp =(unsigned int *)key1; +unsigned int i; +index = 0; +for (i=0; i<(key1_size/4); i++) + index += *(tmp+i); +index = index%hsize; +{ +//printf ("generic_lookup index %d key %s\n", index, key1); +} + +p = htable[index]; +while (p) { + if (!memcmp(p->key1, key1, key1_size)) + { + return p; + } + p = p->next; +} +/*RFS_ASSERT(0);*/ +return NULL; +} + +struct generic_entry *generic_lookup1(unsigned int key1, unsigned int key2, unsigned int key3, struct generic_entry **htable, int hsize) +{ +unsigned int index; +struct generic_entry *p; + +index = ((unsigned int)key1)%hsize; +p = htable[index]; +while (p) { + if (p->key1==key1) + return p; + p = p->next; +} +/*RFS_ASSERT(0);*/ +return NULL; +} + +struct generic_entry *generic_lookup2(unsigned int key1, unsigned int key2, unsigned int key3, struct generic_entry **htable, int hsize) +{ +unsigned int index; +struct generic_entry *p; + +index = (key1 + key2)%hsize; +p = htable[index]; +while (p) { + if ( (p->key1==key1) + && (p->key2==key2) + ) + return p; + p = p->next; +} +/*RFS_ASSERT(0);*/ +return NULL; +} + +void generic_display(struct generic_entry **htable, int hsize, int numkeys) +{ +int i; +struct generic_entry *p; +int counter = 0; + +for (i=0;i<hsize;i++) { + p = htable[i]; + if (p) + printf ("htable[%d]", i); + while (p) { + if (numkeys==1) + printf("%d, ", p->key1); + else if (numkeys==2) + printf("(%d,%x), ", p->key1,p->key2); + else + printf("(%d,%d,%d), ", p->key1,p->key2,p->key3); + p = p->next; + counter++; + if ((counter%4)==3) + printf("\n"); + } +} +printf("\ncounter was %d\n",counter); +} + +/* create an entry for a block */ +void generic_long_insert4(unsigned int key1, unsigned int key2, unsigned int key3, unsigned int key4, unsigned int key5, unsigned int key6, struct generic_long_entry **htable, int hsize) +{ +int index; +struct generic_long_entry *p,*q; + +index = (key1 + key2 + key3 + key4)%hsize; +p = q = htable[index]; +while (p) { + if ((p->key1==key1) + && (p->key2==key2) + && (p->key3==key3) + && (p->key4==key4) + && (p->key5==key5) + && (p->key6==key6)) { + return; + } + q = p; + p = p->next; +} +if (!p) { + p = (struct generic_long_entry *)malloc(sizeof(struct generic_long_entry)); + p->key1 = key1; + p->key2 = key2; + p->key3 = key3; + p->key4 = key4; + p->key5 = key5; + p->key6 = key6; + p->next = NULL; +} +if (!q) + htable[index] = p; +else + q->next = p; +return; +} + +struct generic_entry *generic_long_lookup4(unsigned int key1, unsigned int key2, unsigned int key3, unsigned int key4, struct generic_long_entry **htable, int hsize) +{ +int index; +struct generic_long_entry *p; + +index = (key1 + key2 + key3 + key4 )%hsize; +p = htable[index]; +while (p) { + if ( (p->key1==key1) + && (p->key2==key2) + && (p->key3==key3) + && (p->key4==key4) + ) + return p; + p = p->next; +} +/*RFS_ASSERT(0);*/ +return NULL; +} diff --git a/TBBT/trace_play/generic_hash.h b/TBBT/trace_play/generic_hash.h new file mode 100644 index 0000000..cf48bd4 --- /dev/null +++ b/TBBT/trace_play/generic_hash.h @@ -0,0 +1,24 @@ +#include "rfs_c_def.h" +struct generic_entry { + char key1[MAX_TRACE_FH_SIZE]; + int key2; + int key3; + struct generic_entry *next; +}; + +struct generic_long_entry { + int key1; + int key2; + int key3; + int key4; + int key5; + int key6; + struct generic_long_entry *next; +}; + +void generic_insert(char * key1, unsigned int key2, unsigned int key3, struct generic_entry **htable, int hsize); +void generic_delete(char * key1, unsigned int key2, unsigned int key3, struct generic_entry **htable, int hsize); +struct generic_entry *generic_lookup(char * key1, unsigned int key2, unsigned int key3, struct generic_entry **htable, int hsize); + +void generic_display(struct generic_entry **htable, int hsize, int numkeys); + diff --git a/TBBT/trace_play/hash.h b/TBBT/trace_play/hash.h new file mode 100644 index 0000000..808a95f --- /dev/null +++ b/TBBT/trace_play/hash.h @@ -0,0 +1,87 @@ +#ifndef RFS_HASH_H +#define RFS_HASH_H + +/* + * hash.h: + * header file for routines to manipulate hash_tble lookup, + * insert and delete. + * + * $Id: hash.h,v 1.1 2002/03/11 20:25:52 ningning Exp $ + * + * Changes: + * $Log: hash.h,v $ + * Revision 1.1 2002/03/11 20:25:52 ningning + * hash function for file handle map completely implemented. + * + */ + +#include <sys/stat.h> +#include <sys/time.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include "../common/rfs_defines.h" +#include "rfsu_defines.h" + +typedef struct rfsfh rfskey_t; + +struct hash_ent_struct { +#ifdef HASH_DOUBLE_SAFE + struct timeval time; +#endif + rfskey_t key; + int entry; + struct hash_ent_struct * next; +}; + +typedef struct hash_ent_struct hash_ent_t; + +typedef struct { + int size; + int used_entry; /* number of hash table entrie which contains valid data */ + int valid_num; /* number of valid data including those on the chain */ + int (*hash_func) (rfskey_t * key, int size); + /* it's not C++ class, so we have to get size from outside */ + hash_ent_t * buf; +} hash_tbl_t; + +inline int hash_func1 (rfskey_t * key, int size); +inline int blk_hash_func (blkkey_t * key, int size); +int hash_tbl_init (hash_tbl_t * hash_tbl); +int hash_tbl_insert (hash_tbl_t * hash_tbl, rfskey_t * key, int entry, int mode); +int hash_tbl_delete (hash_tbl_t * hash_tbl, rfskey_t * key); +int hash_tbl_lookup (hash_tbl_t * hash_tbl, rfskey_t * key); + +#ifdef ULFS + +struct blk_hash_ent_struct { +#ifdef HASH_DOUBLE_SAFE + struct timeval time; +#endif + blkkey_t key; + int entry; + struct blk_hash_ent_struct * next; +}; + +typedef struct blk_hash_ent_struct blk_hash_ent_t; + +typedef struct { + int size; + int keysize; + int entsize; + int quiet_flag; + int used_entry; /* number of hash table entrie which contains valid data */ + int valid_num; /* number of valid data including those on the chain */ + int (*hash_func) (blkkey_t * key, int size); + /* it's not C++ class, so we have to get size from outside */ + blk_hash_ent_t * buf; +} blk_hash_tbl_t; + +int blk_hash_tbl_init (blk_hash_tbl_t * hash_tbl); +int blk_hash_tbl_insert (blk_hash_tbl_t * hash_tbl, blkkey_t * key, int entry, int mode); +//int blk_hash_tbl_delete (blk_hash_tbl_t * hash_tbl, blkkey_t * key); +int blk_hash_tbl_lookup (blk_hash_tbl_t * hash_tbl, blkkey_t * key); + +#endif + +#endif diff --git a/TBBT/trace_play/init_holder.c b/TBBT/trace_play/init_holder.c new file mode 100644 index 0000000..f12d683 --- /dev/null +++ b/TBBT/trace_play/init_holder.c @@ -0,0 +1,57 @@ +#include <stdio.h> +#include <sys/stat.h> +#include <fcntl.h> +#define FILE_NUM 25000 +#define DIR_NUM 1 +#define FILE_SIZE 40960 + +int print_usage() +{ + printf("init_holder DIR_NUM HOLDER_NUM HOLDER_SIZE testdir\n"); +} + +main4(int argc, char ** argv) +{ + int i, j; + char name[256]; + char cmd[1024]; + char buf[FILE_SIZE]; + int fd, ret; + char testdir[1024]; + + if (argc!=5) { + print_usage(); + exit (-1); + } + if (DIR_NUM !=1) { + for (i=0; i<DIR_NUM; i++) { + memset (name, 0, sizeof(name)); + sprintf(name, "%s/%d", testdir, i); + ret = mkdir (name, S_IRWXU); + if (ret == -1) { + perror(name); + exit(-1); + } + } + } + + for (j=0; j<DIR_NUM; j++) { + for (i=0; i<FILE_NUM/DIR_NUM; i++) { + if (DIR_NUM == 1) + sprintf(name, "%s/%d", testdir, i); + else + sprintf(name, "%s/%d/%d", testdir, j, i); + fd = open (name, O_CREAT|O_WRONLY); + if (fd == -1) { + perror(name); + exit(-1); + } + ret = write (fd, buf, FILE_SIZE ); + close (fd); + if (ret!=FILE_SIZE) { + printf("try to write %d , get %d\n", SIZE, ret); + exit (-1); + } + } + } +} diff --git a/TBBT/trace_play/nfsd_nfsfh_cust.h b/TBBT/trace_play/nfsd_nfsfh_cust.h new file mode 100644 index 0000000..942dc92 --- /dev/null +++ b/TBBT/trace_play/nfsd_nfsfh_cust.h @@ -0,0 +1,114 @@ +#define __u32 unsigned int +#define u32 unsigned int +#define __u16 unsigned short +#define u16 unsigned short +#define __u64 unsigned long long +#define u64 unsigned long long +#define __u8 unsigned char +#define u8 unsigned char +#define __s64 signed long long + +struct nfs_fhbase_old { + __u32 fb_dcookie; /* dentry cookie - always 0xfeebbaca */ + __u32 fb_ino; /* our inode number */ + __u32 fb_dirino; /* dir inode number, 0 for directories */ + __u32 fb_dev; /* our device */ + __u32 fb_xdev; + __u32 fb_xino; + __u32 fb_generation; +}; + +/* + * This is the new flexible, extensible style NFSv2/v3 file handle. + * by Neil Brown <neilb@cse.unsw.edu.au> - March 2000 + * + * The file handle is seens as a list of 4byte words. + * The first word contains a version number (1) and four descriptor bytes + * that tell how the remaining 3 variable length fields should be handled. + * These three bytes are auth_type, fsid_type and fileid_type. + * + * All 4byte values are in host-byte-order. + * + * The auth_type field specifies how the filehandle can be authenticated + * This might allow a file to be confirmed to be in a writable part of a + * filetree without checking the path from it upto the root. + * Current values: + * 0 - No authentication. fb_auth is 0 bytes long + * Possible future values: + * 1 - 4 bytes taken from MD5 hash of the remainer of the file handle + * prefixed by a secret and with the important export flags. + * + * The fsid_type identifies how the filesystem (or export point) is + * encoded. + * Current values: + * 0 - 4 byte device id (ms-2-bytes major, ls-2-bytes minor), 4byte inode number + * NOTE: we cannot use the kdev_t device id value, because kdev_t.h + * says we mustn't. We must break it up and reassemble. + * Possible future encodings: + * 1 - 4 byte user specified identifier + * + * The fileid_type identified how the file within the filesystem is encoded. + * This is (will be) passed to, and set by, the underlying filesystem if it supports + * filehandle operations. The filesystem must not use the value '0' or '0xff' and may + * only use the values 1 and 2 as defined below: + * Current values: + * 0 - The root, or export point, of the filesystem. fb_fileid is 0 bytes. + * 1 - 32bit inode number, 32 bit generation number. + * 2 - 32bit inode number, 32 bit generation number, 32 bit parent directory inode number. + * + */ +struct nfs_fhbase_new { + __u8 fb_version; /* == 1, even => nfs_fhbase_old */ + __u8 fb_auth_type; + __u8 fb_fsid_type; + __u8 fb_fileid_type; +#ifndef RFS + __u32 fb_auth[1]; +/* __u32 fb_fsid[0]; floating */ +/* __u32 fb_fileid[0]; floating */ +#else + __u16 fb_dev_major; + __u16 fb_dev_minor; + __u32 fb_dev_ino; + __u32 fb_ino; + __u32 fb_generation; + __u32 fb_dirino; +#endif + +}; + +// RFS struct knfsd_fh { +struct knfs_fh { // RFS + unsigned int fh_size; /* significant for NFSv3. + * Points to the current size while building + * a new file handle + */ + union { + struct nfs_fhbase_old fh_old; + __u32 fh_pad[NFS3_FHSIZE/4]; + struct nfs_fhbase_new fh_new; + } fh_base; +}; + +#define ofh_dcookie fh_base.fh_old.fb_dcookie +#define ofh_ino fh_base.fh_old.fb_ino +#define ofh_dirino fh_base.fh_old.fb_dirino +#define ofh_dev fh_base.fh_old.fb_dev +#define ofh_xdev fh_base.fh_old.fb_xdev +#define ofh_xino fh_base.fh_old.fb_xino +#define ofh_generation fh_base.fh_old.fb_generation + +#define fh_version fh_base.fh_new.fb_version +#define fh_fsid_type fh_base.fh_new.fb_fsid_type +#define fh_auth_type fh_base.fh_new.fb_auth_type +#define fh_fileid_type fh_base.fh_new.fb_fileid_type +#define fh_auth fh_base.fh_new.fb_auth + +// RFS +#define fh_dev_major fh_base.fh_new.fb_dev_major +#define fh_dev_minor fh_base.fh_new.fb_dev_minor +#define fh_dev_ino fh_base.fh_new.fb_dev_ino +#define fh_ino fh_base.fh_new.fb_ino +#define fh_generation fh_base.fh_new.fb_generation +#define fh_dirino fh_base.fh_new.fb_dirino + diff --git a/TBBT/trace_play/profile.c b/TBBT/trace_play/profile.c new file mode 100755 index 0000000..23548c1 --- /dev/null +++ b/TBBT/trace_play/profile.c @@ -0,0 +1,215 @@ +/* + * profile.c + * + * $Id: profile.c,v 1.1 2002/08/21 22:08:01 ningning Exp $ + * Changes: + * $Log: profile.c,v $ + * Revision 1.1 2002/08/21 22:08:01 ningning + * *** empty log message *** + * + */ + +#include <sys/time.h> +#include <sys/sem.h> +#include <unistd.h> +#include <stdio.h> +#include "profile.h" +#include "rfs_assert.h" +//#include "rfs_c_def.h" +extern FILE * profile_fp; + +static struct timezone tz; +inline void calculate_interval + (struct timeval * ts, struct timeval * te, struct timeval * interval) +{ + if (te->tv_usec < ts->tv_usec) { + if (te->tv_sec <= ts->tv_sec) { + printf ("te->tv_sec %d ts->tv_sec %d\n", te->tv_sec, ts->tv_sec); + printf ("te->tv_usec %d ts->tv_usec %d\n", te->tv_usec, ts->tv_usec); + } + RFS_ASSERT (te->tv_sec > ts->tv_sec); + te->tv_usec += 1000000; + te->tv_sec -= 1; + } + + interval->tv_sec = te->tv_sec - ts->tv_sec; + interval->tv_usec = te->tv_usec - ts->tv_usec; + if (interval->tv_usec > 1000000) { + if (interval->tv_usec > 2000000) { + printf ("interval->tv_sec %d interval->tv_usec %d \n", interval->tv_sec, interval->tv_usec); + printf ("ts->tv_sec %d ts->tv_usec %d \n", ts->tv_sec, ts->tv_usec); + printf ("te->tv_sec %d te->tv_usec %d \n", te->tv_sec, te->tv_usec); + } + /* Sometimes it can happend that te->tv_usec > 1000000 */ + interval->tv_sec += 1; + interval->tv_usec -= 1000000; + RFS_ASSERT (interval->tv_usec < 1000000); + } +} + +inline void normalize_profile (int pos, struct timeval * time) +{ + if (!(time->tv_sec >=0 && time->tv_usec >=0 && time->tv_usec < 2000000)) { + printf ("pos %d tv_sec %d tv_usec %d\n", pos, time->tv_sec, time->tv_usec); + }; + RFS_ASSERT (time->tv_sec >=0 && time->tv_usec >=0 && time->tv_usec < 2000000); + while (time->tv_usec >= 1000000) { + time->tv_usec -= 1000000; + time->tv_sec += 1; + } +} + +inline void start_real_profile (profile_t * profile) +{ + start_profile(profile); +} + +inline void end_real_profile (profile_t * profile) +{ + end_profile(profile); +} + +inline void start_profile (profile_t * profile) +{ +/* + if (strlen(profile->about) < 3) { + printf ("total_profile address: %x %x\n", &total_profile, profile); + } +*/ + + gettimeofday(&(profile->ts), &tz); + normalize_profile (1, &(profile->ts)); +} + +inline void end_profile (profile_t * profile) +{ + struct timeval te, teorg; + struct timeval * ts; + struct timeval * in; + struct timeval oldin; + +/* + //printf ("end_profile %s\n", profile->about); + + if (strlen(profile->about) < 3) { + printf ("total_profile address: %x %x\n", &total_profile, profile); + } +*/ + + oldin = profile->in; + in = &(profile->in); + ts = &(profile->ts); + + gettimeofday(&te, &tz); + normalize_profile (2, &te); + teorg = te; + + RFS_ASSERT (te.tv_sec >= ts->tv_sec); + RFS_ASSERT (te.tv_usec >=0 && ts->tv_usec >=0); + while (te.tv_usec < ts->tv_usec) { + if (te.tv_sec <= ts->tv_sec) { + printf ("%s ts.tv_sec %d ts.tv_usec %d\n", profile->about, ts->tv_sec, ts->tv_usec); + printf ("teorg.tv_sec %d teorg.tv_usec %d\n", teorg.tv_sec, teorg.tv_usec); + } + RFS_ASSERT (te.tv_sec > ts->tv_sec); + te.tv_usec += 1000000; + te.tv_sec -= 1; + } + + if (!(in->tv_sec >=0 && in->tv_usec >=0)) { + printf ("in->tv_sec %d, in->tv_usec %d\n", in->tv_sec, in->tv_usec); + }; + RFS_ASSERT (in->tv_sec >=0 && in->tv_usec >=0); + in->tv_sec += te.tv_sec - ts->tv_sec; + in->tv_usec += te.tv_usec - ts->tv_usec; + normalize_profile (3, in); + + if (!(in->tv_sec >=0 && in->tv_sec <864000)) { + printf (" ts.tv_sec %d ts.tv_usec %d\n", ts->tv_sec, ts->tv_usec); + printf (" te.tv_sec %d te.tv_usec %d\n", te.tv_sec, te.tv_usec); + printf (" in.tv_sec %d in.tv_usec %d\n", in->tv_sec, in->tv_usec); + printf (" oldin.tv_sec %d oldin.tv_usec %d\n", oldin.tv_sec, oldin.tv_usec); + } + + profile->num ++; + profile->ts = teorg; +} + +inline void init_profile (char * string, profile_t * profile) +{ + RFS_ASSERT (strlen(string) < sizeof (profile->about)); + memset (profile, 0, sizeof(profile_t)); + strcpy (profile->about, string); +} + +inline int calculate_avg_timeval (struct timeval * in, int num) +{ + unsigned long long i; + int ret; + + if (in->tv_sec < 2000) { + return ((in->tv_sec*1000000+in->tv_usec)/num ); + } else { + i = ((unsigned long long)in->tv_sec)*1000000 + in->tv_usec; + i/= num; + RFS_ASSERT (i<2000000000); + ret = i; + return ret; + } +} + +inline void print_profile (char * string, profile_t * profile) +{ + struct timeval * ts = &(profile->ts); + struct timeval * in = &(profile->in); + +/* + if (strcmp (string, profile->about)) { + printf ("print_profile string %s about %s\n", string, profile->about); + } +*/ + + //RFS_ASSERT (!strcmp (string, profile->about)); + if (in->tv_usec<0 || in->tv_usec>1000000) { + printf ("%s in->tv_usec %d, in->tv_sec %d num %d\n", profile->about, in->tv_usec, in->tv_sec, profile->num); + } + + RFS_ASSERT (in->tv_usec>=0 && in->tv_usec<1000000); + + if (!(in->tv_sec >=0 && in->tv_sec <864000)) { + printf ("%s ts.tv_sec %d ts.tv_usec %d\n", profile->about, ts->tv_sec, ts->tv_usec); + printf ("%s in.tv_sec %d in.tv_usec %d\n", profile->about, in->tv_sec, in->tv_usec); + } + RFS_ASSERT (in->tv_sec >=0 && in->tv_sec <864000); /* it's about 10 days */ + + if (profile->num == 0) { + printf("... %40s %3d.%06d num %d \n", profile->about, in->tv_sec, in->tv_usec, + profile->num); + + if (profile_fp) { + fprintf(profile_fp, "... %40s %3d.%06d num %d \n", profile->about, in->tv_sec, + in->tv_usec, profile->num ); + //perror("print_profile_1"); + } + } else { + + int avg = calculate_avg_timeval (in, profile->num); + printf("... %40s %3d.%06d num %d avg %d \n", profile->about, in->tv_sec, in->tv_usec, + profile->num, avg); + + if (profile_fp) { + fprintf(profile_fp, "... %40s %3d.%06d num %d avg %d \n", profile->about, in->tv_sec, + in->tv_usec, profile->num, avg); + } + +/* + printf("... %40s %3d.%06d num %d avg %d \n", string, in->tv_sec, in->tv_usec, + profile->num, (in->tv_sec*1000000+in->tv_usec)/profile->num ); + + if (profile_fp) { + fprintf(profile_fp, "... %40s %3d.%06d num %d avg %d \n", string, in->tv_sec, + in->tv_usec, profile->num, (in->tv_sec*1000000+in->tv_usec)/profile->num ); + } +*/ + } +} diff --git a/TBBT/trace_play/profile.h b/TBBT/trace_play/profile.h new file mode 100755 index 0000000..606652e --- /dev/null +++ b/TBBT/trace_play/profile.h @@ -0,0 +1,27 @@ +#ifndef PROFILE_H +#define PROFILE_H + +/* profile.h: header file for profiling routine + * $Id: profile.h,v 1.2 2002/08/21 22:11:40 ningning Exp $ + * Changes: + * + * $Log: profile.h,v $ + * Revision 1.2 2002/08/21 22:11:40 ningning + * *** empty log message *** + * + */ + +#include <sys/time.h> +#define MAX_PROFILE_ABOUT_LEN 128 +typedef struct { + struct timeval ts; + struct timeval in; + int num; + char about[MAX_PROFILE_ABOUT_LEN]; +} profile_t; + +extern inline void start_profile (profile_t * profile); +extern inline void end_profile (profile_t * profile); +extern inline void print_profile (char * string, profile_t * profile); +extern inline void init_profile (char * string, profile_t * profile); +#endif diff --git a/TBBT/trace_play/rfs_3_ops.c b/TBBT/trace_play/rfs_3_ops.c new file mode 100644 index 0000000..f91bf05 --- /dev/null +++ b/TBBT/trace_play/rfs_3_ops.c @@ -0,0 +1,827 @@ +#ifndef lint +static char sfs_3_opsSid[] = "@(#)sfs_3_ops.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> +#include "sfs_c_def.h" +#include "rfs_c_def.h" + +#define TRY_SETARG_FAST +#define TRY_SETRES_FAST +extern fh_map_t * lookup_fh (char * trace_fh); +extern void print_result(void); + +char * lookup_fhandle(char * fhandle) +{ + fh_map_t * fh; + fh = lookup_fh(fhandle); + RFS_ASSERT (fh); + return ((char *)&fh->play_fh); +} + + +#define setarg_fhandle(fhp) \ + fh_map_t * fh; \ + t = strstr (line, "fh"); \ + RFS_ASSERT (t); \ + t += 3; \ + fh = lookup_fh(t); \ + RFS_ASSERT (fh); \ + (void) memmove((char *)fhp, &(fh->play_fh), \ + sizeof (nfs_fh3)); \ + t+=TRACE_FH_SIZE+1; + //t = strchr (t, ' '); t++; \ + +void setarg_getattr (int index, char * line, GETATTR3args * args) +{ + char * t; +#ifdef TRY_SETARG_FAST + memcpy (&args->object, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); +#else + setarg_fhandle(&args->object); +#endif +} + +struct ladtime * adjust_time (struct timeval tm, int * sec, int * usec) +{ + struct ladtime trace_pkt_time; + static struct ladtime trace_arg_time; + struct timeval curtmp; + struct ladtime cur; + + /* not sure whether sec ==0 means anything special, do not adjust the timestamp for this */ + if (*sec ==0) { + RFS_ASSERT (0); + RFS_ASSERT (*usec == 0); + trace_arg_time.sec = 0; + trace_arg_time.usec = 0; + return (&trace_arg_time); + } + trace_pkt_time.sec = tm.tv_sec; + trace_pkt_time.usec = tm.tv_usec; + + trace_arg_time.sec = *sec; + trace_arg_time.usec = *usec; + + gettimeofday(&curtmp, NULL); + cur.sec = curtmp.tv_sec; + cur.usec = curtmp.tv_usec; + + //fprintf (stderr, "trace_pkt_time %d.%d trace_arg_time %d.%d cur %d.%d\n", trace_pkt_time.sec, trace_pkt_time.usec, trace_arg_time.sec, trace_arg_time.usec, cur.sec, cur.usec); + + ADDTIME (trace_arg_time, cur); + //fprintf(stderr, "after add, %d.%d\n", trace_arg_time.sec, trace_arg_time.usec); + RFS_ASSERT (LARGERTIME (trace_arg_time, trace_pkt_time)); + SUBTIME (trace_arg_time, trace_pkt_time); + return (&trace_arg_time); +} + +char * parse_create_mode(char * t, createmode3 * mode) +{ + *mode = UNCHECKED; + return (t+2); + /* anyway, we can not get concrete result from the trace, just chose this mode */ + RFS_ASSERT (0); +} + +char * parse_sattr3(char * t, sattr3 * args, sattrguard3 * guard, int index) +{ + int i,j; + struct ladtime * tm; + + /* set the default value of SETATTR3args->*/ + args->mode.set_it = FALSE; + args->uid.set_it = FALSE; + args->gid.set_it = FALSE; + args->size.set_it = FALSE; + args->atime.set_it = FALSE; + args->mtime.set_it = FALSE; + + //fprintf(stderr, "parse_sattr: line %s\n", t); + while (1) { + if (!strncmp (t, "mode", 4)) { + t+=5; + sscanf(t, "%x", &i); + args->mode.set_it = TRUE; + args->mode.mode = i; // (uint32_t) 0666; + } else if (!strncmp (t, "ctime", 5)) { + RFS_ASSERT (guard); + t+=6; + RFS_ASSERT (strncmp(t, "SERVER", 6)); + sscanf (t, "%d.%d", &i, &j); + tm = adjust_time (dep_tab[index].timestamp, &i, &j); +#ifndef IGNORE_SETATTR_CTIME + guard->check = TRUE; +#endif + guard->obj_ctime.seconds = tm->sec; + guard->obj_ctime.nseconds = tm->usec*1000; + } else if (!strncmp (t, "atime", 5)) { + t+=6; + if (!strncmp(t, "SERVER", 6)) { + args->atime.set_it = SET_TO_SERVER_TIME; + } else { + args->atime.set_it = SET_TO_CLIENT_TIME; + sscanf (t, "%d.%d", &i, &j); + if (i==0) { + RFS_ASSERT (j==0); + args->atime.atime.seconds = 0; + args->atime.atime.nseconds = 0; + } else { + tm = adjust_time (dep_tab[index].timestamp, &i, &j); + args->atime.atime.seconds = tm->sec; + args->atime.atime.nseconds = tm->usec * 1000; + } + } + } else if (!strncmp (t, "mtime", 5)) { + t+=6; + if (!strncmp(t, "SERVER", 6)) { + args->mtime.set_it = SET_TO_SERVER_TIME; + } else { + args->mtime.set_it = SET_TO_CLIENT_TIME; + sscanf (t, "%d.%d", &i, &j); + if (i==0) { + RFS_ASSERT (j==0); + args->mtime.mtime.seconds = 0; + args->mtime.mtime.nseconds = 0; + } else { + tm = adjust_time (dep_tab[index].timestamp, &i, &j); + args->mtime.set_it = TRUE; + args->mtime.mtime.seconds = tm->sec; + args->mtime.mtime.nseconds = tm->usec * 1000; + } + } + } else if (!strncmp (t, "size", 4)) { + t+=5; + sscanf(t, "%x", &i); + args->size.set_it = TRUE; + args->size.size._p._u = (uint32_t) 0; + args->size.size._p._l = (uint32_t) i; + } else if (!strncmp (t, "gid", 3)) { + t+=4; + sscanf(t, "%x", &i); + args->gid.set_it = TRUE; +#ifdef TAKE_CARE_SETATTR_GID + args->gid.gid = i; +#else + args->gid.gid = 513; +#endif + } else if ( !strncmp (t, "uid", 3)) { + t+=4; + sscanf(t, "%x", &i); + args->uid.set_it = TRUE; +#ifdef TAKE_CARE_SETATTR_UID + args->uid.uid = i; +#else + args->uid.uid = 513; +#endif + } else if (!strncmp (t, "con", 3)) { + break; + } else if (!strncmp (t, "sdata", 5)) { + break; + } else { + fprintf(stderr, "parse_sattr t: %s\n", t); + RFS_ASSERT (0); + } + + while (*t!=' ') + t++; + t++; + } + return t; +} + +char * parse_name (char * t, char * buf) +{ + int i; + if (!strncmp(t, "fn2", 3)) + t+=4; + else if (!strncmp(t, "fn", 2)) + t+=3; + else if (!strncmp(t, "name2", 5)) + t+=6; + else if (!strncmp(t, "name", 4)) + t+=5; + else if (!strncmp(t, "sdata", 5)) + t+=6; + else { + fprintf(stderr, "%s\n", t); + RFS_ASSERT (0); + } + + RFS_ASSERT (*t=='"'); + t++; + i = 0; + while (*t!='"') + buf[i++] = *t++; // ??? name buffer? + RFS_ASSERT ((*t)=='"'); + buf[i] = 0; + return (t+2); +} + +char * parse_access_mode (char * line, int * mode) +{ + *mode = ACCESS3_READ; + return line+2; + /* anyway the information in the trace is not enough, so we just make up something */ +} + +char * parse_stable_mode (char * line, stable_how * mode) +{ + switch (*line) { + case 'U': *mode = UNSTABLE; + break; + case 'F': *mode = FILE_SYNC; + break; + case 'D': *mode = DATA_SYNC; + break; + default: + RFS_ASSERT (0); + } + return line +2; +} + + +void setarg_setattr (int index, char * line, SETATTR3args * args) +{ + char * t; + int i, j; + +#ifdef TRY_SETARG_FAST + memcpy (&args->object, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle(&args->object); +#endif + + args->guard.check = FALSE; + t = parse_sattr3 (t, &(args->new_attributes), &(args->guard), index); +} + +void setarg_lookup (int index, char * line, LOOKUP3args * args, char * Cur_filename) +{ + char * t; +#ifdef TRY_SETARG_FAST + memcpy (&args->what.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle(&args->what.dir) +#endif + t = parse_name (t, Cur_filename); + args->what.name = Cur_filename; +} + +void setarg_access (int index, char * line, ACCESS3args * args) +{ + char * t; + +#ifdef TRY_SETARG_FAST + memcpy (&args->object, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle(&args->object); +#endif + + parse_access_mode (t, &args->access); //ACCESS3_MODIFY; // ??? the actual parameter can be different +} + +void setarg_readlink (int index, char * line, READLINK3args * args) +{ + char * t; +#ifdef TRY_SETARG_FAST + memcpy (&args->symlink, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle (&args->symlink); +#endif +} + +void setarg_read (int index, char * line, READ3args * args, char * buf) +{ + char * t; + int i; +#ifdef TRY_SETARG_FAST + memcpy (&args->file, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle (&args->file); +#endif + + if (line[TRACE_VERSION_POS]=='3') { + t = strstr (t, "off"); + RFS_ASSERT (t); + t+=4; + } else { + t = strstr (t, "offset"); + RFS_ASSERT (t); + t+=7; + } + sscanf (t, "%x", &i); + + RFS_ASSERT (i>=0 && i<0x7FFFFFFF) + args->offset._p._u = 0; + args->offset._p._l = i; +#ifdef SEQUEN_READ + if (index>=memory_trace_index.head) + args->offset._p._l = (index-memory_trace_index.head)*4096; +#endif + t = strstr (t, "count"); + RFS_ASSERT (t); + t+=6; + sscanf (t, "%x", &i); + + RFS_ASSERT (i <= 32768); + if (i > NFS_MAXDATA) { + read_data_owe += (i-NFS_MAXDATA); + read_data_adjust_times ++; + if (read_data_owe > 1073741824) { + read_data_owe -= 1073741824; + read_data_owe_GB ++; + } + + //printf ("adjust read count from %d to %d\n", i, NFS_MAXDATA); + i = NFS_MAXDATA; + } + read_data_total += i; + if (read_data_total > 1073741824) { + read_data_total -= 1073741824; + read_data_total_GB ++; + } + + args->count = i; +} + +void setarg_write (int index, char * line, WRITE3args * args, char * buf) +{ + char * t; + int i; +#ifdef TRY_SETARG_FAST + memcpy (&args->file, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle (&args->file); +#endif + + //fprintf (stderr, "process write: %s\n", line); + if (line[TRACE_VERSION_POS]=='3') { + t = strstr (t, "off"); + RFS_ASSERT (t); + t+=4; + } else { + RFS_ASSERT (line[TRACE_VERSION_POS]=='2'); + t = strstr (t, "offset"); + RFS_ASSERT (t); + t+=7; + } + + sscanf (t, "%x", &i); + RFS_ASSERT (i>=0 && i<0x7FFFFFFF) + args->offset._p._u = 0; + args->offset._p._l = i; + + t = strstr (t, "count"); + RFS_ASSERT (t); + t+=6; + sscanf (t, "%x", &i); + RFS_ASSERT (i <= 32768); + if (i > NFS_MAXDATA) { + write_data_owe += (i-NFS_MAXDATA); + if (write_data_owe > 1073741824) { + write_data_owe -= 1073741824; + write_data_owe_GB ++; + } + write_data_adjust_times ++; + //printf ("adjust write count from %d to %d\n", i, NFS_MAXDATA); + i = NFS_MAXDATA; + } + write_data_total += i; + if (write_data_total > 1073741824) { + write_data_total -= 1073741824; + write_data_total_GB ++; + } + + RFS_ASSERT (i < MAX_BUF1_SIZE-128); /* 128 is some random safe number to add */ + args->count = i; + + if (line[TRACE_VERSION_POS]==3) { + t = strstr (t, "stable"); + RFS_ASSERT (t); + t+=7; + parse_stable_mode(t, &args->stable); /* *t can be F, U, etc */ + } else + args->stable = UNSTABLE; + args->data.data_len = args->count; + args->data.data_val = buf; +} + +void setarg_create (int index, char * line, CREATE3args * args, char * Cur_filename) +{ + char * t; + //fprintf(stderr, "process create %s\n", line); +#ifdef TRY_SETARG_FAST + memcpy (&args->where.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle (&args->where.dir); +#endif + t = parse_name (t, Cur_filename); + args->where.name = Cur_filename; + if (line[TRACE_VERSION_POS]=='3') { + RFS_ASSERT (!strncmp(t, "how", 3)); + t+=4; + t = parse_create_mode (t, &args->how.mode); + } else + args->how.mode = UNCHECKED; + t = parse_sattr3 (t, &(args->how.createhow3_u.obj_attributes), NULL, index); +} + +void setarg_mkdir (int index, char * line, MKDIR3args * args, char * Cur_filename) +{ + char * t; +#ifdef TRY_SETARG_FAST + memcpy (&args->where.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle (&args->where.dir); +#endif + t = parse_name (t, Cur_filename); + args->where.name = Cur_filename; + t = parse_sattr3 (t, &(args->attributes), NULL, index); +} + +void setarg_symlink(int index, char * line, SYMLINK3args * args, char * Cur_filename, char * sym_data) +{ + char * t; +#ifdef TRY_SETARG_FAST + memcpy (&args->where.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle (&args->where.dir); +#endif + t = parse_name (t, Cur_filename); + args->where.name = Cur_filename; + if (line[TRACE_VERSION_POS]=='2') { + t = parse_name (t, sym_data); + t = parse_sattr3 (t, &(args->symlink.symlink_attributes), NULL, index); + } else { + t = parse_sattr3 (t, &(args->symlink.symlink_attributes), NULL, index); + t = parse_name (t, sym_data); + } + args->symlink.symlink_data = sym_data; +} + +void setarg_mknod(int index, char * line, MKNOD3args * args, char * Cur_filename) +{ + RFS_ASSERT (0); + +#ifdef notdef + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = Cur_filename; + args.what.type = NF3FIFO; + args.what.mknoddata3_u.pipe_attributes.mode.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.mode.mode = (NFSMODE_FIFO | 0777); + args.what.mknoddata3_u.pipe_attributes.uid.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.uid.uid = Cur_uid; + args.what.mknoddata3_u.pipe_attributes.gid.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.gid.gid = Cur_gid; + args.what.mknoddata3_u.pipe_attributes.size.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.size.size._p._u = (uint32_t) 0; + args.what.mknoddata3_u.pipe_attributes.size.size._p._l = + (uint32_t) 512; + args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.atime.atime.seconds = + Cur_time.esec; + args.what.mknoddata3_u.pipe_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.what.mknoddata3_u.pipe_attributes.mtime.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.mtime.mtime.seconds = + Cur_time.esec; + args.what.mknoddata3_u.pipe_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; +#endif +} + +void setarg_remove (int index, char * line, REMOVE3args * args, char * Cur_filename) +{ + char * t; +#ifdef TRY_SETARG_FAST + memcpy (&args->object.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle(&args->object.dir) +#endif + t = parse_name (t, Cur_filename); + args->object.name = Cur_filename; +} + +void setarg_rmdir (int index, char * line, RMDIR3args * args, char * Cur_filename) +{ + char * t; +#ifdef TRY_SETARG_FAST + memcpy (&args->object.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle(&args->object.dir) +#endif + t = parse_name (t, Cur_filename); + args->object.name = Cur_filename; +} + +void setarg_rename (int index, char * line, RENAME3args * args, char * fromname, char * toname) +{ + char * t; +#ifdef TRY_SETARG_FAST + memcpy (&args->from.dir, &dep_tab[index].fh_2->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh_2 + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh_2, ' '); t++; +#else + setarg_fhandle(&args->from.dir) +#endif + t = parse_name (t, fromname); + args->from.name = fromname; + + t = strstr (t, "fh2"); + RFS_ASSERT (t); + t += 4; + memmove((char *)&args->to.dir, lookup_fhandle(t), sizeof (nfs_fh3)); + t+=65; + + t = parse_name (t, toname); + args->to.name = toname; +} + +void setarg_link (int index, char * line, LINK3args * args, char * Cur_filename) +{ + char * t; + +#ifdef TRY_SETARG_FAST + memcpy (&args->file, &dep_tab[index].fh_2->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh_2 + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh_2, ' '); t++; +#else + setarg_fhandle(&args->file) +#endif + + t = strstr (t, "fh2"); + RFS_ASSERT (t); + t += 4; + memmove((char *)&args->link.dir, lookup_fhandle(t), sizeof (nfs_fh3)); + t+=65; + + t = parse_name (t, Cur_filename); + args->link.name = Cur_filename; +} + +void setarg_readdir (int index, char * line, READDIR3args * args) +{ + char * t; + +#ifdef TRY_SETARG_FAST + memcpy (&args->dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle(&args->dir); +#endif + /* args->cookieverf is notset, it is not implemented in the linux-2.4.7 */ + sscanf(t, "cookie %d count %d", &args->cookie._p._l, &args->count); + (void) memset((char *) args->cookieverf, '\0', NFS3_COOKIEVERFSIZE); + args->cookie._p._u = (uint32_t) 0; +} + +void setarg_readdirplus (int index, char * line, READDIRPLUS3args * args) +{ + char * t; + +#ifdef TRY_SETARG_FAST + memcpy (&args->dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle(&args->dir); +#endif + /* args->cookieverf is notset, it is not implemented in the linux-2.4.7 */ + sscanf(t, "cookie %d count %d maxcnt", &args->cookie._p._l, &args->dircount, &args->maxcount); + (void) memset((char *) args->cookieverf, '\0', NFS3_COOKIEVERFSIZE); + args->cookie._p._u = (uint32_t) 0; + +#ifdef notdef + /* set up the arguments */ + (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.cookie._p._l = args.cookie._p._u = (uint32_t) 0; + (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE); + args.dircount = DEFAULT_MAX_BUFSIZE; + args.maxcount = DEFAULT_MAX_BUFSIZE; +#endif +} + +void setarg_fsstat (int index, char * line, FSSTAT3args * args) +{ + char * t; + setarg_fhandle(&args->fsroot); +} + +void setarg_fsinfo (int index, char * line, FSINFO3args * args) +{ + char * t; + setarg_fhandle(&args->fsroot); +} + +void setarg_pathconf (int index, char * line, PATHCONF3args * args) +{ + char * t; +#ifdef TRY_SETARG_FAST + memcpy (&args->object, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3)); + //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1; + t = strchr(dep_tab[index].trace_fh, ' '); t++; +#else + setarg_fhandle(&args->object); +#endif +} + +void setarg_commit (int index, char * line, COMMIT3args * args) +{ + RFS_ASSERT (0); + +#ifdef notdef + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + args.offset._p._u = args.offset._p._l = (uint32_t) 0; + args.count = Cur_file_ptr->attributes3.size._p._l; +#endif +} + +void setbuf_void (char * buf) +{ + return; +} + +void setbuf_invalid (char * buf) +{ + RFS_ASSERT (0); +} + +void setres_lookup (LOOKUP3res * reply) +{ +#ifndef TRY_SETRES_FAST + (void) memset((char *) &(reply->resok.object), '\0', sizeof (nfs_fh3)); +#endif +} + +void setres_readlink (READLINK3res * reply, char * sym_data) +{ + /* Have lower layers fill in the data directly. */ + reply->resok.data = sym_data; +} + +void setres_read (READ3res * reply, char * buf) +{ + /* Have lower layers fill in the data directly. */ + reply->resok.data.data_len = 0; + reply->resok.data.data_val = buf; +} + +void setres_readdir (READDIR3res * reply, entry3 * entry_stream) +{ +#ifndef TRY_SETRES_FAST + /* Have lower layers fill in the data directly. */ + (void) memset((char *) reply, '\0', sizeof (READDIR3res)); + (void) memset((char *) entry_stream, '\0', + sizeof (entry3) * SFS_MAXDIRENTS); +#endif + reply->resok.count = SFS_MAXDIRENTS; + reply->resok.reply.entries = entry_stream; +} + +void setres_readdirplus (READDIRPLUS3res * reply, entryplus3 * entry_stream) +{ +#ifndef TRY_SETRES_FAST + (void) memset((char *) reply, '\0', sizeof (READDIRPLUS3res)); + //printf ("sizeof(entryplus3) %d SFS_MAXDIRENT %d\n", sizeof (entryplus3), SFS_MAXDIRENTS); + (void) memset((char *) entry_stream, '\0', + sizeof (entryplus3) * SFS_MAXDIRENTS); +#endif + reply->resok.count = SFS_MAXDIRENTS; + reply->resok.reply.entries = entry_stream; +} + +#define NFSPROC3_INVALID -1 +/* the array is indexed by sfs operation number */ +rfs_op_type rfs_Ops[TOTAL] = { +{NFSPROC3_NULL, setbuf_void, setbuf_void, xdr_void, xdr_void}, +{NFSPROC3_GETATTR, setarg_getattr, setbuf_void, xdr_GETATTR3args, xdr_GETATTR3res}, +{NFSPROC3_SETATTR, setarg_setattr, setbuf_void, xdr_SETATTR3args, xdr_SETATTR3res}, +{NFSPROC3_INVALID, setbuf_invalid, setbuf_invalid, NULL, NULL}, +{NFSPROC3_LOOKUP, setarg_lookup, setres_lookup, xdr_LOOKUP3args, xdr_LOOKUP3res}, +{NFSPROC3_READLINK, setarg_readlink, setres_readlink, xdr_READLINK3args, xdr_READLINK3res}, +{NFSPROC3_READ, setarg_read, setres_read, xdr_READ3args, xdr_READ3res}, +{NFSPROC3_INVALID, setbuf_invalid, setbuf_invalid, NULL, NULL}, +{NFSPROC3_WRITE, setarg_write, setbuf_void, xdr_WRITE3args, xdr_WRITE3res}, +{NFSPROC3_CREATE, setarg_create, setbuf_void, xdr_CREATE3args, xdr_CREATE3res}, +{NFSPROC3_REMOVE, setarg_remove, setbuf_void, xdr_REMOVE3args, xdr_REMOVE3res}, +{NFSPROC3_RENAME, setarg_rename, setbuf_void, xdr_RENAME3args, xdr_RENAME3res}, +{NFSPROC3_LINK, setarg_link, setbuf_void, xdr_LINK3args, xdr_LINK3res}, +{NFSPROC3_SYMLINK, setarg_symlink, setbuf_void, xdr_SYMLINK3args, xdr_SYMLINK3res}, +{NFSPROC3_MKDIR, setarg_mkdir, setbuf_void, xdr_MKDIR3args, xdr_MKDIR3res}, +{NFSPROC3_RMDIR, setarg_rmdir, setbuf_void, xdr_RMDIR3args, xdr_RMDIR3res}, +{NFSPROC3_READDIR, setarg_readdir, setres_readdir, xdr_READDIR3args, xdr_READDIR3res}, +{NFSPROC3_FSSTAT, setarg_fsstat, setbuf_void, xdr_FSSTAT3args, xdr_FSSTAT3res}, +{NFSPROC3_ACCESS, setarg_access, setbuf_void, xdr_ACCESS3args, xdr_ACCESS3res}, +{NFSPROC3_COMMIT, setarg_commit, setbuf_void, xdr_COMMIT3args, xdr_COMMIT3res}, +{NFSPROC3_FSINFO, setarg_fsinfo, setbuf_void, xdr_FSINFO3args, xdr_FSINFO3res}, +{NFSPROC3_MKNOD, setarg_mknod, setbuf_void, xdr_MKNOD3args, xdr_MKNOD3res}, +{NFSPROC3_PATHCONF, setarg_pathconf, setbuf_void, xdr_PATHCONF3args, xdr_PATHCONF3res}, +{NFSPROC3_READDIRPLUS, setarg_readdirplus, setres_readdirplus, xdr_READDIRPLUS3args, xdr_READDIRPLUS3res}}; + +/* + * -------------------- NFS ops vector -------------------- + */ +/* + * per operation information + */ +sfs_op_type nfsv3_Ops[] = { + +/* name mix op call no req req req results */ +/* pcnt class targ call pcnt cnt targ */ + + { "null", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "getattr", 11, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "setattr", 1, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "root", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "lookup", 27, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readlink", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "read", 18, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "wrcache", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "write", 9, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "create", 1, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "remove", 1, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rename", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "link", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "symlink", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "mkdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rmdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdir", 2, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsstat", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "access", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "commit", 5, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsinfo", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "mknod", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "pathconf", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdirplus", 9, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "TOTAL", 100, Lookup, 0, 0, 0.0, 0, 0, { 0, }} +}; + +sfs_op_type *Ops; + diff --git a/TBBT/trace_play/rfs_3_ops.c.old b/TBBT/trace_play/rfs_3_ops.c.old new file mode 100644 index 0000000..37a47be --- /dev/null +++ b/TBBT/trace_play/rfs_3_ops.c.old @@ -0,0 +1,739 @@ +#ifndef lint +static char sfs_3_opsSid[] = "@(#)sfs_3_ops.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> +#include "sfs_c_def.h" +#include "rfs_c_def.h" + +extern fh_map_t * lookup_fh (char * trace_fh); + +char * lookup_fhandle(char * fhandle) +{ + fh_map_t * fh; + fh = lookup_fh(fhandle); + RFS_ASSERT (fh); + return ((char *)&fh->play_fh); +} + + +#define setarg_fhandle(fhp) \ + fh_map_t * fh; \ + t = strstr (line, "fh"); \ + RFS_ASSERT (t); \ + t += 3; \ + fh = lookup_fh(t); \ + RFS_ASSERT (fh); \ + (void) memmove((char *)fhp, &(fh->play_fh), \ + sizeof (nfs_fh3)); \ + t+=TRACE_FH_SIZE+1; + +void setarg_getattr (int index, char * line, GETATTR3args * args) +{ + char * t; + setarg_fhandle(&args->object); +} + +struct ladtime * adjust_time (struct timeval tm, int * sec, int * usec) +{ + struct ladtime trace_pkt_time; + static struct ladtime trace_arg_time; + struct timeval curtmp; + struct ladtime cur; + + /* not sure whether sec ==0 means anything special, do not adjust the timestamp for this */ + if (*sec ==0) { + RFS_ASSERT (0); + RFS_ASSERT (*usec == 0); + trace_arg_time.sec = 0; + trace_arg_time.usec = 0; + return (&trace_arg_time); + } + trace_pkt_time.sec = tm.tv_sec; + trace_pkt_time.usec = tm.tv_usec; + + trace_arg_time.sec = *sec; + trace_arg_time.usec = *usec; + + gettimeofday(&curtmp, NULL); + cur.sec = curtmp.tv_sec; + cur.usec = curtmp.tv_usec; + + //fprintf (stderr, "trace_pkt_time %d.%d trace_arg_time %d.%d cur %d.%d\n", trace_pkt_time.sec, trace_pkt_time.usec, trace_arg_time.sec, trace_arg_time.usec, cur.sec, cur.usec); + + ADDTIME (trace_arg_time, cur); + //fprintf(stderr, "after add, %d.%d\n", trace_arg_time.sec, trace_arg_time.usec); + RFS_ASSERT (LARGERTIME (trace_arg_time, trace_pkt_time)); + SUBTIME (trace_arg_time, trace_pkt_time); + return (&trace_arg_time); +} + +char * parse_create_mode(char * t, createmode3 * mode) +{ + *mode = UNCHECKED; + return (t+2); + /* anyway, we can not get concrete result from the trace, just chose this mode */ + RFS_ASSERT (0); +} + +char * parse_sattr3(char * t, sattr3 * args, sattrguard3 * guard, int index) +{ + int i,j; + struct ladtime * tm; + + /* set the default value of SETATTR3args->*/ + args->mode.set_it = FALSE; + args->uid.set_it = FALSE; + args->gid.set_it = FALSE; + args->size.set_it = FALSE; + args->atime.set_it = FALSE; + args->mtime.set_it = FALSE; + + //fprintf(stderr, "parse_sattr: line %s\n", t); + while (1) { + if (!strncmp (t, "mode", 4)) { + t+=5; + sscanf(t, "%x", &i); + args->mode.set_it = TRUE; + args->mode.mode = i; // (uint32_t) 0666; + } else if (!strncmp (t, "ctime", 5)) { + RFS_ASSERT (guard); + t+=6; + RFS_ASSERT (strncmp(t, "SERVER", 6)); + sscanf (t, "%d.%d", &i, &j); + tm = adjust_time (dep_tab[index].timestamp, &i, &j); +#ifndef IGNORE_SETATTR_CTIME + guard->check = TRUE; +#endif + guard->obj_ctime.seconds = tm->sec; + guard->obj_ctime.nseconds = tm->usec*1000; + } else if (!strncmp (t, "atime", 5)) { + t+=6; + if (!strncmp(t, "SERVER", 6)) { + args->atime.set_it = SET_TO_SERVER_TIME; + } else { + args->atime.set_it = SET_TO_CLIENT_TIME; + sscanf (t, "%d.%d", &i, &j); + if (i==0) { + RFS_ASSERT (j==0); + args->atime.atime.seconds = 0; + args->atime.atime.nseconds = 0; + } else { + tm = adjust_time (dep_tab[index].timestamp, &i, &j); + args->atime.atime.seconds = tm->sec; + args->atime.atime.nseconds = tm->usec * 1000; + } + } + } else if (!strncmp (t, "mtime", 5)) { + t+=6; + if (!strncmp(t, "SERVER", 6)) { + args->mtime.set_it = SET_TO_SERVER_TIME; + } else { + args->mtime.set_it = SET_TO_CLIENT_TIME; + sscanf (t, "%d.%d", &i, &j); + if (i==0) { + RFS_ASSERT (j==0); + args->mtime.mtime.seconds = 0; + args->mtime.mtime.nseconds = 0; + } else { + tm = adjust_time (dep_tab[index].timestamp, &i, &j); + args->mtime.set_it = TRUE; + args->mtime.mtime.seconds = tm->sec; + args->mtime.mtime.nseconds = tm->usec * 1000; + } + } + } else if (!strncmp (t, "size", 4)) { + t+=5; + sscanf(t, "%x", &i); + args->size.set_it = TRUE; + args->size.size._p._u = (uint32_t) 0; + args->size.size._p._l = (uint32_t) i; + } else if (!strncmp (t, "gid", 3)) { + t+=4; + sscanf(t, "%x", &i); + args->gid.set_it = TRUE; +#ifdef TAKE_CARE_SETATTR_GID + args->gid.gid = i; +#else + args->gid.gid = 513; +#endif + } else if ( !strncmp (t, "uid", 3)) { + t+=4; + sscanf(t, "%x", &i); + args->uid.set_it = TRUE; +#ifdef TAKE_CARE_SETATTR_UID + args->uid.uid = i; +#else + args->uid.uid = 513; +#endif + } else if (!strncmp (t, "con", 3)) { + break; + } else if (!strncmp (t, "sdata", 5)) { + break; + } else { + fprintf(stderr, "parse_sattr t: %s\n", t); + RFS_ASSERT (0); + } + + while (*t!=' ') + t++; + t++; + } + return t; +} + +char * parse_name (char * t, char * buf) +{ + int i; + if (!strncmp(t, "fn2", 3)) + t+=4; + else if (!strncmp(t, "fn", 2)) + t+=3; + else if (!strncmp(t, "name2", 5)) + t+=6; + else if (!strncmp(t, "name", 4)) + t+=5; + else if (!strncmp(t, "sdata", 5)) + t+=6; + else { + fprintf(stderr, "%s\n", t); + RFS_ASSERT (0); + } + + RFS_ASSERT (*t=='"'); + t++; + i = 0; + while (*t!='"') + buf[i++] = *t++; // ??? name buffer? + RFS_ASSERT ((*t)=='"'); + buf[i] = 0; + return (t+2); +} + +char * parse_access_mode (char * line, int * mode) +{ + *mode = ACCESS3_READ; + return line+2; + /* anyway the information in the trace is not enough, so we just make up something */ +} + +char * parse_stable_mode (char * line, stable_how * mode) +{ + switch (*line) { + case 'U': *mode = UNSTABLE; + break; + case 'F': *mode = FILE_SYNC; + break; + case 'D': *mode = DATA_SYNC; + break; + default: + RFS_ASSERT (0); + } + return line +2; +} + + +void setarg_setattr (int index, char * line, SETATTR3args * args) +{ + char * t; + int i, j; + + setarg_fhandle(&args->object); + args->guard.check = FALSE; + t = parse_sattr3 (t, &(args->new_attributes), &(args->guard), index); +} + +void setarg_lookup (int index, char * line, LOOKUP3args * args, char * Cur_filename) +{ + char * t; + setarg_fhandle(&args->what.dir) + t = parse_name (t, Cur_filename); + args->what.name = Cur_filename; +} + +void setarg_access (int index, char * line, ACCESS3args * args) +{ + char * t; + + setarg_fhandle (&args->object); + parse_access_mode (t, &args->access); //ACCESS3_MODIFY; // ??? the actual parameter can be different +} + +void setarg_readlink (int index, char * line, READLINK3args * args) +{ + char * t; + setarg_fhandle (&args->symlink); +} + +void setarg_read (int index, char * line, READ3args * args, char * buf) +{ + char * t; + int i; + setarg_fhandle (&args->file); + + if (line[TRACE_VERSION_POS]=='3') { + t = strstr (t, "off"); + RFS_ASSERT (t); + t+=4; + } else { + t = strstr (t, "offset"); + RFS_ASSERT (t); + t+=7; + } + sscanf (t, "%x", &i); + + RFS_ASSERT (i>=0 && i<0x7FFFFFFF) + args->offset._p._u = 0; + args->offset._p._l = i; + t = strstr (t, "count"); + RFS_ASSERT (t); + t+=6; + sscanf (t, "%x", &i); + + RFS_ASSERT (i <= 32768); + if (i > NFS_MAXDATA) { + read_data_owe += (i-NFS_MAXDATA); + read_data_adjust_times ++; + if (read_data_owe > 1073741824) { + read_data_owe -= 1073741824; + read_data_owe_GB ++; + } + + //printf ("adjust read count from %d to %d\n", i, NFS_MAXDATA); + i = NFS_MAXDATA; + } + read_data_total += i; + RFS_ASSERT (read_data_total <1073741824); + + args->count = i; +} + +void setarg_write (int index, char * line, WRITE3args * args, char * buf) +{ + char * t; + int i; + setarg_fhandle (&args->file); + + //fprintf (stderr, "process write: %s\n", line); + if (line[TRACE_VERSION_POS]=='3') { + t = strstr (t, "off"); + RFS_ASSERT (t); + t+=4; + } else { + RFS_ASSERT (line[TRACE_VERSION_POS]=='2'); + t = strstr (t, "offset"); + RFS_ASSERT (t); + t+=7; + } + + sscanf (t, "%x", &i); + RFS_ASSERT (i>=0 && i<0x7FFFFFFF) + args->offset._p._u = 0; + args->offset._p._l = i; + + t = strstr (t, "count"); + RFS_ASSERT (t); + t+=6; + sscanf (t, "%x", &i); + RFS_ASSERT (i <= 32768); + if (i > NFS_MAXDATA) { + write_data_owe += (i-NFS_MAXDATA); + if (write_data_owe > 1073741824) { + write_data_owe -= 1073741824; + write_data_owe_GB ++; + } + write_data_adjust_times ++; + //printf ("adjust write count from %d to %d\n", i, NFS_MAXDATA); + i = NFS_MAXDATA; + } + write_data_total += i; + RFS_ASSERT (write_data_total <1073741824); + + RFS_ASSERT (i < MAX_BUF1_SIZE-128); /* 128 is some random safe number to add */ + args->count = i; + + if (line[TRACE_VERSION_POS]==3) { + t = strstr (t, "stable"); + RFS_ASSERT (t); + t+=7; + parse_stable_mode(t, &args->stable); /* *t can be F, U, etc */ + } else + args->stable = UNSTABLE; + args->data.data_len = args->count; + args->data.data_val = buf; +} + +void setarg_create (int index, char * line, CREATE3args * args, char * Cur_filename) +{ + char * t; + //fprintf(stderr, "process create %s\n", line); + setarg_fhandle (&args->where.dir); + t = parse_name (t, Cur_filename); + args->where.name = Cur_filename; + if (line[TRACE_VERSION_POS]=='3') { + RFS_ASSERT (!strncmp(t, "how", 3)); + t+=4; + t = parse_create_mode (t, &args->how.mode); + } else + args->how.mode = UNCHECKED; + t = parse_sattr3 (t, &(args->how.createhow3_u.obj_attributes), NULL, index); +} + +void setarg_create_old (int index, char * line, CREATE3args * retargs, char * Cur_filename) +{ + CREATE3args args; + + sprintf(Cur_filename, "%d", index); + if (rfs_debug) + printf ("create file %s\n", Cur_filename); + + /* set up the arguments */ + (void) memmove((char *)&args.where.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = Cur_filename; //RFS need a buffer for the name + args.how.mode = UNCHECKED; + args.how.createhow3_u.obj_attributes.mode.set_it = TRUE; + args.how.createhow3_u.obj_attributes.mode.mode = (NFSMODE_REG | 0666); + args.how.createhow3_u.obj_attributes.uid.set_it = TRUE; + args.how.createhow3_u.obj_attributes.uid.uid = Cur_uid; + args.how.createhow3_u.obj_attributes.gid.set_it = TRUE; + args.how.createhow3_u.obj_attributes.gid.gid = Cur_gid; + args.how.createhow3_u.obj_attributes.atime.set_it = TRUE; + args.how.createhow3_u.obj_attributes.atime.atime.seconds = Cur_time.esec; + args.how.createhow3_u.obj_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.how.createhow3_u.obj_attributes.mtime.set_it = TRUE; + args.how.createhow3_u.obj_attributes.mtime.mtime.seconds = Cur_time.esec; + args.how.createhow3_u.obj_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + args.how.createhow3_u.obj_attributes.size.set_it = TRUE; + args.how.createhow3_u.obj_attributes.size.size._p._u = (uint32_t) 0; + args.how.createhow3_u.obj_attributes.size.size._p._l = (uint32_t) 0; + + memcpy (retargs, &args, sizeof (CREATE3args)); +} + +void setarg_mkdir (int index, char * line, MKDIR3args * args, char * Cur_filename) +{ + char * t; + setarg_fhandle (&args->where.dir); + t = parse_name (t, Cur_filename); + args->where.name = Cur_filename; + t = parse_sattr3 (t, &(args->attributes), NULL, index); +} + +void setarg_symlink(int index, char * line, SYMLINK3args * args, char * Cur_filename, char * sym_data) +{ + char * t; + setarg_fhandle (&args->where.dir); + t = parse_name (t, Cur_filename); + args->where.name = Cur_filename; + if (line[TRACE_VERSION_POS]=='2') { + t = parse_name (t, sym_data); + t = parse_sattr3 (t, &(args->symlink.symlink_attributes), NULL, index); + } else { + t = parse_sattr3 (t, &(args->symlink.symlink_attributes), NULL, index); + t = parse_name (t, sym_data); + } + args->symlink.symlink_data = sym_data; +} + +void setarg_mknod(int index, char * line, MKNOD3args * args, char * Cur_filename) +{ + RFS_ASSERT (0); + +#ifdef notdef + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = Cur_filename; + args.what.type = NF3FIFO; + args.what.mknoddata3_u.pipe_attributes.mode.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.mode.mode = (NFSMODE_FIFO | 0777); + args.what.mknoddata3_u.pipe_attributes.uid.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.uid.uid = Cur_uid; + args.what.mknoddata3_u.pipe_attributes.gid.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.gid.gid = Cur_gid; + args.what.mknoddata3_u.pipe_attributes.size.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.size.size._p._u = (uint32_t) 0; + args.what.mknoddata3_u.pipe_attributes.size.size._p._l = + (uint32_t) 512; + args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.atime.atime.seconds = + Cur_time.esec; + args.what.mknoddata3_u.pipe_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.what.mknoddata3_u.pipe_attributes.mtime.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.mtime.mtime.seconds = + Cur_time.esec; + args.what.mknoddata3_u.pipe_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; +#endif +} + +void setarg_remove (int index, char * line, REMOVE3args * args, char * Cur_filename) +{ + char * t; + setarg_fhandle(&args->object.dir) + t = parse_name (t, Cur_filename); + args->object.name = Cur_filename; +} + +void setarg_rmdir (int index, char * line, RMDIR3args * args, char * Cur_filename) +{ + char * t; + setarg_fhandle(&args->object.dir) + t = parse_name (t, Cur_filename); + args->object.name = Cur_filename; +} + +void setarg_rename (int index, char * line, RENAME3args * args, char * fromname, char * toname) +{ + char * t; + setarg_fhandle(&args->from.dir) + t = parse_name (t, fromname); + args->from.name = fromname; + + t = strstr (t, "fh2"); + RFS_ASSERT (t); + t += 4; + memmove((char *)&args->to.dir, lookup_fhandle(t), sizeof (nfs_fh3)); + t+=65; + + t = parse_name (t, toname); + args->to.name = toname; +} + +void setarg_link (int index, char * line, LINK3args * args, char * Cur_filename) +{ + char * t; + + setarg_fhandle(&args->file) + + t = strstr (t, "fh2"); + RFS_ASSERT (t); + t += 4; + memmove((char *)&args->link.dir, lookup_fhandle(t), sizeof (nfs_fh3)); + t+=65; + + t = parse_name (t, Cur_filename); + args->link.name = Cur_filename; +} + +void setarg_readdir (int index, char * line, READDIR3args * args) +{ + char * t; + + setarg_fhandle(&args->dir); + /* args->cookieverf is notset, it is not implemented in the linux-2.4.7 */ + sscanf(t, "cookie %d count %d", &args->cookie._p._l, &args->count); + (void) memset((char *) args->cookieverf, '\0', NFS3_COOKIEVERFSIZE); + args->cookie._p._u = (uint32_t) 0; +} + +void setarg_readdirplus (int index, char * line, READDIRPLUS3args * args) +{ + char * t; + + setarg_fhandle(&args->dir); + /* args->cookieverf is notset, it is not implemented in the linux-2.4.7 */ + sscanf(t, "cookie %d count %d maxcnt", &args->cookie._p._l, &args->dircount, &args->maxcount); + (void) memset((char *) args->cookieverf, '\0', NFS3_COOKIEVERFSIZE); + args->cookie._p._u = (uint32_t) 0; + +#ifdef notdef + /* set up the arguments */ + (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.cookie._p._l = args.cookie._p._u = (uint32_t) 0; + (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE); + args.dircount = DEFAULT_MAX_BUFSIZE; + args.maxcount = DEFAULT_MAX_BUFSIZE; +#endif +} + +void setarg_fsstat (int index, char * line, FSSTAT3args * args) +{ + char * t; + setarg_fhandle(&args->fsroot); +} + +void setarg_fsinfo (int index, char * line, FSINFO3args * args) +{ + char * t; + setarg_fhandle(&args->fsroot); +} + +void setarg_pathconf (int index, char * line, PATHCONF3args * args) +{ + char * t; + setarg_fhandle(&args->object); +} + +void setarg_commit (int index, char * line, COMMIT3args * args) +{ + RFS_ASSERT (0); + +#ifdef notdef + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + args.offset._p._u = args.offset._p._l = (uint32_t) 0; + args.count = Cur_file_ptr->attributes3.size._p._l; +#endif +} + +void setbuf_void (char * buf) +{ + return; +} + +void setbuf_invalid (char * buf) +{ + RFS_ASSERT (0); +} + +void setres_lookup (LOOKUP3res * reply) +{ + (void) memset((char *) &(reply->resok.object), '\0', sizeof (nfs_fh3)); +} + +void setres_readlink (READLINK3res * reply, char * sym_data) +{ + /* Have lower layers fill in the data directly. */ + reply->resok.data = sym_data; +} + +void setres_read (READ3res * reply, char * buf) +{ + /* Have lower layers fill in the data directly. */ + reply->resok.data.data_len = 0; + reply->resok.data.data_val = buf; +} + +void setres_readdir (READDIR3res * reply, entry3 * entry_stream) +{ + /* Have lower layers fill in the data directly. */ + (void) memset((char *) reply, '\0', sizeof (READDIR3res)); + (void) memset((char *) entry_stream, '\0', + sizeof (entry3) * SFS_MAXDIRENTS); + reply->resok.count = SFS_MAXDIRENTS; + reply->resok.reply.entries = entry_stream; +} + +void setres_readdirplus (READDIRPLUS3res * reply, entryplus3 * entry_stream) +{ + (void) memset((char *) reply, '\0', sizeof (READDIRPLUS3res)); + //printf ("sizeof(entryplus3) %d SFS_MAXDIRENT %d\n", sizeof (entryplus3), SFS_MAXDIRENTS); + (void) memset((char *) entry_stream, '\0', + sizeof (entryplus3) * SFS_MAXDIRENTS); + reply->resok.count = SFS_MAXDIRENTS; + reply->resok.reply.entries = entry_stream; +} + +#define NFSPROC3_INVALID -1 +/* the array is indexed by sfs operation number */ +rfs_op_type rfs_Ops[TOTAL] = { +{NFSPROC3_NULL, setbuf_void, setbuf_void, xdr_void, xdr_void}, +{NFSPROC3_GETATTR, setarg_getattr, setbuf_void, xdr_GETATTR3args, xdr_GETATTR3res}, +{NFSPROC3_SETATTR, setarg_setattr, setbuf_void, xdr_SETATTR3args, xdr_SETATTR3res}, +{NFSPROC3_INVALID, setbuf_invalid, setbuf_invalid, NULL, NULL}, +{NFSPROC3_LOOKUP, setarg_lookup, setres_lookup, xdr_LOOKUP3args, xdr_LOOKUP3res}, +{NFSPROC3_READLINK, setarg_readlink, setres_readlink, xdr_READLINK3args, xdr_READLINK3res}, +{NFSPROC3_READ, setarg_read, setres_read, xdr_READ3args, xdr_READ3res}, +{NFSPROC3_INVALID, setbuf_invalid, setbuf_invalid, NULL, NULL}, +{NFSPROC3_WRITE, setarg_write, setbuf_void, xdr_WRITE3args, xdr_WRITE3res}, +{NFSPROC3_CREATE, setarg_create, setbuf_void, xdr_CREATE3args, xdr_CREATE3res}, +{NFSPROC3_REMOVE, setarg_remove, setbuf_void, xdr_REMOVE3args, xdr_REMOVE3res}, +{NFSPROC3_RENAME, setarg_rename, setbuf_void, xdr_RENAME3args, xdr_RENAME3res}, +{NFSPROC3_LINK, setarg_link, setbuf_void, xdr_LINK3args, xdr_LINK3res}, +{NFSPROC3_SYMLINK, setarg_symlink, setbuf_void, xdr_SYMLINK3args, xdr_SYMLINK3res}, +{NFSPROC3_MKDIR, setarg_mkdir, setbuf_void, xdr_MKDIR3args, xdr_MKDIR3res}, +{NFSPROC3_RMDIR, setarg_rmdir, setbuf_void, xdr_RMDIR3args, xdr_RMDIR3res}, +{NFSPROC3_READDIR, setarg_readdir, setres_readdir, xdr_READDIR3args, xdr_READDIR3res}, +{NFSPROC3_FSSTAT, setarg_fsstat, setbuf_void, xdr_FSSTAT3args, xdr_FSSTAT3res}, +{NFSPROC3_ACCESS, setarg_access, setbuf_void, xdr_ACCESS3args, xdr_ACCESS3res}, +{NFSPROC3_COMMIT, setarg_commit, setbuf_void, xdr_COMMIT3args, xdr_COMMIT3res}, +{NFSPROC3_FSINFO, setarg_fsinfo, setbuf_void, xdr_FSINFO3args, xdr_FSINFO3res}, +{NFSPROC3_MKNOD, setarg_mknod, setbuf_void, xdr_MKNOD3args, xdr_MKNOD3res}, +{NFSPROC3_PATHCONF, setarg_pathconf, setbuf_void, xdr_PATHCONF3args, xdr_PATHCONF3res}, +{NFSPROC3_READDIRPLUS, setarg_readdirplus, setres_readdirplus, xdr_READDIRPLUS3args, xdr_READDIRPLUS3res}}; + +/* + * -------------------- NFS ops vector -------------------- + */ +/* + * per operation information + */ +sfs_op_type nfsv3_Ops[] = { + +/* name mix op call no req req req results */ +/* pcnt class targ call pcnt cnt targ */ + + { "null", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "getattr", 11, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "setattr", 1, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "root", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "lookup", 27, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readlink", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "read", 18, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "wrcache", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "write", 9, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "create", 1, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "remove", 1, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rename", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "link", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "symlink", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "mkdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rmdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdir", 2, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsstat", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "access", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "commit", 5, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsinfo", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "mknod", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "pathconf", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdirplus", 9, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "TOTAL", 100, Lookup, 0, 0, 0.0, 0, 0, { 0, }} +}; + +sfs_op_type *Ops; + diff --git a/TBBT/trace_play/rfs_assert.h b/TBBT/trace_play/rfs_assert.h new file mode 100644 index 0000000..ec360d5 --- /dev/null +++ b/TBBT/trace_play/rfs_assert.h @@ -0,0 +1,13 @@ +#ifndef RFS_ASSERT_H +#define RFS_ASSERT_H +#define RFS_ASSERT(condition) \ + if (!(condition)) { \ + fprintf(stderr, "Assertion failed: line %d, file \"%s\"\n", \ + __LINE__, __FILE__); \ + fflush(stdout); \ + fflush(stdout); \ + fflush(stderr); \ + fflush(stderr); \ + exit(-1); \ + } +#endif diff --git a/TBBT/trace_play/rfs_c_age.c b/TBBT/trace_play/rfs_c_age.c new file mode 100644 index 0000000..cdf9c6c --- /dev/null +++ b/TBBT/trace_play/rfs_c_age.c @@ -0,0 +1,1393 @@ +/* rfs_age_unit_base.c */ +#include <sys/vfs.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include "rfs_assert.h" +#include "profile.h" +#define MKDIR 1 +#define RMDIR 2 +#define CREATE 3 +#define REMOVE 4 +#define WRITE 5 +#define TRUNCATE 6 + +#define MAX_FILES 400000 +#define MAX_DIRS 100000 +#define FILE_FH_HTABLE_SIZE MAX_FILES +#define MAX_NAMELEN 512 +#define MAX_PLAY_PATH_SIZE 256 +//#define MAX_COMMAND_LEN (MAX_PLAY_PATH_SIZE+16) +//#define NFS_MAXDATA 4096 +#define NFS_MAXDATA 32768 +#define TRACE_FH_SIZE 64 + +#define FH_T_FLAG_FREE 0 +#define FH_T_FLAG_IN_USE 1 +#define IS_FILE 0 +#define IS_DIR 1 +#define EXIST 0 +#define NON_EXIST 1 +#define COMPLETE 3 +#define ACTIVE 0 +#define INACTIVE 1 +#define DONT_CARE 2 + +int EVEN_CHUNK_SIZE = 0; +int STAGE_NUM = 10; + +static char ftypename[3][32] = {"FILE", "DIR", "FTYPE_DONT_CARE"}; +static char activename[3][32] = {"ACTIVE", "INACTIVE", "ACTIVE_DONT_CARE"}; +static char existname[4][32] = {"EXIST", "NON_EXIST", "EXIST_DONT_CARE", "COMPLETE"}; + +typedef struct { + char flag; + char ftype; + char exist_flag; + int psfh; + int size; + int cur_size; + int accumulated_write_size; + //char trace_fh [TRACE_FH_SIZE+1]; + char path[MAX_PLAY_PATH_SIZE]; +} fh_t; + +typedef struct { + char name[32]; + fh_t * fh; + //struct generic_entry * htable; + int fh_size; + int fh_max; + int active_fh_max; + //int index; + //int htable_size; +} fh_info_t; + +fh_info_t obj_fh; +profile_t read_line_profile, fgets_profile; +char trace_file[MAX_NAMELEN]; +FILE * profile_fp = NULL; +char testdir[MAX_NAMELEN]; + +int active_obj_num = 0; +int exist_active_obj_num = 0; +static int active_file_num = 0, active_dir_num =0, age_file_num = 0, age_dir_num = 0; + +int age_create_num = 0; +int age_mkdir_num = 0; +int assure_create_num = 0; +int assure_mkdir_num = 0; +int age_write_num = 0; +int nonage_write_num = 0; +int overlap_write_num = 0; + +int fs_size_MB = 0, fs_size = 0; +int rfs_debug = 0; + + +int ACTIVE_RATIO; +int FILE_RATIO = 50; +int WRITE_CHUNK_NUM; +int MAX_FS_SIZE_MB = 1000000; +int DISK_FRAGMENT_SIZE = 4096; +int DISK_FRAGMENT_SIZE_MASK = 0xFFFFF000; + +int MIN_WRITE_SIZE; + +int aging_dirs () +{ + +} + +int f() +{}; + +int init_profile_variables() +{ + init_profile ("read_line profile", &read_line_profile); + init_profile ("fgets profile", &fgets_profile); +} + +int init_fh_info (char * name, fh_info_t * fh_infop, int fh_size, int htable_size) +{ + int i; + + RFS_ASSERT (strlen(name) < sizeof(fh_infop->name)); + strcpy (fh_infop->name, name); + fh_infop->fh_max = 0; + //fh_infop->index = 0; + fh_infop->fh_size = fh_size; + //fh_infop->htable_size = htable_size; + fh_infop->fh = (fh_t *)malloc (sizeof(fh_t)*fh_size); + RFS_ASSERT (fh_infop->fh); + //fh_infop->htable = malloc (sizeof(struct*generic_entry)*htable_size); + //RFS_ASSERT (fh_infop->htable); + printf("initialize %s size %d bytes\n", + //name, sizeof(fh_t)*fh_size + sizeof(struct*generic_entry)*htable_size); + name, sizeof(fh_t)*fh_size); + + for (i=0; i<fh_size; i++) + fh_infop->fh[i].flag = FH_T_FLAG_FREE; +} + +int init() +{ +// init_fh_info ("file_fh", &file_fh, MAX_FILES, MAX_FILES); +// init_fh_info ("dir_fh", &dir_fh, MAX_DIRS, MAX_DIRS); + init_fh_info ("obj_fh", &obj_fh, MAX_FILES+MAX_DIRS, MAX_FILES+MAX_DIRS); +} + +int add_fh_t (fh_info_t * fh_table, char * path, int sfh, int psfh, int size, int ftype, int exist_flag, int active_flag) +{ + int i; + + if (size == -1) + fs_size += DISK_FRAGMENT_SIZE; + else { + fs_size += ((size+DISK_FRAGMENT_SIZE-1) & DISK_FRAGMENT_SIZE_MASK); + if (size > (DISK_FRAGMENT_SIZE*12)) // first indirect block + fs_size += DISK_FRAGMENT_SIZE; + } + if (fs_size > 1000000) { + fs_size_MB += fs_size/1000000; + fs_size = fs_size % 1000000; + } + + RFS_ASSERT (sfh >0); + + if (active_flag == ACTIVE) + active_obj_num ++; + else + RFS_ASSERT (sfh >= fh_table->active_fh_max); + + if (rfs_debug) + printf ("add to %s path %s sfh %d size %d %s %s %s\n", fh_table->name, path, sfh, size, + ftypename[ftype], existname[exist_flag], activename[active_flag]); + + RFS_ASSERT ( (sfh>=0) && (sfh<fh_table->fh_size) ); + RFS_ASSERT (fh_table->fh[sfh].flag==FH_T_FLAG_FREE); + fh_table->fh[sfh].flag = FH_T_FLAG_IN_USE; + if (sfh >= fh_table->fh_max) + fh_table->fh_max = sfh+1; + RFS_ASSERT (strlen(path)<MAX_PLAY_PATH_SIZE); + strcpy (fh_table->fh[sfh].path, path); + fh_table->fh[sfh].psfh = psfh; + fh_table->fh[sfh].size = size; + fh_table->fh[sfh].cur_size = 0; + fh_table->fh[sfh].accumulated_write_size = 0; + fh_table->fh[sfh].ftype = ftype; + fh_table->fh[sfh].exist_flag = exist_flag; + if (active_flag == ACTIVE) { + if (ftype == IS_FILE) + active_file_num ++; + else { + RFS_ASSERT (ftype== IS_DIR); + active_dir_num ++; + } + } else { + if (ftype == IS_FILE) + age_file_num ++; + else { + RFS_ASSERT (ftype== IS_DIR); + age_dir_num ++; + } + } + //print_fh_map(fh_table); +} + + +int loop_write (int fd, char * buf, int buflen) +{ + int ret; + int pos = 0; + + while (1) { + ret = write (fd, buf+pos, buflen-pos); + + if (ret == -1) { + RFS_ASSERT (errno == ENOSPC); + fflush(stdout); + perror ("loop write"); + check_free_blocks(0); + return -1; + } + if (ret == buflen-pos) + break; + pos += ret; + } + return 0; +} + +int assure_exist(int sfh, char * path, int ftype_flag) +{ + char name[MAX_NAMELEN]; + int ret; + char *p, *q; + int non_exist_flag = 0; + int count=0; + struct stat st; + + if (rfs_debug) + printf("assure_exist %s\n", path); + + ret = stat (path, &st); + if (ret == 0) { + if (ftype_flag == IS_DIR) { + RFS_ASSERT (st.st_mode & S_IFDIR); + RFS_ASSERT (!(st.st_mode & S_IFREG)); + } else { + RFS_ASSERT (st.st_mode & S_IFREG); + RFS_ASSERT (!(st.st_mode & S_IFDIR)); + } + return 0; + } + if (errno!=ENOENT) { + perror(path); + } + //RFS_ASSERT (errno == ENOENT); + + p = path; + q = name; + if (*p=='/') { + *q='/'; + p++; + q++; + } + while (count++<100) { + /* copy the next component from path to name */ + for (; *p!=0 && *p!='/'; p++, q++ ) + *q = *p; + *q = 0; + ret = stat (name, &st); + if (ret == -1) { + if (errno != ENOENT) + perror (name); + RFS_ASSERT (errno == ENOENT) + if ((*p)==0 && (ftype_flag==IS_FILE)) { + ret = creat (name, S_IRWXU); + if (ret == -1) + perror (name); + RFS_ASSERT (ret >=0); + assure_create_num ++; + if (rfs_debug) + printf("sfh %d create %s\n", sfh, name); + close(ret); + } else { + ret = mkdir (name, S_IRWXU); + if (ret == -1) + perror (name); + RFS_ASSERT (ret >=0); + assure_mkdir_num ++; + if (rfs_debug) { + if (*p==0) + printf("sfh %d mkdir %s\n", sfh, name); + else + printf("sfh %d middle mkdir %s\n", sfh, name); + } + RFS_ASSERT (ret ==0); + } + } + if ((*p)=='/') { + *q = '/'; + p++; q++; + } else { + RFS_ASSERT ((*p)==0) + return 0; + } + } + RFS_ASSERT (0); +} + + +int print_fh_map(fh_info_t * fhp) +{ + int i; + int num = 0; + int active_obj_num = 0; + + + for (i=0; i<fhp->fh_max; i++) { + if (fhp->fh[i].flag == FH_T_FLAG_IN_USE) { + num ++; + if (i < fhp->active_fh_max) + active_obj_num++; + + if (rfs_debug) + printf("%s[%d] %s %s %s\n", fhp->name, i, fhp->fh[i].path, ftypename[fhp->fh[i].ftype], existname[fhp->fh[i].exist_flag]); + } + } + fprintf(stderr, "fh_max %d active_fh_max %d, in_use_num %d entries active_obj_num %d \n", fhp->fh_max, fhp->active_fh_max, num, active_obj_num); +} + +void read_fh_map_line_skimmer (char * buf) +{ + char * p; + char name[MAX_NAMELEN]; + int psfh, sfh, size; + char filename[MAX_NAMELEN]; + + sfh = 0; + if (!strncmp(buf, "::DIR ", strlen("::DIR "))) { + strcpy (name, testdir); + if (buf[6]=='/') { + sscanf(buf, "::DIR %s %d\n", name+strlen(name), &sfh); + add_fh_t (&obj_fh, name, sfh, -1, -1, IS_DIR, NON_EXIST, ACTIVE); + } /* else { + RFS_ASSERT (!strncmp(buf,"::DIR Fake 1\n", strlen("::DIR Fake 1\n"))); + sfh = 1; + add_fh_t (&obj_fh, name, sfh, -1, -1, IS_DIR, EXIST, ACTIVE); + exist_active_obj_num ++; + } */ + } else { + + if (!strncmp(buf, "::TBDIR", strlen("::TBDIR"))) + return; + + p = strstr(buf, "parent"); + RFS_ASSERT (p); + sscanf(p, "parent %d\n", &psfh); + RFS_ASSERT (obj_fh.fh[psfh].flag == FH_T_FLAG_IN_USE); + p = strstr(p, "name"); + RFS_ASSERT (p); + if (!strncmp(p, "name xx", strlen("name xx"))) { + sscanf(p, "name xx-%s sfh %d size %x", filename, &sfh, &size); + //printf ("name xx-%s sfh %d\n", filename, sfh); + } else { + sscanf(p, "name \"%s sfh %d size %x", filename, &sfh, &size); + //printf ("name %s sfh %d\n", filename, sfh); + filename[strlen(filename)-1]=0; + } + strcpy (name, obj_fh.fh[psfh].path); + strcat (name, "/"); + strcat (name, filename); + add_fh_t (&obj_fh, name, sfh, psfh, size, IS_FILE, NON_EXIST, ACTIVE); + } +} + +void read_fh_map_line_ls (char * buf) +{ + static int sfh = 2; + int size; + char name[MAX_NAMELEN]; + char * p = name; + + strcpy (name, testdir); + strcat (name, "/"); + if (strchr(buf, ' ')) { + sscanf(buf, "%d %s\n", &size, p+strlen(name)); + add_fh_t (&obj_fh, name, sfh, -1, size, IS_FILE, NON_EXIST, ACTIVE); + } else { + sscanf(buf, "%s\n", p+strlen(name)); + add_fh_t (&obj_fh, name, sfh, -1, -1, IS_DIR, NON_EXIST, ACTIVE); + }; + sfh ++; +} + +void read_fh_map (char * fh_map_file) +{ + FILE * fp; + int i = 0; + char buf[1024]; + int lineno = 0; + int fh_map_debug =0; +#define FH_MAP_FORMAT_SKIMMER 0 +#define FH_MAP_FORMAT_LS 1 + int fh_map_format; + + if (strstr(fh_map_file, ".ski")) + fh_map_format = FH_MAP_FORMAT_SKIMMER; + else + fh_map_format = FH_MAP_FORMAT_LS; + + fp = fopen(fh_map_file, "r"); + if (!fp) { + printf ("can not opern %s\n", fh_map_file); + perror("open"); + exit (0); + } + RFS_ASSERT (fp!=NULL); + + + memset(buf, 0, sizeof(buf)); + + while (fgets(buf, 1024, fp)) { + RFS_ASSERT (fh_map_debug==0); + lineno ++; + if (rfs_debug) + printf ("line %d %s", lineno, buf); + if (lineno % 10000==0) + printf("%d fh_map entry read\n", lineno); + if (fh_map_format == FH_MAP_FORMAT_SKIMMER) + read_fh_map_line_skimmer(buf); + else + read_fh_map_line_ls (buf); + } + + fclose(fp); + obj_fh.active_fh_max = obj_fh.fh_max; + if (fh_map_debug) { + print_fh_map (&obj_fh); + } +} + +int print_usage() +{ + printf("\n\nagefs fmt4 HOLDER_NUM HOLDER_SIZE DISK_FRAGMENT_SIZE testdir\n"); + printf("Note: fmt4 is used to initialize the holders in a logical partition before starting writing aged files in a specific pattern as by fmt3 command\n"); + + printf("\n\nagefs fmt3 aged_file CHUNK_SIZE CHUNK_DISTANCE CHUNK_NUM START_BNO HOLDER_SIZE HOLDER_NUM DISK_FRAGMENT_SIZE testdir\n"); + printf("Note: one file is written as CHUNK_NUM number of continuous chunks on disk, each chunk is of CHUNK_SIZE blocks, the distance between two adjacent chunks is CHUNK_DISTANCE blocks\n"); + + printf("\n\nagefs fmt2 size1 .. size_n num testdir\n"); + printf("Note: N file is writen interleavingly for _num_ times, each time _size1_ bytes is written to file1, _size2_ bytes is written to file2, _sizen_ bytes is written to filen\n"); + + printf("\n\nagefs EVEN_CHUNK_SIZE FILE_RATIO ACTIVE_RATIO WRITE_CHUNK_NUM MAX_FS_SIZE_MB STAGE_NUM fh_path_map testdir\n"); + printf("Note: EVEN_CHUNK_SIZE: if 1, each file is chopped to equal size chunks, if 0, each file size is chopped randomly but with the average size to be CHUNK_SIZE"); + printf(" FILE_RATIO: percentage of number of inactive files over number of inactive file system objects\n"); + printf(" ACTIVE_RATIO: percentage of number of active file system objects over all file system objects\n"); + printf(" WRITE_CHUNK_NUM: when a file is initialized, it is written in several open-close session interleved with writes to other files. Except small files where file_size/WRITE_CHUNK_SIZE is less than DISK_FRAGMENT_SIZE, each open-close session on the average write (file_size/WRITE_CHUNK_NUM) bytes. \n"); + printf(" MAX_FS_SIZE_MB: another condition to stop initialization, either all active file is initialized, or max file system size is reached\n"); + printf(" STAGE_NUM: divide the writing of files into several stages, each stage finish initialization of some of the files. The bigger the STAGE_NUM, the less concurrency is there\n"); +} + +inline char * read_line (int disk_index) +{ + static FILE * fp=NULL; + static int start=0; + static int start_disk_index=0; + int i; + static int finish_flag = 0; + +#define READ_LINE_BUF_SIZE 1000 +#define READ_LINE_LENGTH 32 + + static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH]; + start_profile (&read_line_profile); + + if (fp==NULL) { + if (strcmp(trace_file, "stdin")) { + fp = fopen(trace_file, "r"); + if (!fp) { + printf("can not open files %s\n", fp); + perror("open"); + } + } else { + fp = stdin; + } + RFS_ASSERT (fp!=NULL); + for (i=0; i<READ_LINE_BUF_SIZE; i++) { + start_profile(&fgets_profile); + if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) { + RFS_ASSERT (0); + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]); + } + } + + RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE) + if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) { + if (finish_flag) { + return NULL; + } + start_profile(&fgets_profile); + if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) { + end_profile(&fgets_profile); + fclose(fp); + finish_flag = 1; + return NULL; + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]); + start = (start+1) % READ_LINE_BUF_SIZE; + start_disk_index ++; + } + RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE) + i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE; + + end_profile (&read_line_profile); + return (line_buf[i]); +} + +int print_result() +{ + struct statfs stfs; + int ret; + static struct statfs first_stfs; + static int first_entry = 1; + + ret = statfs (testdir, &stfs); + RFS_ASSERT (ret == 0); + if (first_entry) { + first_entry = 0; + first_stfs = stfs; + } + + fprintf(stderr, "active_file_num %d active_dir_num %d age_file_num %d age_dir_num %d\n", + active_file_num, active_dir_num, age_file_num, age_dir_num); + fprintf(stderr, "number of used file nodes %d, used (4K) blocks in fs %d (%d MB)\n", first_stfs.f_ffree-stfs.f_ffree, first_stfs.f_bfree - stfs.f_bfree, (first_stfs.f_bfree-stfs.f_bfree)/(1000000/DISK_FRAGMENT_SIZE)); + fprintf(stderr, "assure_create_num %d assure_mkdir_num %d\n", assure_create_num, assure_mkdir_num); +} + +typedef struct { + int pcnt; /* percentile */ + int size; /* file size in KB */ +} sfs_io_file_size_dist; + +sfs_io_file_size_dist Default_file_size_dist[] = { + /* percentage KB size */ +#ifdef notdef + { 100, 128}, + { 94, 64}, /* 4% */ + { 97, 128}, /* 3% */ +#endif + { 33, 1}, /* 33% */ + { 54, 2}, /* 21% */ + { 67, 4}, /* 13% */ + { 77, 8}, /* 10% */ + { 85, 16}, /* 8% */ + { 90, 32}, /* 5% */ + { 94, 64}, /* 4% */ + { 97, 128}, /* 3% */ + { 99, 256}, /* 2% */ + { 100, 1024}, /* 1% */ + { 0, 0} +}; + +/* + * For a value between 0-99, return a size based on distribution + */ +static int +get_file_size() +{ + static file_array_initialized = 0; + static int file_size_array[100]; + int i; + + i = random() % 100; + + if (i < 0 || i > 99) + return (0); + + if (file_array_initialized == 0) { + int j, k; + for (j = 0, k = 0; j < 100; j++) { + if (j >= Default_file_size_dist[k].pcnt && + Default_file_size_dist[k + 1].size != 0) + k++; + file_size_array[j] = Default_file_size_dist[k].size * 1024; + } + file_array_initialized++; + } + return (file_size_array[i]); +} + +int range_random(int min, int max) +{ + int i; + i = (random()%(max-min)) + min; + return i; +} + +/* answer 1 with a probability of percent/100 */ +int decide(int percent) +{ + int i = random()%100; + if (i<percent) + return 1; + else + return 0; +} + +int select_obj (fh_info_t * fhp, int ftype, int exist_flag, int active_flag, int min, int max) +{ + int i; + int sfh, count = 0; + + //printf ("select_obj %s %s %s\n", ftypename[ftype], existname[exist_flag], activename[active_flag]); + if (active_flag == ACTIVE) { + sfh = range_random (0, fhp->active_fh_max); + for (i=0; i<fhp->active_fh_max; i++) { + if ((fhp->fh[sfh].flag == FH_T_FLAG_IN_USE) && + ((ftype==DONT_CARE) || (ftype ==fhp->fh[sfh].ftype)) && + ((exist_flag==DONT_CARE) || (fhp->fh[sfh].exist_flag == exist_flag))) { + return sfh; + } + sfh = (sfh+1) % fhp->active_fh_max; + } + } else { + //min = 0; + //max = fhp->fh_max; + //printf ("select_obj min %d max %d\n", min, max); + RFS_ASSERT (active_flag == DONT_CARE); + RFS_ASSERT (exist_flag == EXIST); + sfh = range_random (min, max); + for (i=min; i<max; i++) { + // printf("check %d\n", sfh); + RFS_ASSERT ((sfh>=min) && (sfh<max)); + if ((fhp->fh[sfh].flag == FH_T_FLAG_IN_USE) && + ((ftype==DONT_CARE) || (fhp->fh[sfh].ftype == ftype)) && + (fhp->fh[sfh].exist_flag == EXIST)) { + return sfh; + } + sfh++; + if (sfh==max) + sfh = min; + } + } +/* + for (i=min; i<max; i++) { + if ((fhp->fh[i].flag == FH_T_FLAG_IN_USE) && + (fhp->fh[i].ftype == IS_FILE) ) { + if (fhp->fh[i].exist_flag == EXIST) { + printf ("actually %d\n", i); + } + RFS_ASSERT (fhp->fh[i].exist_flag != EXIST); + RFS_ASSERT (fhp->fh[i].exist_flag == COMPLETE); + } + } +*/ + return -1; +} + +/* append "size" to file "path" */ +int append_file (int sfh, char * path, int size) +{ + int fd; + int written_bytes = 0; + int ret; +#define BUF_SIZE 32768 + static char buf[BUF_SIZE]; + + if (rfs_debug) + printf ("sfh %d append_file %s size %d\n", sfh, path, size); + + fd = open (path, O_WRONLY|O_APPEND); + if (fd==-1) + perror(path); + RFS_ASSERT (fd > 0); + + while (written_bytes+NFS_MAXDATA < size) { + ret = loop_write (fd, buf, NFS_MAXDATA); + RFS_ASSERT (ret!=-1); + written_bytes += NFS_MAXDATA; + } + ret = loop_write (fd, buf, size-written_bytes); + RFS_ASSERT (ret!=-1); + close(fd); +} + +int get_write_size (int target_size, int cur_size) +{ + int i; + + if (target_size - cur_size < MIN_WRITE_SIZE) + return (target_size - cur_size); + + /* target_size/WRITE_CHUNK_NUM would be the average value of i */ + if (target_size < WRITE_CHUNK_NUM) { + i = MIN_WRITE_SIZE; + } else { + + if (EVEN_CHUNK_SIZE) + i = target_size/WRITE_CHUNK_NUM; + else + i = random() % (2*(target_size/WRITE_CHUNK_NUM)); + + if (i < MIN_WRITE_SIZE) + i = MIN_WRITE_SIZE; + } + + if (i > (target_size - cur_size)) + i = target_size - cur_size; + + return i; +} + +FILE * fplog; + + +int CHUNK_SIZE; +int CHUNK_DISTANCE; +int CHUNK_NUM; +int START_BNO; +int HOLDER_NUM; +int HOLDER_SIZE; +int INDIRECT_FANOUT; +char agename[1024]; + +#define MAX_DISK_FRAGMENT_SIZE 4096 +#define MAX_HOLDER_SIZE 10 +#define HOLDER_DIR_NUM 1 +#define DUMMY_FILE_COUNT 1000 + +main4(int argc, char ** argv) +{ + int i, j; + char name[256]; + char cmd[1024]; + char * buf; + int fd, ret; + char testdir[1024]; + + if (argc!=6) { + print_usage(); + return; + } + i = 2; + HOLDER_NUM = atoi(argv[i++]); + HOLDER_SIZE = atoi(argv[i++]); + DISK_FRAGMENT_SIZE = atoi(argv[i++]); + RFS_ASSERT (DISK_FRAGMENT_SIZE <= MAX_DISK_FRAGMENT_SIZE); + DISK_FRAGMENT_SIZE_MASK = ~(DISK_FRAGMENT_SIZE-1); + RFS_ASSERT ((DISK_FRAGMENT_SIZE_MASK == 0xFFFFF000) || + (DISK_FRAGMENT_SIZE_MASK == 0xFFFFFC00) ); + strcpy (testdir, argv[i]); + + fprintf(fplog, "main4: initialize the holders HOLDER_NUM %d HOLDER_SIZE %d DISK_FRAGMENT_SIZE %d testdir %s\n", + HOLDER_NUM, HOLDER_SIZE, DISK_FRAGMENT_SIZE, testdir); + fflush(fplog); + + buf = (char *)malloc (HOLDER_SIZE*DISK_FRAGMENT_SIZE); + RFS_ASSERT (buf); + + /* create some dummy files */ + for (i=0; i<DUMMY_FILE_COUNT; i++) { + memset (name, 0, sizeof(name)); + sprintf(name, "%s/dummy%d", testdir, i); + fd = creat (name, S_IRWXU); + if (fd == -1) { + perror(name); + exit(-1); + } + close (fd); + } + + /* create directories */ + if (HOLDER_DIR_NUM !=1) { + for (i=0; i<HOLDER_DIR_NUM; i++) { + memset (name, 0, sizeof(name)); + sprintf(name, "%s/%d", testdir, i); + ret = mkdir (name, S_IRWXU); + if (ret == -1) { + perror(name); + exit(-1); + } + } + } + + /* create regular files */ + for (j=0; j<HOLDER_DIR_NUM; j++) { + for (i=0; i<HOLDER_NUM/HOLDER_DIR_NUM; i++) { + if (HOLDER_DIR_NUM == 1) + sprintf(name, "%s/%d", testdir, i); + else + sprintf(name, "%s/%d/%d", testdir, j, i); + fd = open (name, O_CREAT|O_WRONLY); + if (fd == -1) { + perror(name); + exit(-1); + } + ret = loop_write (fd, buf, HOLDER_SIZE*DISK_FRAGMENT_SIZE); + close (fd); + if (ret == -1) + break; + } + } + +#ifdef notdef + /* delete the dummy files */ + for (i=0; i<DUMMY_FILE_COUNT; i++) { + memset (name, 0, sizeof(name)); + sprintf(name, "%s/dummy%d", testdir, i); + ret = unlink (name); + if (ret == -1) { + perror(name); + exit(-1); + } + } +#endif +} + +int append_space_occupier() +{ + static char name [1024]; + static FILE * fp = NULL; + char buf[MAX_DISK_FRAGMENT_SIZE]; + int ret; + if (fp == NULL) { + sprintf(name, "%s/space_ocuupier", testdir); + fp = fopen (name, "a+"); + RFS_ASSERT (fp!= NULL); + ret = fwrite (buf, DISK_FRAGMENT_SIZE, 1, fp); + if (ret != 1) { + perror("append space occupier"); + } + fclose (fp); + fp = NULL; + }; + ret = fwrite (buf, DISK_FRAGMENT_SIZE, 1, fp); + if (ret != 1) { + perror("append space occupier"); + } + RFS_ASSERT (ret == 1); +} + +int create_one_dummy_file() +{ + int i, fd, ret; + static int index = 0; + static char name[1024]; + struct stat st; + + for (i=0; i<DUMMY_FILE_COUNT; i++) { + index = (index+1) % DUMMY_FILE_COUNT; + sprintf(name, "%s/dummy%d", testdir, i); + ret = stat (name, &st); + if (ret == -1) { + RFS_ASSERT (errno == ENOENT); + fd = open (name, O_CREAT|O_WRONLY); + RFS_ASSERT (fd >=0); + close (fd); + return 0; + }; + } +} + +int delete_one_dummy_file() +{ + int i,ret; + static int index = 0; + static char name[1024]; + struct stat st; + + for (i=0; i<DUMMY_FILE_COUNT; i++) { + index = (index+1) % DUMMY_FILE_COUNT; + sprintf(name, "%s/dummy%d", testdir, i); + ret = stat (name, &st); + if (ret == 0) { + ret = unlink (name); + RFS_ASSERT (ret == 0); + return; + } else + RFS_ASSERT (errno == ENOENT); + } + RFS_ASSERT (0); +} + +int create_sub_holder_file(int holderno, int sub_holderno) +{ + char name[256]; + int fd, ret; + static char buf[MAX_DISK_FRAGMENT_SIZE]; + + + RFS_ASSERT (MAX_DISK_FRAGMENT_SIZE >= DISK_FRAGMENT_SIZE); + + sprintf(name, "%d.%d", holderno, sub_holderno); + printf ("create/write %s\n", name); + fd = open (name, O_CREAT|O_WRONLY); + RFS_ASSERT (fd >=0); + loop_write (fd, buf, DISK_FRAGMENT_SIZE); + close(fd); +} + +int get_free_blocks () +{ + static struct statfs stfs; + int ret; + + ret = statfs (testdir, &stfs); + RFS_ASSERT (ret == 0); + return (stfs.f_bfree); +} + +int print_free_blocks (char *string) +{ + static struct statfs stfs; + int ret; + + ret = statfs (testdir, &stfs); + RFS_ASSERT (ret == 0); + printf("%s f_bfree %d \n", string, stfs.f_bfree); +} + +int check_free_blocks (int num) +{ + static struct statfs stfs; + int ret; + + ret = statfs (testdir, &stfs); + RFS_ASSERT (ret == 0); + if (stfs.f_bfree!=num) { + printf("f_bfree %d expected %d\n", stfs.f_bfree, num); + RFS_ASSERT (0); + } +} + +int progress_on_aged_file(int * num) +{ + char buf[MAX_DISK_FRAGMENT_SIZE*MAX_HOLDER_SIZE]; + static need_indirect_blocks = 0; + static skip_for_indirect_blocks = 0; + static int blkno = 0; + + printf ("\n"); + print_free_blocks("progress_on_aged_file begin"); + + if (skip_for_indirect_blocks == need_indirect_blocks) { + //check_free_blocks(free_block_num); + //RFS_ASSERT (free_block_num >= (1+need_indirect_blocks)); + (*num) --; + printf("append to aged file %d bytes\n", DISK_FRAGMENT_SIZE); + append_file (0, agename, DISK_FRAGMENT_SIZE); + //*free_block_num -= (need_indirect_blocks +1) + //check_free_blocks(free_block_num); + + blkno ++; + if (((blkno - 12) % INDIRECT_FANOUT) == 0) { + if (((blkno - (INDIRECT_FANOUT+12)) % (INDIRECT_FANOUT*INDIRECT_FANOUT)) == 0) { + if (blkno == 12 + INDIRECT_FANOUT + INDIRECT_FANOUT*INDIRECT_FANOUT) { + printf ("need_indirect_blocks is set to 3 blkno %d\n", blkno); + need_indirect_blocks = 3; + } else { + printf ("need_indirect_blocks is set to 2 blkno %d\n", blkno); + need_indirect_blocks = 2; + } + } else { + printf ("need_indirect_blocks is set to 1 blkno %d\n", blkno); + need_indirect_blocks = 1; + }; + } else { + need_indirect_blocks = 0; + } + skip_for_indirect_blocks = 0; + } else { + skip_for_indirect_blocks ++; + } + + printf ("skip_for_indirect_blocks -- %d\n", skip_for_indirect_blocks); + print_free_blocks("progress_on_aged_file end"); +} + +int free_blocks (char * agename, int start, int num) +{ + int holderno; + char name [128]; + int ret; + struct stat st; + int sub_holderno; + int i; + + printf ("free_blocks start %d num %d\n", start, num); + +BEGIN: + check_free_blocks(0); + if (num == 0) + return start; + holderno = start/HOLDER_SIZE; + sub_holderno = start%HOLDER_SIZE; + + sprintf (name, "%d", holderno); + + ret = stat (name, &st); + if (ret == -1) { + RFS_ASSERT (errno == ENOENT); + for (i=sub_holderno; (i<HOLDER_SIZE && num>0); i++) { + sprintf(name, "%d.%d", holderno, i); + ret = stat (name, &st); + if (ret == 0) { + + printf ("sub_holder file %s is unlinked\n", name); + ret = unlink(name); + RFS_ASSERT (ret == 0); + + create_one_dummy_file(); + + printf ("write to age file %d bytes\n", DISK_FRAGMENT_SIZE); + + progress_on_aged_file(&num); + + } else { + printf ("sub_holder file %s is already used\n", name); + RFS_ASSERT ((ret == -1) && (errno ==ENOENT)); + } + start ++; + } + goto BEGIN; + } + + RFS_ASSERT (ret == 0); + RFS_ASSERT (st.st_size == HOLDER_SIZE * DISK_FRAGMENT_SIZE); + printf ("holder file %s is unlinked\n", name); + ret = unlink(name); + RFS_ASSERT (ret == 0); + check_free_blocks(HOLDER_SIZE); + + /* create the sub holders before the first slot that we need */ + for (i=0; i<sub_holderno; i++) { + delete_one_dummy_file(); + create_sub_holder_file (holderno, i); + } + + /* + i = (HOLDER_SIZE - sub_holderno) < num? (HOLDER_SIZE - sub_holderno): num; + sub_holderno += i; + start += i; + num -= i; + check_free_blocks (i); + ret = loop_write (agefd, buf, DISK_FRAGMENT_SIZE*i); + RFS_ASSERT (ret != -1); + */ + i = HOLDER_SIZE - sub_holderno; + check_free_blocks(i); + + while ((sub_holderno < HOLDER_SIZE) && (num>0)) { + sub_holderno ++; + start ++; + + progress_on_aged_file(&num); + + RFS_ASSERT (ret != -1); + } + + /* create the sub holders after the slot that we need */ + for (i=sub_holderno; i<HOLDER_SIZE; i++) { + delete_one_dummy_file(); + create_sub_holder_file (holderno, i); + } + + create_one_dummy_file(); + goto BEGIN; +} + +int main3(int argc, char ** argv) +{ + + int block_anchor; + int i; + int agefd; + char * buf; + char cwd[1024]; + char * ret; + struct stat st; + + if (argc!=11) { + print_usage(); + return; + } + i = 2; + + CHUNK_SIZE = atoi(argv[i++]); + CHUNK_DISTANCE = atoi (argv[i++]); + CHUNK_NUM = atoi (argv[i++]); + START_BNO = atoi (argv[i++]); + HOLDER_SIZE = atoi (argv[i++]); + RFS_ASSERT (HOLDER_SIZE <= MAX_HOLDER_SIZE); + HOLDER_NUM = atoi (argv[i++]); + DISK_FRAGMENT_SIZE = atoi (argv[i++]); + RFS_ASSERT (DISK_FRAGMENT_SIZE <= MAX_DISK_FRAGMENT_SIZE); + INDIRECT_FANOUT = (DISK_FRAGMENT_SIZE/sizeof(int)); + strcpy (testdir, argv[i++]); + strcpy (agename, testdir); + strcat (agename, argv[i++]); + ret = stat (agename, &st); + if (ret!=-1) { + printf ("%s already exists\n", agename); + } + RFS_ASSERT (errno == ENOENT); + + fprintf(fplog, "main3: to age one file %s in a customized way, CHUNK_SIZE %d CHUNK_DISTANCE %d CHUNK_NUM %d START_BNO %d HOLDER_SIZE %d HOLDER_NUM %d DISK_FRAGMENT_SIZE %d MAX_DISK_FRAGMENT_SIZE %d testdir %s\n", agename, CHUNK_SIZE, CHUNK_DISTANCE, CHUNK_NUM, START_BNO, HOLDER_SIZE, HOLDER_NUM, DISK_FRAGMENT_SIZE, MAX_DISK_FRAGMENT_SIZE, testdir); + fflush(fplog); + + /* change working directory */ + ret = getcwd(cwd, sizeof(cwd)); + RFS_ASSERT (ret == cwd); + i = chdir (testdir); + RFS_ASSERT (i==0); + + if (START_BNO == -1) { + block_anchor = random() % (HOLDER_NUM*HOLDER_SIZE); + } else { + RFS_ASSERT (START_BNO >=0); + block_anchor = START_BNO % (HOLDER_NUM*HOLDER_SIZE); + } + while (get_free_blocks()!=0) { + print_free_blocks("fill up initial file system blocks using space occupier"); + append_file (0, "/b6/space_occupier", DISK_FRAGMENT_SIZE); + }; + delete_one_dummy_file(); + agefd = open (agename, O_CREAT|O_WRONLY); + RFS_ASSERT (agefd>=0); + close (agefd); + + buf = (char *)malloc (CHUNK_SIZE*DISK_FRAGMENT_SIZE); + RFS_ASSERT (buf); + + for (i=0; i<CHUNK_NUM; i++) { + block_anchor = (block_anchor + CHUNK_DISTANCE) % (HOLDER_NUM * HOLDER_SIZE); + block_anchor = free_blocks (agename, block_anchor, CHUNK_SIZE); + } + + check_free_blocks(0); + i = chdir (cwd); + RFS_ASSERT (i==0); +} + +int main2(int argc, char ** argv) +{ + int i, j; + int size[3], num; + FILE * fp[3]; + char name[3][128]; + char cmd[1024]; + + if (argc <= 4) { + print_usage(); + return; + } + num = atoi(argv[argc-2]); + strcpy (testdir, argv[argc-1]); + fprintf(fplog, "main2: generate interleaved files\n"); + fprintf(fplog, "testdir %s number of files %d ", testdir, num); + for (i=0; i<argc-4; i++) { + size[i] = atoi(argv[i+2]); + fprintf(fplog, "size[%d] %d ", i, size[i]); + + RFS_ASSERT (size[i] >=0 && size[i] < 1000000000); + strcpy (name[i], testdir); + sprintf (name[i], "%s/file%d", testdir, i); + sprintf(cmd, "touch %s", name[i]); + system(cmd); + printf ("write %s \n", name[i]); + }; + fprintf(fplog, "\n"); + fflush(fplog); + + for (j=0; j<num; j++) { + for (i=0; i<argc-4; i++) + append_file (i, name[i], size[i]); + } +} + +int main(int argc, char ** argv) +{ + int i; + char cmd[1024]; + char AGELOG_NAME[1024]= "/home/ningning/agefs.log"; + + sprintf(cmd, "date >> %s", AGELOG_NAME); + system (cmd); + fplog = fopen(AGELOG_NAME, "a"); + RFS_ASSERT (fplog); + for (i=0; i<argc; i++) { + fprintf(fplog, "%s ", argv[i]); + } + fprintf (fplog, "\n"); + + if (argc>1 && (!strcmp(argv[1], "fmt2"))) + main2 (argc, argv); + else if (argc>1 && (!strcmp(argv[1], "fmt3"))) + main3 (argc, argv); + else if (argc>1 && (!strcmp(argv[1], "fmt4"))) + main4 (argc, argv); + else + main1 (argc, argv); +END: + fclose (fplog); + sprintf(cmd, "date >> %s", AGELOG_NAME); + system (cmd); +} + +int main1(int argc, char ** argv) +{ + char * buf; + static int disk_index=0; + int nfs3proc, size, off, count; + char procname[16]; + struct stat st; + int ret; + int i,j,k; + int ftype_flag = 0, active_flag = 0; + char name[MAX_PLAY_PATH_SIZE]; + int sfh, psfh; + char mapname[1024]; + + profile_t create_profile, write_profile; + if (argc!=9) { + print_usage(); + return; + } + + init(); + EVEN_CHUNK_SIZE = atoi(argv[1]); + RFS_ASSERT ((EVEN_CHUNK_SIZE==0) || (EVEN_CHUNK_SIZE==1)); + FILE_RATIO = atoi (argv[2]); + ACTIVE_RATIO = atoi(argv[3]); + WRITE_CHUNK_NUM = atoi(argv[4]); + MAX_FS_SIZE_MB = atoi(argv[5]); + + if (WRITE_CHUNK_NUM==0) + MIN_WRITE_SIZE = 2000000000; + else { + //MIN_WRITE_SIZE = DISK_FRAGMENT_SIZE; + MIN_WRITE_SIZE = 1; + } + + STAGE_NUM = atoi (argv[6]); + strcpy (mapname, argv[7]); + strcpy (testdir, argv[8]); + ret = stat (testdir, &st); + if ((ret == -1) && (errno==ENOENT)) { + ret = mkdir (testdir, S_IRWXU); + } + RFS_ASSERT (ret >= 0); + + + /* add testdir to obj_fh */ + add_fh_t (&obj_fh, testdir, 1, -1, -1, IS_DIR, EXIST, ACTIVE); + exist_active_obj_num ++; + if (ACTIVE_RATIO >0) + read_fh_map (mapname); + + print_fh_map(&obj_fh); + init_profile_variables(); + + fprintf(fplog, "main1: populate the file system with both trace files and randomly generated files\n"); + fprintf(fplog, "EVEN_CHUNK_SIZE %d FILE_RATIO %d ACTIVE_RATIO %d WRITE_CHUNK_NUM %d MAX_FS_SIZE_MB %d STAGE_NUM %d fh_map %s testdir %s\n", EVEN_CHUNK_SIZE, FILE_RATIO, ACTIVE_RATIO, WRITE_CHUNK_NUM, MAX_FS_SIZE_MB, STAGE_NUM, mapname, testdir); + system("date"); + printf("EVEN_CHUNK_SIZE %d FILE_RATIO %d ACTIVE_RATIO %d WRITE_CHUNK_NUM %d MAX_FS_SIZE_MB %d STAGE_NUM %d fh_map %s testdir %s\n", EVEN_CHUNK_SIZE, FILE_RATIO, ACTIVE_RATIO, WRITE_CHUNK_NUM, MAX_FS_SIZE_MB, STAGE_NUM, mapname, testdir); + fflush(fplog); + + profile_fp = fplog; + init_profile ("create_profile", &create_profile); + init_profile ("write_profile", &write_profile); + + start_profile (&create_profile); + printf ("start creat/mkdir, active_obj_num %d\n", active_obj_num); + for (i=0; (exist_active_obj_num <= active_obj_num) && (fs_size_MB < MAX_FS_SIZE_MB); i++) { + + if ((i!=0) && ((i%10000)==0)) { + fprintf (stderr, "\n%d object created, exist_active_obj_num %d expected size %d MB\n", i, exist_active_obj_num, fs_size_MB); + } + + /* decide on the exact active obj or populated obj */ + if (decide(ACTIVE_RATIO)) { + sfh = select_obj (&obj_fh, DONT_CARE, NON_EXIST, ACTIVE, 0, obj_fh.fh_max); + if (sfh == -1) + break; + + obj_fh.fh[sfh].exist_flag = EXIST; + exist_active_obj_num ++; + ftype_flag = obj_fh.fh[sfh].ftype; + size = obj_fh.fh[sfh].size; + } else { + psfh = select_obj (&obj_fh, IS_DIR, EXIST, DONT_CARE, 0, obj_fh.fh_max); + strcpy (name, obj_fh.fh[psfh].path); + sfh = obj_fh.fh_max; + sprintf(name+strlen(name), "/AGE%d", obj_fh.fh_max); + + /* decide next obj is file or directory */ + if (decide(FILE_RATIO)) { + ftype_flag = IS_FILE; + size = get_file_size(); + } else { + ftype_flag = IS_DIR; + size = -1; + } + add_fh_t (&obj_fh, name, sfh, psfh, size, ftype_flag, EXIST, INACTIVE); + + } + + /* make sure/create the obj pathname on disk */ + assure_exist (sfh, obj_fh.fh[sfh].path, ftype_flag); + /* write file to sizes according certain distribution + if (ftype_flag == IS_FILE) + append_file (obj_fh.fh[sfh].path, obj_fh.fh[sfh].size); + */ + } + fprintf (stderr, "\n%d object created, exist_active_obj_num %d expected size %d MB\n", i, exist_active_obj_num, fs_size_MB); + + end_profile (&create_profile); + start_profile (&write_profile); + //write_file_range (0, obj_fh.fh_max); + for (i=1; i<=STAGE_NUM; i++) { + write_file_range (obj_fh.fh_max*(i-1)/STAGE_NUM, obj_fh.fh_max*i/STAGE_NUM); + //fprintf(stderr, "getchar\n"); + //getchar(); + } + end_profile (&write_profile); + + print_profile ("create_profile", &create_profile); + print_profile ("write_profile", &write_profile); + printf ("exist_active_obj_num %d active_obj_num %d fs_size_MB %d\n", + exist_active_obj_num, active_obj_num, fs_size_MB); + + print_fh_map(&obj_fh); + print_result(); + printf ("end of file system initialization\n"); + system("date"); +} + +int write_file_range (int min, int max) +{ + int i, j, k, sfh; + int write_size, disk_write_size; + + i = 0, j=0, k=0; + printf ("start writing files min %d max %d\n", min, max); + while (1) { + sfh = select_obj (&obj_fh, IS_FILE, EXIST, DONT_CARE, min, max); +/* + if (!decide(obj_fh.fh[sfh].size*100/(MIN_WRITE_SIZE*WRITE_CHUNK_NUM))) { + printf("skip writing small file\n"); + continue; + } +*/ + if (sfh == -1) + break; + write_size = get_write_size (obj_fh.fh[sfh].size, obj_fh.fh[sfh].cur_size); + obj_fh.fh[sfh].cur_size += write_size; + if (obj_fh.fh[sfh].cur_size == obj_fh.fh[sfh].size) { + obj_fh.fh[sfh].exist_flag = COMPLETE; + }; + +#define ACCUMULATE_SMALL_WRITE +// This option improves speed by 12 times. +#ifdef ACCUMULATE_SMALL_WRITE + obj_fh.fh[sfh].accumulated_write_size += write_size; + if (obj_fh.fh[sfh].exist_flag == COMPLETE) { + disk_write_size = obj_fh.fh[sfh].accumulated_write_size; + } else { + disk_write_size = (obj_fh.fh[sfh].accumulated_write_size - + (obj_fh.fh[sfh].accumulated_write_size%DISK_FRAGMENT_SIZE)); + }; + obj_fh.fh[sfh].accumulated_write_size -= disk_write_size; +#else + disk_write_size = write_size; +#endif + + if (disk_write_size >0) { + append_file (sfh, obj_fh.fh[sfh].path, disk_write_size); + if ((i%1000)==0) { + printf ("%d C ", i); + fflush(stdout); + k++; + if ((k%10)==0) + printf("\n"); + } + i++; + } else { + if ((j%100000)==0) { + printf ("%d c ", j); + fflush(stdout); + k++; + if ((k%10)==0) + printf("\n"); + } + j++; + } + } +} + diff --git a/TBBT/trace_play/rfs_c_age.c.trace_base b/TBBT/trace_play/rfs_c_age.c.trace_base new file mode 100644 index 0000000..adeeca5 --- /dev/null +++ b/TBBT/trace_play/rfs_c_age.c.trace_base @@ -0,0 +1,551 @@ +#include <sys/vfs.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include "rfs_assert.h" +#include "profile.h" +#define MKDIR 1 +#define RMDIR 2 +#define CREATE 3 +#define REMOVE 4 +#define WRITE 5 +#define TRUNCATE 6 + +#define MAX_FILES 100000 +#define MAX_DIRS 100000 +#define FILE_FH_HTABLE_SIZE MAX_FILES +#define MAX_NAMELEN 256 +#define MAX_PLAY_PATH_SIZE 256 +#define MAX_COMMAND_LEN (MAX_PLAY_PATH_SIZE+16) +#define NFS_MAXDATA 32768 +#define TRACE_FH_SIZE 64 + +#define FH_T_FLAG_FREE 0 +#define FH_T_FLAG_IN_USE 1 +#define IS_FILE 0 +#define IS_DIR 1 +#define NOT_EXIST 0 +#define EXIST 1 + +typedef struct { + char flag; + //char trace_fh [TRACE_FH_SIZE+1]; + char path[MAX_PLAY_PATH_SIZE]; +} fh_t; + +typedef struct { + char name[32]; + fh_t * fh; + //struct generic_entry * htable; + int fh_size; + int fh_max; + int index; + //int htable_size; +} fh_info_t; + +fh_info_t file_fh, dir_fh; +profile_t read_line_profile, fgets_profile; +char trace_file[MAX_NAMELEN]; +FILE * profile_fp = NULL; +char testdir[MAX_NAMELEN]; + +int age_create_num = 0; +int age_mkdir_num = 0; +int assure_create_num = 0; +int assure_mkdir_num = 0; +int age_write_num = 0; +int nonage_write_num = 0; +int overlap_write_num = 0; + +int init_profile_variables() +{ + init_profile ("read_line profile", &read_line_profile); + init_profile ("fgets profile", &fgets_profile); +} + +int init_fh_info (char * name, fh_info_t * fh_infop, int fh_size, int htable_size) +{ + RFS_ASSERT (strlen(name) < sizeof(fh_infop->name)); + strcpy (fh_infop->name, name); + fh_infop->fh_max = 0; + fh_infop->index = 0; + fh_infop->fh_size = fh_size; + //fh_infop->htable_size = htable_size; + fh_infop->fh = (fh_t *)malloc (sizeof(fh_t)*fh_size); + RFS_ASSERT (fh_infop->fh); + //fh_infop->htable = malloc (sizeof(struct*generic_entry)*htable_size); + //RFS_ASSERT (fh_infop->htable); + printf("initialize %s size %d bytes\n", + //name, sizeof(fh_t)*fh_size + sizeof(struct*generic_entry)*htable_size); + name, sizeof(fh_t)*fh_size); +} + +int init() +{ + init_fh_info ("file_fh", &file_fh, MAX_FILES, MAX_FILES); + init_fh_info ("dir_fh", &dir_fh, MAX_DIRS, MAX_DIRS); +} + +int add_fh_t (fh_info_t * fh_table, char * path, int exist_flag) +{ + int i; + + for (i=0; i<fh_table->fh_size; i++,fh_table->index++) { + if (fh_table->index==fh_table->fh_size) + fh_table->index = 0; + if (fh_table->fh[fh_table->index].flag == FH_T_FLAG_FREE) { + fh_table->fh[fh_table->index].flag = FH_T_FLAG_IN_USE; + //RFS_ASSERT(strlen(path)<MAX_PLAY_PATH_SIZE); + strcpy (fh_table->fh[fh_table->index].path, path); + if (fh_table->index > fh_table->fh_max) + fh_table->fh_max = fh_table->index; + return 0; + } + } + //print_fh_map(fh_table); + RFS_ASSERT (0); +} + +int create_mkdir_op (int flag) +{ + static int fhno = 0; + char name[MAX_NAMELEN]; + char command[MAX_COMMAND_LEN]; + int i; + int fd; + int count = 0; + fh_info_t * fh_infop; + + while (count++ < 100) { + i = random()%dir_fh.fh_max; + if (dir_fh.fh[i].flag==FH_T_FLAG_IN_USE) { + assure_exist(dir_fh.fh[i].path); + strcpy (name, dir_fh.fh[i].path); + if (flag == IS_FILE) { + sprintf (name+strlen(name), "AGEfile%d", fhno++); + fd = creat (name, S_IRWXU); + age_create_num++; + //printf ("create fd %d\n", fd); + close(fd); + fh_infop = &file_fh; + } else { + sprintf (name+strlen(name), "AGEdir%d", fhno++); + fd = mkdir (name, S_IRWXU); + age_mkdir_num++; + fh_infop = &dir_fh; + } + if (fd == -1) { + perror(""); + if (errno == ENOENT) { + dir_fh.fh[i].flag = FH_T_FLAG_FREE; + continue; + } else + RFS_ASSERT (0); + } + add_fh_t (fh_infop, name, EXIST); + RFS_ASSERT (fd >=0); + return 0; + } + }; + return -1; +} + +int remove_op () +{ + int i; + int count = 0; + int ret; + + while (count++<100) { + i = random()%file_fh.fh_max; + if (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) { +/* + if (!strstr(file_fh.fh[i].path, "AGE")) + continue; +*/ + assure_exist(file_fh.fh[i].path); + ret = remove (file_fh.fh[i].path); + RFS_ASSERT (ret ==0); + file_fh.fh[i].flag = FH_T_FLAG_FREE; + return 0; + } + } + return -1; +} + +int rmdir_op() +{ + int i; + int count=0; + char command[MAX_COMMAND_LEN]; + int ret; + + while (count++<100) { + i = random()%dir_fh.fh_max; + if ( (dir_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) { +/* + if (!strstr(file_fh.fh[i].path, "AGE")) + continue; +*/ + assure_exist(file_fh.fh[i].path); + ret = rmdir (dir_fh.fh[i].path); + if (ret == 0) { + dir_fh.fh[i].flag = FH_T_FLAG_FREE; + return 0; + } + RFS_ASSERT ((ret == -1) && (errno == ENOTEMPTY)); + //strcpy (command, "rm -r %s", dir_fh.fh[i].path); + //system (command); + } + } + return -1; +} + +int loop_write (int fd, char * buf, int buflen) +{ + int ret; + int pos = 0; + + while (1) { + ret = write (fd, buf+pos, buflen-pos); + + if (ret == -1) { + perror ("loop write"); + exit (-1); + } + if (ret == buflen-pos) + break; + pos += ret; + } + return 0; +} + +int assure_exist(char * path) +{ + char name[MAX_NAMELEN]; + int ret; + char *p, *q; + int non_exist_flag = 0; + int count=0; + struct stat st; + + ret = stat (path, &st); + if (ret == 0) + return 0; + RFS_ASSERT (errno == ENOENT); + + RFS_ASSERT (!strstr (path, "AGE")); + p = path; + q = name; + while (count++<100) { + for (; *p!=0 && *p!='/'; p++, q++ ) + *q = *p; + *q = 0; + ret = stat (name, &st); + if (ret == -1) { + RFS_ASSERT (errno == ENOENT) + if ((*p)==0) { + ret = creat (name, S_IRWXU); + assure_create_num ++; + RFS_ASSERT (ret >=0); + close(ret); + } else { + ret = mkdir (name, S_IRWXU); + assure_mkdir_num ++; + RFS_ASSERT (ret >=0); + } + } + if ((*p)=='/') { + *q = '/'; + p++; q++; + } else { + RFS_ASSERT ((*p)==0) + return 0; + } + } + RFS_ASSERT (0); +} + +int write_op (int off, int size) +{ + static char buf[NFS_MAXDATA]; + int i; + int count=0; + int fd; + int ret; + struct stat st; + + RFS_ASSERT (size <= NFS_MAXDATA); + while (count++<100) { + i = random()%file_fh.fh_max; + if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) { + assure_exist(file_fh.fh[i].path); + fd = open(file_fh.fh[i].path, O_WRONLY); + if (fd == -1) + perror(""); + //else + //printf ("write fd %d\n", fd); + RFS_ASSERT (fd!=-1); + fstat (fd, &st); + if (st.st_size < (off+size)) { + int written_bytes = 0; + while (written_bytes+NFS_MAXDATA < off+size-st.st_size) { + loop_write (fd, buf, NFS_MAXDATA); + written_bytes += NFS_MAXDATA; + } + loop_write (fd, buf, off+size-st.st_size-written_bytes); + if (strstr(file_fh.fh[i].path, "AGE")) { + age_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA; + } else + nonage_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA; + } else + overlap_write_num++; +/* + if (strstr(file_fh.fh[i].path, "AGE")) { + age_write_num++; + } else + nonage_write_num++; + loop_write (fd, buf, size); +*/ + close(fd); + return 0; + }; + } + return -1; +} + +int truncate_op(int size) +{ + int i; + int count=0; + int ret; + + while (count++<100) { + i = random()%file_fh.fh_max; + if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) { +/* + if (!strstr(file_fh.fh[i].path, "AGE")) + continue; +*/ + assure_exist (file_fh.fh[i].path); + ret = truncate(file_fh.fh[i].path, size); + if (ret ==0) + return 0; + RFS_ASSERT (errno == ENOENT); + file_fh.fh[i].flag = FH_T_FLAG_FREE; + continue; + } + }; + return -1; +} + +int print_fh_map(fh_info_t * fhp) +{ + int i; + int num = 0; + for (i=0; i<fhp->fh_max; i++) { + if (fhp->fh[i].flag == FH_T_FLAG_IN_USE) { + num ++; + printf("%s[%d] %s\n", fhp->name, i, fhp->fh[i].path); + } + } + fprintf(stderr, "fh_max %d total %d entries \n", fhp->fh_max, num); +} + +void read_fh_map(char * fh_map_file) +{ + FILE * fp; + int i = 0; + char buf[1024]; + char trace_fh[TRACE_FH_SIZE]; + char intbuf[9]; + char * trace_path; + char * p; + int map_flag; + int lineno = 0; + int fh_map_debug =0; + char name[MAX_NAMELEN]; + + fp = fopen(fh_map_file, "r"); + if (!fp) { + printf ("can not opern %s\n", fh_map_file); + perror("open"); + exit (0); + } + RFS_ASSERT (fp!=NULL); + + intbuf[8]=0; + + memset(buf, 0, sizeof(buf)); + while (fgets(buf, 1024, fp)) { + RFS_ASSERT (fh_map_debug==0); + lineno ++; + if (lineno % 10000==0) + printf("%d fh_map entry read\n", lineno); + + RFS_ASSERT (buf[strlen(buf)-1]=='\n'); + buf[strlen(buf)-1]=0; + + trace_path = buf + TRACE_FH_SIZE +1; + + strcpy (name, testdir); + strcat (name, trace_path); + if ((*(buf+strlen(buf)-1))=='/') { + *(buf+strlen(buf)-1)=0; + add_fh_t (&dir_fh, name, NOT_EXIST); + } else { + add_fh_t (&file_fh, name, NOT_EXIST); + } + } + + fclose(fp); + if (fh_map_debug) { + print_fh_map (&file_fh); + print_fh_map (&dir_fh); + } +} + +int print_usage() +{ + printf("age trace_file fh_path_map testdir\n"); +} + +inline char * read_line (int disk_index) +{ + static FILE * fp=NULL; + static int start=0; + static int start_disk_index=0; + int i; + static int finish_flag = 0; + +#define READ_LINE_BUF_SIZE 1000 +#define READ_LINE_LENGTH 32 + + static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH]; + start_profile (&read_line_profile); + + if (fp==NULL) { + if (strcmp(trace_file, "stdin")) { + fp = fopen(trace_file, "r"); + if (!fp) { + printf("can not open files %s\n", fp); + perror("open"); + } + } else { + fp = stdin; + } + RFS_ASSERT (fp!=NULL); + for (i=0; i<READ_LINE_BUF_SIZE; i++) { + start_profile(&fgets_profile); + if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) { + RFS_ASSERT (0); + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]); + } + } + + RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE) + if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) { + if (finish_flag) { + return NULL; + } + start_profile(&fgets_profile); + if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) { + end_profile(&fgets_profile); + fclose(fp); + finish_flag = 1; + return NULL; + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]); + start = (start+1) % READ_LINE_BUF_SIZE; + start_disk_index ++; + } + RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE) + i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE; + + end_profile (&read_line_profile); + return (line_buf[i]); +} + +int f() +{}; + +int print_result() +{ + struct statfs stfs; + int ret; + ret = statfs (testdir, &stfs); + RFS_ASSERT (ret == 0); + fprintf(stderr, "number of files %d size of file system %d\n", stfs.f_files, stfs.f_bfree); + fprintf(stderr, "assure_create_num %d assure_mkdir_num %d, age_create_num %d age_mkdir_num %d age_write_num %d nonage_write_num %d overlap_write_num %d\n", + assure_create_num, assure_mkdir_num, age_create_num, age_mkdir_num, age_write_num, nonage_write_num, overlap_write_num); + printf("assure_create_num %d assure_mkdir_num %d, age_create_num %d age_mkdir_num %d age_write_num %d nonage_write_num %d overlap_write_num %d\n", + assure_create_num, assure_mkdir_num, age_create_num, age_mkdir_num, age_write_num, nonage_write_num, overlap_write_num); +} + +int main(int argc, char ** argv) +{ + char * buf; + static int disk_index=0; + int j, nfs3proc, size, off, count; + char procname[16]; + struct stat st; + int ret; + + if (argc!=4) { + print_usage(); + exit(0); + } + + init(); + strcpy (trace_file, argv[1]); + strcpy (testdir, argv[3]); + ret = stat (testdir, &st); + RFS_ASSERT (ret == 0); + read_fh_map(argv[2]); + init_profile_variables(); + + while ((buf=read_line(++disk_index))!=NULL) { + + memset (procname, 0, sizeof(procname)); + //printf ("line[%d] %s", disk_index, buf); + sscanf (&buf[0], "%s", procname); + j = 0; + while ((*(buf+j)!=' ') && (*(buf+j)!='\n')) + j++; + j++; + if (!strcmp(procname, "write")) + sscanf (&buf[j], "off %x count %x", &off, &count); + + if (!strcmp(procname, "setattr")) + sscanf (&buf[j], "size %x", &size); + + if (!strcmp(procname, "write")) + ret = write_op(off, count); + else if (!strcmp(procname, "create")) + ret = create_mkdir_op(IS_FILE); + else if (!strcmp(procname, "mkdir")) + ret = create_mkdir_op(IS_DIR); + else if (!strcmp(procname, "remove")) + ret = remove_op(); + else if (!strcmp(procname, "rmdir")) + ret = rmdir_op(); + else if (!strcmp(procname, "setattr")) + ret = truncate_op(size); + else { + printf("disk_index %d procname %s\n", disk_index, procname); + RFS_ASSERT (0); + } + if (ret!=0) { + printf("execute disk_line[%d] %s error\n", disk_index, buf); + } + if ((disk_index%100)==0) { + fprintf (stderr, "%d disk trace parsed \n", disk_index); + print_result(); + } + } + print_result(); +} diff --git a/TBBT/trace_play/rfs_c_age.c.unit_base b/TBBT/trace_play/rfs_c_age.c.unit_base new file mode 100644 index 0000000..176ad54 --- /dev/null +++ b/TBBT/trace_play/rfs_c_age.c.unit_base @@ -0,0 +1,915 @@ +/* rfs_age_unit_base.c */ +#include <sys/vfs.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include "rfs_assert.h" +#include "profile.h" +#define MKDIR 1 +#define RMDIR 2 +#define CREATE 3 +#define REMOVE 4 +#define WRITE 5 +#define TRUNCATE 6 + +#define MAX_FILES 100000 +#define MAX_DIRS 100000 +#define FILE_FH_HTABLE_SIZE MAX_FILES +#define MAX_NAMELEN 512 +#define MAX_PLAY_PATH_SIZE 1024 +#define MAX_COMMAND_LEN (MAX_PLAY_PATH_SIZE+16) +#define NFS_MAXDATA 4096 +//#define NFS_MAXDATA 32768 +#define TRACE_FH_SIZE 64 + +#define FH_T_FLAG_FREE 0 +#define FH_T_FLAG_IN_USE 1 +#define IS_FILE 0 +#define IS_DIR 1 +#define EXIST 0 +#define NON_EXIST 1 +#define COMPLETE 3 +#define ACTIVE 0 +#define INACTIVE 1 +#define DONT_CARE 2 +#define FILE_RATIO 50 +#define DISK_FRAGMENT_SIZE 4096 +//#define FRAGMENT_NUM 5 +//#define MIN_WRITE_SIZE 512 +//#define MIN_WRITE_SIZE 2000000000 + +static char ftypename[3][32] = {"FILE", "DIR", "FTYPE_DONT_CARE"}; +static char activename[3][32] = {"ACTIVE", "INACTIVE", "ACTIVE_DONT_CARE"}; +static char existname[4][32] = {"EXIST", "NON_EXIST", "EXIST_DONT_CARE", "COMPLETE"}; + +typedef struct { + char flag; + char ftype; + char exist_flag; + int psfh; + int size; + int cur_size; + //char trace_fh [TRACE_FH_SIZE+1]; + char path[MAX_PLAY_PATH_SIZE]; +} fh_t; + +typedef struct { + char name[32]; + fh_t * fh; + //struct generic_entry * htable; + int fh_size; + int fh_max; + int active_fh_max; + //int index; + //int htable_size; +} fh_info_t; + +fh_info_t obj_fh; +profile_t read_line_profile, fgets_profile; +char trace_file[MAX_NAMELEN]; +FILE * profile_fp = NULL; +char testdir[MAX_NAMELEN]; + +int active_obj_num = 0; +int exist_active_obj_num = 0; +static int active_file_num = 0, active_dir_num =0, age_file_num = 0, age_dir_num = 0; + +int age_create_num = 0; +int age_mkdir_num = 0; +int assure_create_num = 0; +int assure_mkdir_num = 0; +int age_write_num = 0; +int nonage_write_num = 0; +int overlap_write_num = 0; + +int rfs_debug = 0; + + +int ACTIVE_RATIO; +int FRAGMENT_NUM; +int MIN_WRITE_SIZE = 512; + +int aging_dirs () +{ + +} + + +int init_profile_variables() +{ + init_profile ("read_line profile", &read_line_profile); + init_profile ("fgets profile", &fgets_profile); +} + +int init_fh_info (char * name, fh_info_t * fh_infop, int fh_size, int htable_size) +{ + int i; + + RFS_ASSERT (strlen(name) < sizeof(fh_infop->name)); + strcpy (fh_infop->name, name); + fh_infop->fh_max = 0; + //fh_infop->index = 0; + fh_infop->fh_size = fh_size; + //fh_infop->htable_size = htable_size; + fh_infop->fh = (fh_t *)malloc (sizeof(fh_t)*fh_size); + RFS_ASSERT (fh_infop->fh); + //fh_infop->htable = malloc (sizeof(struct*generic_entry)*htable_size); + //RFS_ASSERT (fh_infop->htable); + printf("initialize %s size %d bytes\n", + //name, sizeof(fh_t)*fh_size + sizeof(struct*generic_entry)*htable_size); + name, sizeof(fh_t)*fh_size); + + for (i=0; i<fh_size; i++) + fh_infop->fh[i].flag = FH_T_FLAG_FREE; +} + +int init() +{ +// init_fh_info ("file_fh", &file_fh, MAX_FILES, MAX_FILES); +// init_fh_info ("dir_fh", &dir_fh, MAX_DIRS, MAX_DIRS); + init_fh_info ("obj_fh", &obj_fh, MAX_FILES+MAX_DIRS, MAX_FILES+MAX_DIRS); +} + +int add_fh_t (fh_info_t * fh_table, char * path, int sfh, int psfh, int size, int ftype, int exist_flag, int active_flag) +{ + int i; + + RFS_ASSERT (sfh >0); + + if (active_flag == ACTIVE) + active_obj_num ++; + else + RFS_ASSERT (sfh >= fh_table->active_fh_max); + + if (rfs_debug) + printf ("add to %s path %s sfh %d size %d %s %s %s\n", fh_table->name, path, sfh, size, + ftypename[ftype], existname[exist_flag], activename[active_flag]); + + RFS_ASSERT ( (sfh>=0) && (sfh<fh_table->fh_size) ); + RFS_ASSERT (fh_table->fh[sfh].flag==FH_T_FLAG_FREE); + fh_table->fh[sfh].flag = FH_T_FLAG_IN_USE; + if (sfh >= fh_table->fh_max) + fh_table->fh_max = sfh+1; + strcpy (fh_table->fh[sfh].path, path); + fh_table->fh[sfh].psfh = psfh; + fh_table->fh[sfh].size = size; + fh_table->fh[sfh].cur_size = 0; + fh_table->fh[sfh].ftype = ftype; + fh_table->fh[sfh].exist_flag = exist_flag; + if (active_flag == ACTIVE) { + if (ftype == IS_FILE) + active_file_num ++; + else { + RFS_ASSERT (ftype== IS_DIR); + active_dir_num ++; + } + } else { + if (ftype == IS_FILE) + age_file_num ++; + else { + RFS_ASSERT (ftype== IS_DIR); + age_dir_num ++; + } + } + //print_fh_map(fh_table); +} + + +int loop_write (int fd, char * buf, int buflen) +{ + int ret; + int pos = 0; + + while (1) { + ret = write (fd, buf+pos, buflen-pos); + + if (ret == -1) { + printf ("fd %d\n", fd); + perror ("loop write"); + exit (-1); + } + if (ret == buflen-pos) + break; + pos += ret; + } + return 0; +} + +int assure_exist(int sfh, char * path, int ftype_flag) +{ + char name[MAX_NAMELEN]; + int ret; + char *p, *q; + int non_exist_flag = 0; + int count=0; + struct stat st; + + if (rfs_debug) + printf("assure_exist %s\n", path); + + ret = stat (path, &st); + if (ret == 0) + return 0; + RFS_ASSERT (errno == ENOENT); + + p = path; + q = name; + if (*p=='/') { + *q='/'; + p++; + q++; + } + while (count++<100) { + /* copy the next component from path to name */ + for (; *p!=0 && *p!='/'; p++, q++ ) + *q = *p; + *q = 0; + ret = stat (name, &st); + if (ret == -1) { + RFS_ASSERT (errno == ENOENT) + if ((*p)==0 && (ftype_flag==IS_FILE)) { + ret = creat (name, S_IRWXU); + if (ret == -1) + perror (name); + RFS_ASSERT (ret >=0); + assure_create_num ++; + if (rfs_debug) + printf("sfh %d create %s\n", sfh, name); + close(ret); + } else { + ret = mkdir (name, S_IRWXU); + assure_mkdir_num ++; + if (rfs_debug) { + if (*p==0) + printf("sfh %d mkdir %s\n", sfh, name); + else + printf("sfh %d middle mkdir %s\n", sfh, name); + } + RFS_ASSERT (ret >=0); + } + } + if ((*p)=='/') { + *q = '/'; + p++; q++; + } else { + RFS_ASSERT ((*p)==0) + return 0; + } + } + RFS_ASSERT (0); +} + + +int print_fh_map(fh_info_t * fhp) +{ + int i; + int num = 0; + int active_obj_num = 0; + + + for (i=0; i<fhp->fh_max; i++) { + if (fhp->fh[i].flag == FH_T_FLAG_IN_USE) { + num ++; + if (i < fhp->active_fh_max) + active_obj_num++; + + if (rfs_debug) + printf("%s[%d] %s %s %s\n", fhp->name, i, fhp->fh[i].path, ftypename[fhp->fh[i].ftype], existname[fhp->fh[i].exist_flag]); + } + } + fprintf(stderr, "fh_max %d active_fh_max %d, in_use_num %d entries active_obj_num %d \n", fhp->fh_max, fhp->active_fh_max, num, active_obj_num); +} + +void read_fh_map(char * fh_map_file) +{ + FILE * fp; + int i = 0; + char buf[1024]; + char trace_fh[TRACE_FH_SIZE]; + char intbuf[9]; + char * trace_path; + char * p; + int map_flag; + int lineno = 0; + int fh_map_debug =0; + char name[MAX_NAMELEN]; + int sfh; + + fp = fopen(fh_map_file, "r"); + if (!fp) { + printf ("can not opern %s\n", fh_map_file); + perror("open"); + exit (0); + } + RFS_ASSERT (fp!=NULL); + + intbuf[8]=0; + + memset(buf, 0, sizeof(buf)); + while (fgets(buf, 1024, fp)) { + RFS_ASSERT (fh_map_debug==0); + lineno ++; + if (rfs_debug) + printf ("line %d %s", lineno, buf); + if (lineno % 10000==0) + printf("%d fh_map entry read\n", lineno); + + sfh = 0; + if (!strncmp(buf, "::DIR ", strlen("::DIR "))) { + strcpy (name, testdir); + if (buf[6]=='/') { + sscanf(buf, "::DIR %s %d\n", name+strlen(name), &sfh); + add_fh_t (&obj_fh, name, sfh, -1, -1, IS_DIR, NON_EXIST, ACTIVE); + } else { + RFS_ASSERT (!strncmp(buf,"::DIR Fake 1\n", strlen("::DIR Fake 1\n"))); + sfh = 1; + add_fh_t (&obj_fh, name, sfh, -1, -1, IS_DIR, EXIST, ACTIVE); + exist_active_obj_num ++; + } + } else { + char * p; + int psfh, sfh, size; + char filename[MAX_NAMELEN]; + + if (!strncmp(buf, "::TBDIR", strlen("::TBDIR"))) + continue; + + p = strstr(buf, "parent"); + RFS_ASSERT (p); + sscanf(p, "parent %d\n", &psfh); + RFS_ASSERT (obj_fh.fh[psfh].flag == FH_T_FLAG_IN_USE); + p = strstr(p, "name"); + RFS_ASSERT (p); + if (!strncmp(p, "name xx", strlen("name xx"))) { + sscanf(p, "name xx-%s sfh %d size %x", filename, &sfh, &size); + //printf ("name xx-%s sfh %d\n", filename, sfh); + } else { + sscanf(p, "name \"%s sfh %d size %x", filename, &sfh, &size); + //printf ("name %s sfh %d\n", filename, sfh); + filename[strlen(filename)-1]=0; + } + strcpy (name, obj_fh.fh[psfh].path); + strcat (name, "/"); + strcat (name, filename); + add_fh_t (&obj_fh, name, sfh, psfh, size, IS_FILE, NON_EXIST, ACTIVE); + } + } + + fclose(fp); + obj_fh.active_fh_max = obj_fh.fh_max; + if (fh_map_debug) { + print_fh_map (&obj_fh); + } +} + +int print_usage() +{ + printf("agefs ACTIVE_RATIO FRAGMENT_NUM fh_path_map testdir\n"); + printf("Note: if populate_scale is 4, the total active file size is 1GB\n"); + printf(" then the total initial file system size is about 4GB\n"); +} + +inline char * read_line (int disk_index) +{ + static FILE * fp=NULL; + static int start=0; + static int start_disk_index=0; + int i; + static int finish_flag = 0; + +#define READ_LINE_BUF_SIZE 1000 +#define READ_LINE_LENGTH 32 + + static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH]; + start_profile (&read_line_profile); + + if (fp==NULL) { + if (strcmp(trace_file, "stdin")) { + fp = fopen(trace_file, "r"); + if (!fp) { + printf("can not open files %s\n", fp); + perror("open"); + } + } else { + fp = stdin; + } + RFS_ASSERT (fp!=NULL); + for (i=0; i<READ_LINE_BUF_SIZE; i++) { + start_profile(&fgets_profile); + if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) { + RFS_ASSERT (0); + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]); + } + } + + RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE) + if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) { + if (finish_flag) { + return NULL; + } + start_profile(&fgets_profile); + if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) { + end_profile(&fgets_profile); + fclose(fp); + finish_flag = 1; + return NULL; + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]); + start = (start+1) % READ_LINE_BUF_SIZE; + start_disk_index ++; + } + RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE) + i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE; + + end_profile (&read_line_profile); + return (line_buf[i]); +} + +int f() +{}; + +int print_result() +{ + struct statfs stfs; + int ret; + static struct statfs first_stfs; + static int first_entry = 1; + + ret = statfs (testdir, &stfs); + RFS_ASSERT (ret == 0); + if (first_entry) { + first_entry = 0; + first_stfs = stfs; + } + + fprintf(stderr, "active_file_num %d active_dir_num %d age_file_num %d age_dir_num %d\n", + active_file_num, active_dir_num, age_file_num, age_dir_num); + fprintf(stderr, "number of used file nodes %d, used (4K) blocks in fs %d (%d MB)\n", first_stfs.f_ffree-stfs.f_ffree, first_stfs.f_bfree - stfs.f_bfree, (first_stfs.f_bfree-stfs.f_bfree)/(1000000/4096)); + fprintf(stderr, "assure_create_num %d assure_mkdir_num %d\n", assure_create_num, assure_mkdir_num); +} + +typedef struct { + int pcnt; /* percentile */ + int size; /* file size in KB */ +} sfs_io_file_size_dist; + +sfs_io_file_size_dist Default_file_size_dist[] = { + /* percentage KB size */ + { 94, 64}, /* 4% */ + { 97, 128}, /* 3% */ +#ifdef notdef + { 33, 1}, /* 33% */ + { 54, 2}, /* 21% */ + { 67, 4}, /* 13% */ + { 77, 8}, /* 10% */ + { 85, 16}, /* 8% */ + { 90, 32}, /* 5% */ + { 94, 64}, /* 4% */ + { 97, 128}, /* 3% */ +#endif + { 99, 256}, /* 2% */ + { 100, 1024}, /* 1% */ + { 0, 0} +}; + +/* + * For a value between 0-99, return a size based on distribution + */ +static int +get_file_size() +{ + static file_array_initialized = 0; + static int file_size_array[100]; + int i; + + i = random() % 100; + + if (i < 0 || i > 99) + return (0); + + if (file_array_initialized == 0) { + int j, k; + for (j = 0, k = 0; j < 100; j++) { + if (j >= Default_file_size_dist[k].pcnt && + Default_file_size_dist[k + 1].size != 0) + k++; + file_size_array[j] = Default_file_size_dist[k].size * 1024; + } + file_array_initialized++; + } + return (file_size_array[i]); +} + +int range_random(int min, int max) +{ + int i; + i = random()%(max-min) + min; + return i; +} + +/* answer 1 with a probability of percent/100 */ +int decide(int percent) +{ + int i = random()%100; + if (i<percent) + return 1; + else + return 0; +} + +int select_obj (fh_info_t * fhp, int ftype, int exist_flag, int active_flag) +{ + int i; + int min, max; + int sfh, count = 0; + + //printf ("select_obj %s %s %s\n", ftypename[ftype], existname[exist_flag], activename[active_flag]); + if (active_flag == ACTIVE) { + sfh = range_random (0, fhp->active_fh_max); + for (i=0; i<fhp->active_fh_max; i++) { + if ((fhp->fh[sfh].flag == FH_T_FLAG_IN_USE) && + ((ftype==DONT_CARE) || (ftype ==fhp->fh[sfh].ftype)) && + ((exist_flag==DONT_CARE) || (fhp->fh[sfh].exist_flag == exist_flag))) + return sfh; + sfh = (sfh+1) % fhp->active_fh_max; + } + } else { + RFS_ASSERT (active_flag == DONT_CARE); + RFS_ASSERT (exist_flag == EXIST); + sfh = range_random (0, fhp->fh_max); + for (i=0; i<fhp->fh_max; i++) { + if ((fhp->fh[sfh].flag == FH_T_FLAG_IN_USE) && + ((ftype==DONT_CARE) || (fhp->fh[sfh].ftype == ftype)) && + (fhp->fh[sfh].exist_flag == EXIST)) { + return sfh; + } + sfh = (sfh+1) % fhp->fh_max; + } + } + return -1; + + print_fh_map(&obj_fh); + printf ("active_obj_num %d exist_active_obj_num %d \n", active_obj_num, exist_active_obj_num); + printf ("failed select_obj %s %s\n", ftypename[ftype], activename[active_flag]); + RFS_ASSERT (0); +} + +/* append "size" to file "path" */ +int append_file (int sfh, char * path, int size) +{ + int fd; + int written_bytes = 0; + static char buf[NFS_MAXDATA]; + + if (rfs_debug) + printf ("sfh %d append_file %s size %d\n", sfh, path, size); + + fd = open (path, O_WRONLY|O_APPEND); + if (fd==-1) + perror(path); + RFS_ASSERT (fd > 0); + + while (written_bytes+NFS_MAXDATA < size) { + loop_write (fd, buf, NFS_MAXDATA); + written_bytes += NFS_MAXDATA; + } + loop_write (fd, buf, size-written_bytes); + close(fd); +} + +int get_write_size (int target_size, int cur_size) +{ + int i; + if (target_size - cur_size < MIN_WRITE_SIZE) + return (target_size - cur_size); + + /* target_size/FRAGMENT_NUM would be the average value of i */ + if (target_size < FRAGMENT_NUM) { + i = MIN_WRITE_SIZE; + } else { + i = random() % (2*(target_size/FRAGMENT_NUM)); + if (i < MIN_WRITE_SIZE) + i = MIN_WRITE_SIZE; + } + if (i > (target_size - cur_size)) + i = target_size - cur_size; + + return i; +} + +int main(int argc, char ** argv) +{ + char * buf; + static int disk_index=0; + int j, nfs3proc, size, off, count; + char procname[16]; + struct stat st; + int ret; + int i; + int ftype_flag = 0, active_flag = 0; + char name[MAX_PLAY_PATH_SIZE]; + int sfh, psfh; + + if (argc!=5) { + print_usage(); + exit(0); + } + + init(); + ACTIVE_RATIO = atoi(argv[1]); + FRAGMENT_NUM = atoi(argv[2]); + if (FRAGMENT_NUM==0) + MIN_WRITE_SIZE = 2000000000; + else + MIN_WRITE_SIZE = DISK_FRAGMENT_SIZE; + + strcpy (testdir, argv[4]); + ret = stat (testdir, &st); + if ((ret == -1) && (errno==ENOENT)) { + ret = mkdir (testdir, S_IRWXU); + } + RFS_ASSERT (ret >= 0); + read_fh_map(argv[3]); + print_fh_map(&obj_fh); + init_profile_variables(); + +/* NOTE: should have put file and directories in one table */ + + for (i=0; exist_active_obj_num < active_obj_num; i++) { + + if ((i!=0) && ((i%1000)==0)) { + fprintf (stderr, "\n%d object created \n", i); + print_result(); + } + + /* decide on the exact active obj or populated obj */ + if (decide(ACTIVE_RATIO)) { + sfh = select_obj (&obj_fh, DONT_CARE, NON_EXIST, ACTIVE); + if (sfh == -1) + break; + + obj_fh.fh[sfh].exist_flag = EXIST; + exist_active_obj_num ++; + ftype_flag = obj_fh.fh[sfh].ftype; + size = obj_fh.fh[sfh].size; + +/* + { + int tmpfh = sfh; + while (obj_fh.fh[--tmpfh].exist_flag == NON_EXIST) { + if (strstr(obj_fh.fh[sfh].path, obj_fh.fh[tmpfh].path)) { + obj_fh.fh[tmpfh].exist_flag = EXIST; + //printf ("set %s to exist due to %s\n", obj_fh.fh[tmpfh].path, obj_fh.fh[sfh].path); + exist_active_obj_num ++; + } + } + } +*/ + } else { + psfh = select_obj (&obj_fh, IS_DIR, EXIST, DONT_CARE); + strcpy (name, obj_fh.fh[psfh].path); + sfh = obj_fh.fh_max; + sprintf(name+strlen(name), "/AGE%d", obj_fh.fh_max); + + /* decide next obj is file or directory */ + if (decide(FILE_RATIO)) { + ftype_flag = IS_FILE; + size = get_file_size(); + } else { + ftype_flag = IS_DIR; + size = -1; + } + add_fh_t (&obj_fh, name, sfh, psfh, size, ftype_flag, EXIST, INACTIVE); + + } + + /* make sure/create the obj pathname on disk */ + assure_exist (sfh, obj_fh.fh[sfh].path, ftype_flag); + /* write file to sizes according certain distribution + if (ftype_flag == IS_FILE) + append_file (obj_fh.fh[sfh].path, obj_fh.fh[sfh].size); + */ + + } + + i = 0; + printf ("start writing files\n"); + while (1) { + int write_size; + sfh = select_obj (&obj_fh, IS_FILE, EXIST, DONT_CARE); + if (sfh == -1) + break; + write_size = get_write_size (obj_fh.fh[sfh].size, obj_fh.fh[sfh].cur_size); + append_file (sfh, obj_fh.fh[sfh].path, write_size); + obj_fh.fh[sfh].cur_size += write_size; + if (obj_fh.fh[sfh].cur_size == obj_fh.fh[sfh].size) { + obj_fh.fh[sfh].exist_flag = COMPLETE; + } + if ((i%100)==0) + printf ("%d append_file operation performed\n", i); + i++; + } + + printf ("end of file system object creation\n"); + print_fh_map(&obj_fh); + print_result(); +} + +#ifdef notdef +int create_mkdir_op (int flag) +{ + static int fhno = 0; + char name[MAX_NAMELEN]; + char command[MAX_COMMAND_LEN]; + int i; + int fd; + int count = 0; + fh_info_t * fh_infop; + + while (count++ < 100) { + i = random()%dir_fh.fh_max; + if (dir_fh.fh[i].flag==FH_T_FLAG_IN_USE) { + assure_exist(dir_fh.fh[i].path); + strcpy (name, dir_fh.fh[i].path); + if (flag == IS_FILE) { + sprintf (name+strlen(name), "AGEfile%d", fhno++); + fd = creat (name, S_IRWXU); + age_create_num++; + //printf ("create fd %d\n", fd); + close(fd); + fh_infop = &file_fh; + } else { + sprintf (name+strlen(name), "AGEdir%d", fhno++); + fd = mkdir (name, S_IRWXU); + age_mkdir_num++; + fh_infop = &dir_fh; + } + if (fd == -1) { + perror(""); + if (errno == ENOENT) { + dir_fh.fh[i].flag = FH_T_FLAG_FREE; + continue; + } else + RFS_ASSERT (0); + } + add_fh_t (fh_infop, name, EXIST); + RFS_ASSERT (fd >=0); + return 0; + } + }; + return -1; +} + +int remove_op () +{ + int i; + int count = 0; + int ret; + + while (count++<100) { + i = random()%file_fh.fh_max; + if (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) { +/* + if (!strstr(file_fh.fh[i].path, "AGE")) + continue; +*/ + assure_exist(file_fh.fh[i].path); + ret = remove (file_fh.fh[i].path); + RFS_ASSERT (ret ==0); + file_fh.fh[i].flag = FH_T_FLAG_FREE; + return 0; + } + } + return -1; +} + +int rmdir_op() +{ + int i; + int count=0; + char command[MAX_COMMAND_LEN]; + int ret; + + while (count++<100) { + i = random()%dir_fh.fh_max; + if ( (dir_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) { +/* + if (!strstr(file_fh.fh[i].path, "AGE")) + continue; +*/ + assure_exist(file_fh.fh[i].path); + ret = rmdir (dir_fh.fh[i].path); + if (ret == 0) { + dir_fh.fh[i].flag = FH_T_FLAG_FREE; + return 0; + } + RFS_ASSERT ((ret == -1) && (errno == ENOTEMPTY)); + //strcpy (command, "rm -r %s", dir_fh.fh[i].path); + //system (command); + } + } + return -1; +} + +int write_op (int off, int size) +{ + static char buf[NFS_MAXDATA]; + int i; + int count=0; + int fd; + int ret; + struct stat st; + + RFS_ASSERT (size <= NFS_MAXDATA); + while (count++<100) { + i = random()%file_fh.fh_max; + if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) { + assure_exist(file_fh.fh[i].path); + fd = open(file_fh.fh[i].path, O_WRONLY); + if (fd == -1) + perror(""); + //else + //printf ("write fd %d\n", fd); + RFS_ASSERT (fd!=-1); + fstat (fd, &st); + if (st.st_size < (off+size)) { + int written_bytes = 0; + while (written_bytes+NFS_MAXDATA < off+size-st.st_size) { + loop_write (fd, buf, NFS_MAXDATA); + written_bytes += NFS_MAXDATA; + } + loop_write (fd, buf, off+size-st.st_size-written_bytes); + if (strstr(file_fh.fh[i].path, "AGE")) { + age_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA; + } else + nonage_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA; + } else + overlap_write_num++; +/* + if (strstr(file_fh.fh[i].path, "AGE")) { + age_write_num++; + } else + nonage_write_num++; + loop_write (fd, buf, size); +*/ + close(fd); + return 0; + }; + } + return -1; +} + +int truncate_op(int size) +{ + int i; + int count=0; + int ret; + + while (count++<100) { + i = random()%file_fh.fh_max; + if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) { +/* + if (!strstr(file_fh.fh[i].path, "AGE")) + continue; +*/ + assure_exist (file_fh.fh[i].path); + ret = truncate(file_fh.fh[i].path, size); + if (ret ==0) + return 0; + RFS_ASSERT (errno == ENOENT); + file_fh.fh[i].flag = FH_T_FLAG_FREE; + continue; + } + }; + return -1; +} + +int aging_files () +{ + char name[MAX_NAMELEN]; + int sfh; /* file to be aged */ + int psfh; + int ret; + struct stat st; + int agefd; + + /* get the sfh and size of the selected file to be aged */ + sfh = select_obj (&obj_fh, IS_FILE, EXIST, ACTIVE); + ret = stat (obj_fh.fh[sfh].path, &st); + RFS_ASSERT (ret == 0); + ret = truncate(obj_fh.fh[i].path, st.st_size/2); + RFS_ASSERT (ret==0); + + psfh = obj_fh.fh[sfh].psfh; + strcpy (name, obj_fh.fh[psfh].path); + sprintf(name+strlen(name), "/AGE%d", obj_fh.fh_max); + agefd = creat (name, S_IRWXU); + //write (agefs, buf, 0); + + add_fh_t (&obj_fh, name, sfh, psfh, size, ftype_flag, EXIST, INACTIVE); +} +#endif diff --git a/TBBT/trace_play/rfs_c_age.obsolete.c b/TBBT/trace_play/rfs_c_age.obsolete.c new file mode 100644 index 0000000..aab4644 --- /dev/null +++ b/TBBT/trace_play/rfs_c_age.obsolete.c @@ -0,0 +1,194 @@ +#ifdef notdef +int create_mkdir_op (int flag) +{ + static int fhno = 0; + char name[MAX_NAMELEN]; + char command[MAX_COMMAND_LEN]; + int i; + int fd; + int count = 0; + fh_info_t * fh_infop; + + while (count++ < 100) { + i = random()%dir_fh.fh_max; + if (dir_fh.fh[i].flag==FH_T_FLAG_IN_USE) { + assure_exist(dir_fh.fh[i].path); + strcpy (name, dir_fh.fh[i].path); + if (flag == IS_FILE) { + sprintf (name+strlen(name), "AGEfile%d", fhno++); + fd = creat (name, S_IRWXU); + age_create_num++; + //printf ("create fd %d\n", fd); + close(fd); + fh_infop = &file_fh; + } else { + sprintf (name+strlen(name), "AGEdir%d", fhno++); + fd = mkdir (name, S_IRWXU); + age_mkdir_num++; + fh_infop = &dir_fh; + } + if (fd == -1) { + perror(""); + if (errno == ENOENT) { + dir_fh.fh[i].flag = FH_T_FLAG_FREE; + continue; + } else + RFS_ASSERT (0); + } + add_fh_t (fh_infop, name, EXIST); + RFS_ASSERT (fd >=0); + return 0; + } + }; + return -1; +} + +int remove_op () +{ + int i; + int count = 0; + int ret; + + while (count++<100) { + i = random()%file_fh.fh_max; + if (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) { +/* + if (!strstr(file_fh.fh[i].path, "AGE")) + continue; +*/ + assure_exist(file_fh.fh[i].path); + ret = remove (file_fh.fh[i].path); + RFS_ASSERT (ret ==0); + file_fh.fh[i].flag = FH_T_FLAG_FREE; + return 0; + } + } + return -1; +} + +int rmdir_op() +{ + int i; + int count=0; + char command[MAX_COMMAND_LEN]; + int ret; + + while (count++<100) { + i = random()%dir_fh.fh_max; + if ( (dir_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) { +/* + if (!strstr(file_fh.fh[i].path, "AGE")) + continue; +*/ + assure_exist(file_fh.fh[i].path); + ret = rmdir (dir_fh.fh[i].path); + if (ret == 0) { + dir_fh.fh[i].flag = FH_T_FLAG_FREE; + return 0; + } + RFS_ASSERT ((ret == -1) && (errno == ENOTEMPTY)); + //strcpy (command, "rm -r %s", dir_fh.fh[i].path); + //system (command); + } + } + return -1; +} + +int write_op (int off, int size) +{ + static char buf[NFS_MAXDATA]; + int i; + int count=0; + int fd; + int ret; + struct stat st; + + RFS_ASSERT (size <= NFS_MAXDATA); + while (count++<100) { + i = random()%file_fh.fh_max; + if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) { + assure_exist(file_fh.fh[i].path); + fd = open(file_fh.fh[i].path, O_WRONLY); + if (fd == -1) + perror(""); + //else + //printf ("write fd %d\n", fd); + RFS_ASSERT (fd!=-1); + fstat (fd, &st); + if (st.st_size < (off+size)) { + int written_bytes = 0; + while (written_bytes+NFS_MAXDATA < off+size-st.st_size) { + loop_write (fd, buf, NFS_MAXDATA); + written_bytes += NFS_MAXDATA; + } + loop_write (fd, buf, off+size-st.st_size-written_bytes); + if (strstr(file_fh.fh[i].path, "AGE")) { + age_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA; + } else + nonage_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA; + } else + overlap_write_num++; +/* + if (strstr(file_fh.fh[i].path, "AGE")) { + age_write_num++; + } else + nonage_write_num++; + loop_write (fd, buf, size); +*/ + close(fd); + return 0; + }; + } + return -1; +} + +int truncate_op(int size) +{ + int i; + int count=0; + int ret; + + while (count++<100) { + i = random()%file_fh.fh_max; + if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) { +/* + if (!strstr(file_fh.fh[i].path, "AGE")) + continue; +*/ + assure_exist (file_fh.fh[i].path); + ret = truncate(file_fh.fh[i].path, size); + if (ret ==0) + return 0; + RFS_ASSERT (errno == ENOENT); + file_fh.fh[i].flag = FH_T_FLAG_FREE; + continue; + } + }; + return -1; +} + +int aging_files () +{ + char name[MAX_NAMELEN]; + int sfh; /* file to be aged */ + int psfh; + int ret; + struct stat st; + int agefd; + + /* get the sfh and size of the selected file to be aged */ + sfh = select_obj (&obj_fh, IS_FILE, EXIST, ACTIVE); + ret = stat (obj_fh.fh[sfh].path, &st); + RFS_ASSERT (ret == 0); + ret = truncate(obj_fh.fh[i].path, st.st_size/2); + RFS_ASSERT (ret==0); + + psfh = obj_fh.fh[sfh].psfh; + strcpy (name, obj_fh.fh[psfh].path); + sprintf(name+strlen(name), "/AGE%d", obj_fh.fh_max); + agefd = creat (name, S_IRWXU); + //write (agefs, buf, 0); + + add_fh_t (&obj_fh, name, sfh, psfh, size, ftype_flag, EXIST, INACTIVE); +} +#endif diff --git a/TBBT/trace_play/rfs_c_dat.c b/TBBT/trace_play/rfs_c_dat.c new file mode 100644 index 0000000..bcd3525 --- /dev/null +++ b/TBBT/trace_play/rfs_c_dat.c @@ -0,0 +1,188 @@ +#include "rfs_c_def.h" +#include "generic_hash.h" +dep_tab_t dep_tab[DEP_TAB_SIZE]; +int req_num_with_new_fh = 0; +int req_num_with_discard_fh = 0; +int req_num_with_init_fh =0; + +int event_order [EVENT_ORDER_SIZE]; +int event_order_index = 0; + +memory_trace_ent_t memory_trace[MAX_MEMORY_TRACE_LINES]; + +/* the offset between the replay time and timestamp in the log */ +struct ladtime current; +#if 0 +//struct ladtime trace_starttime = {1003636801, 39949, 0}; +struct ladtime trace_starttime = {1003723201, 313373, 0}; +#else +/* timestamp extracted from the used trace. G. Jason Peng */ +//struct ladtime trace_starttime = {1005620400, 499221, 0}; +/* timestamp extracted nfsdump.gzip.pair Ningning for osdi measurement */ +struct ladtime trace_starttime = {1085067131, 107476, 0}; +#endif +struct ladtime time_offset; + +fh_map_t fh_map [FH_MAP_SIZE]; +struct generic_entry * fh_htable[FH_HTABLE_SIZE]; +int fh_i=0; +int fh_map_debug = 0; +struct generic_entry * fh_htable [FH_HTABLE_SIZE]; + +#ifdef notdef +/* the array is indexed by sfs operation number */ +rfs_op_type rfs_Ops[TOTAL] = { +{NFSPROC3_NULL, setbuf_void, setbuf_void, xdr_void, xdr_void}, +{NFSPROC3_GETATTR, setarg_GETATTR3, setbuf_void, xdr_GETATTR3args, xdr_GETATTR3res}, +{NFSPROC3_SETATTR, setarg_SETATTR3, setbuf_void, xdr_SETATTR3args, xdr_SETATTR3res}, +{NFSPROC3_INVALID, setbuf_invalid, setbuf_invalid, xdr_invalid, xdr_invalid}, +{NFSPROC3_LOOKUP, setarg_LOOKUP3, setres_lookup, xdr_LOOKUP3args, xdr_LOOKUP3res}, +{NFSPROC3_READLINK, setarg_READLINK3, setres_readlink, xdr_READLINK3args, xdr_READLINK3res}, +{NFSPROC3_READ, setarg_READ3, setres_read, xdr_READ3args, xdr_READ3res}, +{NFSPROC3_INVALID, setarg_invalid, setbuf_invalid, xdr_invalid, xdr_invalid}, +{NFSPROC3_WRITE, setarg_WRITE3, setbuf_void, xdr_WRITE3args, xdr_WRITE3res}, +{NFSPROC3_CREATE, setarg_CREATE3, setbuf_void, xdr_CREATE3args, xdr_CREATE3res}, +{NFSPROC3_REMOVE, setarg_REMOVE3, setbuf_void, xdr_REMOVE3args, xdr_REMOVE3res}, +{NFSPROC3_RENAME, setarg_RENAME3, setbuf_void, xdr_RENAME3args, xdr_RENAME3res}, +{NFSPROC3_LINK, setarg_LINK3, setbuf_void, xdr_LINK3args, xdr_LINK3res}, +{NFSPROC3_SYMLINK, setarg_SYMLINK3, setbuf_void, xdr_SYMLINK3args, xdr_SYMLINK3res}, +{NFSPROC3_MKDIR, setarg_MKDIR3, setbuf_void, xdr_MKDIR3args, xdr_MKDIR3res}, +{NFSPROC3_RMDIR, setarg_RMDIR3, setbuf_void, xdr_RMDIR3args, xdr_RMDIR3res}, +{NFSPROC3_READDIR, setarg_READDIR3, setres_readdir, xdr_READDIR3args, xdr_READDIR3res}, +{NFSPROC3_FSSTAT, setarg_FSSTAT3, setbuf_void, xdr_FSSTAT3args, xdr_FSSTAT3res}, +{NFSPROC3_ACCESS, setarg_ACCESS3, setbuf_void, xdr_ACCESS3args, xdr_ACCESS3res}, +{NFSPROC3_COMMIT, setarg_COMMIT3, setbuf_void, xdr_COMMIT3args, xdr_COMMIT3res}, +{NFSPROC3_FSINFO, setarg_FSINFO3, setbuf_void, xdr_FSINFO3args, xdr_FSINFO3res}, +{NFSPROC3_MKNOD, setarg_MKNOD3, setbuf_void, xdr_MKNOD3args, xdr_MKNOD3res}, +{NFSPROC3_PATHCONF, setarg_PATHCONF3, setbuf_void, xdr_PATHCONF3args, xdr_PATHCONF3res} +{NFSPROC3_READDIRPLUS, setarg_READDIRPLUS3, setres_readdirplus, xdr_READDIRPLUS3args, xdr_READDIRPLUS3res}}; + +/* + * -------------------- NFS ops vector -------------------- + */ +/* + * per operation information + */ +sfs_op_type nfsv3_Ops[] = { + +/* name mix op call no req req req results */ +/* pcnt class targ call pcnt cnt targ */ + + { "null", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "getattr", 11, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "setattr", 1, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "root", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "lookup", 27, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readlink", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "read", 18, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "wrcache", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "write", 9, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "create", 1, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "remove", 1, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rename", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "link", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "symlink", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "mkdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rmdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdir", 2, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsstat", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "access", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "commit", 5, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsinfo", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "mknod", 0, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "pathconf", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdirplus", 9, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "TOTAL", 100, Lookup, 0, 0, 0.0, 0, 0, { 0, }} +}; +#endif + +sfs_op_type *Ops; + +int num_out_reqs = 0; + +cyclic_index_t dep_tab_index; +cyclic_index_t dep_window_index; +cyclic_index_t memory_trace_index; +int dep_window_max = 0; + +/* note that for each dep_tab entry, there is a memory trace line, but + * not vise vesa because some memory trace line may not have corresponding + * dep_tab entry. According entry TIMESTAMP value, the order is + * + * memory_trace_tail line < dep_tab_tail entry < dep_window_max entry < + * dep_tab_head entry < memory_trace_head entry */ + +int rfs_debug = 0; +int per_packet_debug = 0; +int adjust_play_window_debug = 0; +int dependency_debug = 0; +int profile_debug = 0; +int quiet_flag = 0; +int stage = FIRST_STAGE; +int read_data_owe = 0; +int read_data_total = 0; +int write_data_owe = 0; +int write_data_total = 0; +int read_data_adjust_times = 0; +int write_data_adjust_times = 0; +int read_data_owe_GB = 0; +int write_data_owe_GB = 0; +int read_data_total_GB = 0; +int write_data_total_GB = 0; + +int failed_create_command_num = 0; +int failed_other_command_num = 0; +int skipped_readlink_command_num = 0; +int skipped_custom_command_num = 0; +int fh_path_map_err_num = 0; +int skipped_fsstat_command_num = 0; +int missing_reply_num = 0; +int rename_rmdir_noent_reply_num = 0; +int rmdir_not_empty_reply_num = 0; +int loose_access_control_reply_num = 0; +int lookup_err_due_to_rename_num = 0; +int lookup_err_due_to_parallel_remove_num = 0; +int lookup_eaccess_enoent_mismatch_num = 0; +int read_io_err_num = 0; +int stale_fhandle_err_num = 0; +int proper_reply_num = 0; +int run_stage_proper_reply_num = 0; +int lookup_retry_num = 0; +int can_not_catch_speed_num = 0; +int can_not_catch_speed_num_total = 0; +int poll_timeout_0_num = 0; +int poll_timeout_pos_num = 0; +int abnormal_EEXIST_num = 0; +int abnormal_ENOENT_num = 0; + +FILE * profile_fp = 0; +profile_t total_profile; +profile_t valid_get_nextop_profile; +profile_t invalid_get_nextop_profile; +profile_t valid_poll_and_get_reply_profile; +profile_t invalid_poll_and_get_reply_profile; +profile_t execute_next_request_profile; +profile_t receive_next_reply_profile; +profile_t decode_reply_profile; +profile_t check_reply_profile; +profile_t add_create_object_profile; +profile_t prepare_argument_profile; +profile_t biod_clnt_call_profile; +profile_t check_timeout_profile; +profile_t adjust_play_window_profile; +profile_t fgets_profile; +profile_t read_line_profile; +profile_t read_trace_profile; + +int PLAY_SCALE = 1; +int skip_sec = 0; +int trace_timestamp1=0, trace_timestamp2=0; + +int disk_io_status = TRACE_BUF_FULL; +int WARMUP_TIME = 0; /* other values that has been used: 100 */ +int disk_index = -1; + +int TRACE_COMMAND_REPLY_FLAG_POS=36; +int TRACE_VERSION_POS=37; +int TRACE_MSGID_POS=39; +int TRACE_FH_SIZE=64; diff --git a/TBBT/trace_play/rfs_c_def.h b/TBBT/trace_play/rfs_c_def.h new file mode 100644 index 0000000..daeea6c --- /dev/null +++ b/TBBT/trace_play/rfs_c_def.h @@ -0,0 +1,357 @@ +#ifndef RFS_H +#define RFS_H +#include "sfs_c_def.h" +#include "profile.h" +#include "rfs_assert.h" + +/* the maximum number of operations depended by one operation */ +/* the dependency include read/write, write/write, all operations with a + * one file handle/delete or rename's target if exists, the dependency + * does not include the create/all operations with relevant file handle + * This dependency is maintained by a flag in file handle mapping table + */ +#define MAX_DEP_OPS 10 +//#define DEP_TAB_SIZE 200000 /* the dependency table size */ +/* Right now we don't wrap around with dep_tab, for each request, there + * is one entry in dependency table and one entry in lines buffer. + */ +#define REDUCE_MEMORY_TRACE_SIZE + +#define EVENT_ORDER_SIZE 1000000 +#ifdef REDUCE_MEMORY_TRACE_SIZE +//#define DEP_TAB_SIZE 1100000 /* the dependency table size */ +#define DEP_TAB_SIZE 100000 /* the dependency table size */ +//#define DEP_TAB_SIZE 10000 /* the dependency table size */ +#define MAX_MEMORY_TRACE_LINES DEP_TAB_SIZE +//#define MAX_TRACE_LINE_LENGTH 768 +#define MAX_TRACE_LINE_LENGTH 300 +#else +#define MAX_TRACE_LINE_LENGTH 768 +#define DEP_TAB_SIZE 500000 /* the dependency table size */ +#define MAX_MEMORY_TRACE_LINES DEP_TAB_SIZE*2 +#endif + +#define FH_MAP_SIZE 400000 +//#define FH_MAP_SIZE 40000 +#define FH_HTABLE_SIZE FH_MAP_SIZE + +/* trace play policy related defines */ +#define SPEED_UP // only one of SPEED_UP and SLOW_DOWN is defined +//#define SLOW_DOWN +extern int PLAY_SCALE; +//#define TIME_PLAY /* play according original timestamp or scaled timestamp together with dependency */ +#define NO_DEPENDENCY_TABLE +//#define MAX_COMMAND_REPLY_DISTANCE 1000 +#define MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR 1000 +#define MAX_COMMAND_REPLY_DISTANCE 2 + +#define WORKLOAD_PLAY /* play according dependency and as fast as it can, ignore timestamp */ + +#ifdef TIME_PLAY +#define MAX_PLAY_WINDOW 8 +#define MAX_OUTSTANDING_REQ 4 /* too big outstanding window reduces throughput */ +#else +#define MAX_PLAY_WINDOW 1000 +//#define MAX_PLAY_WINDOW 8 +#define MAX_OUTSTANDING_REQ 8 +//#define MAX_OUTSTANDING_REQ 16 +#endif +#define UDP /* currently we only support using UDP, support for TCP will be added later */ + +/* the flag to indicate the current status of a trace request */ +#define DEP_FLAG_FREE 0 /* free entry */ +#define DEP_FLAG_INIT 1 /* initialized */ +#define DEP_FLAG_CANDIDATE 2 +#define DEP_FLAG_WAIT_FHANDLE 3 /* waiting for leading fhandle */ +#define DEP_FLAG_FHANDLE_READY 4 +#define DEP_FLAG_WAIT_DIRECTORY 5 +#define DEP_FLAG_DIRECTORY_READY 6 +#define DEP_FLAG_WAIT_DELETE 7 /* waiting for deleting the object */ +#define DEP_FLAG_SENT 8 /* Request has been sent out, waiting for replies */ +#define DEP_FLAG_DONE 9 /* Reply has been received and processed */ +#define BUSY 1 +#define IDLE 0 + +#define MAX_ARG_RES_SIZE 512 +#define MAX_BUF1_SIZE 65536 /* for readdirplus, 208 each entry, max 128 entries */ +#define MAX_BUF2_SIZE NFS_MAXPATHLEN + +extern int TRACE_COMMAND_REPLY_FLAG_POS; +extern int TRACE_VERSION_POS; +extern int TRACE_MSGID_POS; +extern int TRACE_FH_SIZE; + +/* Two setbuf_t procedure exists for each NFS operation, one for setting up NFS + * operation argument buffer, another for setting up NFS operation result receiving buffer. + * + * When setting up argument, the function takes two argument, the first + * argument is index to dep_tab, the second argument is the buffer pointer to + * be setup. Sometimes, there is no extra argument other than index, for example: read + * Sometimes, there is one extra argument, e.g the buffer for lookup name. + * Sometimes, there are two extra arguments, e..g rename, symlink. + * + * void (*setbuf_t)(int index, char * arg, char * arg); + * + * When setting up receiving result buffer, the function takes one arguement, + * the buffer pointer. + * void (*setbuf_t)(char * res, char * arg); + */ +typedef void (*setbuf_t)(); + +typedef struct { + int nfsv3_proc; + setbuf_t setarg; + setbuf_t setres; + xdrproc_t xdr_arg; + xdrproc_t xdr_res; +} rfs_op_type; + +#define MAX_TRACE_FH_SIZE 64 +#define TRACE_FH_FILE_SIZE 48 +#define TRACE_FH_DIR_SIZE 40 +#define MAX_PLAY_PATH_SIZE 256 + +/* flags in fh_map_t */ +#define FH_MAP_FLAG_FREE 0 +#define FH_MAP_FLAG_DISCARD 1 +#define FH_MAP_FLAG_PARTIAL 2 +#define FH_MAP_FLAG_COMPLETE 3 +typedef struct { + int flag; + int lock; + char trace_fh [MAX_TRACE_FH_SIZE+1]; + char path[MAX_PLAY_PATH_SIZE]; + nfs_fh3 play_fh; +} fh_map_t; + +typedef struct { + int disk_index; + int trace_status; + char reply_trace_fh[MAX_TRACE_FH_SIZE+1]; + char line[MAX_TRACE_LINE_LENGTH]; +} memory_trace_ent_t; + + +typedef struct { + int flag; + int disk_index; + int memory_index; + char * line; + char * reply_line; + fh_map_t * fh; + fh_map_t * fh_2; + char * trace_fh; + char * trace_fh_2; +#ifdef REDUCE_MEMORY_TRACE_SIZE + char * reply_trace_fh; +#endif + int proc; /* the prototype independent NFS operation number defined in sfs_c_def.h + * e.g. NULLCALL NFS3PROC_READ etc */ + struct timeval timestamp; /* The timestamp of a request in the trace */ +#ifdef NO_DEPENDENCY_TABLE + int dep_ops[MAX_DEP_OPS]; /* Other requests need to be processed prior this request */ + /* dep_tab[i] == -1 means the dependency has been resolved */ + int init_dep_num; /* The number of request being depended initially */ + int cur_dep_num; /* The number of remaining ones which have not been finished */ + /* at initialization time, init_dep_num == cur_dep_num */ +#endif + int biod_req_index; /* Index in to the biod_req array which contains all outstanding requests */ + struct ladtime start; + struct ladtime stop; + struct ladtime interval; + int skip_sec; + int status; + int trace_status; +} dep_tab_t; + +typedef struct { + char name[32]; + int head; + int tail; + int size; +} cyclic_index_t; + +#define CYCLIC_INIT(str,index,SIZE) \ + {index.head=0; index.tail=0; index.size=SIZE; \ + RFS_ASSERT(strlen(str)<sizeof(index.name)); \ + strcpy(index.name, str); } + +#define CYCLIC_PRINT(index) \ + {printf("%s head %d tail %d, size %d\n", index.name, index.head, index.tail, index.size);} + +#define CYCLIC_FULL(index) \ + (((index.head+1)%index.size)==index.tail) + +#define CYCLIC_EMPTY(index) \ + (index.tail==index.head) + +#define CYCLIC_NUM(index) \ + ((index.head + index.size - index.tail) % index.size) + +#define CYCLIC_MOVE_HEAD(index) \ + { \ + if (CYCLIC_FULL(index)) { \ + CYCLIC_PRINT(index) \ + } \ + RFS_ASSERT (!CYCLIC_FULL(index)); \ + index.head=(index.head+1)%(index.size); \ + } + +#define CYCLIC_MOVE_TAIL(index) \ + { \ + RFS_ASSERT (!CYCLIC_EMPTY(index)) \ + index.tail=(index.tail+1)%(index.size); \ + } + +/* +#define CYCLIC_MOVE_TAIL_TO(index,dest) \ + { \ + int oldnum = CYCLIC_NUM(index); \ + if (! ((dest>=0) && (dest<index.size) && (dest!=index.head))) { \ + CYCLIC_PRINT(index); \ + printf("dest %d\n", dest); \ + } \ + RFS_ASSERT ((dest>=0) && (dest<index.size) && (dest!=index.head)) \ + index.tail = dest; \ + if (CYCLIC_NUM(index) > oldnum) { \ + CYCLIC_PRINT(index); \ + printf("dest %d old_num %d \n", dest, oldnum); \ + } \ + RFS_ASSERT (CYCLIC_NUM(index) <= oldnum); \ + } +*/ + +#define CYCLIC_MINUS(A,B,size) ((A+size-B)%size) +#define CYCLIC_ADD(A,B,size) ((A+B)%size) +#define CYCLIC_LESS(index,A,B) \ +(CYCLIC_MINUS(A,index.tail,index.size)<CYCLIC_MINUS(B,index.tail,index.size)) + +extern struct ladtime current; +extern struct ladtime trace_starttime; +extern struct ladtime time_offset; + +extern rfs_op_type rfs_Ops[]; +extern sfs_op_type nfsv3_Ops[]; +extern sfs_op_type * Ops; +extern int num_out_reqs; + +extern cyclic_index_t dep_tab_index; +extern cyclic_index_t dep_window_index; +extern cyclic_index_t memory_trace_index; +int dep_window_max; /* the first entry outside of the dependency window */ +/* If ordered by TIMESTAMP, + * memory_trace_index.tail <= dep_tab_index.tail < dep_window_max <= + * dep_tab_index.head <= memory_trace_index.head + */ + +extern fh_map_t fh_map [FH_MAP_SIZE]; +extern int fh_i; +extern int fh_map_debug ; +extern struct generic_entry * fh_htable [FH_HTABLE_SIZE]; + +extern dep_tab_t dep_tab[DEP_TAB_SIZE]; +extern int req_num_with_new_fh; +extern int req_num_with_discard_fh; +extern int req_num_with_init_fh; +extern memory_trace_ent_t memory_trace[MAX_MEMORY_TRACE_LINES]; +extern int event_order[]; +extern int event_order_index; +extern struct biod_req * biod_reqp; +extern int max_biod_reqs; +extern int rfs_debug; +extern int per_packet_debug; +extern int profile_debug; +extern int adjust_play_window_debug; +extern int dependency_debug; +extern int quiet_flag; +extern int read_data_owe; +extern int write_data_owe; +extern int read_data_total; +extern int write_data_total; +extern int read_data_adjust_times; +extern int write_data_adjust_times; +extern int read_data_owe_GB; +extern int write_data_owe_GB; +extern int read_data_total_GB; +extern int write_data_total_GB; + +extern int failed_create_command_num; +extern int failed_other_command_num; +extern int skipped_readlink_command_num; +extern int skipped_custom_command_num; +extern int fh_path_map_err_num; +extern int skipped_fsstat_command_num; +extern int missing_reply_num; +extern int lookup_noent_reply_num; +extern int rename_rmdir_noent_reply_num; +extern int rmdir_not_empty_reply_num; +extern int loose_access_control_reply_num; +extern int lookup_err_due_to_rename_num; +extern int lookup_err_due_to_parallel_remove_num; +extern int lookup_eaccess_enoent_mismatch_num; +extern int read_io_err_num; +extern int stale_fhandle_err_num; +extern int proper_reply_num; +extern int run_stage_proper_reply_num; +extern int lookup_retry_num; +extern int can_not_catch_speed_num_total; +extern int can_not_catch_speed_num; +extern int poll_timeout_0_num; +extern int poll_timeout_pos_num; +extern int abnormal_EEXIST_num; +extern int abnormal_ENOENT_num; + +#define FIRST_STAGE 0 +#define READ_DEP_TAB_STAGE 1 +#define TRACE_PLAY_STAGE 2 +extern int stage; + +#define IGNORE_SETATTR_CTIME +//#define TAKE_CARE_SETATTR_GID +//#define TAKE_CARE_SETATTR_UID +//#define TAKE_CARE_CREATE_MODE_BY_DAN +//#define TAKE_CARE_OTHER_FAILED_COMMAND +//#define TAKE_CARE_CUSTOM_COMMAND +//#define TAKE_CARE_FSSTAT_COMMAND +//#define TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES +//#define TAKE_CARE_NOEMPTY_RMDIR +//#define TAKE_CARE_LOOKUP_EACCESS_ENOENT_MISMATCH +//#define TAKE_CARE_ACCESS_ERROR +//#define TAKE_CARE_SYMBOLIC_LINK +#define TOLERANT_READ_IO_ERR +#define TOLERANT_STALE_FHANDLE_ERR +#define IO_THREAD +//#define RECV_THREAD can not tune up the version with receive thread quickly + +extern FILE * profile_fp; +extern profile_t total_profile; +extern profile_t valid_get_nextop_profile; +extern profile_t invalid_get_nextop_profile; +extern profile_t valid_poll_and_get_reply_profile; +extern profile_t invalid_poll_and_get_reply_profile; +extern profile_t execute_next_request_profile; +extern profile_t receive_next_reply_profile; +extern profile_t decode_reply_profile; +extern profile_t check_reply_profile; +extern profile_t add_create_object_profile; +extern profile_t prepare_argument_profile; +extern profile_t biod_clnt_call_profile; +extern profile_t check_timeout_profile; +extern profile_t adjust_play_window_profile; +extern profile_t fgets_profile; +extern profile_t read_line_profile; +extern profile_t read_trace_profile; + +extern int skip_sec; +extern int trace_timestamp1, trace_timestamp2; +//#define SEQUEN_READ +//#define SEQUEN_READ_NUM 1000 + +#define TRACE_BUF_FULL 0 +#define TRACE_FILE_END 1 +#define ASYNC_RPC_SEM_KEY 60000 +extern int disk_io_status; +extern int WARMUP_TIME; +extern int disk_index; +//#define NO_SEM +#endif diff --git a/TBBT/trace_play/rpc/auth.h b/TBBT/trace_play/rpc/auth.h new file mode 100755 index 0000000..b184cf5 --- /dev/null +++ b/TBBT/trace_play/rpc/auth.h @@ -0,0 +1,185 @@ +/* + * @(#)auth.h 2.1 97/10/23 + */ + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/* @(#)auth.h 2.3 88/08/07 4.0 RPCSRC; from 1.17 88/02/08 SMI */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * auth.h, Authentication interface. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * The data structures are completely opaque to the client. The client + * is required to pass a AUTH * to routines that create rpc + * "sessions". + */ + + +#define MAX_AUTH_BYTES 400 +#define MAXNETNAMELEN 255 /* maximum length of network user's name */ + +/* + * Status returned from authentication check + */ +enum auth_stat { + AUTH_OK=0, + /* + * failed at remote end + */ + AUTH_BADCRED=1, /* bogus credentials (seal broken) */ + AUTH_REJECTEDCRED=2, /* client should begin new session */ + AUTH_BADVERF=3, /* bogus verifier (seal broken) */ + AUTH_REJECTEDVERF=4, /* verifier expired or was replayed */ + AUTH_TOOWEAK=5, /* rejected due to security reasons */ + /* + * failed locally + */ + AUTH_INVALIDRESP=6, /* bogus response verifier */ + AUTH_FAILED=7 /* some unknown reason */ +}; + +union des_block { + struct { + uint_t high; + uint_t low; + } key; + char c[8]; +}; +typedef union des_block des_block; +extern bool_t xdr_des_block(); + +/* + * Authentication info. Opaque to client. + */ +struct opaque_auth { + enum_t oa_flavor; /* flavor of auth */ + void *oa_base; /* address of more auth stuff */ + uint_t oa_length; /* not to exceed MAX_AUTH_BYTES */ +}; + + +/* + * Auth handle, interface to client side authenticators. + */ +typedef struct { + struct opaque_auth ah_cred; + struct opaque_auth ah_verf; + union des_block ah_key; + struct auth_ops { + void (*ah_nextverf)(); + int (*ah_marshal)(); /* nextverf & serialize */ + int (*ah_validate)(); /* validate varifier */ + int (*ah_refresh)(); /* refresh credentials */ + void (*ah_destroy)(); /* destroy this structure */ + } *ah_ops; + void * ah_private; +} AUTH; + + +/* + * Authentication ops. + * The ops and the auth handle provide the interface to the authenticators. + * + * AUTH *auth; + * XDR *xdrs; + * struct opaque_auth verf; + */ +#define AUTH_NEXTVERF(auth) \ + ((*((auth)->ah_ops->ah_nextverf))(auth)) +#define auth_nextverf(auth) \ + ((*((auth)->ah_ops->ah_nextverf))(auth)) + +#define AUTH_MARSHALL(auth, xdrs) \ + ((*((auth)->ah_ops->ah_marshal))(auth, xdrs)) +#define auth_marshall(auth, xdrs) \ + ((*((auth)->ah_ops->ah_marshal))(auth, xdrs)) + +#define AUTH_VALIDATE(auth, verfp) \ + ((*((auth)->ah_ops->ah_validate))((auth), verfp)) +#define auth_validate(auth, verfp) \ + ((*((auth)->ah_ops->ah_validate))((auth), verfp)) + +#define AUTH_REFRESH(auth) \ + ((*((auth)->ah_ops->ah_refresh))(auth)) +#define auth_refresh(auth) \ + ((*((auth)->ah_ops->ah_refresh))(auth)) + +#define AUTH_DESTROY(auth) \ + ((*((auth)->ah_ops->ah_destroy))(auth)) +#define auth_destroy(auth) \ + ((*((auth)->ah_ops->ah_destroy))(auth)) + + +extern struct opaque_auth _null_auth; + + +/* + * These are the various implementations of client side authenticators. + */ + +/* + * Unix style authentication + * AUTH *authunix_create(machname, uid, gid, len, aup_gids) + * char *machname; + * int uid; + * int gid; + * int len; + * int *aup_gids; + */ +extern AUTH *authunix_create(char *, uid_t, gid_t, int, gid_t *); +extern AUTH *authunix_create_default(void); +extern AUTH *authnone_create(); /* takes no parameters */ +extern AUTH *authdes_create(); + +#define AUTH_NONE 0 /* no authentication */ +#define AUTH_NULL 0 /* backward compatibility */ +#define AUTH_UNIX 1 /* unix style (uid, gids) */ +#define AUTH_SHORT 2 /* short hand unix style */ +#define AUTH_DES 3 /* des style (encrypted timestamps) */ diff --git a/TBBT/trace_play/rpc/auth_none.c b/TBBT/trace_play/rpc/auth_none.c new file mode 100755 index 0000000..4644ac4 --- /dev/null +++ b/TBBT/trace_play/rpc/auth_none.c @@ -0,0 +1,154 @@ +#ifndef lint +static char sfs_auth_none_c_id[] = "@(#)auth_none.c 2.1 97/10/23"; +#endif +/* @(#)auth_none.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)auth_none.c 1.19 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * auth_none.c + * Creates a client authentication handle for passing "null" + * credentials and verifiers to remote systems. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdlib.h> +#include "rpc/rpc.h" +#define MAX_MARSHEL_SIZE 20 + +/* + * Authenticator operations routines + */ +static void authnone_verf(void); +static void authnone_destroy(void); +static bool_t authnone_marshal(AUTH *, XDR *); +static bool_t authnone_validate(void); +static bool_t authnone_refresh(void); + +static struct auth_ops ops = { + authnone_verf, + authnone_marshal, + authnone_validate, + authnone_refresh, + authnone_destroy +}; + +static struct authnone_private { + AUTH no_client; + char marshalled_client[MAX_MARSHEL_SIZE]; + uint_t mcnt; +} *authnone_private; + +AUTH * +authnone_create(void) +{ + register struct authnone_private *ap = authnone_private; + XDR xdr_stream; + register XDR *xdrs; + + if (ap == 0) { + ap = (struct authnone_private *)calloc(1, + sizeof (struct authnone_private)); + if (ap == 0) + return (0); + authnone_private = ap; + } + if (!ap->mcnt) { + ap->no_client.ah_cred = ap->no_client.ah_verf = _null_auth; + ap->no_client.ah_ops = &ops; + xdrs = &xdr_stream; + xdrmem_create(xdrs, ap->marshalled_client, (uint_t)MAX_MARSHEL_SIZE, + XDR_ENCODE); + (void)xdr_opaque_auth(xdrs, &ap->no_client.ah_cred); + (void)xdr_opaque_auth(xdrs, &ap->no_client.ah_verf); + ap->mcnt = XDR_GETPOS(xdrs); + XDR_DESTROY(xdrs); + } + return (&ap->no_client); +} + +/*ARGSUSED*/ +static bool_t +authnone_marshal( + AUTH *client, + XDR *xdrs) +{ + register struct authnone_private *ap = authnone_private; + + if (ap == 0) + return (0); + return ((*xdrs->x_ops->x_putbytes)(xdrs, + ap->marshalled_client, ap->mcnt)); +} + +static void +authnone_verf(void) +{ +} + +static bool_t +authnone_validate(void) +{ + + return (TRUE); +} + +static bool_t +authnone_refresh(void) +{ + + return (FALSE); +} + +static void +authnone_destroy(void) +{ +} diff --git a/TBBT/trace_play/rpc/auth_unix.c b/TBBT/trace_play/rpc/auth_unix.c new file mode 100755 index 0000000..06f7033 --- /dev/null +++ b/TBBT/trace_play/rpc/auth_unix.c @@ -0,0 +1,345 @@ +#ifndef lint +static char sfs_auth_unix_c_id[] = "@(#)auth_unix.c 2.1 97/10/23"; +#endif +/* @(#)auth_unix.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)auth_unix.c 1.19 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * auth_unix.c, Implements UNIX style authentication parameters. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * The system is very weak. The client uses no encryption for it's + * credentials and only sends null verifiers. The server sends backs + * null verifiers or optionally a verifier that suggests a new short hand + * for the credentials. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <netdb.h> + +#include "rpc/rpc.h" + +extern getmyhostname(char *, int); + +/* + * Unix authenticator operations vector + */ +static void authunix_nextverf(AUTH *); +static bool_t authunix_marshal(AUTH *, XDR *); +static bool_t authunix_validate(AUTH *, struct opaque_auth); +static bool_t authunix_refresh(AUTH *); +static void authunix_destroy(AUTH *); + +static struct auth_ops auth_unix_ops = { + authunix_nextverf, + authunix_marshal, + authunix_validate, + authunix_refresh, + authunix_destroy +}; + +/* + * This struct is pointed to by the ah_private field of an auth_handle. + */ +struct audata { + struct opaque_auth au_origcred; /* original credentials */ + struct opaque_auth au_shcred; /* short hand cred */ + uint32_t au_shfaults; /* short hand cache faults */ + char au_marshed[MAX_AUTH_BYTES]; + uint_t au_mpos; /* xdr pos at end of marshed */ +}; +#define AUTH_PRIVATE(auth) ((struct audata *)auth->ah_private) + +static bool_t marshal_new_auth(AUTH *); + + +/* + * Create a unix style authenticator. + * Returns an auth handle with the given stuff in it. + */ +AUTH * +authunix_create( + char *machname, + uid_t uid, + gid_t gid, + int len, + gid_t *aup_gids) +{ + struct authunix_parms aup; + char mymem[MAX_AUTH_BYTES]; + struct timeval now; + XDR xdrs; + register AUTH *auth; + register struct audata *au; + + /* + * Allocate and set up auth handle + */ + auth = (AUTH *)mem_alloc(sizeof(AUTH)); +#ifndef KERNEL + if (auth == NULL) { + (void)fprintf(stderr, "authunix_create: out of memory\n"); + return (NULL); + } +#endif + au = (struct audata *)mem_alloc(sizeof(struct audata)); +#ifndef KERNEL + if (au == NULL) { + (void)fprintf(stderr, "authunix_create: out of memory\n"); + return (NULL); + } +#endif + auth->ah_ops = &auth_unix_ops; + auth->ah_private = (void *)au; + auth->ah_verf = au->au_shcred = _null_auth; + au->au_shfaults = 0; + + /* + * fill in param struct from the given params + */ + (void)gettimeofday(&now, (struct timezone *)0); + aup.aup_time = now.tv_sec; + aup.aup_machname = machname; + aup.aup_uid = uid; + aup.aup_gid = gid; + aup.aup_len = (uint_t)len; + aup.aup_gids = (int *)aup_gids; + + /* + * Serialize the parameters into origcred + */ + xdrmem_create(&xdrs, mymem, MAX_AUTH_BYTES, XDR_ENCODE); + if (! xdr_authunix_parms(&xdrs, &aup)) + abort(); + au->au_origcred.oa_length = len = XDR_GETPOS(&xdrs); + au->au_origcred.oa_flavor = AUTH_UNIX; +#ifdef KERNEL + au->au_origcred.oa_base = mem_alloc((uint_t) len); +#else + if ((au->au_origcred.oa_base = mem_alloc((uint_t) len)) == NULL) { + (void)fprintf(stderr, "authunix_create: out of memory\n"); + return (NULL); + } +#endif + memmove(au->au_origcred.oa_base, mymem, (uint_t)len); + + /* + * set auth handle to reflect new cred. + */ + auth->ah_cred = au->au_origcred; + marshal_new_auth(auth); + return (auth); +} + +/* + * Returns an auth handle with parameters determined by doing lots of + * syscalls. + */ +AUTH * +authunix_create_default(void) +{ + register int len; + char machname[MAX_MACHINE_NAME + 1]; + uid_t uid; + gid_t gid; + gid_t gids[NGRPS]; + + if (getmyhostname(machname, MAX_MACHINE_NAME) == -1) + abort(); + machname[MAX_MACHINE_NAME] = 0; + uid = geteuid(); + gid = getegid(); + if ((len = getgroups(NGRPS, gids)) < 0) + abort(); + return (authunix_create(machname, uid, gid, len, gids)); +} + +/* + * authunix operations + */ + +/* ARGSUSED */ +static void +authunix_nextverf( + AUTH *auth) +{ + /* no action necessary */ +} + +static bool_t +authunix_marshal( + AUTH *auth, + XDR *xdrs) +{ + register struct audata *au = AUTH_PRIVATE(auth); + + return (XDR_PUTBYTES(xdrs, au->au_marshed, au->au_mpos)); +} + +static bool_t +authunix_validate( + AUTH *auth, + struct opaque_auth verf) +{ + register struct audata *au; + XDR xdrs; + + if (verf.oa_flavor == AUTH_SHORT) { + au = AUTH_PRIVATE(auth); + xdrmem_create(&xdrs, verf.oa_base, verf.oa_length, XDR_DECODE); + + if (au->au_shcred.oa_base != NULL) { + mem_free(au->au_shcred.oa_base, + au->au_shcred.oa_length); + au->au_shcred.oa_base = NULL; + } + if (xdr_opaque_auth(&xdrs, &au->au_shcred)) { + auth->ah_cred = au->au_shcred; + } else { + xdrs.x_op = XDR_FREE; + (void)xdr_opaque_auth(&xdrs, &au->au_shcred); + au->au_shcred.oa_base = NULL; + auth->ah_cred = au->au_origcred; + } + marshal_new_auth(auth); + } + return (TRUE); +} + +static bool_t +authunix_refresh( + AUTH *auth) +{ + register struct audata *au = AUTH_PRIVATE(auth); + struct authunix_parms aup; + struct timeval now; + XDR xdrs; + register int stat; + + if (auth->ah_cred.oa_base == au->au_origcred.oa_base) { + /* there is no hope. Punt */ + return (FALSE); + } + au->au_shfaults ++; + + /* first deserialize the creds back into a struct authunix_parms */ + aup.aup_machname = NULL; + aup.aup_gids = (int *)NULL; + xdrmem_create(&xdrs, au->au_origcred.oa_base, + au->au_origcred.oa_length, XDR_DECODE); + stat = xdr_authunix_parms(&xdrs, &aup); + if (! stat) + goto done; + + /* update the time and serialize in place */ + (void)gettimeofday(&now, (struct timezone *)0); + aup.aup_time = now.tv_sec; + xdrs.x_op = XDR_ENCODE; + XDR_SETPOS(&xdrs, 0); + stat = xdr_authunix_parms(&xdrs, &aup); + if (! stat) + goto done; + auth->ah_cred = au->au_origcred; + marshal_new_auth(auth); +done: + /* free the struct authunix_parms created by deserializing */ + xdrs.x_op = XDR_FREE; + (void)xdr_authunix_parms(&xdrs, &aup); + XDR_DESTROY(&xdrs); + return (stat); +} + +static void +authunix_destroy( + AUTH *auth) +{ + register struct audata *au = AUTH_PRIVATE(auth); + + mem_free(au->au_origcred.oa_base, au->au_origcred.oa_length); + + if (au->au_shcred.oa_base != NULL) + mem_free(au->au_shcred.oa_base, au->au_shcred.oa_length); + + mem_free(auth->ah_private, sizeof(struct audata)); + + if (auth->ah_verf.oa_base != NULL) + mem_free(auth->ah_verf.oa_base, auth->ah_verf.oa_length); + + mem_free((void *)auth, sizeof(AUTH)); +} + +/* + * Marshals (pre-serializes) an auth struct. + * sets private data, au_marshed and au_mpos + */ +static bool_t +marshal_new_auth( + AUTH *auth) +{ + XDR xdr_stream; + register XDR *xdrs = &xdr_stream; + register struct audata *au = AUTH_PRIVATE(auth); + + xdrmem_create(xdrs, au->au_marshed, MAX_AUTH_BYTES, XDR_ENCODE); + if ((! xdr_opaque_auth(xdrs, &(auth->ah_cred))) || + (! xdr_opaque_auth(xdrs, &(auth->ah_verf)))) { + perror("auth_none.c - Fatal marshalling problem"); + } else { + au->au_mpos = XDR_GETPOS(xdrs); + } + XDR_DESTROY(xdrs); + return (TRUE); +} diff --git a/TBBT/trace_play/rpc/auth_unix.h b/TBBT/trace_play/rpc/auth_unix.h new file mode 100755 index 0000000..24147e0 --- /dev/null +++ b/TBBT/trace_play/rpc/auth_unix.h @@ -0,0 +1,95 @@ +/* + * @(#)auth_unix.h 2.1 97/10/23 + */ + +/* @(#)auth_unix.h 2.2 88/07/29 4.0 RPCSRC; from 1.8 88/02/08 SMI */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)auth_unix.h 1.5 86/07/16 SMI */ + +/* + * auth_unix.h, Protocol for UNIX style authentication parameters for RPC + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + + +/* + * The system is very weak. The client uses no encryption for it + * credentials and only sends null verifiers. The server sends backs + * null verifiers or optionally a verifier that suggests a new short hand + * for the credentials. + */ + +/* The machine name is part of a credential; it may not exceed 255 bytes */ +#define MAX_MACHINE_NAME 255 + +/* gids compose part of a credential; there may not be more than 16 of them */ +#define NGRPS 16 + +/* + * Unix style credentials. + */ +struct authunix_parms { + uint32_t aup_time; + char *aup_machname; + int aup_uid; + int aup_gid; + uint_t aup_len; + int *aup_gids; +}; + +extern bool_t xdr_authunix_parms(); + +/* + * If a response verifier has flavor AUTH_SHORT, + * then the body of the response verifier encapsulates the following structure; + * again it is serialized in the obvious fashion. + */ +struct short_hand_verf { + struct opaque_auth new_cred; +}; diff --git a/TBBT/trace_play/rpc/authunix_prot.c b/TBBT/trace_play/rpc/authunix_prot.c new file mode 100755 index 0000000..bc18e7b --- /dev/null +++ b/TBBT/trace_play/rpc/authunix_prot.c @@ -0,0 +1,86 @@ +#ifndef lint +static char sfs_authunix_prot_c_id[] = "@(#)authunix_prot.c 2.1 97/10/23"; +#endif +/* @(#)authunix_prot.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)authunix_prot.c 1.15 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * authunix_prot.c + * XDR for UNIX style authentication parameters for RPC + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include "rpc/types.h" +#include "rpc/xdr.h" +#include "rpc/auth.h" +#include "rpc/auth_unix.h" + +/* + * XDR for unix authentication parameters. + */ +bool_t +xdr_authunix_parms( + XDR *xdrs, + struct authunix_parms *p) +{ + + if (xdr_uint32_t(xdrs, &(p->aup_time)) + && xdr_string(xdrs, &(p->aup_machname), MAX_MACHINE_NAME) + && xdr_int(xdrs, &(p->aup_uid)) + && xdr_int(xdrs, &(p->aup_gid)) + && xdr_array(xdrs, (void **)&(p->aup_gids), + &(p->aup_len), NGRPS, sizeof(int), xdr_int) ) { + return (TRUE); + } + return (FALSE); +} + diff --git a/TBBT/trace_play/rpc/bindresvport.c b/TBBT/trace_play/rpc/bindresvport.c new file mode 100755 index 0000000..32927fe --- /dev/null +++ b/TBBT/trace_play/rpc/bindresvport.c @@ -0,0 +1,103 @@ +#ifndef lint +static char sfs_bindresvport_id[] = "@(#)bindresvport.c 2.1 97/10/23"; +#endif +/* 2.2 88/07/29 4.0 RPCSRC 1.8 88/02/08 SMI */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Copyright (c) 1987 by Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include "rpc/rpc.h" +#include <sys/types.h> +#include "rpc/osdep.h" + +/* + * Bind a socket to a privileged IP port + */ +int +bindresvport( + int sd, + struct sockaddr_in *sin) +{ + int res; + static int16_t port; + struct sockaddr_in myaddr; + int i; + +#define STARTPORT 600 +#define ENDPORT (IPPORT_RESERVED - 1) +#define NPORTS (ENDPORT - STARTPORT + 1) + + if (sin == (struct sockaddr_in *)0) { + sin = &myaddr; + memset(sin, '\0', sizeof (struct sockaddr_in)); + sin->sin_family = AF_INET; + } else if (sin->sin_family != AF_INET) { + errno = EPFNOSUPPORT; + return (-1); + } + if (port == 0) { + port = (getpid() % NPORTS) + STARTPORT; + } + res = -1; + errno = EADDRINUSE; + for (i = 0; i < NPORTS && res < 0 && errno == EADDRINUSE; i++) { + sin->sin_port = htons(port++); + if (port > ENDPORT) { + port = STARTPORT; + } + res = bind(sd, (struct sockaddr *)sin, sizeof(struct sockaddr_in)); + } + return (res); +} diff --git a/TBBT/trace_play/rpc/clnt.h b/TBBT/trace_play/rpc/clnt.h new file mode 100755 index 0000000..3288d5b --- /dev/null +++ b/TBBT/trace_play/rpc/clnt.h @@ -0,0 +1,384 @@ +/* + * @(#)clnt.h 2.1 97/10/23 + */ + +/* @(#)clnt.h 2.1 88/07/29 4.0 RPCSRC; from 1.31 88/02/08 SMI*/ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * clnt.h - Client side remote procedure call interface. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + + +#ifndef _CLNT_ +#define _CLNT_ + +/* + * Rpc calls return an enum clnt_stat. This should be looked at more, + * since each implementation is required to live with this (implementation + * independent) list of errors. + */ +enum clnt_stat { + RPC_SUCCESS=0, /* call succeeded */ + /* + * local errors + */ + RPC_CANTENCODEARGS=1, /* can't encode arguments */ + RPC_CANTDECODERES=2, /* can't decode results */ + RPC_CANTSEND=3, /* failure in sending call */ + RPC_CANTRECV=4, /* failure in receiving result */ + RPC_TIMEDOUT=5, /* call timed out */ + /* + * remote errors + */ + RPC_VERSMISMATCH=6, /* rpc versions not compatible */ + RPC_AUTHERROR=7, /* authentication error */ + RPC_PROGUNAVAIL=8, /* program not available */ + RPC_PROGVERSMISMATCH=9, /* program version mismatched */ + RPC_PROCUNAVAIL=10, /* procedure unavailable */ + RPC_CANTDECODEARGS=11, /* decode arguments error */ + RPC_SYSTEMERROR=12, /* generic "other problem" */ + + /* + * callrpc & clnt_create errors + */ + RPC_UNKNOWNHOST=13, /* unknown host name */ + RPC_UNKNOWNPROTO=17, /* unkown protocol */ + + /* + * _ create errors + */ + RPC_PMAPFAILURE=14, /* the pmapper failed in its call */ + RPC_PROGNOTREGISTERED=15, /* remote program is not registered */ + /* + * unspecified error + */ + RPC_FAILED=16 +}; + + +/* + * Error info. + */ +struct rpc_err { + enum clnt_stat re_status; + union { + int RE_errno; /* realated system error */ + enum auth_stat RE_why; /* why the auth error occurred */ + struct { + uint32_t low; /* lowest verion supported */ + uint32_t high; /* highest verion supported */ + } RE_vers; + struct { /* maybe meaningful if RPC_FAILED */ + int32_t s1; + int32_t s2; + } RE_lb; /* life boot & debugging only */ + } ru; +#define re_errno ru.RE_errno +#define re_why ru.RE_why +#define re_vers ru.RE_vers +#define re_lb ru.RE_lb +}; + + +/* + * Client rpc handle. + * Created by individual implementations, see e.g. rpc_udp.c. + * Client is responsible for initializing auth, see e.g. auth_none.c. + */ +typedef struct { + AUTH *cl_auth; /* authenticator */ + struct clnt_ops *cl_ops; + void *cl_private; /* private stuff */ +} CLIENT; + +struct clnt_ops { + enum clnt_stat (*cl_call)(CLIENT *, uint32_t, xdrproc_t, void *, xdrproc_t, void *, struct timeval); + void (*cl_abort)(CLIENT *); + void (*cl_geterr)(CLIENT *, struct rpc_err *); + bool_t (*cl_freeres)(CLIENT *, xdrproc_t, void *); + void (*cl_destroy)(CLIENT *); + bool_t (*cl_control)(CLIENT *, uint_t, void *); + bool_t (*cl_getreply)(CLIENT *, xdrproc_t, void *, int, + uint32_t *, uint32_t *, struct timeval *); + int (*cl_poll)(CLIENT *, uint32_t); +}; + +/* + * client side rpc interface ops + * + * Parameter types are: + * + */ + +/* + * enum clnt_stat + * CLNT_CALL(rh, proc, xargs, argsp, xres, resp, timeout) + * CLIENT *rh; + * uint32_t proc; + * xdrproc_t xargs; + * void * argsp; + * xdrproc_t xres; + * void * resp; + * struct timeval timeout; + */ +#define CLNT_CALL(rh, proc, xargs, argsp, xres, resp, secs) \ + ((*(rh)->cl_ops->cl_call)(rh, proc, xargs, argsp, xres, resp, secs)) +#define clnt_call(rh, proc, xargs, argsp, xres, resp, secs) \ + ((*(rh)->cl_ops->cl_call)(rh, proc, xargs, argsp, xres, resp, secs)) + +/* + * void + * CLNT_ABORT(rh); + * CLIENT *rh; + */ +#define CLNT_ABORT(rh) ((*(rh)->cl_ops->cl_abort)(rh)) +#define clnt_abort(rh) ((*(rh)->cl_ops->cl_abort)(rh)) + +/* + * struct rpc_err + * CLNT_GETERR(rh); + * CLIENT *rh; + */ +#define CLNT_GETERR(rh,errp) ((*(rh)->cl_ops->cl_geterr)(rh, errp)) +#define clnt_geterr(rh,errp) ((*(rh)->cl_ops->cl_geterr)(rh, errp)) + + +/* + * bool_t + * CLNT_FREERES(rh, xres, resp); + * CLIENT *rh; + * xdrproc_t xres; + * void * resp; + */ +#define CLNT_FREERES(rh,xres,resp) ((*(rh)->cl_ops->cl_freeres)(rh,xres,resp)) +#define clnt_freeres(rh,xres,resp) ((*(rh)->cl_ops->cl_freeres)(rh,xres,resp)) + +/* + * bool_t + * CLNT_CONTROL(cl, request, info) + * CLIENT *cl; + * uint_t request; + * void *info; + */ +#define CLNT_CONTROL(cl,rq,in) ((*(cl)->cl_ops->cl_control)(cl,rq,in)) +#define clnt_control(cl,rq,in) ((*(cl)->cl_ops->cl_control)(cl,rq,in)) + +/* + * control operations that apply to both udp and tcp transports + */ +#define CLSET_TIMEOUT 1 /* set timeout (timeval) */ +#define CLGET_TIMEOUT 2 /* get timeout (timeval) */ +#define CLGET_SERVER_ADDR 3 /* get server's address (sockaddr) */ +/* + * udp only control operations + */ +#define CLSET_RETRY_TIMEOUT 4 /* set retry timeout (timeval) */ +#define CLGET_RETRY_TIMEOUT 5 /* get retry timeout (timeval) */ + +/* + * void + * CLNT_DESTROY(rh); + * CLIENT *rh; + */ +#define CLNT_DESTROY(rh) ((*(rh)->cl_ops->cl_destroy)(rh)) +#define clnt_destroy(rh) ((*(rh)->cl_ops->cl_destroy)(rh)) + +/* + * bool_t + * CLNT_GETREPLY(rh,xres,xresp,xid,tv) + * CLIENT *rh; + * xdrproc_t xres; + * void * resp; + * int cnt; + * uint32_t *xids; + * uint32_t *xid; + * struct timeval *tv; + */ +#define CLNT_GETREPLY(rh,xres,xresp,cnt,xids,xid,tv) ((*(rh)->cl_ops->cl_getreply)(rh,xres,xresp,cnt,xids,xid,tv)) +#define clnt_getreply(rh,xres,xresp,cnt,xids,xid,tv) ((*(rh)->cl_ops->cl_getreply)(rh,xres,xresp,cnt,xids,xid,tv)) + +/* + * bool_t + * CLNT_POLL(rh,xusec) + * CLIENT *rh; + * uint32_t xusec; + */ +#define CLNT_POLL(rh,xid) ((*(rh)->cl_ops->cl_poll)(rh,xid)) +#define clnt_poll(rh,xid) ((*(rh)->cl_ops->cl_poll)(rh,xid)) + +/* + * RPCTEST is a test program which is accessable on every rpc + * transport/port. It is used for testing, performance evaluation, + * and network administration. + */ + +#define RPCTEST_PROGRAM ((uint32_t)1) +#define RPCTEST_VERSION ((uint32_t)1) +#define RPCTEST_NULL_PROC ((uint32_t)2) +#define RPCTEST_NULL_BATCH_PROC ((uint32_t)3) + +/* + * By convention, procedure 0 takes null arguments and returns them + */ + +#define NULLPROC ((uint32_t)0) + +/* + * Below are the client handle creation routines for the various + * implementations of client side rpc. They can return NULL if a + * creation failure occurs. + */ + +/* + * Memory based rpc (for speed check and testing) + * CLIENT * + * clntraw_create(prog, vers) + * uint32_t prog; + * uint32_t vers; + */ +extern CLIENT *clntraw_create(uint32_t, uint32_t); + +/* + * Generic client creation routine. Supported protocols are "udp" and "tcp" + */ +extern CLIENT *clnt_create(char *, uint32_t, uint32_t, char *); + + +/* + * TCP based rpc + * CLIENT * + * sfs_ctcp_create(raddr, prog, vers, sockp, sendsz, recvsz) + * struct sockaddr_in *raddr; + * uint32_t prog; + * uint32_t version; + * register int *sockp; + * uint_t sendsz; + * uint_t recvsz; + */ +extern CLIENT *sfs_ctcp_create(struct sockaddr_in *, uint32_t, uint32_t, + int *, uint_t, uint_t); +extern CLIENT *clnttcp_create(struct sockaddr_in *, uint32_t, uint32_t, + int *, uint_t, uint_t); + +/* + * UDP based rpc. + * CLIENT * + * sfs_cudp_create(raddr, program, version, wait, sockp) + * struct sockaddr_in *raddr; + * uint32_t program; + * uint32_t version; + * struct timeval wait; + * int *sockp; + * + * Same as above, but you specify max packet sizes. + * CLIENT * + * sfs_cudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz) + * struct sockaddr_in *raddr; + * uint32_t program; + * uint32_t version; + * struct timeval wait; + * int *sockp; + * uint_t sendsz; + * uint_t recvsz; + */ +extern CLIENT *clntudp_create(struct sockaddr_in *, uint32_t, uint32_t, + struct timeval, int *); +extern CLIENT *clntudp_bufcreate(struct sockaddr_in *, uint32_t, uint32_t, + struct timeval, int *, uint_t, uint_t); +extern CLIENT *sfs_cudp_create(struct sockaddr_in *, uint32_t, uint32_t, + struct timeval, int *); +extern CLIENT *sfs_cudp_bufcreate(struct sockaddr_in *, uint32_t, uint32_t, + struct timeval, int *, uint_t, uint_t); + +/* + * Print why creation failed + */ +extern void clnt_pcreateerror(char *); +extern char *clnt_spcreateerror(char *); + +/* + * Like clnt_perror(), but is more verbose in its output + */ +extern void clnt_perrno(enum clnt_stat); + +/* + * Print an English error message, given the client error code + */ +extern void clnt_perror(CLIENT *, char *); +extern char *clnt_sperror(CLIENT *, char *); + +/* + * If a creation fails, the following allows the user to figure out why. + */ +struct rpc_createerr { + enum clnt_stat cf_stat; + struct rpc_err cf_error; /* useful when cf_stat == RPC_PMAPFAILURE */ +}; + +extern struct rpc_createerr rpc_createerr; + +/* + * Copy error message to buffer. + */ +extern char *clnt_sperrno(enum clnt_stat); + +extern int callrpc(char *, int, int, int, xdrproc_t, char *, xdrproc_t, char *); + +extern int bindresvport(int sd, struct sockaddr_in *sin); + +#define UDPMSGSIZE (63 * 1024) /* rpc imposed limit on udp msg size */ +#define RPCSMALLMSGSIZE 400 /* a more reasonable packet size */ + +#if !defined(RPC_ANYSOCK) +#define RPC_ANYSOCK -1 +#endif + +#endif /*!_CLNT_*/ diff --git a/TBBT/trace_play/rpc/clnt_generic.c b/TBBT/trace_play/rpc/clnt_generic.c new file mode 100755 index 0000000..16538ef --- /dev/null +++ b/TBBT/trace_play/rpc/clnt_generic.c @@ -0,0 +1,133 @@ +#ifndef lint +static char sfs_clnt_generic_id[] = "@(#)clnt_generic.c 2.1 97/10/23"; +#endif + +/* @(#)clnt_generic.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI"; +#endif +/* + * Copyright (C) 1987, Sun Microsystems, Inc. + */ + +#include <string.h> +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include <errno.h> +#include <netdb.h> + +/* + * Generic client creation: takes (hostname, program-number, protocol) and + * returns client handle. Default options are set, which the user can + * change using the rpc equivalent of ioctl()'s. + */ +CLIENT * +clnt_create(char *hostname, + uint32_t prog, + uint32_t vers, + char *proto) +{ + struct hostent *h; + struct protoent *p; + struct sockaddr_in sin; + int sock; + struct timeval tv; + CLIENT *client; + + h = gethostbyname(hostname); + if (h == NULL) { + rpc_createerr.cf_stat = RPC_UNKNOWNHOST; + return (NULL); + } + if (h->h_addrtype != AF_INET) { + /* + * Only support INET for now + */ + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = EAFNOSUPPORT; + return (NULL); + } + sin.sin_family = h->h_addrtype; + sin.sin_port = 0; + memset(sin.sin_zero, '\0', sizeof(sin.sin_zero)); + memmove((char*)&sin.sin_addr, h->h_addr, h->h_length); + p = getprotobyname(proto); + if (p == NULL) { + rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + rpc_createerr.cf_error.re_errno = EPFNOSUPPORT; + return (NULL); + } + sock = RPC_ANYSOCK; + switch (p->p_proto) { + case IPPROTO_UDP: + tv.tv_sec = 5; + tv.tv_usec = 0; + client = sfs_cudp_create(&sin, prog, vers, tv, &sock); + if (client == NULL) { + return (NULL); + } + tv.tv_sec = 25; + clnt_control(client, CLSET_TIMEOUT, &tv); + break; + case IPPROTO_TCP: + client = sfs_ctcp_create(&sin, prog, vers, &sock, 0, 0); + if (client == NULL) { + return (NULL); + } + tv.tv_sec = 25; + tv.tv_usec = 0; + clnt_control(client, CLSET_TIMEOUT, &tv); + break; + default: + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = EPFNOSUPPORT; + return (NULL); + } + return (client); +} diff --git a/TBBT/trace_play/rpc/clnt_perror.c b/TBBT/trace_play/rpc/clnt_perror.c new file mode 100755 index 0000000..71d1502 --- /dev/null +++ b/TBBT/trace_play/rpc/clnt_perror.c @@ -0,0 +1,316 @@ +#ifndef lint +static char sfs_clnt_perror_c_id[] = "@(#)clnt_perror.c 2.1 97/10/23"; +#endif +/* @(#)clnt_perror.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_perror.c 1.15 87/10/07 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_perror.c + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "rpc/rpc.h" + +static char *auth_errmsg(enum auth_stat); + +static char * +_buf() +{ + static char *buf = NULL; + if (buf == NULL) + buf = (char *)malloc(256); + return (buf); +} + +/* + * Print reply error info + */ +char * +clnt_sperror( + CLIENT *rpch, + char *s) +{ + struct rpc_err e; + char *err; + char *str = _buf(); + char *strstart = str; + + if (str == 0) + return (0); + CLNT_GETERR(rpch, &e); + + (void) sprintf(str, "%s: ", s); + str += strlen(str); + + (void) strcpy(str, clnt_sperrno(e.re_status)); + str += strlen(str); + + switch (e.re_status) { + case RPC_SUCCESS: + case RPC_CANTENCODEARGS: + case RPC_CANTDECODERES: + case RPC_TIMEDOUT: + case RPC_PROGUNAVAIL: + case RPC_PROCUNAVAIL: + case RPC_CANTDECODEARGS: + case RPC_SYSTEMERROR: + case RPC_UNKNOWNHOST: + case RPC_UNKNOWNPROTO: + case RPC_PMAPFAILURE: + case RPC_PROGNOTREGISTERED: + case RPC_FAILED: + break; + + case RPC_CANTSEND: + case RPC_CANTRECV: + (void) sprintf(str, "; errno = %s", + strerror(e.re_errno)); + str += strlen(str); + break; + + case RPC_VERSMISMATCH: + (void) sprintf(str, + "; low version = %lu, high version = %lu", + e.re_vers.low, e.re_vers.high); + str += strlen(str); + break; + + case RPC_AUTHERROR: + err = auth_errmsg(e.re_why); + (void) sprintf(str,"; why = "); + str += strlen(str); + if (err != NULL) { + (void) sprintf(str, "%s",err); + } else { + (void) sprintf(str, + "(unknown authentication error - %d)", + (int) e.re_why); + } + str += strlen(str); + break; + + case RPC_PROGVERSMISMATCH: + (void) sprintf(str, + "; low version = %lu, high version = %lu", + e.re_vers.low, e.re_vers.high); + str += strlen(str); + break; + + default: /* unknown */ + (void) sprintf(str, + "; s1 = %lu, s2 = %lu", + e.re_lb.s1, e.re_lb.s2); + str += strlen(str); + break; + } + (void) sprintf(str, "\n"); + return(strstart) ; +} + +void +clnt_perror( + CLIENT *rpch, + char *s) +{ + (void) fprintf(stderr,"%s",clnt_sperror(rpch,s)); +} + + +struct rpc_errtab { + enum clnt_stat status; + char *message; +}; + +static struct rpc_errtab rpc_errlist[] = { + { RPC_SUCCESS, + "RPC: Success" }, + { RPC_CANTENCODEARGS, + "RPC: Can't encode arguments" }, + { RPC_CANTDECODERES, + "RPC: Can't decode result" }, + { RPC_CANTSEND, + "RPC: Unable to send" }, + { RPC_CANTRECV, + "RPC: Unable to receive" }, + { RPC_TIMEDOUT, + "RPC: Timed out" }, + { RPC_VERSMISMATCH, + "RPC: Incompatible versions of RPC" }, + { RPC_AUTHERROR, + "RPC: Authentication error" }, + { RPC_PROGUNAVAIL, + "RPC: Program unavailable" }, + { RPC_PROGVERSMISMATCH, + "RPC: Program/version mismatch" }, + { RPC_PROCUNAVAIL, + "RPC: Procedure unavailable" }, + { RPC_CANTDECODEARGS, + "RPC: Server can't decode arguments" }, + { RPC_SYSTEMERROR, + "RPC: Remote system error" }, + { RPC_UNKNOWNHOST, + "RPC: Unknown host" }, + { RPC_UNKNOWNPROTO, + "RPC: Unknown protocol" }, + { RPC_PMAPFAILURE, + "RPC: Port mapper failure" }, + { RPC_PROGNOTREGISTERED, + "RPC: Program not registered"}, + { RPC_FAILED, + "RPC: Failed (unspecified error)"} +}; + + +/* + * This interface for use by clntrpc + */ +char * +clnt_sperrno( + enum clnt_stat stat) +{ + int i; + + for (i = 0; i < sizeof(rpc_errlist)/sizeof(struct rpc_errtab); i++) { + if (rpc_errlist[i].status == stat) { + return (rpc_errlist[i].message); + } + } + return ("RPC: (unknown error code)"); +} + +void +clnt_perrno( + enum clnt_stat num) +{ + (void) fprintf(stderr,"%s",clnt_sperrno(num)); +} + + +char * +clnt_spcreateerror(char *s) +{ + char *sp; + char *str = _buf(); + + if (str == 0) + return(0); + (void) sprintf(str, "%s: ", s); + (void) strcat(str, clnt_sperrno(rpc_createerr.cf_stat)); + switch (rpc_createerr.cf_stat) { + case RPC_PMAPFAILURE: + (void) strcat(str, " - "); + (void) strcat(str, + clnt_sperrno(rpc_createerr.cf_error.re_status)); + break; + + case RPC_SYSTEMERROR: + sp = strerror(rpc_createerr.cf_error.re_errno); + (void) strcat(str, " - "); + if (rpc_createerr.cf_error.re_errno > 0 + && sp != NULL) + (void) strcat(str, sp); + else + (void) sprintf(&str[strlen(str)], "Error %d", + rpc_createerr.cf_error.re_errno); + break; + } + (void) strcat(str, "\n"); + return (str); +} + +void +clnt_pcreateerror(char *s) +{ + (void) fprintf(stderr, "%s", clnt_spcreateerror(s)); +} + +struct auth_errtab { + enum auth_stat status; + char *message; +}; + +static struct auth_errtab auth_errlist[] = { + { AUTH_OK, + "Authentication OK" }, + { AUTH_BADCRED, + "Invalid client credential" }, + { AUTH_REJECTEDCRED, + "Server rejected credential" }, + { AUTH_BADVERF, + "Invalid client verifier" }, + { AUTH_REJECTEDVERF, + "Server rejected verifier" }, + { AUTH_TOOWEAK, + "Client credential too weak" }, + { AUTH_INVALIDRESP, + "Invalid server verifier" }, + { AUTH_FAILED, + "Failed (unspecified error)" }, +}; + +static char * +auth_errmsg( + enum auth_stat stat) +{ + int i; + + for (i = 0; i < sizeof(auth_errlist)/sizeof(struct auth_errtab); i++) { + if (auth_errlist[i].status == stat) { + return(auth_errlist[i].message); + } + } + return(NULL); +} diff --git a/TBBT/trace_play/rpc/clnt_simple.c b/TBBT/trace_play/rpc/clnt_simple.c new file mode 100755 index 0000000..ab5ae9a --- /dev/null +++ b/TBBT/trace_play/rpc/clnt_simple.c @@ -0,0 +1,141 @@ +#ifndef lint +static char sfs_clnt_simple_id[] = "@(#)clnt_simple.c 2.1 97/10/23"; +#endif +/* @(#)clnt_simple.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_simple.c 1.35 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_simple.c + * Simplified front end to rpc. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include <netdb.h> +#include <string.h> + +static struct callrpc_private { + CLIENT *client; + int socket; + int oldprognum, oldversnum, valid; + char *oldhost; +} *callrpc_private; + +callrpc( + char *host, + int prognum, + int versnum, + int procnum, + xdrproc_t inproc, + char *in, + xdrproc_t outproc, + char *out) +{ + register struct callrpc_private *crp = callrpc_private; + struct sockaddr_in server_addr; + enum clnt_stat clnt_stat; + struct hostent *hp; + struct timeval timeout, tottimeout; + + if (crp == 0) { + crp = (struct callrpc_private *)calloc(1, + sizeof (struct callrpc_private)); + if (crp == 0) + return (0); + callrpc_private = crp; + } + if (crp->oldhost == NULL) { + crp->oldhost = malloc(256); + crp->oldhost[0] = 0; + crp->socket = RPC_ANYSOCK; + } + if (crp->valid && crp->oldprognum == prognum && crp->oldversnum == versnum + && strcmp(crp->oldhost, host) == 0) { + /* reuse old client */ + } else { + crp->valid = 0; + (void)close(crp->socket); + crp->socket = RPC_ANYSOCK; + if (crp->client) { + clnt_destroy(crp->client); + crp->client = NULL; + } + if ((hp = gethostbyname(host)) == NULL) + return ((int) RPC_UNKNOWNHOST); + timeout.tv_usec = 0; + timeout.tv_sec = 5; + memmove((char *)&server_addr.sin_addr, hp->h_addr, hp->h_length); + server_addr.sin_family = AF_INET; + server_addr.sin_port = 0; + if ((crp->client = clntudp_create(&server_addr, (uint32_t)prognum, + (uint32_t)versnum, timeout, &crp->socket)) == NULL) + return ((int) rpc_createerr.cf_stat); + crp->valid = 1; + crp->oldprognum = prognum; + crp->oldversnum = versnum; + (void) strcpy(crp->oldhost, host); + } + tottimeout.tv_sec = 25; + tottimeout.tv_usec = 0; + clnt_stat = clnt_call(crp->client, procnum, inproc, in, + outproc, out, tottimeout); + /* + * if call failed, empty cache + */ + if (clnt_stat != RPC_SUCCESS) + crp->valid = 0; + return ((int) clnt_stat); +} diff --git a/TBBT/trace_play/rpc/clnt_tcp.c b/TBBT/trace_play/rpc/clnt_tcp.c new file mode 100755 index 0000000..4054f01 --- /dev/null +++ b/TBBT/trace_play/rpc/clnt_tcp.c @@ -0,0 +1,522 @@ +#ifndef lint +static char sfs_clnt_tcp_c_id[] = "@(#)clnt_tcp.c 2.1 97/10/23"; +#endif +/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_tcp.c, Implements a TCP/IP based, client side RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * TCP based RPC supports 'batched calls'. + * A sequence of calls may be batched-up in a send buffer. The rpc call + * return immediately to the client even though the call was not necessarily + * sent. The batching occurs if the results' xdr routine is NULL (0) AND + * the rpc timeout value is zero (see clnt.h, rpc). + * + * Clients should NOT casually batch calls that in fact return results; that is, + * the server side should be aware that a call is batched and not produce any + * return message. Batched calls that produce many result messages can + * deadlock (netlock) the client and the server.... + * + * Now go hang yourself. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include <netdb.h> +#include <errno.h> +#include "rpc/pmap_clnt.h" + +#define MCALL_MSG_SIZE 24 + +struct ct_data { + int ct_sock; + bool_t ct_closeit; + struct timeval ct_wait; + bool_t ct_waitset; /* wait set by clnt_control? */ + struct sockaddr_in ct_addr; + struct rpc_err ct_error; + char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ + uint_t ct_mpos; /* pos after marshal */ + XDR ct_xdrs; +}; + +static int readtcp(struct ct_data *, char *, int); +static int writetcp(struct ct_data *, char *, int); + +static enum clnt_stat clnttcp_call(CLIENT *, uint32_t, xdrproc_t, void *, + xdrproc_t, void *, struct timeval); +static void clnttcp_abort(CLIENT *); +static void clnttcp_geterr(CLIENT *, struct rpc_err *); +static bool_t clnttcp_freeres(CLIENT *, xdrproc_t, void *); +static bool_t clnttcp_control(CLIENT *, uint_t, void *); +static void clnttcp_destroy(CLIENT *h); +static bool_t clnttcp_getreply(CLIENT *, xdrproc_t, void *, + int, uint32_t *, uint32_t *, struct timeval *); +static int clnttcp_poll(CLIENT *, uint32_t); + + +static struct clnt_ops tcp_ops = { + clnttcp_call, + clnttcp_abort, + clnttcp_geterr, + clnttcp_freeres, + clnttcp_destroy, + clnttcp_control, + clnttcp_getreply, + clnttcp_poll +}; + +/* + * Create a client handle for a tcp/ip connection. + * If *sockp<0, *sockp is set to a newly created TCP socket and it is + * connected to raddr. If *sockp non-negative then + * raddr is ignored. The rpc/tcp package does buffering + * similar to stdio, so the client must pick send and receive buffer sizes,]; + * 0 => use the default. + * If raddr->sin_port is 0, then a binder on the remote machine is + * consulted for the right port number. + * NB: *sockp is copied into a private area. + * NB: It is the clients responsibility to close *sockp. + * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this + * something more useful. + */ +CLIENT * +clnttcp_create( + struct sockaddr_in *raddr, + uint32_t prog, + uint32_t vers, + int *sockp, + uint_t sendsz, + uint_t recvsz) +{ + CLIENT *h; + struct ct_data *ct; + struct timeval now; + struct rpc_msg call_msg; + + h = (CLIENT *)mem_alloc(sizeof(CLIENT)); + if (h == NULL) { + (void)fprintf(stderr, "clnttcp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + ct = (struct ct_data *)mem_alloc(sizeof(struct ct_data)); + if (ct == NULL) { + (void)fprintf(stderr, "clnttcp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + + /* + * If no port number given ask the pmap for one + */ + if (raddr->sin_port == 0) { + uint16_t port; + if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) { + mem_free((void *)ct, sizeof(struct ct_data)); + mem_free((void *)h, sizeof(CLIENT)); + return ((CLIENT *)NULL); + } + raddr->sin_port = htons(port); + } + + /* + * If no socket given, open one + */ + if (*sockp < 0) { + *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + (void)bindresvport(*sockp, (struct sockaddr_in *)0); + if ((*sockp < 0) + || (connect(*sockp, (struct sockaddr *)raddr, + sizeof(struct sockaddr_in)) < 0)) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + (void)close(*sockp); + goto fooy; + } + ct->ct_closeit = TRUE; + } else { + ct->ct_closeit = FALSE; + } + + /* + * Set up private data struct + */ + ct->ct_sock = *sockp; + ct->ct_wait.tv_usec = 0; + ct->ct_waitset = FALSE; + ct->ct_addr = *raddr; + + /* + * Initialize call message + */ + (void)gettimeofday(&now, (struct timezone *)0); + call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = prog; + call_msg.rm_call.cb_vers = vers; + + /* + * pre-serialize the staic part of the call msg and stash it away + */ + xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, + XDR_ENCODE); + if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) { + if (ct->ct_closeit) { + (void)close(*sockp); + } + goto fooy; + } + ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs)); + XDR_DESTROY(&(ct->ct_xdrs)); + + /* + * Create a client handle which uses xdrrec for serialization + * and authnone for authentication. + */ + xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz, + (void *)ct, readtcp, writetcp); + h->cl_ops = &tcp_ops; + h->cl_private = (char *) ct; + h->cl_auth = authnone_create(); + return (h); + +fooy: + /* + * Something goofed, free stuff and barf + */ + mem_free((void *)ct, sizeof(struct ct_data)); + mem_free((void *)h, sizeof(CLIENT)); + return ((CLIENT *)NULL); +} + +static enum clnt_stat +clnttcp_call( + CLIENT *h, + uint32_t proc, + xdrproc_t xdr_args, + void * args_ptr, + xdrproc_t xdr_results, + void * results_ptr, + struct timeval timeout) +{ + struct ct_data *ct = (struct ct_data *) h->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + struct rpc_msg reply_msg; + uint32_t x_id; + uint32_t *msg_x_id = (uint32_t *)(ct->ct_mcall); /* yuk */ + bool_t shipnow; + int refreshes = 2; + + if (!ct->ct_waitset) { + ct->ct_wait = timeout; + } + + shipnow = + (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0 + && timeout.tv_usec == 0) ? FALSE : TRUE; + +call_again: + xdrs->x_op = XDR_ENCODE; + ct->ct_error.re_status = RPC_SUCCESS; + x_id = ntohl(--(*msg_x_id)); + if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) || + (! XDR_PUTLONG(xdrs, (int32_t *)&proc)) || + (! AUTH_MARSHALL(h->cl_auth, xdrs)) || + (! (*xdr_args)(xdrs, args_ptr))) { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTENCODEARGS; + (void)xdrrec_endofrecord(xdrs, TRUE); + return (ct->ct_error.re_status); + } + if (! xdrrec_endofrecord(xdrs, shipnow)) + return (ct->ct_error.re_status = RPC_CANTSEND); + if (! shipnow) + return (RPC_SUCCESS); + /* + * Hack to provide rpc-based message passing + */ + if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { + return(ct->ct_error.re_status = RPC_TIMEDOUT); + } + + + /* + * Keep receiving until we get a valid transaction id + */ + xdrs->x_op = XDR_DECODE; + /* CONSTCOND */ + while (TRUE) { + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = NULL; + reply_msg.acpted_rply.ar_results.proc = xdr_void; + if (! xdrrec_skiprecord(xdrs)) + return (ct->ct_error.re_status); + /* now decode and validate the response header */ + if (! xdr_replymsg(xdrs, &reply_msg)) { + if (ct->ct_error.re_status == RPC_SUCCESS) + continue; + return (ct->ct_error.re_status); + } + if (reply_msg.rm_xid == x_id) + break; + } + + /* + * process header + */ + _seterr_reply(&reply_msg, &(ct->ct_error)); + if (ct->ct_error.re_status == RPC_SUCCESS) { + if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) { + ct->ct_error.re_status = RPC_AUTHERROR; + ct->ct_error.re_why = AUTH_INVALIDRESP; + } else if (! (*xdr_results)(xdrs, results_ptr)) { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTDECODERES; + } + /* free verifier ... */ + if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { + xdrs->x_op = XDR_FREE; + (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf)); + } + } /* end successful completion */ + else { + /* maybe our credentials need to be refreshed ... */ + if (refreshes-- && AUTH_REFRESH(h->cl_auth)) + goto call_again; + } /* end of unsuccessful completion */ + return (ct->ct_error.re_status); +} + +static void +clnttcp_geterr( + CLIENT *h, + struct rpc_err *errp) +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + *errp = ct->ct_error; +} + +static bool_t +clnttcp_freeres( + CLIENT *cl, + xdrproc_t xdr_res, + void * res_ptr) +{ + struct ct_data *ct = (struct ct_data *)cl->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_res)(xdrs, res_ptr)); +} + +/* ARGSUSED */ +static void +clnttcp_abort(CLIENT *c) +{ +} + +static bool_t +clnttcp_control( + CLIENT *cl, + uint_t request, + void *info) +{ + struct ct_data *ct = (struct ct_data *)cl->cl_private; + + switch (request) { + case CLSET_TIMEOUT: + ct->ct_wait = *(struct timeval *)info; + ct->ct_waitset = TRUE; + break; + case CLGET_TIMEOUT: + *(struct timeval *)info = ct->ct_wait; + break; + case CLGET_SERVER_ADDR: + *(struct sockaddr_in *)info = ct->ct_addr; + break; + default: + return (FALSE); + } + return (TRUE); +} + + +static void +clnttcp_destroy( + CLIENT *h) +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + if (ct->ct_closeit) { + (void)close(ct->ct_sock); + } + XDR_DESTROY(&(ct->ct_xdrs)); + mem_free((void *)ct, sizeof(struct ct_data)); + mem_free((void *)h, sizeof(CLIENT)); +} + +/* + * Interface between xdr serializer and tcp connection. + * Behaves like the system calls, read & write, but keeps some error state + * around for the rpc level. + */ +static int +readtcp( + struct ct_data *ct, + char *buf, + int len) +{ +#ifdef FD_SETSIZE + fd_set mask; + fd_set readfds; + + if (len == 0) + return (0); + FD_ZERO(&mask); + FD_SET(ct->ct_sock, &mask); +#else + int mask = 1 << (ct->ct_sock); + int readfds; + + if (len == 0) + return (0); + +#endif /* def FD_SETSIZE */ + /* CONSTCOND */ + while (TRUE) { + readfds = mask; + switch (select(_rpc_dtablesize(), &readfds, NULL, NULL, + &(ct->ct_wait))) { + case 0: + ct->ct_error.re_status = RPC_TIMEDOUT; + return (-1); + + case -1: + if (errno == EINTR) + continue; + ct->ct_error.re_status = RPC_CANTRECV; + ct->ct_error.re_errno = errno; + return (-1); + } + break; + } + switch (len = read(ct->ct_sock, buf, len)) { + + case 0: + /* premature eof */ + ct->ct_error.re_errno = ECONNRESET; + ct->ct_error.re_status = RPC_CANTRECV; + len = -1; /* it's really an error */ + break; + + case -1: + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTRECV; + break; + } + return (len); +} + +static int +writetcp( + struct ct_data *ct, + char *buf, + int len) +{ + int i, cnt; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) { + if ((i = write(ct->ct_sock, buf, cnt)) == -1) { + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTSEND; + return (-1); + } + } + return (len); +} + +/* ARGSUSED */ +static bool_t +clnttcp_getreply( + CLIENT *cl, + xdrproc_t xproc, + void *xres, + int cnt, + uint32_t *xids, + uint32_t *xid, + struct timeval *tv) +{ + return (FALSE); +} + +/* ARGSUSED */ +static int +clnttcp_poll( + CLIENT *cl, + uint32_t usec) +{ + return (-1); +} + diff --git a/TBBT/trace_play/rpc/clnt_udp.c b/TBBT/trace_play/rpc/clnt_udp.c new file mode 100755 index 0000000..d362f10 --- /dev/null +++ b/TBBT/trace_play/rpc/clnt_udp.c @@ -0,0 +1,510 @@ +#ifndef lint +static char sfs_clnt_id[] = "@(#)clnt_udp.c 2.1 97/10/23"; +#endif +/* @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_udp.c, Implements a UDP/IP based, client side RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#ifndef FreeBSD +#include <stropts.h> +#endif /* ndef Free BSD */ +#include <string.h> +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include <netdb.h> +#include <errno.h> +#include "rpc/pmap_clnt.h" + +/* + * Private data kept per client handle + */ +struct cu_data { + int cu_sock; + bool_t cu_closeit; + struct sockaddr_in cu_raddr; + int cu_rlen; + struct timeval cu_wait; + struct timeval cu_total; + struct rpc_err cu_error; + XDR cu_outxdrs; + uint_t cu_xdrpos; + uint_t cu_sendsz; + char *cu_outbuf; + uint_t cu_recvsz; + char cu_inbuf[1]; +}; + +/* + * UDP bases client side rpc operations + */ +static enum clnt_stat clntudp_call(CLIENT *, uint32_t, xdrproc_t, void *, + xdrproc_t, void *, struct timeval); +static void clntudp_abort(CLIENT *); +static void clntudp_geterr(CLIENT *, struct rpc_err *); +static bool_t clntudp_freeres(CLIENT *, xdrproc_t, void *); +static bool_t clntudp_control(CLIENT *, uint_t, void *); +static void clntudp_destroy(CLIENT *); +static bool_t clntudp_getreply(CLIENT *, xdrproc_t, void *, + int, uint32_t *, uint32_t *, struct timeval *); +static int clntudp_poll(CLIENT *, uint32_t); + +static struct clnt_ops udp_ops = { + clntudp_call, + clntudp_abort, + clntudp_geterr, + clntudp_freeres, + clntudp_destroy, + clntudp_control, + clntudp_getreply, + clntudp_poll +}; + +/* + * Create a UDP based client handle. + * If *sockp<0, *sockp is set to a newly created UPD socket. + * If raddr->sin_port is 0 a binder on the remote machine + * is consulted for the correct port number. + * NB: It is the clients responsibility to close *sockp. + * NB: The rpch->cl_auth is initialized to null authentication. + * Caller may wish to set this something more useful. + * + * wait is the amount of time used between retransmitting a call if + * no response has been heard; retransmition occurs until the actual + * rpc call times out. + * + * sendsz and recvsz are the maximum allowable packet sizes that can be + * sent and received. + */ +CLIENT * +clntudp_bufcreate( + struct sockaddr_in *raddr, + uint32_t program, + uint32_t version, + struct timeval wait, + int *sockp, + uint_t sendsz, + uint_t recvsz) +{ + CLIENT *cl; + struct cu_data *cu; + struct timeval now; + struct rpc_msg call_msg; + + cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); + if (cl == NULL) { + (void) fprintf(stderr, "clntudp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + sendsz = ((sendsz + 3) / 4) * 4; + recvsz = ((recvsz + 3) / 4) * 4; + cu = (struct cu_data *)mem_alloc(sizeof(struct cu_data) + + sendsz + recvsz); + if (cu == NULL) { + (void) fprintf(stderr, "clntudp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + cu->cu_outbuf = &cu->cu_inbuf[recvsz]; + + (void)gettimeofday(&now, (struct timezone *)0); + if (raddr->sin_port == 0) { + uint16_t port; + if ((port = + pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { + goto fooy; + } + raddr->sin_port = htons(port); + } + cl->cl_ops = &udp_ops; + cl->cl_private = (char *)cu; + cu->cu_raddr = *raddr; + cu->cu_rlen = sizeof (cu->cu_raddr); + cu->cu_wait = wait; + cu->cu_total.tv_sec = -1; + cu->cu_total.tv_usec = -1; + cu->cu_sendsz = sendsz; + cu->cu_recvsz = recvsz; + call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = program; + call_msg.rm_call.cb_vers = version; + xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, + sendsz, XDR_ENCODE); + if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { + goto fooy; + } + cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); + if (*sockp < 0) { +#if defined(O_NONBLOCK) + int flags; +#elif defined(FIONBIO) + int dontblock = 1; +#endif + + *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (*sockp < 0) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + /* attempt to bind to prov port */ + (void)bindresvport(*sockp, (struct sockaddr_in *)0); + /* the sockets rpc controls are non-blocking */ +#if defined(O_NONBLOCK) + flags = fcntl(*sockp, F_GETFL, 0) | O_NONBLOCK; + (void)fcntl(*sockp, F_SETFL, flags); +#elif defined(FIONBIO) + (void)ioctl(*sockp, FIONBIO, (char *) &dontblock); +#endif + cu->cu_closeit = TRUE; + } else { + cu->cu_closeit = FALSE; + } + cu->cu_sock = *sockp; + cl->cl_auth = authnone_create(); + return (cl); +fooy: + if (cu) + mem_free((void *)cu, sizeof(struct cu_data) + sendsz + recvsz); + if (cl) + mem_free((void *)cl, sizeof(CLIENT)); + return ((CLIENT *)NULL); +} + +CLIENT * +clntudp_create( + struct sockaddr_in *raddr, + uint32_t program, + uint32_t version, + struct timeval wait, + int *sockp) +{ + + return(clntudp_bufcreate(raddr, program, version, wait, sockp, + UDPMSGSIZE, UDPMSGSIZE)); +} + +static enum clnt_stat +clntudp_call( + CLIENT *cl, + uint32_t proc, + xdrproc_t xargs, + void * argsp, + xdrproc_t xresults, + void * resultsp, + struct timeval utimeout) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + XDR *xdrs; + int outlen; + int inlen; +#if defined(AIX) + size_t fromlen; +#else + int fromlen; +#endif +#ifdef FD_SETSIZE + fd_set readfds; + fd_set mask; +#else + int readfds; + int mask; +#endif /* def FD_SETSIZE */ + struct sockaddr_in from; + struct rpc_msg reply_msg; + XDR reply_xdrs; + struct timeval time_waited; + bool_t ok; + int nrefreshes = 2; /* number of times to refresh cred */ + struct timeval timeout; + + if (cu->cu_total.tv_usec == -1) { + timeout = utimeout; /* use supplied timeout */ + } else { + timeout = cu->cu_total; /* use default timeout */ + } + + time_waited.tv_sec = 0; + time_waited.tv_usec = 0; +call_again: + xdrs = &(cu->cu_outxdrs); + xdrs->x_op = XDR_ENCODE; + XDR_SETPOS(xdrs, cu->cu_xdrpos); + /* + * the transaction is the first thing in the out buffer + */ + (*(uint32_t *)(cu->cu_outbuf))++; + if ((! XDR_PUTLONG(xdrs, (int32_t *)&proc)) || + (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || + (! (*xargs)(xdrs, argsp))) + return (cu->cu_error.re_status = RPC_CANTENCODEARGS); + outlen = (int)XDR_GETPOS(xdrs); + +send_again: + if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, + (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) + != outlen) { + cu->cu_error.re_errno = errno; + return (cu->cu_error.re_status = RPC_CANTSEND); + } + + /* + * Hack to provide rpc-based message passing + */ + if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { + return (cu->cu_error.re_status = RPC_TIMEDOUT); + } + /* + * sub-optimal code appears here because we have + * some clock time to spare while the packets are in flight. + * (We assume that this is actually only executed once.) + */ + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = resultsp; + reply_msg.acpted_rply.ar_results.proc = xresults; +#ifdef FD_SETSIZE + FD_ZERO(&mask); + FD_SET(cu->cu_sock, &mask); +#else + mask = 1 << cu->cu_sock; +#endif /* def FD_SETSIZE */ + for (;;) { + readfds = mask; + switch (select(_rpc_dtablesize(), &readfds, NULL, + NULL, &(cu->cu_wait))) { + + case 0: + time_waited.tv_sec += cu->cu_wait.tv_sec; + time_waited.tv_usec += cu->cu_wait.tv_usec; + while (time_waited.tv_usec >= 1000000) { + time_waited.tv_sec++; + time_waited.tv_usec -= 1000000; + } + if ((time_waited.tv_sec < timeout.tv_sec) || + ((time_waited.tv_sec == timeout.tv_sec) && + (time_waited.tv_usec < timeout.tv_usec))) + goto send_again; + return (cu->cu_error.re_status = RPC_TIMEDOUT); + + /* + * buggy in other cases because time_waited is not being + * updated. + */ + case -1: + if (errno == EINTR) + continue; + cu->cu_error.re_errno = errno; + return (cu->cu_error.re_status = RPC_CANTRECV); + } + do { + fromlen = sizeof(struct sockaddr); + inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, + (int) cu->cu_recvsz, 0, + (struct sockaddr *)&from, &fromlen); + } while (inlen < 0 && errno == EINTR); + if (inlen < 0) { + if (errno == EWOULDBLOCK) + continue; + cu->cu_error.re_errno = errno; + return (cu->cu_error.re_status = RPC_CANTRECV); + } + if (inlen < sizeof(uint32_t)) + continue; + /* see if reply transaction id matches sent id */ + if (*((uint32_t *)(cu->cu_inbuf)) != *((uint32_t *)(cu->cu_outbuf))) + continue; + /* we now assume we have the proper reply */ + break; + } + + /* + * now decode and validate the response + */ + xdrmem_create(&reply_xdrs, cu->cu_inbuf, (uint_t)inlen, XDR_DECODE); + ok = xdr_replymsg(&reply_xdrs, &reply_msg); + /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ + if (ok) { + _seterr_reply(&reply_msg, &(cu->cu_error)); + if (cu->cu_error.re_status == RPC_SUCCESS) { + if (! AUTH_VALIDATE(cl->cl_auth, + &reply_msg.acpted_rply.ar_verf)) { + cu->cu_error.re_status = RPC_AUTHERROR; + cu->cu_error.re_why = AUTH_INVALIDRESP; + } + if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { + xdrs->x_op = XDR_FREE; + (void)xdr_opaque_auth(xdrs, + &(reply_msg.acpted_rply.ar_verf)); + } + } /* end successful completion */ + else { + /* maybe our credentials need to be refreshed ... */ + if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) { + nrefreshes--; + goto call_again; + } + } /* end of unsuccessful completion */ + } /* end of valid reply message */ + else { + cu->cu_error.re_status = RPC_CANTDECODERES; + } + return (cu->cu_error.re_status); +} + +static void +clntudp_geterr( + CLIENT *cl, + struct rpc_err *errp) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + + *errp = cu->cu_error; +} + + +static bool_t +clntudp_freeres( + CLIENT *cl, + xdrproc_t xdr_res, + void * res_ptr) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + XDR *xdrs = &(cu->cu_outxdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_res)(xdrs, res_ptr)); +} + +/* ARGSUSED */ +static void +clntudp_abort( + CLIENT *h) +{ +} + +static bool_t +clntudp_control( + CLIENT *cl, + uint_t request, + void *info) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + + switch (request) { + case CLSET_TIMEOUT: + cu->cu_total = *(struct timeval *)info; + break; + case CLGET_TIMEOUT: + *(struct timeval *)info = cu->cu_total; + break; + case CLSET_RETRY_TIMEOUT: + cu->cu_wait = *(struct timeval *)info; + break; + case CLGET_RETRY_TIMEOUT: + *(struct timeval *)info = cu->cu_wait; + break; + case CLGET_SERVER_ADDR: + *(struct sockaddr_in *)info = cu->cu_raddr; + break; + default: + return (FALSE); + } + return (TRUE); +} + +static void +clntudp_destroy( + CLIENT *cl) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + + if (cu->cu_closeit) { + (void)close(cu->cu_sock); + } + XDR_DESTROY(&(cu->cu_outxdrs)); + mem_free((void *)cu, (sizeof(struct cu_data) + cu->cu_sendsz + cu->cu_recvsz)); + mem_free((void *)cl, sizeof(CLIENT)); +} + +/* ARGSUSED */ +static bool_t +clntudp_getreply( + CLIENT *cl, + xdrproc_t xproc, + void *xres, + int cnt, + uint32_t *xids, + uint32_t *xid, + struct timeval *tv) +{ + return (FALSE); +} + +/* ARGSUSED */ +static int +clntudp_poll( + CLIENT *cl, + uint32_t usec) +{ + return (-1); +} diff --git a/TBBT/trace_play/rpc/get_myaddress.c b/TBBT/trace_play/rpc/get_myaddress.c new file mode 100755 index 0000000..37bb930 --- /dev/null +++ b/TBBT/trace_play/rpc/get_myaddress.c @@ -0,0 +1,169 @@ +#ifndef lint +static char sfs_get_myaddress_id[] = "@(#)get_myaddress.c 2.1 97/10/23"; +#endif +/* @(#)get_myaddress.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)get_myaddress.c 1.4 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * get_myaddress.c + * + * Get client's IP address via ioctl. This avoids using the yellowpages. + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#ifndef FreeBSD +#include <stropts.h> +#endif /* ndef FreeBSD */ +#include <string.h> +#include "rpc/rpc.h" +#include "rpc/pmap_prot.h" +#include "rpc/osdep.h" +#include <sys/utsname.h> + +#ifdef FreeBSD +#include <netdb.h> +#define MY_NAMELEN 256 +static char myhostname[MY_NAMELEN]; +#endif /* def FreeBSD */ + +void +get_myaddress( + struct sockaddr_in *addr) +{ + int s; + char buf[BUFSIZ]; + struct ifconf ifc; + struct ifreq ifreq, *ifr; + int len; + struct sockaddr_in tmp_addr; +#ifdef _AIX + char *cp, *cplim; +#endif + +#ifdef FreeBSD + static struct in_addr *my_addr; + struct hostent *ent; + + /* In FreeBSD, SIOCGIFCONF provides AF_LINK information, not AF_INET. */ + gethostname(myhostname, MY_NAMELEN); + ent = gethostbyname(myhostname); + if (ent == NULL) { + fprintf(stderr, "lookup on server's name failed\n"); + exit(1); + } + bzero(addr, sizeof(struct sockaddr_in)); + my_addr = (struct in_addr *)(*(ent->h_addr_list)); + bcopy(my_addr, &(addr->sin_addr.s_addr), sizeof(struct in_addr)); + addr->sin_family = AF_INET; + addr->sin_port = htons(PMAPPORT); + return; +#endif /* def FreeBSD */ + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("get_myaddress: socket"); + exit(1); + } + ifc.ifc_len = sizeof (buf); + ifc.ifc_buf = buf; + if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) { + perror("get_myaddress: ioctl (get interface configuration)"); + exit(1); + } + ifr = ifc.ifc_req; +#ifdef _AIX + cplim = buf + ifc.ifc_len; + for (cp = buf; cp < cplim; + cp += MAX(sizeof(struct ifreq), + (sizeof (ifr->ifr_name) + + MAX((ifr->ifr_addr).sa_len, sizeof(ifr->ifr_addr))))) { + ifr = (struct ifreq *)cp; +#else + for (len = ifc.ifc_len; len; len -= sizeof(ifreq), ifr++) { +#endif + ifreq = *ifr; + /* Save address, since SIOCGIFFLAGS may scribble over *ifr. */ + tmp_addr = *((struct sockaddr_in *)&ifr->ifr_addr); + if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + perror("get_myaddress: ioctl"); + exit(1); + } + if ((ifreq.ifr_flags & IFF_UP) && + ifr->ifr_addr.sa_family == AF_INET) { + *addr = tmp_addr; + addr->sin_port = htons(PMAPPORT); + break; + } + } + (void) close(s); +} + +/* + * A generic gethostname + */ +int +getmyhostname(char *name, int namelen) +{ +#if !defined(HAS_GETHOSTNAME) + struct utsname utsname; + int ret; + + ret = uname(&utsname); + if (ret == -1) + return (-1); + (void) strncpy(name, utsname.nodename, namelen); + return (0); +#else + return(gethostname(name, namelen)); +#endif +} diff --git a/TBBT/trace_play/rpc/getrpcent.c b/TBBT/trace_play/rpc/getrpcent.c new file mode 100755 index 0000000..2649af6 --- /dev/null +++ b/TBBT/trace_play/rpc/getrpcent.c @@ -0,0 +1,250 @@ +#ifndef lint +static char sfs_getrpcent_id[] = "@(#)getrpcent.c 2.1 97/10/23"; +#endif +/* @(#)getrpcent.c 2.2 88/07/29 4.0 RPCSRC */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)getrpcent.c 1.9 87/08/11 Copyr 1984 Sun Micro"; +#endif +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Copyright (c) 1985 by Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include "rpc/rpc.h" +#include "rpc/netdb.h" +#include "rpc/osdep.h" + +/* + * Internet version. + */ +struct rpcdata { + FILE *rpcf; + char *current; + int currentlen; + int stayopen; +#define MAXALIASES 35 + char *rpc_aliases[MAXALIASES]; + struct rpcent rpc; + char line[BUFSIZ+1]; + char *domain; +} *rpcdata; + +extern void setrpcent(int); +extern void endrpcent(void); +static struct rpcent *interpret(char *, int); + +static char RPCDB[] = "/etc/rpc"; + +static struct rpcdata * +_rpcdata(void) +{ + register struct rpcdata *d = rpcdata; + + if (d == 0) { + d = (struct rpcdata *)calloc(1, sizeof (struct rpcdata)); + rpcdata = d; + } + return (d); +} + +struct rpcent * +getrpcbynumber( + int number) +{ + register struct rpcdata *d = _rpcdata(); + register struct rpcent *p; + + if (d == 0) + return (0); + setrpcent(0); + while ((p = getrpcent()) != NULL) { + if (p->r_number == number) + break; + } + endrpcent(); + return (p); +} + +struct rpcent * +getrpcbyname( + char *name) +{ + struct rpcent *rpc; + char **rp; + + setrpcent(0); + while((rpc = getrpcent()) != NULL) { + if (strcmp(rpc->r_name, name) == 0) + return (rpc); + for (rp = rpc->r_aliases; *rp != NULL; rp++) { + if (strcmp(*rp, name) == 0) + return (rpc); + } + } + endrpcent(); + return (NULL); +} + +void +setrpcent( + int f) +{ + register struct rpcdata *d = _rpcdata(); + + if (d == 0) + return; + if (d->rpcf == NULL) + d->rpcf = fopen(RPCDB, "r"); + else + rewind(d->rpcf); + if (d->current) + free(d->current); + d->current = NULL; + d->stayopen |= f; +} + +void +endrpcent(void) +{ + register struct rpcdata *d = _rpcdata(); + + if (d == 0) + return; + if (d->current && !d->stayopen) { + free(d->current); + d->current = NULL; + } + if (d->rpcf && !d->stayopen) { + fclose(d->rpcf); + d->rpcf = NULL; + } +} + +struct rpcent * +getrpcent(void) +{ + register struct rpcdata *d = _rpcdata(); + + if (d == 0) + return(NULL); + if (d->rpcf == NULL && (d->rpcf = fopen(RPCDB, "r")) == NULL) + return (NULL); + if (fgets(d->line, BUFSIZ, d->rpcf) == NULL) + return (NULL); + return (interpret(d->line, strlen(d->line))); +} + +static struct rpcent * +interpret( + char *val, + int len) +{ + register struct rpcdata *d = _rpcdata(); + char *p; + register char *cp, **q; + + if (d == 0) + return (NULL); + strncpy(d->line, val, len); + p = d->line; + d->line[len] = '\n'; + if (*p == '#') + return (getrpcent()); + cp = strchr(p, '#'); + if (cp == NULL) { + cp = strchr(p, '\n'); + if (cp == NULL) + return (getrpcent()); + } + *cp = '\0'; + cp = strchr(p, ' '); + if (cp == NULL) { + cp = strchr(p, '\t'); + if (cp == NULL) + return (getrpcent()); + } + *cp++ = '\0'; + /* THIS STUFF IS INTERNET SPECIFIC */ + d->rpc.r_name = d->line; + while (*cp == ' ' || *cp == '\t') + cp++; + d->rpc.r_number = atoi(cp); + q = d->rpc.r_aliases = d->rpc_aliases; + cp = strchr(p, ' '); + if (cp != NULL) + *cp++ = '\0'; + else { + cp = strchr(p, '\t'); + if (cp != NULL) + *cp++ = '\0'; + } + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &(d->rpc_aliases[MAXALIASES - 1])) + *q++ = cp; + cp = strchr(p, ' '); + if (cp != NULL) + *cp++ = '\0'; + else { + cp = strchr(p, '\t'); + if (cp != NULL) + *cp++ = '\0'; + } + } + *q = NULL; + return (&d->rpc); +} diff --git a/TBBT/trace_play/rpc/getrpcport.c b/TBBT/trace_play/rpc/getrpcport.c new file mode 100755 index 0000000..6434082 --- /dev/null +++ b/TBBT/trace_play/rpc/getrpcport.c @@ -0,0 +1,77 @@ +#ifndef lint +static char sfs_getrpcport_id[] = "@(#)getrpcport.c 2.1 97/10/23"; +#endif +/* @(#)getrpcport.c 2.1 88/07/29 4.0 RPCSRC */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)getrpcport.c 1.3 87/08/11 SMI"; +#endif +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Copyright (c) 1985 by Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <string.h> +#include "rpc/rpc.h" +#include <netdb.h> +#include "rpc/osdep.h" + +getrpcport(host, prognum, versnum, proto) + char *host; +{ + struct sockaddr_in addr; + struct hostent *hp; + + if ((hp = gethostbyname(host)) == NULL) + return (0); + memmove((char *) &addr.sin_addr, hp->h_addr, hp->h_length); + addr.sin_family = AF_INET; + addr.sin_port = 0; + return (pmap_getport(&addr, prognum, versnum, proto)); +} diff --git a/TBBT/trace_play/rpc/netdb.h b/TBBT/trace_play/rpc/netdb.h new file mode 100755 index 0000000..a3a96fc --- /dev/null +++ b/TBBT/trace_play/rpc/netdb.h @@ -0,0 +1,63 @@ +/* + * @(#)netdb.h 2.1 97/10/23 + */ +/* @(#)netdb.h 2.1 88/07/29 3.9 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)rpc.h 1.8 87/07/24 SMI */ + +/* Really belongs in <netdb.h> */ + +struct rpcent { + char *r_name; /* name of server for this rpc program */ + char **r_aliases; /* alias list */ + int r_number; /* rpc program number */ +}; + +extern struct rpcent *getrpcbynumber(int); +extern struct rpcent *getrpcbyname(char *); +extern struct rpcent *getrpcent(void); diff --git a/TBBT/trace_play/rpc/osdep.h b/TBBT/trace_play/rpc/osdep.h new file mode 100755 index 0000000..67598d0 --- /dev/null +++ b/TBBT/trace_play/rpc/osdep.h @@ -0,0 +1,218 @@ +/* + * @(#)osdep.h 2.1 97/10/23 + */ +/* @(#)types.h 1.18 87/07/24 SMI */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +#ifndef __RPC_OSDEP_H__ +#define __RPC_OSDEP_H__ + +/* + * OS dependancies + * + * These are non-XPG4.2 standard include files, if not compiling in a + * strict environment simply #include them, otherwise we must define + * our own missing pieces. The definitions below are specific to + * Solaris 2.X and may be different on other systems. + */ + +#if !defined(_XOPEN_SOURCE) || defined (OSF1) || defined(AIX) +#if defined(SVR4) +#define BSD_COMP +#endif +#if defined(AIX) +#include <sys/param.h> +#endif +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#else +#if defined(_BIG_ENDIAN) && !defined(ntohl) && !defined(lint) +/* big-endian */ +#define ntohl(x) (x) +#define ntohs(x) (x) +#define htonl(x) (x) +#define htons(x) (x) + +#elif !defined(ntohl) /* little-endian */ + +extern unsigned short ntohs(unsigned short ns); +extern unsigned short htons(unsigned short hs); +extern unsigned long ntohl(unsigned long nl); +extern unsigned long htonl(unsigned long hl); + +#endif +/* + * Internet address + * This definition contains obsolete fields for compatibility + * with SunOS 3.x and 4.2bsd. The presence of subnets renders + * divisions into fixed fields misleading at best. New code + * should use only the s_addr field. + */ +struct in_addr { + union { + struct { unsigned char s_b1, s_b2, s_b3, s_b4; } S_un_b; + struct { unsigned short s_w1, s_w2; } S_un_w; + unsigned long S_addr; + } S_un; +#define s_addr S_un.S_addr /* should be used for all code */ +#define s_host S_un.S_un_b.s_b2 /* OBSOLETE: host on imp */ +#define s_net S_un.S_un_b.s_b1 /* OBSOLETE: network */ +#define s_imp S_un.S_un_w.s_w2 /* OBSOLETE: imp */ +#define s_impno S_un.S_un_b.s_b4 /* OBSOLETE: imp # */ +#define s_lh S_un.S_un_b.s_b3 /* OBSOLETE: logical host */ +}; + +struct sockaddr_in { + short sin_family; + unsigned short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +/* + * Structure used by kernel to store most + * addresses. + */ +struct sockaddr { + uint16_t sa_family; /* address family */ + char sa_data[14]; /* up to 14 bytes of direct address */ +}; + +/* + * Interface request structure used for socket + * ioctl's. All interface ioctl's must have parameter + * definitions which begin with ifr_name. The + * remainder may be interface specific. + */ +struct ifreq { +#define IFNAMSIZ 16 + char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + char ifru_oname[IFNAMSIZ]; /* other if name */ + struct sockaddr ifru_broadaddr; + short ifru_flags; + int ifru_metric; + char ifru_data[1]; /* interface dependent data */ + char ifru_enaddr[6]; + int if_muxid[2]; /* mux id's for arp and ip */ + + /* Struct for FDDI ioctl's */ + struct ifr_dnld_reqs { + void *v_addr; + void *m_addr; + void *ex_addr; + uint_t size; + } ifru_dnld_req; + + /* Struct for FDDI stats */ + struct ifr_fddi_stats { + uint_t stat_size; + void *fddi_stats; + } ifru_fddi_stat; + + struct ifr_netmapents { + uint_t map_ent_size, /* size of netmap structure */ + entry_number; /* index into netmap list */ + void *fddi_map_ent; /* pointer to user structure */ + } ifru_netmapent; + + /* Field for generic ioctl for fddi */ + + struct ifr_fddi_gen_struct { + int ifru_fddi_gioctl; /* field for gen ioctl */ + void *ifru_fddi_gaddr; /* Generic ptr to a field */ + } ifru_fddi_gstruct; + + } ifr_ifru; + +#define ifr_addr ifr_ifru.ifru_addr /* address */ +#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */ +#define ifr_oname ifr_ifru.ifru_oname /* other if name */ +#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ +#define ifr_flags ifr_ifru.ifru_flags /* flags */ +#define ifr_metric ifr_ifru.ifru_metric /* metric */ +#define ifr_data ifr_ifru.ifru_data /* for use by interface */ +#define ifr_enaddr ifr_ifru.ifru_enaddr /* ethernet address */ + +/* FDDI specific */ +#define ifr_dnld_req ifr_ifru.ifru_dnld_req +#define ifr_fddi_stat ifr_ifru.ifru_fddi_stat +#define ifr_fddi_netmap ifr_ifru.ifru_netmapent /* FDDI network map entries */ +#define ifr_fddi_gstruct ifr_ifru.ifru_fddi_gstruct + +#define ifr_ip_muxid ifr_ifru.if_muxid[0] +#define ifr_arp_muxid ifr_ifru.if_muxid[1] +}; + +struct ifconf { + int ifc_len; /* size of associated buffer */ + union { + void *ifcu_buf; + struct ifreq *ifcu_req; + } ifc_ifcu; +#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ +#define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */ +}; + +#define AF_INET 2 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#define IPPORT_RESERVED 1024 +#define SOCK_DGRAM 1 +#define SOCK_STREAM 2 +#define SO_SNDBUF 0x1001 +#define SO_RCVBUF 0x1002 +#define SOL_SOCKET 0xffff +#define INADDR_ANY (uint32_t)0x00000000 +#define IFF_BROADCAST 0x2 +#define IFF_UP 0x1 + +#define IOCPARM_MASK 0xff +#define IOC_OUT 0x40000000 +#define IOC_IN 0x80000000 +#define IOC_INOUT (IOC_IN|IOC_OUT) +#define _IOWR(x, y, t) \ + (IOC_INOUT|((((int)sizeof (t))&IOCPARM_MASK)<<16)|(x<<8)|y) +#define SIOCGIFCONF _IOWR('i', 20, struct ifconf) +#define SIOCGIFFLAGS _IOWR('i', 17, struct ifreq) +#define SIOCGIFBRDADDR _IOWR('i', 23, struct ifreq) + +extern int accept(int, struct sockaddr *, int *); +extern int bind(int, struct sockaddr *, int); +extern int connect(int, struct sockaddr *, int); +extern int getsockname(int, struct sockaddr *, int *); +extern int getsockopt(int, int, int, char *, int *); +extern int listen(int, int); +extern int recvfrom(int, char *, int, int, struct sockaddr *, int *); +extern int sendto(int, const char *, int, int, const struct sockaddr *, int); +extern int setsockopt(int, int, int, const char *, int); +extern int socket(int, int, int); +extern unsigned long inet_netof(struct in_addr); +extern struct in_addr inet_makeaddr(int, int); + +#endif /* _XOPEN_SOURCE */ + +#endif /* __RPC_OSDEP_H__ */ diff --git a/TBBT/trace_play/rpc/pmap_clnt.c b/TBBT/trace_play/rpc/pmap_clnt.c new file mode 100755 index 0000000..b2de53f --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_clnt.c @@ -0,0 +1,138 @@ +#ifndef lint +static char sfs_pmap_clnt_id[] = "@(#)pmap_clnt.c 2.1 97/10/23"; +#endif +/* @(#)pmap_clnt.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_clnt.c 1.37 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_clnt.c + * Client interface to pmap rpc service. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "rpc/rpc.h" +#include "rpc/pmap_prot.h" +#include "rpc/pmap_clnt.h" + +static struct timeval timeout = { 5, 0 }; +static struct timeval tottimeout = { 60, 0 }; + +extern void get_myaddress(struct sockaddr_in *addr); + +/* + * Set a mapping between program,version and port. + * Calls the pmap service remotely to do the mapping. + */ +bool_t +pmap_set( + uint32_t program, + uint32_t version, + int protocol, + uint16_t port) +{ + struct sockaddr_in myaddress; + int socket = -1; + register CLIENT *client; + struct pmap parms; + bool_t rslt; + + get_myaddress(&myaddress); + client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS, + timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); + if (client == (CLIENT *)NULL) + return (FALSE); + parms.pm_prog = program; + parms.pm_vers = version; + parms.pm_prot = protocol; + parms.pm_port = port; + if (CLNT_CALL(client, PMAPPROC_SET, xdr_pmap, &parms, xdr_bool, &rslt, + tottimeout) != RPC_SUCCESS) { + clnt_perror(client, "Cannot register service"); + return (FALSE); + } + CLNT_DESTROY(client); + (void)close(socket); + return (rslt); +} + +/* + * Remove the mapping between program,version and port. + * Calls the pmap service remotely to do the un-mapping. + */ +bool_t +pmap_unset( + uint32_t program, + uint32_t version) +{ + struct sockaddr_in myaddress; + int socket = -1; + register CLIENT *client; + struct pmap parms; + bool_t rslt; + + get_myaddress(&myaddress); + client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS, + timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); + if (client == (CLIENT *)NULL) + return (FALSE); + parms.pm_prog = program; + parms.pm_vers = version; + parms.pm_port = parms.pm_prot = 0; + CLNT_CALL(client, PMAPPROC_UNSET, xdr_pmap, &parms, xdr_bool, &rslt, + tottimeout); + CLNT_DESTROY(client); + (void)close(socket); + return (rslt); +} diff --git a/TBBT/trace_play/rpc/pmap_clnt.h b/TBBT/trace_play/rpc/pmap_clnt.h new file mode 100755 index 0000000..9d7b74e --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_clnt.h @@ -0,0 +1,97 @@ +/* + * @(#)pmap_clnt.h 2.1 97/10/23 + */ +/* @(#)pmap_clnt.h 2.1 88/07/29 4.0 RPCSRC; from 1.11 88/02/08 SMI */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * pmap_clnt.h + * Supplies C routines to get to portmap services. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ +#ifndef _SRPC_PMAP_CLNT_H +#define _SRPC_PMAP_CLNT_H + + +/* + * Usage: + * success = pmap_set(program, version, protocol, port); + * success = pmap_unset(program, version); + * port = pmap_getport(address, program, version, protocol); + * head = pmap_getmaps(address); + * clnt_stat = pmap_rmtcall(address, program, version, procedure, + * xdrargs, argsp, xdrres, resp, tout, port_ptr) + * (works for udp only.) + * clnt_stat = clnt_broadcast(program, version, procedure, + * xdrargs, argsp, xdrres, resp, eachresult) + * (like pmap_rmtcall, except the call is broadcasted to all + * locally connected nets. For each valid response received, + * the procedure eachresult is called. Its form is: + * done = eachresult(resp, raddr) + * bool_t done; + * void * resp; + * struct sockaddr_in raddr; + * where resp points to the results of the call and raddr is the + * address if the responder to the broadcast. + */ + +typedef bool_t (*resultproc_t)(); +extern bool_t pmap_set(uint32_t, uint32_t, int, uint16_t); +extern bool_t pmap_unset(uint32_t, uint32_t); +extern struct pmaplist *pmap_getmaps(struct sockaddr_in *); +extern enum clnt_stat pmap_rmtcall(struct sockaddr_in *, uint32_t, uint32_t, + uint32_t, xdrproc_t, void *, + xdrproc_t, void *, + struct timeval, uint32_t *); +extern enum clnt_stat clnt_broadcast(uint32_t, uint32_t, uint32_t, xdrproc_t, + void *, xdrproc_t, + void *, resultproc_t); +extern uint16_t pmap_getport(struct sockaddr_in *, uint32_t, uint32_t, + uint_t); +#endif /* _SRPC_PMAP_CLNT_H */ diff --git a/TBBT/trace_play/rpc/pmap_getmaps.c b/TBBT/trace_play/rpc/pmap_getmaps.c new file mode 100755 index 0000000..ab1ce75 --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_getmaps.c @@ -0,0 +1,105 @@ +#ifndef lint +static char sfs_pmap_getmaps_id[] = "@(#)pmap_getmaps.c 2.1 97/10/23"; +#endif +/* @(#)pmap_getmaps.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_getmaps.c 1.10 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_getmap.c + * Client interface to pmap rpc service. + * contains pmap_getmaps, which is only tcp service involved + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include "rpc/rpc.h" +#include "rpc/pmap_prot.h" +#include "rpc/pmap_clnt.h" +#include "rpc/osdep.h" +#include <netdb.h> + +#define NAMELEN 255 +#define MAX_BROADCAST_SIZE 1400 + + +/* + * Get a copy of the current port maps. + * Calls the pmap service remotely to do get the maps. + */ +struct pmaplist * +pmap_getmaps( + struct sockaddr_in *address) +{ + struct pmaplist *head = (struct pmaplist *)NULL; + int socket = -1; + struct timeval minutetimeout; + register CLIENT *client; + + minutetimeout.tv_sec = 60; + minutetimeout.tv_usec = 0; + address->sin_port = htons(PMAPPORT); + client = clnttcp_create(address, PMAPPROG, + PMAPVERS, &socket, 50, 500); + if (client != (CLIENT *)NULL) { + if (CLNT_CALL(client, PMAPPROC_DUMP, xdr_void, NULL, xdr_pmaplist, + &head, minutetimeout) != RPC_SUCCESS) { + clnt_perror(client, "pmap_getmaps rpc problem"); + } + CLNT_DESTROY(client); + } + (void)close(socket); + address->sin_port = 0; + return (head); +} diff --git a/TBBT/trace_play/rpc/pmap_getport.c b/TBBT/trace_play/rpc/pmap_getport.c new file mode 100755 index 0000000..2457475 --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_getport.c @@ -0,0 +1,110 @@ +#ifndef lint +static char sfs_clnt_id[] = "@(#)pmap_getport.c 2.1 97/10/23"; +#endif +/* @(#)pmap_getport.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_getport.c 1.9 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_getport.c + * Client interface to pmap rpc service. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "rpc/rpc.h" +#include "rpc/pmap_prot.h" +#include "rpc/pmap_clnt.h" +#include "rpc/osdep.h" + +static struct timeval timeout = { 5, 0 }; +static struct timeval tottimeout = { 60, 0 }; + +/* + * Find the mapped port for program,version. + * Calls the pmap service remotely to do the lookup. + * Returns 0 if no map exists. + */ +uint16_t +pmap_getport( + struct sockaddr_in *address, + uint32_t program, + uint32_t version, + uint_t protocol) +{ + uint16_t port = 0; + int socket = -1; + register CLIENT *client; + struct pmap parms; + + address->sin_port = htons(PMAPPORT); + client = clntudp_bufcreate(address, PMAPPROG, + PMAPVERS, timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); + if (client != (CLIENT *)NULL) { + parms.pm_prog = program; + parms.pm_vers = version; + parms.pm_prot = protocol; + parms.pm_port = 0; /* not needed or used */ + if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms, + xdr_uint16_t, &port, tottimeout) != RPC_SUCCESS){ + rpc_createerr.cf_stat = RPC_PMAPFAILURE; + clnt_geterr(client, &rpc_createerr.cf_error); + } else if (port == 0) { + rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; + } + CLNT_DESTROY(client); + } + (void)close(socket); + address->sin_port = 0; + return (port); +} diff --git a/TBBT/trace_play/rpc/pmap_prot.c b/TBBT/trace_play/rpc/pmap_prot.c new file mode 100755 index 0000000..242e846 --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_prot.c @@ -0,0 +1,78 @@ +#ifndef lint +static char sfs_clnt_id[] = "@(#)pmap_prot.c 2.1 97/10/23"; +#endif +/* @(#)pmap_prot.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_prot.c 1.17 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_prot.c + * Protocol for the local binder service, or pmap. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include "rpc/types.h" +#include "rpc/xdr.h" +#include "rpc/pmap_prot.h" + + +bool_t +xdr_pmap( + XDR *xdrs, + struct pmap *regs) +{ + + if (xdr_uint32_t(xdrs, ®s->pm_prog) && + xdr_uint32_t(xdrs, ®s->pm_vers) && + xdr_uint32_t(xdrs, ®s->pm_prot)) + return (xdr_uint32_t(xdrs, ®s->pm_port)); + return (FALSE); +} diff --git a/TBBT/trace_play/rpc/pmap_prot.h b/TBBT/trace_play/rpc/pmap_prot.h new file mode 100755 index 0000000..adbd407 --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_prot.h @@ -0,0 +1,114 @@ +/* + * @(#)pmap_prot.h 2.1 97/10/23 + */ +/* @(#)pmap_prot.h 2.1 88/07/29 4.0 RPCSRC; from 1.14 88/02/08 SMI */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* + * pmap_prot.h + * Protocol for the local binder service, or pmap. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * The following procedures are supported by the protocol: + * + * PMAPPROC_NULL() returns () + * takes nothing, returns nothing + * + * PMAPPROC_SET(struct pmap) returns (bool_t) + * TRUE is success, FALSE is failure. Registers the tuple + * [prog, vers, prot, port]. + * + * PMAPPROC_UNSET(struct pmap) returns (bool_t) + * TRUE is success, FALSE is failure. Un-registers pair + * [prog, vers]. prot and port are ignored. + * + * PMAPPROC_GETPORT(struct pmap) returns (int32_t unsigned). + * 0 is failure. Otherwise returns the port number where the pair + * [prog, vers] is registered. It may lie! + * + * PMAPPROC_DUMP() RETURNS (struct pmaplist *) + * + * PMAPPROC_CALLIT(unsigned, unsigned, unsigned, string<>) + * RETURNS (port, string<>); + * usage: encapsulatedresults = PMAPPROC_CALLIT(prog, vers, proc, encapsulatedargs); + * Calls the procedure on the local machine. If it is not registered, + * this procedure is quite; ie it does not return error information!!! + * This procedure only is supported on rpc/udp and calls via + * rpc/udp. This routine only passes null authentication parameters. + * This file has no interface to xdr routines for PMAPPROC_CALLIT. + * + * The service supports remote procedure calls on udp/ip or tcp/ip socket 111. + */ + +#define PMAPPORT ((uint16_t)111) +#define PMAPPROG ((uint32_t)100000) +#define PMAPVERS ((uint32_t)2) +#define PMAPVERS_PROTO ((uint32_t)2) +#define PMAPVERS_ORIG ((uint32_t)1) +#define PMAPPROC_NULL ((uint32_t)0) +#define PMAPPROC_SET ((uint32_t)1) +#define PMAPPROC_UNSET ((uint32_t)2) +#define PMAPPROC_GETPORT ((uint32_t)3) +#define PMAPPROC_DUMP ((uint32_t)4) +#define PMAPPROC_CALLIT ((uint32_t)5) + +struct pmap { + uint32_t pm_prog; + uint32_t pm_vers; + uint32_t pm_prot; + uint32_t pm_port; +}; + +extern bool_t xdr_pmap(); + +struct pmaplist { + struct pmap pml_map; + struct pmaplist *pml_next; +}; + +extern bool_t xdr_pmaplist(); diff --git a/TBBT/trace_play/rpc/pmap_prot2.c b/TBBT/trace_play/rpc/pmap_prot2.c new file mode 100755 index 0000000..9cb238a --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_prot2.c @@ -0,0 +1,138 @@ +#ifndef lint +static char sfs_clnt_id[] = "@(#)pmap_prot2.c 2.1 97/10/23"; +#endif +/* @(#)pmap_prot2.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_prot2.c 1.3 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_prot2.c + * Protocol for the local binder service, or pmap. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include "rpc/types.h" +#include "rpc/xdr.h" +#include "rpc/pmap_prot.h" + + +/* + * What is going on with linked lists? (!) + * First recall the link list declaration from pmap_prot.h: + * + * struct pmaplist { + * struct pmap pml_map; + * struct pmaplist *pml_map; + * }; + * + * Compare that declaration with a corresponding xdr declaration that + * is (a) pointer-less, and (b) recursive: + * + * typedef union switch (bool_t) { + * + * case TRUE: struct { + * struct pmap; + * pmaplist_t foo; + * }; + * + * case FALSE: struct {}; + * } pmaplist_t; + * + * Notice that the xdr declaration has no nxt pointer while + * the C declaration has no bool_t variable. The bool_t can be + * interpreted as ``more data follows me''; if FALSE then nothing + * follows this bool_t; if TRUE then the bool_t is followed by + * an actual struct pmap, and then (recursively) by the + * xdr union, pamplist_t. + * + * This could be implemented via the xdr_union primitive, though this + * would cause a one recursive call per element in the list. Rather than do + * that we can ``unwind'' the recursion + * into a while loop and do the union arms in-place. + * + * The head of the list is what the C programmer wishes to past around + * the net, yet is the data that the pointer points to which is interesting; + * this sounds like a job for xdr_reference! + */ +bool_t +xdr_pmaplist( + XDR *xdrs, + struct pmaplist **rp) +{ + /* + * more_elements is pre-computed in case the direction is + * XDR_ENCODE or XDR_FREE. more_elements is overwritten by + * xdr_bool when the direction is XDR_DECODE. + */ + bool_t more_elements; + register int freeing = (xdrs->x_op == XDR_FREE); + register struct pmaplist **next; + + /* CONSTCOND */ + while (TRUE) { + more_elements = (bool_t)(*rp != NULL); + if (! xdr_bool(xdrs, &more_elements)) + return (FALSE); + if (! more_elements) + return (TRUE); /* we are done */ + /* + * the unfortunate side effect of non-recursion is that in + * the case of freeing we must remember the next object + * before we free the current object ... + */ + if (freeing) + next = &((*rp)->pml_next); + if (! xdr_reference(xdrs, (void **)rp, + (uint_t)sizeof(struct pmaplist), xdr_pmap)) + return (FALSE); + rp = (freeing) ? next : &((*rp)->pml_next); + } +} diff --git a/TBBT/trace_play/rpc/pmap_rmt.c b/TBBT/trace_play/rpc/pmap_rmt.c new file mode 100755 index 0000000..a834cfd --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_rmt.c @@ -0,0 +1,419 @@ +#ifndef lint +static char sfs_pmap_rmt_id[] = "@(#)pmap_rmt.c 2.1 97/10/23"; +#endif +/* @(#)pmap_rmt.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_rmt.c 1.21 87/08/27 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_rmt.c + * Client interface to pmap rpc service. + * remote call and broadcast service + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#ifndef FreeBSD +#include <stropts.h> +#endif /* ndef FreeBSD */ +#include <string.h> +#include <errno.h> +#include "rpc/rpc.h" +#include "rpc/pmap_prot.h" +#include "rpc/pmap_clnt.h" +#include "rpc/pmap_rmt.h" +#include "rpc/osdep.h" + +#define MAX_BROADCAST_SIZE 1400 + +static struct timeval timeout = { 3, 0 }; + + +/* + * pmapper remote-call-service interface. + * This routine is used to call the pmapper remote call service + * which will look up a service program in the port maps, and then + * remotely call that routine with the given parameters. This allows + * programs to do a lookup and call in one step. +*/ +enum clnt_stat +pmap_rmtcall( + struct sockaddr_in *addr, + uint32_t prog, + uint32_t vers, + uint32_t proc, + xdrproc_t xdrargs, + void *argsp, + xdrproc_t xdrres, + void *resp, + struct timeval tout, + uint32_t *port_ptr) +{ + int socket = -1; + register CLIENT *client; + struct rmtcallargs a; + struct rmtcallres r; + enum clnt_stat stat; + + addr->sin_port = htons(PMAPPORT); + client = clntudp_create(addr, PMAPPROG, PMAPVERS, timeout, &socket); + if (client != (CLIENT *)NULL) { + a.prog = prog; + a.vers = vers; + a.proc = proc; + a.args_ptr = argsp; + a.xdr_args = xdrargs; + r.port_ptr = port_ptr; + r.results_ptr = resp; + r.xdr_results = xdrres; + stat = CLNT_CALL(client, PMAPPROC_CALLIT, xdr_rmtcall_args, &a, + xdr_rmtcallres, &r, tout); + CLNT_DESTROY(client); + } else { + stat = RPC_FAILED; + } + (void)close(socket); + addr->sin_port = 0; + return (stat); +} + + +/* + * XDR remote call arguments + * written for XDR_ENCODE direction only + */ +bool_t +xdr_rmtcall_args( + XDR *xdrs, + struct rmtcallargs *cap) +{ + uint_t lenposition, argposition, position; + + if (xdr_uint32_t(xdrs, &(cap->prog)) && + xdr_uint32_t(xdrs, &(cap->vers)) && + xdr_uint32_t(xdrs, &(cap->proc))) { + lenposition = XDR_GETPOS(xdrs); + if (! xdr_uint32_t(xdrs, &(cap->arglen))) + return (FALSE); + argposition = XDR_GETPOS(xdrs); + if (! (*(cap->xdr_args))(xdrs, cap->args_ptr)) + return (FALSE); + position = XDR_GETPOS(xdrs); + cap->arglen = (uint32_t)position - (uint32_t)argposition; + XDR_SETPOS(xdrs, lenposition); + if (! xdr_uint32_t(xdrs, &(cap->arglen))) + return (FALSE); + XDR_SETPOS(xdrs, position); + return (TRUE); + } + return (FALSE); +} + +/* + * XDR remote call results + * written for XDR_DECODE direction only + */ +bool_t +xdr_rmtcallres( + XDR *xdrs, + struct rmtcallres *crp) +{ + void *port_ptr; + + port_ptr = (void *)crp->port_ptr; + if (xdr_reference(xdrs, &port_ptr, sizeof (uint32_t), + xdr_uint32_t) && xdr_uint32_t(xdrs, &crp->resultslen)) { + crp->port_ptr = (uint32_t *)port_ptr; + return ((*(crp->xdr_results))(xdrs, crp->results_ptr)); + } + return (FALSE); +} + + +/* + * The following is kludged-up support for simple rpc broadcasts. + * Someday a large, complicated system will replace these trivial + * routines which only support udp/ip . + */ + +static int +getbroadcastnets( + struct in_addr *addrs, + int sock, + char *buf) +{ + struct ifconf ifc; + struct ifreq ifreq, *ifr; + struct sockaddr_in *sin; + int n, i; + + ifc.ifc_len = UDPMSGSIZE; + ifc.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) { + perror("broadcast: ioctl (get interface configuration)"); + return (0); + } + ifr = ifc.ifc_req; + for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) { + ifreq = *ifr; + if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + perror("broadcast: ioctl (get interface flags)"); + continue; + } + if ((ifreq.ifr_flags & IFF_BROADCAST) && + (ifreq.ifr_flags & IFF_UP) && + ifr->ifr_addr.sa_family == AF_INET) { + sin = (struct sockaddr_in *)&ifr->ifr_addr; +#ifdef SIOCGIFBRDADDR /* 4.3BSD */ + if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) { + addrs[i++] = inet_makeaddr((int)inet_netof + (sin->sin_addr), INADDR_ANY); + } else { + addrs[i++] = ((struct sockaddr_in*) + &ifreq.ifr_addr)->sin_addr; + } +#else /* 4.2 BSD */ + addrs[i++] = inet_makeaddr(inet_netof + (sin->sin_addr.s_addr), INADDR_ANY); +#endif + } + } + return (i); +} + +enum clnt_stat +clnt_broadcast( + uint32_t prog, /* program number */ + uint32_t vers, /* version number */ + uint32_t proc, /* procedure number */ + xdrproc_t xargs, /* xdr routine for args */ + void * argsp, /* pointer to args */ + xdrproc_t xresults, /* xdr routine for results */ + void * resultsp, /* pointer to results */ + resultproc_t eachresult) /* call with each result obtained */ +{ + enum clnt_stat stat; + AUTH *unix_auth = authunix_create_default(); + XDR xdr_stream; + register XDR *xdrs = &xdr_stream; + int outlen, inlen, nets; +#if defined(AIX) + size_t fromlen; +#else + int fromlen; +#endif /* AIX */ + register int sock; + int on = 1; +#ifdef FD_SETSIZE + fd_set mask; + fd_set readfds; +#else + int readfds; + register int mask; +#endif /* def FD_SETSIZE */ + register int i; + bool_t done = FALSE; + register uint32_t xid; + uint32_t port; + struct in_addr addrs[20]; + struct sockaddr_in baddr, raddr; /* broadcast and response addresses */ + struct rmtcallargs a; + struct rmtcallres r; + struct rpc_msg msg; + struct timeval t; + char outbuf[MAX_BROADCAST_SIZE], inbuf[UDPMSGSIZE]; + + /* + * initialization: create a socket, a broadcast address, and + * preserialize the arguments into a send buffer. + */ + if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("Cannot create socket for broadcast rpc"); + stat = RPC_CANTSEND; + goto done_broad; + } +#ifdef SO_BROADCAST + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof (on)) < 0) { + perror("Cannot set socket option SO_BROADCAST"); + stat = RPC_CANTSEND; + goto done_broad; + } +#endif /* def SO_BROADCAST */ +#ifdef FD_SETSIZE + FD_ZERO(&mask); + FD_SET(sock, &mask); +#else + mask = (1 << sock); +#endif /* def FD_SETSIZE */ + nets = getbroadcastnets(addrs, sock, inbuf); + memset((char *)&baddr, '\0', sizeof (baddr)); + baddr.sin_family = AF_INET; + baddr.sin_port = htons(PMAPPORT); + baddr.sin_addr.s_addr = htonl(INADDR_ANY); +/* baddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); */ + (void)gettimeofday(&t, (struct timezone *)0); + msg.rm_xid = xid = getpid() ^ t.tv_sec ^ t.tv_usec; + t.tv_usec = 0; + msg.rm_direction = CALL; + msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + msg.rm_call.cb_prog = PMAPPROG; + msg.rm_call.cb_vers = PMAPVERS; + msg.rm_call.cb_proc = PMAPPROC_CALLIT; + msg.rm_call.cb_cred = unix_auth->ah_cred; + msg.rm_call.cb_verf = unix_auth->ah_verf; + a.prog = prog; + a.vers = vers; + a.proc = proc; + a.xdr_args = xargs; + a.args_ptr = argsp; + r.port_ptr = &port; + r.xdr_results = xresults; + r.results_ptr = resultsp; + xdrmem_create(xdrs, outbuf, MAX_BROADCAST_SIZE, XDR_ENCODE); + if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &a))) { + stat = RPC_CANTENCODEARGS; + goto done_broad; + } + outlen = (int)xdr_getpos(xdrs); + xdr_destroy(xdrs); + /* + * Basic loop: broadcast a packet and wait a while for response(s). + * The response timeout grows larger per iteration. + */ + for (t.tv_sec = 4; t.tv_sec <= 14; t.tv_sec += 2) { + for (i = 0; i < nets; i++) { + baddr.sin_addr = addrs[i]; + if (sendto(sock, outbuf, outlen, 0, + (struct sockaddr *)&baddr, + sizeof (struct sockaddr)) != outlen) { + perror("Cannot send broadcast packet"); + stat = RPC_CANTSEND; + goto done_broad; + } + } + if (eachresult == NULL) { + stat = RPC_SUCCESS; + goto done_broad; + } + recv_again: + msg.acpted_rply.ar_verf = _null_auth; + msg.acpted_rply.ar_results.where = (void *)&r; + msg.acpted_rply.ar_results.proc = xdr_rmtcallres; + readfds = mask; + switch (select(_rpc_dtablesize(), &readfds, NULL, + NULL, &t)) { + + case 0: /* timed out */ + stat = RPC_TIMEDOUT; + continue; + + case -1: /* some kind of error */ + if (errno == EINTR) + goto recv_again; + perror("Broadcast select problem"); + stat = RPC_CANTRECV; + goto done_broad; + + } /* end of select results switch */ + try_again: + fromlen = sizeof(struct sockaddr); + inlen = recvfrom(sock, inbuf, UDPMSGSIZE, 0, + (struct sockaddr *)&raddr, &fromlen); + if (inlen < 0) { + if (errno == EINTR) + goto try_again; + perror("Cannot receive reply to broadcast"); + stat = RPC_CANTRECV; + goto done_broad; + } + if (inlen < sizeof(uint32_t)) + goto recv_again; + /* + * see if reply transaction id matches sent id. + * If so, decode the results. + */ + xdrmem_create(xdrs, inbuf, (uint_t)inlen, XDR_DECODE); + if (xdr_replymsg(xdrs, &msg)) { + if ((msg.rm_xid == xid) && + (msg.rm_reply.rp_stat == MSG_ACCEPTED) && + (msg.acpted_rply.ar_stat == SUCCESS)) { + raddr.sin_port = htons((uint16_t)port); + done = (*eachresult)(resultsp, &raddr); + } + /* otherwise, we just ignore the errors ... */ + } else { +#ifdef notdef + /* some kind of deserialization problem ... */ + if (msg.rm_xid == xid) + fprintf(stderr, "Broadcast deserialization problem"); + /* otherwise, just random garbage */ +#endif + } + xdrs->x_op = XDR_FREE; + msg.acpted_rply.ar_results.proc = xdr_void; + (void)xdr_replymsg(xdrs, &msg); + (void)(*xresults)(xdrs, resultsp); + xdr_destroy(xdrs); + if (done) { + stat = RPC_SUCCESS; + goto done_broad; + } else { + goto recv_again; + } + } +done_broad: + (void)close(sock); + AUTH_DESTROY(unix_auth); + return (stat); +} + diff --git a/TBBT/trace_play/rpc/pmap_rmt.h b/TBBT/trace_play/rpc/pmap_rmt.h new file mode 100755 index 0000000..40a770a --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_rmt.h @@ -0,0 +1,74 @@ +/* + * @(#)pmap_rmt.h 2.1 97/10/23 + */ +/* @(#)pmap_rmt.h 2.1 88/07/29 4.0 RPCSRC; from 1.2 88/02/08 SMI */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Structures and XDR routines for parameters to and replies from + * the portmapper remote-call-service. + * + * Copyright (C) 1986, Sun Microsystems, Inc. + */ + +struct rmtcallargs { + uint32_t prog, vers, proc, arglen; + void *args_ptr; + xdrproc_t xdr_args; +}; + +bool_t xdr_rmtcall_args(); + +struct rmtcallres { + uint32_t *port_ptr; + uint32_t resultslen; + caddr_t results_ptr; + xdrproc_t xdr_results; +}; + +bool_t xdr_rmtcallres(); diff --git a/TBBT/trace_play/rpc/rpc.h b/TBBT/trace_play/rpc/rpc.h new file mode 100755 index 0000000..43935d6 --- /dev/null +++ b/TBBT/trace_play/rpc/rpc.h @@ -0,0 +1,93 @@ +/* + * @(#)rpc.h 2.1 97/10/23 + */ + +/* @(#)rpc.h 2.4 89/07/11 4.0 RPCSRC; from 1.9 88/02/08 SMI */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * rpc.h, Just includes the billions of rpc header files necessary to + * do remote procedure calling. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#ifndef __RPC_HEADER__ +#define __RPC_HEADER__ + +#include "rpc/types.h" /* some typedefs */ + +#include "rpc/osdep.h" + +/* external data representation interfaces */ +#include "rpc/xdr.h" /* generic (de)serializer */ + +/* Client side only authentication */ +#include "rpc/auth.h" /* generic authenticator (client side) */ + +/* Client side (mostly) remote procedure call */ +#include "rpc/clnt.h" /* generic rpc stuff */ + +#include "rpc/pmap_clnt.h" + +/* semi-private protocol headers */ +#include "rpc/rpc_msg.h" /* protocol for rpc messages */ +#include "rpc/auth_unix.h" /* protocol for unix style cred */ +/* + * Uncomment-out the next line if you are building the rpc library with + * DES Authentication (see the README file in the secure_rpc/ directory). + */ +#ifdef notdef +#include "rpc/auth_des.h" /* protocol for des style cred */ +#endif + +/* Server side only remote procedure callee */ +#include "rpc/svc.h" /* service manager and multiplexer */ +#include "rpc/svc_auth.h" /* service side authenticator */ + +#endif /* ndef __RPC_HEADER__ */ diff --git a/TBBT/trace_play/rpc/rpc_callmsg.c b/TBBT/trace_play/rpc/rpc_callmsg.c new file mode 100755 index 0000000..e7bfdc8 --- /dev/null +++ b/TBBT/trace_play/rpc/rpc_callmsg.c @@ -0,0 +1,214 @@ +#ifndef lint +static char sfs_rpc_callmsg_c_id[] = "@(#)rpc_callmsg.c 2.1 97/10/23"; +#endif +/* @(#)rpc_callmsg.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)rpc_callmsg.c 1.4 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * rpc_callmsg.c + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "rpc/rpc.h" + +/* + * XDR a call message + */ +bool_t +xdr_callmsg( + XDR *xdrs, + struct rpc_msg *cmsg) +{ + int32_t *buf; + struct opaque_auth *oa; + + if (xdrs->x_op == XDR_ENCODE) { + if (cmsg->rm_call.cb_cred.oa_length > MAX_AUTH_BYTES) { + return (FALSE); + } + if (cmsg->rm_call.cb_verf.oa_length > MAX_AUTH_BYTES) { + return (FALSE); + } + buf = XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT + + RNDUP(cmsg->rm_call.cb_cred.oa_length) + + 2 * BYTES_PER_XDR_UNIT + + RNDUP(cmsg->rm_call.cb_verf.oa_length)); + if (buf != NULL) { + IXDR_PUT_LONG(buf, cmsg->rm_xid); + IXDR_PUT_ENUM(buf, cmsg->rm_direction); + if (cmsg->rm_direction != CALL) { + return (FALSE); + } + IXDR_PUT_LONG(buf, cmsg->rm_call.cb_rpcvers); + if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) { + return (FALSE); + } + IXDR_PUT_LONG(buf, cmsg->rm_call.cb_prog); + IXDR_PUT_LONG(buf, cmsg->rm_call.cb_vers); + IXDR_PUT_LONG(buf, cmsg->rm_call.cb_proc); + oa = &cmsg->rm_call.cb_cred; + IXDR_PUT_ENUM(buf, oa->oa_flavor); + IXDR_PUT_LONG(buf, oa->oa_length); + if (oa->oa_length) { + memmove((caddr_t)buf, oa->oa_base, oa->oa_length); + buf += RNDUP(oa->oa_length) / sizeof (int32_t); + } + oa = &cmsg->rm_call.cb_verf; + IXDR_PUT_ENUM(buf, oa->oa_flavor); + IXDR_PUT_LONG(buf, oa->oa_length); + if (oa->oa_length) { + memmove((caddr_t)buf, oa->oa_base, oa->oa_length); + /* no real need.... + buf += RNDUP(oa->oa_length) / sizeof (int32_t); + */ + } + return (TRUE); + } + } + if (xdrs->x_op == XDR_DECODE) { + buf = XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT); + if (buf != NULL) { + cmsg->rm_xid = IXDR_GET_LONG(buf); + cmsg->rm_direction = IXDR_GET_ENUM(buf, enum msg_type); + if (cmsg->rm_direction != CALL) { + return (FALSE); + } + cmsg->rm_call.cb_rpcvers = IXDR_GET_LONG(buf); + if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) { + return (FALSE); + } + cmsg->rm_call.cb_prog = IXDR_GET_LONG(buf); + cmsg->rm_call.cb_vers = IXDR_GET_LONG(buf); + cmsg->rm_call.cb_proc = IXDR_GET_LONG(buf); + oa = &cmsg->rm_call.cb_cred; + oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t); + oa->oa_length = IXDR_GET_LONG(buf); + if (oa->oa_length) { + if (oa->oa_length > MAX_AUTH_BYTES) { + return (FALSE); + } + if (oa->oa_base == NULL) { + oa->oa_base = (caddr_t) + mem_alloc(oa->oa_length); + } + buf = XDR_INLINE(xdrs, RNDUP(oa->oa_length)); + if (buf == NULL) { + if (xdr_opaque(xdrs, oa->oa_base, + oa->oa_length) == FALSE) { + return (FALSE); + } + } else { + memmove(oa->oa_base, (caddr_t)buf, + oa->oa_length); + /* no real need.... + buf += RNDUP(oa->oa_length) / + sizeof (int32_t); + */ + } + } + oa = &cmsg->rm_call.cb_verf; + buf = XDR_INLINE(xdrs, 2 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (xdr_enum(xdrs, &oa->oa_flavor) == FALSE || + xdr_u_int(xdrs, &oa->oa_length) == FALSE) { + return (FALSE); + } + } else { + oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t); + oa->oa_length = IXDR_GET_LONG(buf); + } + if (oa->oa_length) { + if (oa->oa_length > MAX_AUTH_BYTES) { + return (FALSE); + } + if (oa->oa_base == NULL) { + oa->oa_base = (caddr_t) + mem_alloc(oa->oa_length); + } + buf = XDR_INLINE(xdrs, RNDUP(oa->oa_length)); + if (buf == NULL) { + if (xdr_opaque(xdrs, oa->oa_base, + oa->oa_length) == FALSE) { + return (FALSE); + } + } else { + memmove(oa->oa_base, (caddr_t)buf, + oa->oa_length); + /* no real need... + buf += RNDUP(oa->oa_length) / + sizeof (int32_t); + */ + } + } + return (TRUE); + } + } + if ( + xdr_uint32_t(xdrs, &(cmsg->rm_xid)) && + xdr_enum(xdrs, (enum_t *)&(cmsg->rm_direction)) && + (cmsg->rm_direction == CALL) && + xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_rpcvers)) && + (cmsg->rm_call.cb_rpcvers == RPC_MSG_VERSION) && + xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_prog)) && + xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_vers)) && + xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_proc)) && + xdr_opaque_auth(xdrs, &(cmsg->rm_call.cb_cred)) ) + return (xdr_opaque_auth(xdrs, &(cmsg->rm_call.cb_verf))); + return (FALSE); +} + diff --git a/TBBT/trace_play/rpc/rpc_commondata.c b/TBBT/trace_play/rpc/rpc_commondata.c new file mode 100755 index 0000000..c2df017 --- /dev/null +++ b/TBBT/trace_play/rpc/rpc_commondata.c @@ -0,0 +1,62 @@ +#ifndef lint +static char sfs_rpc_commondata_id[] = "@(#)rpc_commondata.c 2.1 97/10/23"; +#endif +/* @(#)rpc_commondata.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#include "rpc/rpc.h" +/* + * This file should only contain common data (global data) that is exported + * by public interfaces + */ +struct opaque_auth _null_auth; +#ifdef FD_SETSIZE +fd_set svc_fdset; +#else +int svc_fds; +#endif /* def FD_SETSIZE */ +struct rpc_createerr rpc_createerr; diff --git a/TBBT/trace_play/rpc/rpc_dtablesize.c b/TBBT/trace_play/rpc/rpc_dtablesize.c new file mode 100755 index 0000000..8c1d9f9 --- /dev/null +++ b/TBBT/trace_play/rpc/rpc_dtablesize.c @@ -0,0 +1,114 @@ +#ifndef lint +static char sfs_clnt_id[] = "@(#)rpc_dtablesize.c 2.1 97/10/23"; +#endif +/* @(#)rpc_dtablesize.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)rpc_dtablesize.c 1.2 87/08/11 Copyr 1987 Sun Micro"; +#endif + + +/* + * Cache the result of getdtablesize(), so we don't have to do an + * expensive system call every time. + */ + +#if !(defined(USE_GETDTABLESIZE) || defined(USE_GETRLIMIT) || defined(USE_NOFILE)) +#define USE_GETDTABLESIZE +#endif + +#ifdef USE_GETDTABLESIZE +#include <unistd.h> + +int +_rpc_dtablesize(void) +{ + static int size = 0; + + if (size == 0) { + size = getdtablesize(); +#ifdef FD_SETSIZE + /* + * FreeBSD select() produces a segmentation violation if + * the argument is larger than FD_SETSIZE. + */ + if (size > FD_SETSIZE) { + size = FD_SETSIZE; + } +#endif /* def FD_SETSIZE */ + } + return (size); +} +#endif + +#ifdef USE_GETRLIMIT +#include <sys/resource.h> + +int +_rpc_dtablesize(void) +{ + static int size = 0; + struct rlimit rlimit; + + if (size == 0) { + (void)getrlimit(RLIMIT_NOFILE, &rlimit); + size = (int) rlimit.rlim_cur; + } + return (size); +} +#endif + +#ifdef USE_NOFILE +#include <sys/param.h> + +int +_rpc_dtablesize(void) +{ + return (NOFILE); +} +#endif diff --git a/TBBT/trace_play/rpc/rpc_msg.h b/TBBT/trace_play/rpc/rpc_msg.h new file mode 100755 index 0000000..fde7b93 --- /dev/null +++ b/TBBT/trace_play/rpc/rpc_msg.h @@ -0,0 +1,215 @@ +/* + * @(#)rpc_msg.h 2.1 97/10/23 + */ +/* @(#)rpc_msg.h 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)rpc_msg.h 1.7 86/07/16 SMI */ + +/* + * rpc_msg.h + * rpc message definition + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#ifndef __RPC_MSG_H +#define __RPC_MSG_H + +#define RPC_MSG_VERSION ((uint32_t) 2) +#define RPC_SERVICE_PORT ((uint16_t) 2048) + +/* + * Bottom up definition of an rpc message. + * NOTE: call and reply use the same overall stuct but + * different parts of unions within it. + */ + +enum msg_type { + CALL=0, + REPLY=1 +}; + +enum reply_stat { + MSG_ACCEPTED=0, + MSG_DENIED=1 +}; + +enum accept_stat { + SUCCESS=0, + PROG_UNAVAIL=1, + PROG_MISMATCH=2, + PROC_UNAVAIL=3, + GARBAGE_ARGS=4, + SYSTEM_ERR=5 +}; + +enum reject_stat { + RPC_MISMATCH=0, + AUTH_ERROR=1 +}; + +/* + * Reply part of an rpc exchange + */ + +/* + * Reply to an rpc request that was accepted by the server. + * Note: there could be an error even though the request was + * accepted. + */ +struct accepted_reply { + struct opaque_auth ar_verf; + enum accept_stat ar_stat; + union { + struct { + uint32_t low; + uint32_t high; + } AR_versions; + struct { + void * where; + xdrproc_t proc; + } AR_results; + /* and many other null cases */ + } ru; +#define ar_results ru.AR_results +#define ar_vers ru.AR_versions +}; + +/* + * Reply to an rpc request that was rejected by the server. + */ +struct rejected_reply { + enum reject_stat rj_stat; + union { + struct { + uint32_t low; + uint32_t high; + } RJ_versions; + enum auth_stat RJ_why; /* why authentication did not work */ + } ru; +#define rj_vers ru.RJ_versions +#define rj_why ru.RJ_why +}; + +/* + * Body of a reply to an rpc request. + */ +struct reply_body { + enum reply_stat rp_stat; + union { + struct accepted_reply RP_ar; + struct rejected_reply RP_dr; + } ru; +#define rp_acpt ru.RP_ar +#define rp_rjct ru.RP_dr +}; + +/* + * Body of an rpc request call. + */ +struct call_body { + uint32_t cb_rpcvers; /* must be equal to two */ + uint32_t cb_prog; + uint32_t cb_vers; + uint32_t cb_proc; + struct opaque_auth cb_cred; + struct opaque_auth cb_verf; /* protocol specific - provided by client */ +}; + +/* + * The rpc message + */ +struct rpc_msg { + uint32_t rm_xid; + enum msg_type rm_direction; + union { + struct call_body RM_cmb; + struct reply_body RM_rmb; + } ru; +#define rm_call ru.RM_cmb +#define rm_reply ru.RM_rmb +}; +#define acpted_rply ru.RM_rmb.ru.RP_ar +#define rjcted_rply ru.RM_rmb.ru.RP_dr + + +/* + * XDR routine to handle a rpc message. + * xdr_callmsg(xdrs, cmsg) + * XDR *xdrs; + * struct rpc_msg *cmsg; + */ +extern bool_t xdr_callmsg(XDR *, struct rpc_msg *); + +/* + * XDR routine to pre-serialize the static part of a rpc message. + * xdr_callhdr(xdrs, cmsg) + * XDR *xdrs; + * struct rpc_msg *cmsg; + */ +extern bool_t xdr_callhdr(XDR *, struct rpc_msg *); + +/* + * XDR routine to handle a rpc reply. + * xdr_replymsg(xdrs, rmsg) + * XDR *xdrs; + * struct rpc_msg *rmsg; + */ +extern bool_t xdr_replymsg(XDR *, struct rpc_msg *); + +/* + * Fills in the error part of a reply message. + * _seterr_reply(msg, error) + * struct rpc_msg *msg; + * struct rpc_err *error; + */ +extern void _seterr_reply(struct rpc_msg *, struct rpc_err *); + +extern int _rpc_dtablesize(void); +extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *); +#endif /* __RPC_MSG_H */ diff --git a/TBBT/trace_play/rpc/rpc_prot.c b/TBBT/trace_play/rpc/rpc_prot.c new file mode 100755 index 0000000..623ee7a --- /dev/null +++ b/TBBT/trace_play/rpc/rpc_prot.c @@ -0,0 +1,307 @@ +#ifndef lint +static char sfs_rpc_prot_id[] = "@(#)rpc_prot.c 2.1 97/10/23"; +#endif +/* @(#)rpc_prot.c 2.3 88/08/07 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)rpc_prot.c 1.36 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * rpc_prot.c + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * This set of routines implements the rpc message definition, + * its serializer and some common rpc utility routines. + * The routines are meant for various implementations of rpc - + * they are NOT for the rpc client or rpc service implementations! + * Because authentication stuff is easy and is part of rpc, the opaque + * routines are also in this program. + */ + +#include "rpc/rpc.h" + +/* * * * * * * * * * * * * * XDR Authentication * * * * * * * * * * * */ + +struct opaque_auth _null_auth; + +/* + * XDR an opaque authentication struct + * (see auth.h) + */ +bool_t +xdr_opaque_auth( + XDR *xdrs, + struct opaque_auth *ap) +{ + + if (xdr_enum(xdrs, &(ap->oa_flavor))) + return (xdr_bytes(xdrs, (void *)&ap->oa_base, + &ap->oa_length, MAX_AUTH_BYTES)); + return (FALSE); +} + +/* + * XDR a DES block + */ +bool_t +xdr_des_block( + XDR *xdrs, + des_block *blkp) +{ + return (xdr_opaque(xdrs, (void *)blkp, sizeof(des_block))); +} + +/* * * * * * * * * * * * * * XDR RPC MESSAGE * * * * * * * * * * * * * * * */ + +/* + * XDR the MSG_ACCEPTED part of a reply message union + */ +bool_t +xdr_accepted_reply( + XDR *xdrs, + struct accepted_reply *ar) +{ + + /* personalized union, rather than calling xdr_union */ + if (! xdr_opaque_auth(xdrs, &(ar->ar_verf))) + return (FALSE); + if (! xdr_enum(xdrs, (enum_t *)&(ar->ar_stat))) + return (FALSE); + switch (ar->ar_stat) { + + case SUCCESS: + return ((*(ar->ar_results.proc))(xdrs, ar->ar_results.where)); + + case PROG_MISMATCH: + if (! xdr_uint32_t(xdrs, &(ar->ar_vers.low))) + return (FALSE); + return (xdr_uint32_t(xdrs, &(ar->ar_vers.high))); + } + return (TRUE); /* TRUE => open ended set of problems */ +} + +/* + * XDR the MSG_DENIED part of a reply message union + */ +bool_t +xdr_rejected_reply( + XDR *xdrs, + struct rejected_reply *rr) +{ + + /* personalized union, rather than calling xdr_union */ + if (! xdr_enum(xdrs, (enum_t *)&(rr->rj_stat))) + return (FALSE); + switch (rr->rj_stat) { + + case RPC_MISMATCH: + if (! xdr_uint32_t(xdrs, &(rr->rj_vers.low))) + return (FALSE); + return (xdr_uint32_t(xdrs, &(rr->rj_vers.high))); + + case AUTH_ERROR: + return (xdr_enum(xdrs, (enum_t *)&(rr->rj_why))); + } + return (FALSE); +} + +static struct xdr_discrim reply_dscrm[3] = { + { (int)MSG_ACCEPTED, xdr_accepted_reply }, + { (int)MSG_DENIED, xdr_rejected_reply }, + { __dontcare__, NULL_xdrproc_t } }; + +/* + * XDR a reply message + */ +bool_t +xdr_replymsg( + XDR *xdrs, + struct rpc_msg *rmsg) +{ + if ( + xdr_uint32_t(xdrs, &(rmsg->rm_xid)) && + xdr_enum(xdrs, (enum_t *)&(rmsg->rm_direction)) && + (rmsg->rm_direction == REPLY) ) + return (xdr_union(xdrs, (enum_t *)&(rmsg->rm_reply.rp_stat), + (void *)&(rmsg->rm_reply.ru), reply_dscrm, NULL_xdrproc_t)); + return (FALSE); +} + + +/* + * Serializes the "static part" of a call message header. + * The fields include: rm_xid, rm_direction, rpcvers, prog, and vers. + * The rm_xid is not really static, but the user can easily munge on the fly. + */ +bool_t +xdr_callhdr( + XDR *xdrs, + struct rpc_msg *cmsg) +{ + + cmsg->rm_direction = CALL; + cmsg->rm_call.cb_rpcvers = RPC_MSG_VERSION; + if ( + (xdrs->x_op == XDR_ENCODE) && + xdr_uint32_t(xdrs, &(cmsg->rm_xid)) && + xdr_enum(xdrs, (enum_t *)&(cmsg->rm_direction)) && + xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_rpcvers)) && + xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_prog)) ) + return (xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_vers))); + return (FALSE); +} + +/* ************************** Client utility routine ************* */ + +static void +accepted( + enum accept_stat acpt_stat, + struct rpc_err *error) +{ + + switch (acpt_stat) { + + case PROG_UNAVAIL: + error->re_status = RPC_PROGUNAVAIL; + return; + + case PROG_MISMATCH: + error->re_status = RPC_PROGVERSMISMATCH; + return; + + case PROC_UNAVAIL: + error->re_status = RPC_PROCUNAVAIL; + return; + + case GARBAGE_ARGS: + error->re_status = RPC_CANTDECODEARGS; + return; + + case SYSTEM_ERR: + error->re_status = RPC_SYSTEMERROR; + return; + + case SUCCESS: + error->re_status = RPC_SUCCESS; + return; + } + /* something's wrong, but we don't know what ... */ + error->re_status = RPC_FAILED; + error->re_lb.s1 = (int32_t)MSG_ACCEPTED; + error->re_lb.s2 = (int32_t)acpt_stat; +} + +static void +rejected( + enum reject_stat rjct_stat, + struct rpc_err *error) +{ + + if ((int)rjct_stat == (int)RPC_VERSMISMATCH) { + error->re_status = RPC_VERSMISMATCH; + return; + } + if (rjct_stat == AUTH_ERROR) { + error->re_status = RPC_AUTHERROR; + return; + } + + /* something's wrong, but we don't know what ... */ + error->re_status = RPC_FAILED; + error->re_lb.s1 = (int32_t)MSG_DENIED; + error->re_lb.s2 = (int32_t)rjct_stat; +} + +/* + * given a reply message, fills in the error + */ +void +_seterr_reply( + struct rpc_msg *msg, + struct rpc_err *error) +{ + + /* optimized for normal, SUCCESSful case */ + switch (msg->rm_reply.rp_stat) { + + case MSG_ACCEPTED: + if (msg->acpted_rply.ar_stat == SUCCESS) { + error->re_status = RPC_SUCCESS; + return; + }; + accepted(msg->acpted_rply.ar_stat, error); + break; + + case MSG_DENIED: + rejected(msg->rjcted_rply.rj_stat, error); + break; + + default: + error->re_status = RPC_FAILED; + error->re_lb.s1 = (int32_t)(msg->rm_reply.rp_stat); + break; + } + switch (error->re_status) { + + case RPC_VERSMISMATCH: + error->re_vers.low = msg->rjcted_rply.rj_vers.low; + error->re_vers.high = msg->rjcted_rply.rj_vers.high; + break; + + case RPC_AUTHERROR: + error->re_why = msg->rjcted_rply.rj_why; + break; + + case RPC_PROGVERSMISMATCH: + error->re_vers.low = msg->acpted_rply.ar_vers.low; + error->re_vers.high = msg->acpted_rply.ar_vers.high; + break; + } +} diff --git a/TBBT/trace_play/rpc/sfs_ctcp.c b/TBBT/trace_play/rpc/sfs_ctcp.c new file mode 100755 index 0000000..d6a19f0 --- /dev/null +++ b/TBBT/trace_play/rpc/sfs_ctcp.c @@ -0,0 +1,727 @@ +#ifndef lint +static char sfs_ctcp_id[] = "@(#)sfs_ctcp.c 2.1 97/10/23"; +#endif +/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_tcp.c, Implements a TCP/IP based, client side RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * TCP based RPC supports 'batched calls'. + * A sequence of calls may be batched-up in a send buffer. The rpc call + * return immediately to the client even though the call was not necessarily + * sent. The batching occurs if the results' xdr routine is NULL (0) AND + * the rpc timeout value is zero (see clnt.h, rpc). + * + * Clients should NOT casually batch calls that in fact return results; that is, + * the server side should be aware that a call is batched and not produce any + * return message. Batched calls that produce many result messages can + * deadlock (netlock) the client and the server.... + * + * Now go hang yourself. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include <netdb.h> +#include <errno.h> +#include "rpc/pmap_clnt.h" + +#define MCALL_MSG_SIZE 24 + +struct ct_data { + int ct_sock; + bool_t ct_closeit; + struct timeval ct_wait; + struct sockaddr_in ct_addr; + struct sockaddr_in ct_oaddr; + struct rpc_err ct_error; + char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ + uint_t ct_mpos; /* pos after marshal */ + XDR ct_xdrs; + int ct_first; + uint32_t ct_prog; + uint32_t ct_vers; + uint_t ct_sendsz; + uint_t ct_recvsz; +}; + +static int readtcp(struct ct_data *, char *, int); +static int writetcp(struct ct_data *ct, char * buf, int len); + +static enum clnt_stat sfs_ctcp_call(CLIENT *, uint32_t, xdrproc_t, void *, + xdrproc_t, void *, struct timeval); +static void sfs_ctcp_abort(CLIENT *); +static void sfs_ctcp_geterr(CLIENT *, struct rpc_err *); +static bool_t sfs_ctcp_freeres(CLIENT *, xdrproc_t, void *); +static bool_t sfs_ctcp_control(CLIENT *, uint_t, void *); +static void sfs_ctcp_destroy(CLIENT *); +static bool_t sfs_ctcp_getreply(CLIENT *, xdrproc_t, void *, + int, uint32_t *, uint32_t *, struct timeval *); +static int sfs_ctcp_poll(CLIENT *, uint32_t); + +static struct clnt_ops tcp_ops = { + sfs_ctcp_call, + sfs_ctcp_abort, + sfs_ctcp_geterr, + sfs_ctcp_freeres, + sfs_ctcp_destroy, + sfs_ctcp_control, + sfs_ctcp_getreply, + sfs_ctcp_poll +}; + +static int +sfs_ctcp_make_conn( + struct ct_data *ct, + struct sockaddr_in *raddr, + int *sockp) +{ + int i; + int min_buf_sz; + int new_buf_sz; + int type; + int error; + int setopt = 1; +#if defined(UNIXWARE) || defined(AIX) + size_t optlen; +#else + int optlen; +#endif + + if (ct->ct_first == 0) + ct->ct_first++; + +#ifdef DEBUG + if (ct->ct_first) + (void) fprintf(stderr, "Re-establishing connection.\n"); +#endif + + /* + * If no port number given ask the pmap for one + */ + if (raddr->sin_port == 0) { + uint16_t port; + if ((port = pmap_getport(raddr, ct->ct_prog, ct->ct_vers, + IPPROTO_TCP)) == 0) { + return (-1); + } + raddr->sin_port = htons(port); + } + + /* + * If no socket given, open one + */ + if (*sockp >= 0) { + ct->ct_closeit = FALSE; + return (*sockp); + } + + ct->ct_closeit = TRUE; + + *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + (void)bindresvport(*sockp, (struct sockaddr_in *)0); + if ((*sockp < 0) + || (connect(*sockp, (struct sockaddr *)raddr, + sizeof(struct sockaddr_in)) < 0)) { + return (-1); + } + + /* + * Need to try to size the socket buffers based on the number of + * outstanding requests desired. NFS reads and writes can do as + * much as 8K per request which can quickly run us out of space + * on the socket buffer queue. Use the maximum number of bio style + * requests * NFS_MAXDATA plus a pad as a starting point for desired + * socket buffer size and then back off by NFS_MAXDATA until the buffer + * sizes are successfully set. Note, the algorithm never sets the + * buffer size to less than the OS default. + */ + type = SO_SNDBUF; + for (i = 0; i < 2; i++) { + optlen = sizeof(min_buf_sz); +#ifdef UNIXWARE + if (getsockopt(*sockp, SOL_SOCKET, type, + (void *)&min_buf_sz, &optlen) < 0) { + /* guess the default */ + min_buf_sz = 18 * 1024; + } +#else + if (getsockopt(*sockp, SOL_SOCKET, type, + (char *)&min_buf_sz, &optlen) < 0) { + /* guess the default */ + min_buf_sz = 18 * 1024; + } +#endif + + new_buf_sz = 512 * 1024; + if (new_buf_sz > min_buf_sz) { + do { + error = setsockopt(*sockp, SOL_SOCKET, + type, (char *)&new_buf_sz, + sizeof(int)); + new_buf_sz -= (8 * 1024); + } while (error != 0 && new_buf_sz > min_buf_sz); + } + + type = SO_RCVBUF; + } + +#ifdef TCP_NODELAY + setsockopt(*sockp, IPPROTO_TCP, TCP_NODELAY, (char *) &setopt, + sizeof(setopt)); +#endif /* TCP_NODELAY */ + + return (*sockp); +} + +/* + * Create a client handle for a tcp/ip connection. + * If *sockp<0, *sockp is set to a newly created TCP socket and it is + * connected to raddr. If *sockp non-negative then + * raddr is ignored. The rpc/tcp package does buffering + * similar to stdio, so the client must pick send and receive buffer sizes,]; + * 0 => use the default. + * If raddr->sin_port is 0, then a binder on the remote machine is + * consulted for the right port number. + * NB: *sockp is copied into a private area. + * NB: It is the clients responsibility to close *sockp. + * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this + * something more useful. + */ +CLIENT * +sfs_ctcp_create( + struct sockaddr_in *raddr, + uint32_t prog, + uint32_t vers, + int *sockp, + uint_t sendsz, + uint_t recvsz) +{ + CLIENT *h; + struct ct_data *ct; + struct timeval now; + struct rpc_msg call_msg; + + h = (CLIENT *)mem_alloc(sizeof(CLIENT)); + if (h == NULL) { + (void)fprintf(stderr, "clnttcp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + ct = (struct ct_data *)mem_alloc(sizeof(struct ct_data)); + if (ct == NULL) { + (void)fprintf(stderr, "clnttcp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + (void) memset(ct, '\0', sizeof (struct ct_data)); + + ct->ct_oaddr = *raddr; + ct->ct_prog = prog; + ct->ct_vers = vers; + + if (sfs_ctcp_make_conn(ct, raddr, sockp) < 0) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + (void)close(*sockp); + goto fooy; + } + + /* + * Set up private data struct + */ + ct->ct_sock = *sockp; + ct->ct_wait.tv_sec = 0; + ct->ct_wait.tv_usec = 0; + ct->ct_addr = *raddr; + + /* + * Initialize call message + */ + (void)gettimeofday(&now, (struct timezone *)0); + call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = prog; + call_msg.rm_call.cb_vers = vers; + + /* + * pre-serialize the static part of the call msg and stash it away + */ + xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, + XDR_ENCODE); + if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) { + if (ct->ct_closeit) { + (void)close(*sockp); + } + goto fooy; + } + ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs)); + XDR_DESTROY(&(ct->ct_xdrs)); + + /* + * Create a client handle which uses xdrrec for serialization + * and authnone for authentication. + */ + xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz, + (void *)ct, readtcp, writetcp); + ct->ct_sendsz = sendsz; + ct->ct_recvsz = recvsz; + h->cl_ops = &tcp_ops; + h->cl_private = (void *) ct; + h->cl_auth = authnone_create(); + return (h); + +fooy: + /* + * Something goofed, free stuff and barf + */ + mem_free((void *)ct, sizeof(struct ct_data)); + mem_free((void *)h, sizeof(CLIENT)); + return ((CLIENT *)NULL); +} + +static enum clnt_stat +get_areply( + CLIENT *h, + struct rpc_msg *reply_msg) +{ + struct ct_data *ct = (struct ct_data *) h->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + + /* CONSTCOND */ + while (TRUE) { + reply_msg->acpted_rply.ar_verf = _null_auth; + reply_msg->acpted_rply.ar_results.where = NULL; + reply_msg->acpted_rply.ar_results.proc = xdr_void; + if (! xdrrec_skiprecord(xdrs)) { + return (ct->ct_error.re_status); + } + /* now decode and validate the response header */ + if (! xdr_replymsg(xdrs, reply_msg)) { + if (ct->ct_error.re_status == RPC_SUCCESS) + continue; + } + return (ct->ct_error.re_status); + } +} + +static enum clnt_stat +proc_header( + CLIENT *h, + struct rpc_msg *reply_msg, + xdrproc_t xdr_results, + void * results_ptr) +{ + struct ct_data *ct = (struct ct_data *) h->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + + /* + * process header + */ + _seterr_reply(reply_msg, &(ct->ct_error)); + if (ct->ct_error.re_status == RPC_SUCCESS) { + if (! AUTH_VALIDATE(h->cl_auth, + &reply_msg->acpted_rply.ar_verf)) { + ct->ct_error.re_status = RPC_AUTHERROR; + ct->ct_error.re_why = AUTH_INVALIDRESP; + } else if (! (*xdr_results)(xdrs, results_ptr)) { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTDECODERES; + } + /* free verifier ... */ + if (reply_msg->acpted_rply.ar_verf.oa_base != NULL) { + xdrs->x_op = XDR_FREE; + (void)xdr_opaque_auth(xdrs, + &(reply_msg->acpted_rply.ar_verf)); + } + } /* end successful completion */ + return (ct->ct_error.re_status); +} + +static enum clnt_stat +sfs_ctcp_call( + CLIENT *h, + uint32_t proc, + xdrproc_t xdr_args, + void * args_ptr, + xdrproc_t xdr_results, + void * results_ptr, + struct timeval timeout) +{ + struct ct_data *ct = (struct ct_data *) h->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + struct rpc_msg reply_msg; + uint32_t x_id; + uint32_t *msg_x_id = (uint32_t *)(ct->ct_mcall); /* yuk */ + bool_t shipnow; + + ct->ct_wait = timeout; + + shipnow = + (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0 + && timeout.tv_usec == 0) ? FALSE : TRUE; + + xdrs->x_op = XDR_ENCODE; + ct->ct_error.re_status = RPC_SUCCESS; + x_id = ntohl(--(*msg_x_id)); + if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) || + (! XDR_PUTLONG(xdrs, (int32_t *)&proc)) || + (! AUTH_MARSHALL(h->cl_auth, xdrs)) || + (! (*xdr_args)(xdrs, args_ptr))) { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTENCODEARGS; + (void)xdrrec_endofrecord(xdrs, TRUE); + return (ct->ct_error.re_status); + } + if (! xdrrec_endofrecord(xdrs, TRUE)) + return (ct->ct_error.re_status = RPC_CANTSEND); + /* + * Hack to provide rpc-based message passing + */ + if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { + if (! shipnow && results_ptr == NULL) + return (RPC_SUCCESS); + + /* + * Double hack, send back xid in results_prt if non-NULL + */ + *(uint32_t *)results_ptr = x_id; + return(ct->ct_error.re_status = RPC_TIMEDOUT); + } + + + /* + * Keep receiving until we get a valid transaction id + */ + xdrs->x_op = XDR_DECODE; + /* CONSTCOND */ + while (TRUE) { + enum clnt_stat res; + + if ((res = get_areply(h, &reply_msg)) != RPC_SUCCESS) + return (res); + + if (reply_msg.rm_xid == x_id) + break; + } + + /* + * process header + */ + return (proc_header(h, &reply_msg, xdr_results, results_ptr)); +} + +static void +sfs_ctcp_geterr( + CLIENT *h, + struct rpc_err *errp) +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + *errp = ct->ct_error; +} + +static bool_t +sfs_ctcp_freeres( + CLIENT *cl, + xdrproc_t xdr_res, + void * res_ptr) +{ + struct ct_data *ct = (struct ct_data *)cl->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_res)(xdrs, res_ptr)); +} + +/* ARGSUSED */ +static void +sfs_ctcp_abort(CLIENT *h) +{ +} + +static bool_t +sfs_ctcp_control( + CLIENT *cl, + uint_t request, + void *info) +{ + struct ct_data *ct = (struct ct_data *)cl->cl_private; + + switch (request) { + case CLGET_SERVER_ADDR: + *(struct sockaddr_in *)info = ct->ct_addr; + break; + default: + return (FALSE); + } + return (TRUE); +} + + +static void +sfs_ctcp_destroy( + CLIENT *h) +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + if (ct->ct_closeit) { + (void)close(ct->ct_sock); + } + XDR_DESTROY(&(ct->ct_xdrs)); + mem_free((void *)ct, sizeof(struct ct_data)); + mem_free((void *)h, sizeof(CLIENT)); +} + +/* + * Interface between xdr serializer and tcp connection. + * Behaves like the system calls, read & write, but keeps some error state + * around for the rpc level. + */ +static int +readtcp(struct ct_data *ct, + char * buf, + int len) +{ +#ifdef FD_SETSIZE + fd_set mask; + fd_set readfds; + + FD_ZERO(&mask); + FD_SET(ct->ct_sock, &mask); +#else + int mask = 1 << (ct->ct_sock); + int readfds; +#endif /* def FD_SETSIZE */ + + if (len == 0) + return (0); + + /* CONSTCOND */ + while (TRUE) { + readfds = mask; + switch (select(_rpc_dtablesize(), &readfds, NULL, NULL, + &(ct->ct_wait))) { + case 0: + ct->ct_error.re_status = RPC_TIMEDOUT; + return (-1); + + case -1: + if (errno == EINTR) + continue; + ct->ct_error.re_status = RPC_CANTRECV; + ct->ct_error.re_errno = errno; + goto lost; + } + break; + } + switch (len = read(ct->ct_sock, buf, len)) { + + case 0: + /* premature eof */ + ct->ct_error.re_errno = ECONNRESET; + ct->ct_error.re_status = RPC_CANTRECV; + len = -1; /* it's really an error */ + goto lost; + + case -1: + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTRECV; + goto lost; + } + return (len); +lost: + /* + * We have lost our connection to the server. Try and + * reestablish it. + */ + (void) close(ct->ct_sock); + ct->ct_addr = ct->ct_oaddr; + ct->ct_sock = -1; + XDR_DESTROY(&(ct->ct_xdrs)); + + (void) sfs_ctcp_make_conn(ct, &ct->ct_addr, &ct->ct_sock); + /* + * Create a client handle which uses xdrrec for + * serialization and authnone for authentication. + */ + xdrrec_create(&(ct->ct_xdrs), ct->ct_sendsz, ct->ct_recvsz, + (void *)ct, readtcp, writetcp); + return (-1); +} + +static int +writetcp( + struct ct_data *ct, + char * buf, + int len) +{ + int i, cnt; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) { + if ((i = write(ct->ct_sock, buf, cnt)) == -1) { + /* + * We have lost our connection to the server. Try and + * reestablish it. + */ + (void) close(ct->ct_sock); + ct->ct_addr = ct->ct_oaddr; + ct->ct_sock = -1; + XDR_DESTROY(&(ct->ct_xdrs)); + + (void) sfs_ctcp_make_conn(ct, &ct->ct_addr, + &ct->ct_sock); + /* + * Create a client handle which uses xdrrec for + * serialization and authnone for authentication. + */ + xdrrec_create(&(ct->ct_xdrs), ct->ct_sendsz, + ct->ct_recvsz, + (void *)ct, readtcp, writetcp); + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTSEND; + return (-1); + } + } + return (len); +} + +/* ARGSUSED */ +static bool_t +sfs_ctcp_getreply( + CLIENT *cl, + xdrproc_t xdr_results, + void *results_ptr, + int cnt, + uint32_t *xids, + uint32_t *xid, + struct timeval *tv) +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + struct rpc_msg reply_msg; + enum clnt_stat res; + int i; + + /* + * Receive just one returning transaction id + */ + xdrs->x_op = XDR_DECODE; + ct->ct_error.re_status = RPC_SUCCESS; + ct->ct_wait.tv_sec = tv->tv_sec; + ct->ct_wait.tv_usec = tv->tv_usec; + + if ((res = get_areply(cl, &reply_msg)) != RPC_SUCCESS) + return (res); + + *xid = reply_msg.rm_xid; + + /* + * Check to make sure xid matchs one that we are interested in + */ + for (i = 0; i < cnt; i++) { + if (xids[i] == *xid) + break; + } + + if (i == cnt) + return (RPC_CANTDECODERES); + + /* + * process header + */ + return (proc_header(cl, &reply_msg, xdr_results, results_ptr)); +} + +/* ARGSUSED */ +static int +sfs_ctcp_poll( + CLIENT *cl, + uint32_t usecs) +{ + struct ct_data *ct = (struct ct_data *)cl->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + struct timeval tv; +#ifdef FD_SETSIZE + fd_set mask; + fd_set readfds; + + FD_ZERO(&mask); + FD_SET(ct->ct_sock, &mask); +#else + int mask = 1 << (ct->ct_sock); + int readfds; +#endif /* def FD_SETSIZE */ + + if (xdrrec_eof(xdrs) == FALSE) + return (1); + + tv.tv_sec = 0; + if (usecs > 1000000) + tv.tv_sec = usecs / 1000000; + tv.tv_usec = usecs % 1000000; + + readfds = mask; + return (select(_rpc_dtablesize(), &readfds, NULL, NULL, &tv)); +} diff --git a/TBBT/trace_play/rpc/sfs_cudp.c b/TBBT/trace_play/rpc/sfs_cudp.c new file mode 100755 index 0000000..1dee989 --- /dev/null +++ b/TBBT/trace_play/rpc/sfs_cudp.c @@ -0,0 +1,604 @@ +#ifndef lint +static char sfs_cudp_id[] = "@(#)sfs_cudp.c 2.1 97/10/23"; +#endif + +/* @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * clnt_udp.c, Implements a UDP/IP based, client side RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#ifndef FreeBSD +#include <stropts.h> +#endif /* ndef FreeBSD */ +#include <string.h> +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include <netdb.h> +#include "rpc/pmap_clnt.h" +#include <errno.h> +#include "rfs_c_def.h" /* Just for the define of RFS */ +/* + * UDP bases client side rpc operations + */ +static enum clnt_stat sfs_cudp_call(CLIENT *, uint32_t, xdrproc_t, + void *, xdrproc_t, void *, + struct timeval); +static void sfs_cudp_abort(CLIENT *h); +static void sfs_cudp_geterr(CLIENT *, struct rpc_err *); +static bool_t sfs_cudp_freeres(CLIENT *, xdrproc_t, void *); +static bool_t sfs_cudp_control(CLIENT *, uint_t, void *); +static void sfs_cudp_destroy(CLIENT *); +static bool_t sfs_cudp_getreply(CLIENT *, xdrproc_t, void *, + int, uint32_t *, uint32_t *, struct timeval *); +static int sfs_cudp_poll(CLIENT *, uint32_t); + +static struct clnt_ops sfs_cudp_ops = { + sfs_cudp_call, + sfs_cudp_abort, + sfs_cudp_geterr, + sfs_cudp_freeres, + sfs_cudp_destroy, + sfs_cudp_control, + sfs_cudp_getreply, + sfs_cudp_poll +}; + +/* + * Private data kept per client handle + */ +struct cu_data { + int cu_sock; + bool_t cu_closeit; + struct sockaddr_in cu_raddr; + int cu_rlen; + struct rpc_err cu_error; + XDR cu_outxdrs; + uint_t cu_xdrpos; + uint_t cu_sendsz; + char *cu_outbuf; + uint_t cu_recvsz; + char cu_inbuf[1]; +}; + +/* + * Create a UDP based client handle. + * If *sockp<0, *sockp is set to a newly created UPD socket. + * If raddr->sin_port is 0 a binder on the remote machine + * is consulted for the correct port number. + * NB: It is the clients responsibility to close *sockp. + * NB: The rpch->cl_auth is initialized to null authentication. + * Caller may wish to set this something more useful. + * + * wait is the amount of time used between retransmitting a call if + * no response has been heard; retransmition occurs until the actual + * rpc call times out. + * + * sendsz and recvsz are the maximum allowable packet sizes that can be + * sent and received. + */ +/* ARGSUSED */ +CLIENT * +sfs_cudp_bufcreate( + struct sockaddr_in *raddr, + uint32_t program, + uint32_t version, + struct timeval wait, + int *sockp, + uint_t sendsz, + uint_t recvsz) +{ + CLIENT *cl; + struct cu_data *cu; + struct timeval now; + struct rpc_msg call_msg; + int min_buf_sz; + int new_buf_sz; + int type; + int i; + int error; +#if defined(UNIXWARE) || defined(AIX) + size_t optlen; +#else + int optlen; +#endif + + cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); + if (cl == NULL) { + (void) fprintf(stderr, "sfs_cudp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + sendsz = ((sendsz + 3) / 4) * 4; + recvsz = ((recvsz + 3) / 4) * 4; + cu = (struct cu_data *)mem_alloc(sizeof(struct cu_data) + + sendsz + recvsz); + if (cu == NULL) { + (void) fprintf(stderr, "sfs_cudp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + cu->cu_outbuf = &cu->cu_inbuf[recvsz]; + + (void)gettimeofday(&now, (struct timezone *)0); + if (raddr->sin_port == 0) { + uint16_t port; + if ((port = + pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { + goto fooy; + } + raddr->sin_port = htons(port); + } + cl->cl_ops = &sfs_cudp_ops; + cl->cl_private = (void *)cu; + cu->cu_raddr = *raddr; + cu->cu_rlen = sizeof (cu->cu_raddr); + cu->cu_sendsz = sendsz; + cu->cu_recvsz = recvsz; + call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = program; + call_msg.rm_call.cb_vers = version; + xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, + sendsz, XDR_ENCODE); + if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { + goto fooy; + } + cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); + if (*sockp < 0) { +#if defined(O_NONBLOCK) + int flags; +#elif defined(FIONBIO) + int dontblock = 1; +#endif + + *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (*sockp < 0) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + /* attempt to bind to prov port */ + (void)bindresvport(*sockp, (struct sockaddr_in *)0); + /* the sockets rpc controls are non-blocking */ +#if defined(O_NONBLOCK) + flags = fcntl(*sockp, F_GETFL, 0) | O_NONBLOCK; + (void)fcntl(*sockp, F_SETFL, flags); +#elif defined(FIONBIO) + (void)ioctl(*sockp, FIONBIO, (char *) &dontblock); +#endif + cu->cu_closeit = TRUE; + } else { + cu->cu_closeit = FALSE; + } + cu->cu_sock = *sockp; + /* + * Need to try to size the socket buffers based on the number of + * outstanding requests desired. NFS reads and writes can do as + * much as 8K per request which can quickly run us out of space + * on the socket buffer queue. Use the maximum number of bio style + * requests * NFS_MAXDATA plus a pad as a starting point for desired + * socket buffer size and then back off by NFS_MAXDATA until the buffer + * sizes are successfully set. Note, the algorithm never sets the + * buffer size to less than the OS default. + */ + type = SO_SNDBUF; + for (i = 0; i < 2; i++) { + optlen = sizeof(min_buf_sz); +#if defined(UNIXWARE) + if (getsockopt(cu->cu_sock, SOL_SOCKET, type, + (void *)&min_buf_sz, &optlen) < 0) { + /* guess the default */ + min_buf_sz = 18 * 1024; + } +#else + if (getsockopt(cu->cu_sock, SOL_SOCKET, type, + (char *)&min_buf_sz, &optlen) < 0) { + /* guess the default */ + min_buf_sz = 18 * 1024; + } +#endif + + new_buf_sz = 512 * 1024; + if (new_buf_sz > min_buf_sz) { + do { + error = setsockopt(cu->cu_sock, SOL_SOCKET, + type, (char *)&new_buf_sz, + sizeof(int)); + new_buf_sz -= (8 * 1024); + } while (error != 0 && new_buf_sz > min_buf_sz); + } + + type = SO_RCVBUF; + } + + cl->cl_auth = authnone_create(); + return (cl); +fooy: + if (cu) + mem_free((void *)cu, sizeof(struct cu_data) + sendsz + recvsz); + if (cl) + mem_free((void *)cl, sizeof(CLIENT)); + return ((CLIENT *)NULL); +} + +CLIENT * +sfs_cudp_create( + struct sockaddr_in *raddr, + uint32_t program, + uint32_t version, + struct timeval wait, + int *sockp) +{ + + return(sfs_cudp_bufcreate(raddr, program, version, wait, sockp, + UDPMSGSIZE, UDPMSGSIZE)); +} + +#ifdef RFS +enum clnt_stat get_areply_udp ( + CLIENT * cl, + uint32_t *xid, + struct timeval *timeout) +{ + return get_areply (cl, xid, timeout); +} +#endif + +static enum clnt_stat +get_areply( + CLIENT *cl, + uint32_t *xid, + struct timeval *timeout) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + int inlen; +#if defined(AIX) + size_t fromlen; +#else + int fromlen; +#endif + struct sockaddr_in from; +#ifdef FD_SETSIZE + fd_set readfds; + fd_set mask; +#else + int readfds; + int mask; +#endif /* def FD_SETSIZE */ + +#ifdef FD_SETSIZE + FD_ZERO(&mask); + FD_SET(cu->cu_sock, &mask); +#else + mask = 1 << cu->cu_sock; +#endif /* def FD_SETSIZE */ + + for (;;) { + readfds = mask; + switch (select(_rpc_dtablesize(), &readfds, NULL, + NULL, timeout)) { + + case 0: + return (cu->cu_error.re_status = RPC_TIMEDOUT); + + case -1: + if (errno == EINTR) + continue; + cu->cu_error.re_errno = errno; + return (cu->cu_error.re_status = RPC_CANTRECV); + } + do { + fromlen = sizeof(struct sockaddr); + inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, + (int) cu->cu_recvsz, 0, + (struct sockaddr *)&from, &fromlen); + } while (inlen < 0 && errno == EINTR); + if (inlen < 0) { + if (errno == EWOULDBLOCK) + continue; + cu->cu_error.re_errno = errno; + return (cu->cu_error.re_status = RPC_CANTRECV); + } + + if (inlen < sizeof(uint32_t)) + continue; + + *xid = ntohl(*((uint32_t *)(cu->cu_inbuf))); + return (RPC_SUCCESS); + } +} + +enum clnt_stat +proc_header( + CLIENT *cl, + xdrproc_t xdr_results, + void *results_ptr) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + XDR *xdrs = &(cu->cu_outxdrs); + struct rpc_msg reply_msg; + XDR reply_xdrs; + bool_t ok; + + /* + * now decode and validate the response + */ + xdrmem_create(&reply_xdrs, cu->cu_inbuf, cu->cu_recvsz, XDR_DECODE); + + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = results_ptr; + reply_msg.acpted_rply.ar_results.proc = xdr_results; + + ok = xdr_replymsg(&reply_xdrs, &reply_msg); + /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ + if (!ok) { + return (cu->cu_error.re_status = RPC_CANTDECODERES); + } + + _seterr_reply(&reply_msg, &(cu->cu_error)); + + if (cu->cu_error.re_status == RPC_SUCCESS) { + if (! AUTH_VALIDATE(cl->cl_auth, + &reply_msg.acpted_rply.ar_verf)) { + cu->cu_error.re_status = RPC_AUTHERROR; + cu->cu_error.re_why = AUTH_INVALIDRESP; + } + if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { + xdrs->x_op = XDR_FREE; + (void)xdr_opaque_auth(xdrs, + &(reply_msg.acpted_rply.ar_verf)); + } + } + + return (cu->cu_error.re_status); +} + +/* + * Non-standard changes. Make a call an at-most-once with a per call + * timer. Ignore the timeout set at creation. Never retransmit. + */ +static enum clnt_stat +sfs_cudp_call( + CLIENT *cl, /* client handle */ + uint32_t proc, /* procedure number */ + xdrproc_t xargs, /* xdr routine for args */ + void * argsp, /* pointer to args */ + xdrproc_t xresults, /* xdr routine for results */ + void * resultsp, /* pointer to results */ + struct timeval timeout) /* seconds to wait before giving up */ +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + XDR *xdrs = &(cu->cu_outxdrs); + int outlen; + uint32_t x_id, r_xid; + + xdrs->x_op = XDR_ENCODE; + XDR_SETPOS(xdrs, cu->cu_xdrpos); + + /* + * the transaction is the first thing in the out buffer + */ + (*(uint32_t *)(cu->cu_outbuf))++; + x_id = ntohl(*(uint32_t *)(cu->cu_outbuf)); + + if ((! XDR_PUTLONG(xdrs, (int32_t *)&proc)) || + (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || + (! (*xargs)(xdrs, argsp))) + return (cu->cu_error.re_status = RPC_CANTENCODEARGS); + + outlen = (int)XDR_GETPOS(xdrs); + + if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, + (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) { + cu->cu_error.re_errno = errno; + return (cu->cu_error.re_status = RPC_CANTSEND); + } + + /* + * Hack to provide rpc-based message passing + */ + if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { + /* + * Double hack, send back xid in results_prt if non-NULL + */ + if (resultsp != NULL) + *(uint32_t *)resultsp = x_id; + + return (cu->cu_error.re_status = RPC_TIMEDOUT); + } + + /* CONSTCOND */ + while (TRUE) { + enum clnt_stat res; + + if ((res = get_areply(cl, &r_xid, &timeout)) != RPC_SUCCESS) + return (res); + + if (r_xid == x_id) + break; + } + + /* + * process header + */ + return (proc_header(cl, xresults, resultsp)); +} + +static void +sfs_cudp_geterr(CLIENT *cl, struct rpc_err *errp) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + + *errp = cu->cu_error; +} + + +static bool_t +sfs_cudp_freeres( + CLIENT *cl, + xdrproc_t xdr_res, + void * res_ptr) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + XDR *xdrs = &(cu->cu_outxdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_res)(xdrs, res_ptr)); +} + +/* ARGSUSED */ +static void +sfs_cudp_abort(CLIENT *h) +{ +} + +static bool_t +sfs_cudp_control( + CLIENT *cl, + uint_t request, + void *info) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + + switch (request) { + case CLGET_SERVER_ADDR: + *(struct sockaddr_in *)info = cu->cu_raddr; + break; + default: + return (FALSE); + } + return (TRUE); +} + +static void +sfs_cudp_destroy(CLIENT *cl) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + + if (cu->cu_closeit) { + (void)close(cu->cu_sock); + } + XDR_DESTROY(&(cu->cu_outxdrs)); + mem_free((void *)cu, (sizeof(struct cu_data) + cu->cu_sendsz + cu->cu_recvsz)); + mem_free((void *)cl, sizeof(CLIENT)); +} + +/* ARGSUSED */ +static bool_t +sfs_cudp_getreply( + CLIENT *cl, + xdrproc_t xproc, + void *xres, + int cnt, + uint32_t *xids, + uint32_t *xid, + struct timeval *tv) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + bool_t res; + int i; + + cu->cu_error.re_status = RPC_SUCCESS; + + if ((res = get_areply(cl, xid, tv)) != RPC_SUCCESS) + return (res); + + /* + * Check to make sure xid matchs one that we are interested in + */ + for (i = 0; i < cnt; i++) { + if (xids[i] == *xid) + break; + } + + if (i == cnt) + return (RPC_CANTDECODERES); + + /* + * process header + */ + return (proc_header(cl, xproc, xres)); +} + +static int +sfs_cudp_poll( + CLIENT *cl, + uint32_t usecs) +{ + struct cu_data *cu = (struct cu_data *)cl->cl_private; + struct timeval tv; +#ifdef FD_SETSIZE + fd_set mask; + fd_set readfds; + + FD_ZERO(&mask); + FD_SET(cu->cu_sock, &mask); +#else + int mask = 1 << (cu->cu_sock); + int readfds; +#endif /* def FD_SETSIZE */ + + tv.tv_sec = 0; + if (usecs > 1000000) + tv.tv_sec = usecs / 1000000; + tv.tv_usec = usecs % 1000000; + + readfds = mask; + return (select(_rpc_dtablesize(), &readfds, NULL, NULL, &tv)); +} diff --git a/TBBT/trace_play/rpc/svc.c b/TBBT/trace_play/rpc/svc.c new file mode 100755 index 0000000..9a8d905 --- /dev/null +++ b/TBBT/trace_play/rpc/svc.c @@ -0,0 +1,479 @@ +#ifndef lint +static char sfs_svc_c_id[] = "@(#)svc.c 2.1 97/10/23"; +#endif +/* @(#)svc.c 2.4 88/08/11 4.0 RPCSRC; from 1.44 88/02/08 SMI */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc.c 1.41 87/10/13 Copyr 1984 Sun Micro"; +#endif + +/* + * svc.c, Server-side remote procedure call interface. + * + * There are two sets of procedures here. The xprt routines are + * for handling transport handles. The svc routines handle the + * list of service routines. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include "rpc/rpc.h" +#include "rpc/pmap_clnt.h" + +#ifdef FD_SETSIZE +static SVCXPRT **xports; +#else +#define NOFILE 32 + +static SVCXPRT *xports[NOFILE]; +#endif /* def FD_SETSIZE */ + +#define NULL_SVC ((struct svc_callout *)0) +#define RQCRED_SIZE 400 /* this size is excessive */ + +/* + * The services list + * Each entry represents a set of procedures (an rpc program). + * The dispatch routine takes request structs and runs the + * apropriate procedure. + */ +static struct svc_callout { + struct svc_callout *sc_next; + uint32_t sc_prog; + uint32_t sc_vers; + void (*sc_dispatch)(); +} *svc_head; + +static struct svc_callout *svc_find(); + +/* *************** SVCXPRT related stuff **************** */ + +/* + * Activate a transport handle. + */ +void +xprt_register( + SVCXPRT *xprt) +{ + int sock = xprt->xp_sock; + +#ifdef FD_SETSIZE + if (xports == NULL) { + xports = (SVCXPRT **) + mem_alloc(FD_SETSIZE * sizeof(SVCXPRT *)); + } + if (sock < _rpc_dtablesize()) { + xports[sock] = xprt; + FD_SET(sock, &svc_fdset); + } +#else + if (sock < NOFILE) { + xports[sock] = xprt; + svc_fds |= (1 << sock); + } +#endif /* def FD_SETSIZE */ + +} + +/* + * De-activate a transport handle. + */ +void +xprt_unregister( + SVCXPRT *xprt) +{ + int sock = xprt->xp_sock; + +#ifdef FD_SETSIZE + if ((sock < _rpc_dtablesize()) && (xports[sock] == xprt)) { + xports[sock] = (SVCXPRT *)0; + FD_CLR(sock, &svc_fdset); + } +#else + if ((sock < NOFILE) && (xports[sock] == xprt)) { + xports[sock] = (SVCXPRT *)0; + svc_fds &= ~(1 << sock); + } +#endif /* def FD_SETSIZE */ +} + + +/* ********************** CALLOUT list related stuff ************* */ + +/* + * Add a service program to the callout list. + * The dispatch routine will be called when a rpc request for this + * program number comes in. + */ +bool_t +svc_register( + SVCXPRT *xprt, + uint32_t prog, + uint32_t vers, + void (*dispatch)(), + int protocol) +{ + struct svc_callout *prev; + struct svc_callout *s; + + if ((s = svc_find(prog, vers, &prev)) != NULL_SVC) { + if (s->sc_dispatch == dispatch) + goto pmap_it; /* he is registering another xptr */ + return (FALSE); + } + s = (struct svc_callout *)mem_alloc(sizeof(struct svc_callout)); + if (s == (struct svc_callout *)0) { + return (FALSE); + } + s->sc_prog = prog; + s->sc_vers = vers; + s->sc_dispatch = dispatch; + s->sc_next = svc_head; + svc_head = s; +pmap_it: + /* now register the information with the local binder service */ + if (protocol) { + return (pmap_set(prog, vers, protocol, xprt->xp_port)); + } + return (TRUE); +} + +/* + * Remove a service program from the callout list. + */ +void +svc_unregister( + uint32_t prog, + uint32_t vers) +{ + struct svc_callout *prev; + struct svc_callout *s; + + if ((s = svc_find(prog, vers, &prev)) == NULL_SVC) + return; + if (prev == NULL_SVC) { + svc_head = s->sc_next; + } else { + prev->sc_next = s->sc_next; + } + s->sc_next = NULL_SVC; + mem_free((char *) s, (uint_t) sizeof(struct svc_callout)); + /* now unregister the information with the local binder service */ + (void)pmap_unset(prog, vers); +} + +/* + * Search the callout list for a program number, return the callout + * struct. + */ +static struct svc_callout * +svc_find( + uint32_t prog, + uint32_t vers, + struct svc_callout **prev) +{ + struct svc_callout *s, *p; + + p = NULL_SVC; + for (s = svc_head; s != NULL_SVC; s = s->sc_next) { + if ((s->sc_prog == prog) && (s->sc_vers == vers)) + goto done; + p = s; + } +done: + *prev = p; + return (s); +} + +/* ******************* REPLY GENERATION ROUTINES ************ */ + +/* + * Send a reply to an rpc request + */ +bool_t +svc_sendreply( + SVCXPRT *xprt, + xdrproc_t xdr_results, + void *xdr_location) +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = SUCCESS; + rply.acpted_rply.ar_results.where = xdr_location; + rply.acpted_rply.ar_results.proc = xdr_results; + return (SVC_REPLY(xprt, &rply)); +} + +/* + * No procedure error reply + */ +void +svcerr_noproc( + SVCXPRT *xprt) +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = PROC_UNAVAIL; + SVC_REPLY(xprt, &rply); +} + +/* + * Can't decode args error reply + */ +void +svcerr_decode( + SVCXPRT *xprt) +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = GARBAGE_ARGS; + SVC_REPLY(xprt, &rply); +} + +/* + * Some system error + */ +void +svcerr_systemerr( + SVCXPRT *xprt) +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = SYSTEM_ERR; + SVC_REPLY(xprt, &rply); +} + +/* + * Authentication error reply + */ +void +svcerr_auth( + SVCXPRT *xprt, + enum auth_stat why) +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_DENIED; + rply.rjcted_rply.rj_stat = AUTH_ERROR; + rply.rjcted_rply.rj_why = why; + SVC_REPLY(xprt, &rply); +} + +/* + * Auth too weak error reply + */ +void +svcerr_weakauth( + SVCXPRT *xprt) +{ + + svcerr_auth(xprt, AUTH_TOOWEAK); +} + +/* + * Program unavailable error reply + */ +void +svcerr_noprog( + SVCXPRT *xprt) +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = PROG_UNAVAIL; + SVC_REPLY(xprt, &rply); +} + +/* + * Program version mismatch error reply + */ +void +svcerr_progvers( + SVCXPRT *xprt, + uint32_t low_vers, + uint32_t high_vers) +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = PROG_MISMATCH; + rply.acpted_rply.ar_vers.low = low_vers; + rply.acpted_rply.ar_vers.high = high_vers; + SVC_REPLY(xprt, &rply); +} + +/* ******************* SERVER INPUT STUFF ******************* */ + +/* + * Get server side input from some transport. + * + * Statement of authentication parameters management: + * This function owns and manages all authentication parameters, specifically + * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and + * the "cooked" credentials (rqst->rq_clntcred). + * However, this function does not know the structure of the cooked + * credentials, so it make the following assumptions: + * a) the structure is contiguous (no pointers), and + * b) the cred structure size does not exceed RQCRED_SIZE bytes. + * In all events, all three parameters are freed upon exit from this routine. + * The storage is trivially management on the call stack in user land, but + * is mallocated in kernel land. + */ + +void +svc_getreqset( +#ifdef FD_SETSIZE + fd_set *readfds) +{ +#else + int *readfds) +{ + int readfds_local = *readfds; +#endif /* def FD_SETSIZE */ + enum xprt_stat stat; + struct rpc_msg msg; + int prog_found; + uint32_t low_vers; + uint32_t high_vers; + struct svc_req r; + SVCXPRT *xprt; + int setsize; + int sock; + char cred_area[2*MAX_AUTH_BYTES + RQCRED_SIZE]; + msg.rm_call.cb_cred.oa_base = cred_area; + msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]); + r.rq_clntcred = &(cred_area[2*MAX_AUTH_BYTES]); + + +#ifdef FD_SETSIZE + setsize = _rpc_dtablesize(); + for (sock = 0; sock < setsize; sock++) { + if (FD_ISSET(sock, readfds)) { +#else + for (sock = 0; readfds_local != 0; sock++, readfds_local >>= 1) { + if ((readfds_local & 1) != 0) { +#endif /* def FD_SETSIZE */ + /* sock has input waiting */ + xprt = xports[sock]; + /* now receive msgs from xprtprt (support batch calls) */ + do { + if (SVC_RECV(xprt, &msg)) { + + /* now find the exported program and call it */ + struct svc_callout *s; + enum auth_stat why; + + r.rq_xprt = xprt; + r.rq_prog = msg.rm_call.cb_prog; + r.rq_vers = msg.rm_call.cb_vers; + r.rq_proc = msg.rm_call.cb_proc; + r.rq_cred = msg.rm_call.cb_cred; + /* first authenticate the message */ + if ((why= _authenticate(&r, &msg)) != AUTH_OK) { + svcerr_auth(xprt, why); + goto call_done; + } + /* now match message with a registered service*/ + prog_found = FALSE; + low_vers = 0 - 1; + high_vers = 0; + for (s = svc_head; s != NULL_SVC; s = s->sc_next) { + if (s->sc_prog == r.rq_prog) { + if (s->sc_vers == r.rq_vers) { + (*s->sc_dispatch)(&r, xprt); + goto call_done; + } /* found correct version */ + prog_found = TRUE; + if (s->sc_vers < low_vers) + low_vers = s->sc_vers; + if (s->sc_vers > high_vers) + high_vers = s->sc_vers; + } /* found correct program */ + } + /* + * if we got here, the program or version + * is not served ... + */ + if (prog_found) + svcerr_progvers(xprt, + low_vers, high_vers); + else + svcerr_noprog(xprt); + /* Fall through to ... */ + } + call_done: + if ((stat = SVC_STAT(xprt)) == XPRT_DIED){ + SVC_DESTROY(xprt); + break; + } + } while (stat == XPRT_MOREREQS); + } + } +} diff --git a/TBBT/trace_play/rpc/svc.h b/TBBT/trace_play/rpc/svc.h new file mode 100755 index 0000000..497a6ca --- /dev/null +++ b/TBBT/trace_play/rpc/svc.h @@ -0,0 +1,314 @@ +/* + * @(#)svc.h 2.1 97/10/23 + */ +/* @(#)svc.h 2.2 88/07/29 4.0 RPCSRC; from 1.20 88/02/08 SMI */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * svc.h, Server-side remote procedure call interface. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#ifndef __SVC_HEADER__ +#define __SVC_HEADER__ + +#include "rpc/rpc_msg.h" +#include "rpc/xdr.h" + +/* + * This interface must manage two items concerning remote procedure calling: + * + * 1) An arbitrary number of transport connections upon which rpc requests + * are received. The two most notable transports are TCP and UDP; they are + * created and registered by routines in svc_tcp.c and svc_udp.c, respectively; + * they in turn call xprt_register and xprt_unregister. + * + * 2) An arbitrary number of locally registered services. Services are + * described by the following four data: program number, version number, + * "service dispatch" function, a transport handle, and a boolean that + * indicates whether or not the exported program should be registered with a + * local binder service; if true the program's number and version and the + * port number from the transport handle are registered with the binder. + * These data are registered with the rpc svc system via svc_register. + * + * A service's dispatch function is called whenever an rpc request comes in + * on a transport. The request's program and version numbers must match + * those of the registered service. The dispatch function is passed two + * parameters, struct svc_req * and SVCXPRT *, defined below. + */ + +enum xprt_stat { + XPRT_DIED, + XPRT_MOREREQS, + XPRT_IDLE +}; + +/* + * Server side transport handle + */ +typedef struct SVCXPRT { + int xp_sock; + uint16_t xp_port; /* associated port number */ + struct xp_ops *xp_ops; +#if defined(AIX) + size_t xp_addrlen; /* length of remote address */ +#else + int xp_addrlen; /* length of remote address */ +#endif + struct sockaddr_in xp_raddr; /* remote address */ + struct opaque_auth xp_verf; /* raw response verifier */ + void * xp_p1; /* private */ + void * xp_p2; /* private */ +} SVCXPRT; + +struct xp_ops { + bool_t (*xp_recv)(SVCXPRT *, struct rpc_msg *); + enum xprt_stat (*xp_stat)(SVCXPRT *); + bool_t (*xp_getargs)(SVCXPRT *, xdrproc_t, void *); + bool_t (*xp_reply)(SVCXPRT *, struct rpc_msg *); + bool_t (*xp_freeargs)(SVCXPRT *, xdrproc_t, void *); + void (*xp_destroy)(SVCXPRT *); +}; +/* + * Approved way of getting address of caller + */ +#define svc_getcaller(x) (&(x)->xp_raddr) + +/* + * Operations defined on an SVCXPRT handle + * + * SVCXPRT *xprt; + * struct rpc_msg *msg; + * xdrproc_t xargs; + * void * argsp; + */ +#define SVC_RECV(xprt, msg) \ + (*(xprt)->xp_ops->xp_recv)((xprt), (msg)) +#define svc_recv(xprt, msg) \ + (*(xprt)->xp_ops->xp_recv)((xprt), (msg)) + +#define SVC_STAT(xprt) \ + (*(xprt)->xp_ops->xp_stat)(xprt) +#define svc_stat(xprt) \ + (*(xprt)->xp_ops->xp_stat)(xprt) + +#define SVC_GETARGS(xprt, xargs, argsp) \ + (*(xprt)->xp_ops->xp_getargs)((xprt), (xargs), (argsp)) +#define svc_getargs(xprt, xargs, argsp) \ + (*(xprt)->xp_ops->xp_getargs)((xprt), (xargs), (argsp)) + +#define SVC_REPLY(xprt, msg) \ + (*(xprt)->xp_ops->xp_reply) ((xprt), (msg)) +#define svc_reply(xprt, msg) \ + (*(xprt)->xp_ops->xp_reply) ((xprt), (msg)) + +#define SVC_FREEARGS(xprt, xargs, argsp) \ + (*(xprt)->xp_ops->xp_freeargs)((xprt), (xargs), (argsp)) +#define svc_freeargs(xprt, xargs, argsp) \ + (*(xprt)->xp_ops->xp_freeargs)((xprt), (xargs), (argsp)) + +#define SVC_DESTROY(xprt) \ + (*(xprt)->xp_ops->xp_destroy)(xprt) +#define svc_destroy(xprt) \ + (*(xprt)->xp_ops->xp_destroy)(xprt) + + +/* + * Service request + */ +struct svc_req { + uint32_t rq_prog; /* service program number */ + uint32_t rq_vers; /* service protocol version */ + uint32_t rq_proc; /* the desired procedure */ + struct opaque_auth rq_cred; /* raw creds from the wire */ + void * rq_clntcred; /* read only cooked cred */ + SVCXPRT *rq_xprt; /* associated transport */ +}; + + +/* + * Service registration + * + * svc_register(xprt, prog, vers, dispatch, protocol) + * SVCXPRT *xprt; + * uint32_t prog; + * uint32_t vers; + * void (*dispatch)(); + * int protocol; + */ +extern bool_t svc_register(SVCXPRT *, uint32_t, uint32_t, void (*)(), int); + +/* + * Service un-registration + * + * svc_unregister(prog, vers) + * uint32_t prog; + * uint32_t vers; + */ +extern void svc_unregister(uint32_t, uint32_t); + +/* + * Transport registration. + * + * xprt_register(xprt) + * SVCXPRT *xprt; + */ +extern void xprt_register(SVCXPRT *); + +/* + * Transport un-register + * + * xprt_unregister(xprt) + * SVCXPRT *xprt; + */ +extern void xprt_unregister(SVCXPRT *); + + + + +/* + * When the service routine is called, it must first check to see if it + * knows about the procedure; if not, it should call svcerr_noproc + * and return. If so, it should deserialize its arguments via + * SVC_GETARGS (defined above). If the deserialization does not work, + * svcerr_decode should be called followed by a return. Successful + * decoding of the arguments should be followed the execution of the + * procedure's code and a call to svc_sendreply. + * + * Also, if the service refuses to execute the procedure due to too- + * weak authentication parameters, svcerr_weakauth should be called. + * Note: do not confuse access-control failure with weak authentication! + * + * NB: In pure implementations of rpc, the caller always waits for a reply + * msg. This message is sent when svc_sendreply is called. + * Therefore pure service implementations should always call + * svc_sendreply even if the function logically returns void; use + * xdr.h - xdr_void for the xdr routine. HOWEVER, tcp based rpc allows + * for the abuse of pure rpc via batched calling or pipelining. In the + * case of a batched call, svc_sendreply should NOT be called since + * this would send a return message, which is what batching tries to avoid. + * It is the service/protocol writer's responsibility to know which calls are + * batched and which are not. Warning: responding to batch calls may + * deadlock the caller and server processes! + */ + +extern bool_t svc_sendreply(SVCXPRT *, xdrproc_t, void *); +extern void svcerr_decode(SVCXPRT *); +extern void svcerr_weakauth(SVCXPRT *); +extern void svcerr_noproc(SVCXPRT *); +extern void svcerr_progvers(SVCXPRT *, uint32_t, uint32_t); +extern void svcerr_auth(SVCXPRT *, enum auth_stat); +extern void svcerr_noprog(SVCXPRT *); +extern void svcerr_systemerr(SVCXPRT *); + +/* + * Lowest level dispatching -OR- who owns this process anyway. + * Somebody has to wait for incoming requests and then call the correct + * service routine. The routine svc_run does infinite waiting; i.e., + * svc_run never returns. + * Since another (co-existant) package may wish to selectively wait for + * incoming calls or other events outside of the rpc architecture, the + * routine svc_getreq is provided. It must be passed readfds, the + * "in-place" results of a select system call (see select, section 2). + */ + +/* + * Global keeper of rpc service descriptors in use + * dynamic; must be inspected before each call to select + */ +#ifdef FD_SETSIZE +extern fd_set svc_fdset; +#define svc_fds svc_fdset.fds_bits[0] /* compatibility */ +#else +extern int svc_fds; +#endif /* def FD_SETSIZE */ + +/* + * a small program implemented by the svc_rpc implementation itself; + * also see clnt.h for protocol numbers. + */ +extern void rpctest_service(); + +extern void svc_getreq(int); +#ifdef FD_SETSIZE +extern void svc_getreqset(fd_set *);/* takes fdset instead of int */ +#else +extern void svc_getreqset(int *); +#endif +extern void svc_run(void); /* never returns */ + +/* + * Socket to use on svcxxx_create call to get default socket + */ +#define RPC_ANYSOCK -1 + +/* + * These are the existing service side transport implementations + */ + +/* + * Memory based rpc for testing and timing. + */ +extern SVCXPRT *svcraw_create(void); + +/* + * Udp based rpc. + */ +extern SVCXPRT *svcudp_create(int); +extern SVCXPRT *svcudp_bufcreate(int, uint_t, uint_t); + +/* + * Tcp based rpc. + */ +extern SVCXPRT *svctcp_create(int, uint_t, uint_t); + +extern int svcudp_enablecache(SVCXPRT *, uint32_t); + + +#endif /* !__SVC_HEADER__ */ diff --git a/TBBT/trace_play/rpc/svc_auth.c b/TBBT/trace_play/rpc/svc_auth.c new file mode 100755 index 0000000..2352630 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_auth.c @@ -0,0 +1,136 @@ +#ifndef lint +static char sfs_svc_auth_id[] = "@(#)svc_auth.c 2.1 97/10/23"; +#endif +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_auth.c 2.1 88/08/07 4.0 RPCSRC; from 1.19 87/08/11 Copyr 1984 Sun Micro"; +#endif +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * svc_auth_nodes.c, Server-side rpc authenticator interface, + * *WITHOUT* DES authentication. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include "rpc/rpc.h" + +/* + * svcauthsw is the bdevsw of server side authentication. + * + * Server side authenticators are called from authenticate by + * using the client auth struct flavor field to index into svcauthsw. + * The server auth flavors must implement a routine that looks + * like: + * + * enum auth_stat + * flavorx_auth(rqst, msg) + * register struct svc_req *rqst; + * register struct rpc_msg *msg; + * + */ + +extern enum auth_stat _svcauth_null(struct svc_req *, struct rpc_msg *); +extern enum auth_stat _svcauth_unix(struct svc_req *, struct rpc_msg *); +extern enum auth_stat _svcauth_short(struct svc_req *, struct rpc_msg *); + +static struct { + enum auth_stat (*authenticator)(struct svc_req *, struct rpc_msg *); +} svcauthsw[] = { + _svcauth_null, /* AUTH_NULL */ + _svcauth_unix, /* AUTH_UNIX */ + _svcauth_short, /* AUTH_SHORT */ +}; +#define AUTH_MAX 2 /* HIGHEST AUTH NUMBER */ + + +/* + * The call rpc message, msg has been obtained from the wire. The msg contains + * the raw form of credentials and verifiers. authenticate returns AUTH_OK + * if the msg is successfully authenticated. If AUTH_OK then the routine also + * does the following things: + * set rqst->rq_xprt->verf to the appropriate response verifier; + * sets rqst->rq_client_cred to the "cooked" form of the credentials. + * + * NB: rqst->rq_cxprt->verf must be pre-alloctaed; + * its length is set appropriately. + * + * The caller still owns and is responsible for msg->u.cmb.cred and + * msg->u.cmb.verf. The authentication system retains ownership of + * rqst->rq_client_cred, the cooked credentials. + * + * There is an assumption that any flavour less than AUTH_NULL is + * invalid. + */ +enum auth_stat +_authenticate( + struct svc_req *rqst, + struct rpc_msg *msg) +{ + register int cred_flavor; + + rqst->rq_cred = msg->rm_call.cb_cred; + rqst->rq_xprt->xp_verf.oa_flavor = _null_auth.oa_flavor; + rqst->rq_xprt->xp_verf.oa_length = 0; + cred_flavor = rqst->rq_cred.oa_flavor; + if ((cred_flavor <= AUTH_MAX) && (cred_flavor >= AUTH_NULL)) { + return ((*(svcauthsw[cred_flavor].authenticator))(rqst, msg)); + } + + return (AUTH_REJECTEDCRED); +} + +/* ARGSUSED */ +enum auth_stat +_svcauth_null( + struct svc_req *rqst, + struct rpc_msg *msg) +{ + + return (AUTH_OK); +} diff --git a/TBBT/trace_play/rpc/svc_auth.h b/TBBT/trace_play/rpc/svc_auth.h new file mode 100755 index 0000000..c765e9f --- /dev/null +++ b/TBBT/trace_play/rpc/svc_auth.h @@ -0,0 +1,62 @@ +/* + * @(#)svc_auth.h 2.1 97/10/23 + */ +/* @(#)svc_auth.h 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)svc_auth.h 1.6 86/07/16 SMI */ + +/* + * svc_auth.h, Service side of rpc authentication. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +/* + * Server side authenticator + */ +extern enum auth_stat _authenticate(); diff --git a/TBBT/trace_play/rpc/svc_auth_unix.c b/TBBT/trace_play/rpc/svc_auth_unix.c new file mode 100755 index 0000000..83ed327 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_auth_unix.c @@ -0,0 +1,156 @@ +#ifndef lint +static char sfs_svc_auth_unix_id[] = "@(#)svc_auth_unix.c 2.1 97/10/23"; +#endif +/* @(#)svc_auth_unix.c 2.3 88/08/01 4.0 RPCSRC; from 1.28 88/02/08 SMI */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Manassas, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_auth_unix.c 1.28 88/02/08 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_auth_unix.c + * Handles UNIX flavor authentication parameters on the service side of rpc. + * There are two svc auth implementations here: AUTH_UNIX and AUTH_SHORT. + * _svcauth_unix does full blown unix style uid,gid+gids auth, + * _svcauth_short uses a shorthand auth to index into a cache of longhand auths. + * Note: the shorthand has been gutted for efficiency. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <string.h> +#include "rpc/rpc.h" + +/* + * Unix longhand authenticator + */ +enum auth_stat +_svcauth_unix( + struct svc_req *rqst, + struct rpc_msg *msg) +{ + enum auth_stat stat; + XDR xdrs; + struct authunix_parms *aup; + int32_t *buf; + struct area { + struct authunix_parms area_aup; + char area_machname[MAX_MACHINE_NAME+1]; + int area_gids[NGRPS]; + } *area; + uint_t auth_len; + int str_len, gid_len; + int i; + + area = (struct area *) rqst->rq_clntcred; + aup = &area->area_aup; + aup->aup_machname = area->area_machname; + aup->aup_gids = area->area_gids; + auth_len = (uint_t)msg->rm_call.cb_cred.oa_length; + xdrmem_create(&xdrs, msg->rm_call.cb_cred.oa_base, auth_len,XDR_DECODE); + buf = XDR_INLINE(&xdrs, auth_len); + if (buf != NULL) { + aup->aup_time = IXDR_GET_LONG(buf); + str_len = IXDR_GET_U_LONG(buf); + if (str_len > MAX_MACHINE_NAME) { + stat = AUTH_BADCRED; + goto done; + } + memmove(aup->aup_machname, (void *)buf, (uint_t)str_len); + aup->aup_machname[str_len] = 0; + str_len = RNDUP(str_len); + buf += str_len / sizeof (int32_t); + aup->aup_uid = IXDR_GET_LONG(buf); + aup->aup_gid = IXDR_GET_LONG(buf); + gid_len = IXDR_GET_U_LONG(buf); + if (gid_len > NGRPS) { + stat = AUTH_BADCRED; + goto done; + } + aup->aup_len = gid_len; + for (i = 0; i < gid_len; i++) { + aup->aup_gids[i] = IXDR_GET_LONG(buf); + } + /* + * five is the smallest unix credentials structure - + * timestamp, hostname len (0), uid, gid, and gids len (0). + */ + if ((5 + gid_len) * BYTES_PER_XDR_UNIT + str_len > auth_len) { + (void) printf("bad auth_len gid %d str %d auth %d\n", + gid_len, str_len, auth_len); + stat = AUTH_BADCRED; + goto done; + } + } else if (! xdr_authunix_parms(&xdrs, aup)) { + xdrs.x_op = XDR_FREE; + (void)xdr_authunix_parms(&xdrs, aup); + stat = AUTH_BADCRED; + goto done; + } + rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; + rqst->rq_xprt->xp_verf.oa_length = 0; + stat = AUTH_OK; +done: + XDR_DESTROY(&xdrs); + return (stat); +} + + +/* + * Shorthand unix authenticator + * Looks up longhand in a cache. + */ +/*ARGSUSED*/ +enum auth_stat +_svcauth_short( + struct svc_req *rqst, + struct rpc_msg *msg) +{ + return (AUTH_REJECTEDCRED); +} diff --git a/TBBT/trace_play/rpc/svc_raw.c b/TBBT/trace_play/rpc/svc_raw.c new file mode 100755 index 0000000..4b85782 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_raw.c @@ -0,0 +1,196 @@ +#ifndef lint +static char sfs_svc_raw_id[] = "@(#)svc_raw.c 2.1 97/10/23"; +#endif +/* @(#)svc_raw.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_raw.c 1.15 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_raw.c, This a toy for simple testing and timing. + * Interface to create an rpc client and server in the same UNIX process. + * This lets us similate rpc and get rpc (round trip) overhead, without + * any interference from the kernal. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "rpc/rpc.h" + + +/* + * This is the "network" that we will be moving data over + */ +static struct svcraw_private { + char _raw_buf[UDPMSGSIZE]; + SVCXPRT server; + XDR xdr_stream; + char verf_body[MAX_AUTH_BYTES]; +} *svcraw_private; + +static bool_t svcraw_recv(SVCXPRT *, struct rpc_msg *); +static enum xprt_stat svcraw_stat(SVCXPRT *); +static bool_t svcraw_getargs(SVCXPRT *, xdrproc_t, void *); +static bool_t svcraw_reply(SVCXPRT *, struct rpc_msg *); +static bool_t svcraw_freeargs(SVCXPRT *, xdrproc_t, void *); +static void svcraw_destroy(SVCXPRT *); + +static struct xp_ops server_ops = { + svcraw_recv, + svcraw_stat, + svcraw_getargs, + svcraw_reply, + svcraw_freeargs, + svcraw_destroy +}; + +SVCXPRT * +svcraw_create(void) +{ + struct svcraw_private *srp = svcraw_private; + + if (srp == 0) { + srp = (struct svcraw_private *)calloc(1, sizeof (struct svcraw_private)); + if (srp == 0) + return (0); + } + srp->server.xp_sock = 0; + srp->server.xp_port = 0; + srp->server.xp_ops = &server_ops; + srp->server.xp_verf.oa_base = srp->verf_body; + xdrmem_create(&srp->xdr_stream, srp->_raw_buf, UDPMSGSIZE, XDR_FREE); + return (&srp->server); +} + +/* ARGSUSED */ +static enum xprt_stat +svcraw_stat(SVCXPRT *x) +{ + + return (XPRT_IDLE); +} + +/* ARGSUSED */ +static bool_t +svcraw_recv( + SVCXPRT *xprt, + struct rpc_msg *msg) +{ + struct svcraw_private *srp = svcraw_private; + XDR *xdrs; + + if (srp == 0) + return (0); + xdrs = &srp->xdr_stream; + xdrs->x_op = XDR_DECODE; + XDR_SETPOS(xdrs, 0); + if (! xdr_callmsg(xdrs, msg)) + return (FALSE); + return (TRUE); +} + +/* ARGSUSED */ +static bool_t +svcraw_reply( + SVCXPRT *xprt, + struct rpc_msg *msg) +{ + struct svcraw_private *srp = svcraw_private; + XDR *xdrs; + + if (srp == 0) + return (FALSE); + xdrs = &srp->xdr_stream; + xdrs->x_op = XDR_ENCODE; + XDR_SETPOS(xdrs, 0); + if (! xdr_replymsg(xdrs, msg)) + return (FALSE); + (void)XDR_GETPOS(xdrs); /* called just for overhead */ + return (TRUE); +} + +/* ARGSUSED */ +static bool_t +svcraw_getargs( + SVCXPRT *xprt, + xdrproc_t xdr_args, + void *args_ptr) +{ + struct svcraw_private *srp = svcraw_private; + + if (srp == 0) + return (FALSE); + return ((*xdr_args)(&srp->xdr_stream, args_ptr)); +} + +/* ARGSUSED */ +static bool_t +svcraw_freeargs( + SVCXPRT *xprt, + xdrproc_t xdr_args, + void *args_ptr) +{ + struct svcraw_private *srp = svcraw_private; + XDR *xdrs; + + if (srp == 0) + return (FALSE); + xdrs = &srp->xdr_stream; + xdrs->x_op = XDR_FREE; + return ((*xdr_args)(xdrs, args_ptr)); +} + +/* ARGSUSED */ +static void +svcraw_destroy(SVCXPRT *x) +{ +} diff --git a/TBBT/trace_play/rpc/svc_run.c b/TBBT/trace_play/rpc/svc_run.c new file mode 100755 index 0000000..0c787b4 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_run.c @@ -0,0 +1,93 @@ +#ifndef lint +static char sfs_svc_run_id[] = "@(#)svc_run.c 2.1 97/10/23"; +#endif +/* @(#)svc_run.c 2.1 88/07/29 4.0 RPCSRC */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_run.c 1.1 87/10/13 Copyr 1984 Sun Micro"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * This is the rpc server side idle loop + * Wait for input, call server program. + */ +#include "rpc/rpc.h" +#include <errno.h> + +void +svc_run(void) +{ +#ifdef FD_SETSIZE + fd_set readfds; +#else + int readfds; +#endif /* def FD_SETSIZE */ + extern int errno; + + for (;;) { +#ifdef FD_SETSIZE + readfds = svc_fdset; +#else + readfds = svc_fds; +#endif /* def FD_SETSIZE */ + switch (select(_rpc_dtablesize(), &readfds, NULL, NULL, + NULL)) { + case -1: + if (errno == EINTR) { + continue; + } + perror("svc_run: - select failed"); + return; + case 0: + continue; + default: + svc_getreqset(&readfds); + } + } +} diff --git a/TBBT/trace_play/rpc/svc_simple.c b/TBBT/trace_play/rpc/svc_simple.c new file mode 100755 index 0000000..ec22a47 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_simple.c @@ -0,0 +1,171 @@ +#ifndef lint +static char sfs_svc_simple_id[] = "@(#)svc_simple.c 2.1 97/10/23"; +#endif +/* @(#)svc_simple.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_simple.c 1.18 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_simple.c + * Simplified front end to rpc. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include <netdb.h> + +static struct proglst { + char *(*p_progname)(); + int p_prognum; + int p_procnum; + xdrproc_t p_inproc, p_outproc; + struct proglst *p_nxt; +} *proglst; +static void universal(struct svc_req *, SVCXPRT *); +static SVCXPRT *transp; + +registerrpc( + int prognum, + int versnum, + int procnum, + char *(*progname)(), + xdrproc_t inproc, + xdrproc_t outproc) +{ + struct proglst *pl; + + if (procnum == NULLPROC) { + (void) fprintf(stderr, + "can't reassign procedure number %d\n", NULLPROC); + return (-1); + } + if (transp == 0) { + transp = svcudp_create(RPC_ANYSOCK); + if (transp == NULL) { + (void) fprintf(stderr, "couldn't create an rpc server\n"); + return (-1); + } + } + (void) pmap_unset((uint32_t)prognum, (uint32_t)versnum); + if (!svc_register(transp, (uint32_t)prognum, (uint32_t)versnum, + universal, IPPROTO_UDP)) { + (void) fprintf(stderr, "couldn't register prog %d vers %d\n", + prognum, versnum); + return (-1); + } + pl = (struct proglst *)malloc(sizeof(struct proglst)); + if (pl == NULL) { + (void) fprintf(stderr, "registerrpc: out of memory\n"); + return (-1); + } + pl->p_progname = progname; + pl->p_prognum = prognum; + pl->p_procnum = procnum; + pl->p_inproc = inproc; + pl->p_outproc = outproc; + pl->p_nxt = proglst; + proglst = pl; + return (0); +} + +static void +universal( + struct svc_req *rqstp, + SVCXPRT *transp) +{ + int prog, proc; + char *outdata; + char xdrbuf[UDPMSGSIZE]; + struct proglst *pl; + + /* + * enforce "procnum 0 is echo" convention + */ + if (rqstp->rq_proc == NULLPROC) { + if (svc_sendreply(transp, xdr_void, (char *)NULL) == FALSE) { + (void) fprintf(stderr, "xxx\n"); + exit(1); + } + return; + } + prog = rqstp->rq_prog; + proc = rqstp->rq_proc; + for (pl = proglst; pl != NULL; pl = pl->p_nxt) + if (pl->p_prognum == prog && pl->p_procnum == proc) { + /* decode arguments into a CLEAN buffer */ + memset(xdrbuf, '\0', sizeof(xdrbuf)); /* required ! */ + if (!svc_getargs(transp, pl->p_inproc, xdrbuf)) { + svcerr_decode(transp); + return; + } + outdata = (*(pl->p_progname))(xdrbuf); + if (outdata == NULL && pl->p_outproc != xdr_void) + /* there was an error */ + return; + if (!svc_sendreply(transp, pl->p_outproc, outdata)) { + (void) fprintf(stderr, + "trouble replying to prog %d\n", + pl->p_prognum); + exit(1); + } + /* free the decoded arguments */ + (void)svc_freeargs(transp, pl->p_inproc, xdrbuf); + return; + } + (void) fprintf(stderr, "never registered prog %d\n", prog); + exit(1); +} + diff --git a/TBBT/trace_play/rpc/svc_tcp.c b/TBBT/trace_play/rpc/svc_tcp.c new file mode 100755 index 0000000..2b8b71a --- /dev/null +++ b/TBBT/trace_play/rpc/svc_tcp.c @@ -0,0 +1,481 @@ +#ifndef lint +static char sfs_svc_tcp_id[] = "@(#)svc_tcp.c 2.1 97/10/23"; +#endif +/* @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_tcp.c, Server side for TCP/IP based RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * Actually implements two flavors of transporter - + * a tcp rendezvouser (a listner and connection establisher) + * and a record/tcp stream. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include <errno.h> + +/* + * Ops vector for TCP/IP based rpc service handle + */ +static bool_t svctcp_recv(SVCXPRT *, struct rpc_msg *); +static enum xprt_stat svctcp_stat(SVCXPRT *); +static bool_t svctcp_getargs(SVCXPRT *, xdrproc_t, void *); +static bool_t svctcp_reply(SVCXPRT *, struct rpc_msg *); +static bool_t svctcp_freeargs(SVCXPRT *, xdrproc_t, void *); +static void svctcp_destroy(SVCXPRT *); +static bool_t svctcp_abortrop(SVCXPRT *, struct rpc_msg *); +static bool_t svctcp_abortgop(SVCXPRT *, xdrproc_t, void *); + +static struct xp_ops svctcp_op = { + svctcp_recv, + svctcp_stat, + svctcp_getargs, + svctcp_reply, + svctcp_freeargs, + svctcp_destroy +}; + +/* + * Ops vector for TCP/IP rendezvous handler + */ +static bool_t rendezvous_request(SVCXPRT *xprt, struct rpc_msg *); +static enum xprt_stat rendezvous_stat(SVCXPRT *); + +static struct xp_ops svctcp_rendezvous_op = { + rendezvous_request, + rendezvous_stat, + svctcp_abortgop, + svctcp_abortrop, + svctcp_abortgop, + svctcp_destroy +}; + +static int readtcp(), writetcp(); +static int readtcp(SVCXPRT *, char *, int); +static int writetcp(SVCXPRT *, char *, int); +static SVCXPRT *makefd_xprt(int, uint_t, uint_t); + +struct tcp_rendezvous { /* kept in xprt->xp_p1 */ + uint_t sendsize; + uint_t recvsize; +}; + +struct tcp_conn { /* kept in xprt->xp_p1 */ + enum xprt_stat strm_stat; + uint32_t x_id; + XDR xdrs; + char verf_body[MAX_AUTH_BYTES]; +}; + +/* + * Usage: + * xprt = svctcp_create(sock, send_buf_size, recv_buf_size); + * + * Creates, registers, and returns a (rpc) tcp based transporter. + * Once *xprt is initialized, it is registered as a transporter + * see (svc.h, xprt_register). This routine returns + * a NULL if a problem occurred. + * + * If sock<0 then a socket is created, else sock is used. + * If the socket, sock is not bound to a port then svctcp_create + * binds it to an arbitrary port. The routine then starts a tcp + * listener on the socket's associated port. In any (successful) case, + * xprt->xp_sock is the registered socket number and xprt->xp_port is the + * associated port number. + * + * Since tcp streams do buffered io similar to stdio, the caller can specify + * how big the send and receive buffers are via the second and third parms; + * 0 => use the system default. + */ +SVCXPRT * +svctcp_create( + int sock, + uint_t sendsize, + uint_t recvsize) +{ + bool_t madesock = FALSE; + SVCXPRT *xprt; + struct tcp_rendezvous *r; + struct sockaddr_in addr; +#if defined(AIX) + size_t len; +#else + int len; +#endif /* AIX */ + + len = sizeof(struct sockaddr_in); + + if (sock == RPC_ANYSOCK) { + if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + perror("svctcp_.c - udp socket creation problem"); + return ((SVCXPRT *)NULL); + } + madesock = TRUE; + } + memset((char *)&addr, '\0', sizeof (addr)); + addr.sin_family = AF_INET; + if (bindresvport(sock, &addr)) { + addr.sin_port = 0; + (void)bind(sock, (struct sockaddr *)&addr, len); + } + if ((getsockname(sock, (struct sockaddr *)&addr, &len) != 0) || + (listen(sock, 2) != 0)) { + perror("svctcp_.c - cannot getsockname or listen"); + if (madesock) + (void)close(sock); + return ((SVCXPRT *)NULL); + } + r = (struct tcp_rendezvous *)mem_alloc(sizeof(struct tcp_rendezvous)); + if (r == NULL) { + (void) fprintf(stderr, "svctcp_create: out of memory\n"); + return (NULL); + } + r->sendsize = sendsize; + r->recvsize = recvsize; + xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT)); + if (xprt == NULL) { + (void) fprintf(stderr, "svctcp_create: out of memory\n"); + return (NULL); + } + xprt->xp_p2 = NULL; + xprt->xp_p1 = (void *)r; + xprt->xp_verf = _null_auth; + xprt->xp_ops = &svctcp_rendezvous_op; + xprt->xp_port = ntohs(addr.sin_port); + xprt->xp_sock = sock; + xprt_register(xprt); + return (xprt); +} + +/* + * Like svtcp_create(), except the routine takes any *open* UNIX file + * descriptor as its first input. + */ +SVCXPRT * +svcfd_create( + int fd, + uint_t sendsize, + uint_t recvsize) +{ + + return (makefd_xprt(fd, sendsize, recvsize)); +} + +static SVCXPRT * +makefd_xprt( + int fd, + uint_t sendsize, + uint_t recvsize) +{ + SVCXPRT *xprt; + struct tcp_conn *cd; + + xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT)); + if (xprt == (SVCXPRT *)NULL) { + (void) fprintf(stderr, "svc_tcp: makefd_xprt: out of memory\n"); + goto done; + } + cd = (struct tcp_conn *)mem_alloc(sizeof(struct tcp_conn)); + if (cd == (struct tcp_conn *)NULL) { + (void) fprintf(stderr, "svc_tcp: makefd_xprt: out of memory\n"); + mem_free((char *) xprt, sizeof(SVCXPRT)); + xprt = (SVCXPRT *)NULL; + goto done; + } + cd->strm_stat = XPRT_IDLE; + xdrrec_create(&(cd->xdrs), sendsize, recvsize, + (void *)xprt, readtcp, writetcp); + xprt->xp_p2 = NULL; + xprt->xp_p1 = (void *)cd; + xprt->xp_verf.oa_base = cd->verf_body; + xprt->xp_addrlen = 0; + xprt->xp_ops = &svctcp_op; /* truely deals with calls */ + xprt->xp_port = 0; /* this is a connection, not a rendezvouser */ + xprt->xp_sock = fd; + xprt_register(xprt); + done: + return (xprt); +} + +/* ARGSUSED */ +static bool_t +rendezvous_request( + SVCXPRT *xprt, + struct rpc_msg *msg) +{ + int sock; + struct tcp_rendezvous *r; + struct sockaddr_in addr; +#if defined(AIX) + size_t len; +#else + int len; +#endif /* AIX */ + + r = (struct tcp_rendezvous *)xprt->xp_p1; + again: + len = sizeof(struct sockaddr_in); + if ((sock = accept(xprt->xp_sock, (struct sockaddr *)&addr, + &len)) < 0) { + if (errno == EINTR) + goto again; + return (FALSE); + } + /* + * make a new transporter (re-uses xprt) + */ + xprt = makefd_xprt(sock, r->sendsize, r->recvsize); + xprt->xp_raddr = addr; + xprt->xp_addrlen = len; + return (FALSE); /* there is never an rpc msg to be processed */ +} + +/* ARGSUSED */ +static enum xprt_stat +rendezvous_stat(SVCXPRT *x) +{ + + return (XPRT_IDLE); +} + +static void +svctcp_destroy( + SVCXPRT *xprt) +{ + struct tcp_conn *cd = (struct tcp_conn *)xprt->xp_p1; + + xprt_unregister(xprt); + (void)close(xprt->xp_sock); + if (xprt->xp_port != 0) { + /* a rendezvouser socket */ + xprt->xp_port = 0; + } else { + /* an actual connection socket */ + XDR_DESTROY(&(cd->xdrs)); + } + mem_free((void *)cd, sizeof(struct tcp_conn)); + mem_free((void *)xprt, sizeof(SVCXPRT)); +} + +/* + * All read operations timeout after 35 seconds. + * A timeout is fatal for the connection. + */ +static struct timeval wait_per_try = { 35, 0 }; + +/* + * reads data from the tcp conection. + * any error is fatal and the connection is closed. + * (And a read of zero bytes is a half closed stream => error.) + */ +static int +readtcp( + SVCXPRT *xprt, + char *buf, + int len) +{ + int sock = xprt->xp_sock; +#ifdef FD_SETSIZE + fd_set mask; + fd_set readfds; + + FD_ZERO(&mask); + FD_SET(sock, &mask); +#else + int mask = 1 << sock; + int readfds; +#endif /* def FD_SETSIZE */ + do { + readfds = mask; + if (select(_rpc_dtablesize(), &readfds, NULL, NULL, + &wait_per_try) <= 0) { + if (errno == EINTR) { + continue; + } + goto fatal_err; + } +#ifdef FD_SETSIZE + } while (!FD_ISSET(sock, &readfds)); +#else + } while (readfds != mask); +#endif /* def FD_SETSIZE */ + if ((len = read(sock, buf, len)) > 0) { + return (len); + } +fatal_err: + ((struct tcp_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED; + return (-1); +} + +/* + * writes data to the tcp connection. + * Any error is fatal and the connection is closed. + */ +static int +writetcp( + SVCXPRT *xprt, + char *buf, + int len) +{ + int i, cnt; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) { + if ((i = write(xprt->xp_sock, buf, cnt)) < 0) { + ((struct tcp_conn *)(xprt->xp_p1))->strm_stat = + XPRT_DIED; + return (-1); + } + } + return (len); +} + +static enum xprt_stat +svctcp_stat( + SVCXPRT *xprt) +{ + struct tcp_conn *cd = + (struct tcp_conn *)(xprt->xp_p1); + + if (cd->strm_stat == XPRT_DIED) + return (XPRT_DIED); + if (! xdrrec_eof(&(cd->xdrs))) + return (XPRT_MOREREQS); + return (XPRT_IDLE); +} + +static bool_t +svctcp_recv( + SVCXPRT *xprt, + struct rpc_msg *msg) +{ + struct tcp_conn *cd = + (struct tcp_conn *)(xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + + xdrs->x_op = XDR_DECODE; + (void)xdrrec_skiprecord(xdrs); + if (xdr_callmsg(xdrs, msg)) { + cd->x_id = msg->rm_xid; + return (TRUE); + } + return (FALSE); +} + +static bool_t +svctcp_getargs( + SVCXPRT *xprt, + xdrproc_t xdr_args, + void * args_ptr) +{ + + return ((*xdr_args)(&(((struct tcp_conn *)(xprt->xp_p1))->xdrs), args_ptr)); +} + +static bool_t +svctcp_freeargs( + SVCXPRT *xprt, + xdrproc_t xdr_args, + void * args_ptr) +{ + XDR *xdrs = + &(((struct tcp_conn *)(xprt->xp_p1))->xdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_args)(xdrs, args_ptr)); +} + +static bool_t +svctcp_reply( + SVCXPRT *xprt, + struct rpc_msg *msg) +{ + struct tcp_conn *cd = + (struct tcp_conn *)(xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + bool_t stat; + + xdrs->x_op = XDR_ENCODE; + msg->rm_xid = cd->x_id; + stat = xdr_replymsg(xdrs, msg); + (void)xdrrec_endofrecord(xdrs, TRUE); + return (stat); +} + +/* ARGSUSED */ +static bool_t +svctcp_abortrop( + SVCXPRT *x, + struct rpc_msg *msg) +{ + abort(); + /* NOTREACHED */ + return (FALSE); +} + +/* ARGSUSED */ +static bool_t +svctcp_abortgop( + SVCXPRT *x, + xdrproc_t argp, + void *arg) +{ + abort(); + /* NOTREACHED */ + return (FALSE); +} diff --git a/TBBT/trace_play/rpc/svc_udp.c b/TBBT/trace_play/rpc/svc_udp.c new file mode 100755 index 0000000..0a801b2 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_udp.c @@ -0,0 +1,507 @@ +#ifndef lint +static char sfs_svc_udp_id[] = "@(#)svc_udp.c 2.1 97/10/23"; +#endif +/* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_udp.c, + * Server side for UDP/IP based RPC. (Does some caching in the hopes of + * achieving execute-at-most-once semantics.) + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include <errno.h> + + +#define rpc_buffer(xprt) ((xprt)->xp_p1) + +static bool_t svcudp_recv(SVCXPRT *, struct rpc_msg *); +static bool_t svcudp_reply(SVCXPRT *, struct rpc_msg *) ; +static enum xprt_stat svcudp_stat(SVCXPRT *); +static bool_t svcudp_getargs(SVCXPRT *, xdrproc_t, void *); +static bool_t svcudp_freeargs(SVCXPRT *, xdrproc_t, void *); +static void svcudp_destroy(SVCXPRT *); + +static struct xp_ops svcudp_op = { + svcudp_recv, + svcudp_stat, + svcudp_getargs, + svcudp_reply, + svcudp_freeargs, + svcudp_destroy +}; + +/* + * kept in xprt->xp_p2 + */ +struct svcudp_data { + uint_t su_iosz; /* byte size of send.recv buffer */ + uint32_t su_xid; /* transaction id */ + XDR su_xdrs; /* XDR handle */ + char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ + char * su_cache; /* cached data, NULL if no cache */ +}; +#define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2)) + +static void cache_set(SVCXPRT *, uint32_t); +static int cache_get(SVCXPRT *, struct rpc_msg *, char **, uint32_t *); + +/* + * Usage: + * xprt = svcudp_create(sock); + * + * If sock<0 then a socket is created, else sock is used. + * If the socket, sock is not bound to a port then svcudp_create + * binds it to an arbitrary port. In any (successful) case, + * xprt->xp_sock is the registered socket number and xprt->xp_port is the + * associated port number. + * Once *xprt is initialized, it is registered as a transporter; + * see (svc.h, xprt_register). + * The routines returns NULL if a problem occurred. + */ +SVCXPRT * +svcudp_bufcreate( + int sock, + uint_t sendsz, + uint_t recvsz) +{ + bool_t madesock = FALSE; + SVCXPRT *xprt; + struct svcudp_data *su; + struct sockaddr_in addr; +#if defined(AIX) + size_t len; +#else + int len; +#endif /* AIX */ + + len = sizeof(struct sockaddr_in); + + if (sock == RPC_ANYSOCK) { + if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("svcudp_create: socket creation problem"); + return ((SVCXPRT *)NULL); + } + madesock = TRUE; + } + memset((char *)&addr, '\0', sizeof (addr)); + addr.sin_family = AF_INET; + if (bindresvport(sock, &addr)) { + addr.sin_port = 0; + (void)bind(sock, (struct sockaddr *)&addr, len); + } + if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) { + perror("svcudp_create - cannot getsockname"); + if (madesock) + (void)close(sock); + return ((SVCXPRT *)NULL); + } + xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT)); + if (xprt == NULL) { + (void)fprintf(stderr, "svcudp_create: out of memory\n"); + return (NULL); + } + su = (struct svcudp_data *)mem_alloc(sizeof(struct svcudp_data)); + if (su == NULL) { + (void)fprintf(stderr, "svcudp_create: out of memory\n"); + return (NULL); + } + su->su_iosz = ((((sendsz > recvsz) ? sendsz : recvsz) + 3) / 4) * 4; + if ((rpc_buffer(xprt) = mem_alloc(su->su_iosz)) == NULL) { + (void)fprintf(stderr, "svcudp_create: out of memory\n"); + return (NULL); + } + xdrmem_create( + &(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE); + su->su_cache = NULL; + xprt->xp_p2 = (caddr_t)su; + xprt->xp_verf.oa_base = su->su_verfbody; + xprt->xp_ops = &svcudp_op; + xprt->xp_port = ntohs(addr.sin_port); + xprt->xp_sock = sock; + xprt_register(xprt); + return (xprt); +} + +SVCXPRT * +svcudp_create( + int sock) +{ + + return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE)); +} + +/*ARGSUSED*/ +static enum xprt_stat +svcudp_stat( + SVCXPRT *xprt) +{ + + return (XPRT_IDLE); +} + +static bool_t +svcudp_recv( + SVCXPRT *xprt, + struct rpc_msg *msg) +{ + struct svcudp_data *su = su_data(xprt); + XDR *xdrs = &(su->su_xdrs); + int rlen; + char *reply; + uint32_t replylen; + + again: + xprt->xp_addrlen = sizeof(struct sockaddr_in); + rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, + 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); + if (rlen == -1 && errno == EINTR) + goto again; + if (rlen < 4*sizeof(uint32_t)) + return (FALSE); + xdrs->x_op = XDR_DECODE; + XDR_SETPOS(xdrs, 0); + if (! xdr_callmsg(xdrs, msg)) + return (FALSE); + su->su_xid = msg->rm_xid; + if (su->su_cache != NULL) { + if (cache_get(xprt, msg, &reply, &replylen)) { + (void) sendto(xprt->xp_sock, reply, (int) replylen, 0, + (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen); + return (TRUE); + } + } + return (TRUE); +} + +static bool_t +svcudp_reply( + SVCXPRT *xprt, + struct rpc_msg *msg) +{ + struct svcudp_data *su = su_data(xprt); + XDR *xdrs = &(su->su_xdrs); + int slen; + bool_t stat = FALSE; + + xdrs->x_op = XDR_ENCODE; + XDR_SETPOS(xdrs, 0); + msg->rm_xid = su->su_xid; + if (xdr_replymsg(xdrs, msg)) { + slen = (int)XDR_GETPOS(xdrs); + if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0, + (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen) + == slen) { + stat = TRUE; + if (su->su_cache && slen >= 0) { + cache_set(xprt, (uint32_t) slen); + } + } + } + return (stat); +} + +static bool_t +svcudp_getargs( + SVCXPRT *xprt, + xdrproc_t xdr_args, + void *args_ptr) +{ + + return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr)); +} + +static bool_t +svcudp_freeargs( + SVCXPRT *xprt, + xdrproc_t xdr_args, + void *args_ptr) +{ + XDR *xdrs = &(su_data(xprt)->su_xdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_args)(xdrs, args_ptr)); +} + +static void +svcudp_destroy( + SVCXPRT *xprt) +{ + struct svcudp_data *su = su_data(xprt); + + xprt_unregister(xprt); + (void)close(xprt->xp_sock); + XDR_DESTROY(&(su->su_xdrs)); + mem_free(rpc_buffer(xprt), su->su_iosz); + mem_free((caddr_t)su, sizeof(struct svcudp_data)); + mem_free((caddr_t)xprt, sizeof(SVCXPRT)); +} + + +/***********this could be a separate file*********************/ + +/* + * Fifo cache for udp server + * Copies pointers to reply buffers into fifo cache + * Buffers are sent again if retransmissions are detected. + */ + +#define SPARSENESS 4 /* 75% sparse */ + +#define CACHE_PERROR(msg) \ + (void) fprintf(stderr,"%s\n", msg) + +#define ALLOC(type, size) \ + (type *) mem_alloc((unsigned) (sizeof(type) * (size))) + +#define BZERO(addr, type, size) \ + memset((void *) addr, '\0', sizeof(type) * (int) (size)) + +/* + * An entry in the cache + */ +typedef struct cache_node *cache_ptr; +struct cache_node { + /* + * Index into cache is xid, proc, vers, prog and address + */ + uint32_t cache_xid; + uint32_t cache_proc; + uint32_t cache_vers; + uint32_t cache_prog; + struct sockaddr_in cache_addr; + /* + * The cached reply and length + */ + char * cache_reply; + uint32_t cache_replylen; + /* + * Next node on the list, if there is a collision + */ + cache_ptr cache_next; +}; + + + +/* + * The entire cache + */ +struct udp_cache { + uint32_t uc_size; /* size of cache */ + cache_ptr *uc_entries; /* hash table of entries in cache */ + cache_ptr *uc_fifo; /* fifo list of entries in cache */ + uint32_t uc_nextvictim; /* points to next victim in fifo list */ + uint32_t uc_prog; /* saved program number */ + uint32_t uc_vers; /* saved version number */ + uint32_t uc_proc; /* saved procedure number */ + struct sockaddr_in uc_addr; /* saved caller's address */ +}; + + +/* + * the hashing function + */ +#define CACHE_LOC(transp, xid) \ + (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size)) + + +/* + * Enable use of the cache. + * Note: there is no disable. + */ +svcudp_enablecache( + SVCXPRT *transp, + uint32_t size) +{ + struct svcudp_data *su = su_data(transp); + struct udp_cache *uc; + + if (su->su_cache != NULL) { + CACHE_PERROR("enablecache: cache already enabled"); + return(0); + } + uc = ALLOC(struct udp_cache, 1); + if (uc == NULL) { + CACHE_PERROR("enablecache: could not allocate cache"); + return(0); + } + uc->uc_size = size; + uc->uc_nextvictim = 0; + uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS); + if (uc->uc_entries == NULL) { + CACHE_PERROR("enablecache: could not allocate cache data"); + return(0); + } + BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS); + uc->uc_fifo = ALLOC(cache_ptr, size); + if (uc->uc_fifo == NULL) { + CACHE_PERROR("enablecache: could not allocate cache fifo"); + return(0); + } + BZERO(uc->uc_fifo, cache_ptr, size); + su->su_cache = (char *) uc; + return(1); +} + + +/* + * Set an entry in the cache + */ +static void +cache_set( + SVCXPRT *xprt, + uint32_t replylen) +{ + cache_ptr victim; + cache_ptr *vicp; + struct svcudp_data *su = su_data(xprt); + struct udp_cache *uc = (struct udp_cache *) su->su_cache; + uint_t loc; + char *newbuf; + + /* + * Find space for the new entry, either by + * reusing an old entry, or by mallocing a new one + */ + victim = uc->uc_fifo[uc->uc_nextvictim]; + if (victim != NULL) { + loc = CACHE_LOC(xprt, victim->cache_xid); + for (vicp = &uc->uc_entries[loc]; + *vicp != NULL && *vicp != victim; + vicp = &(*vicp)->cache_next) + ; + if (*vicp == NULL) { + CACHE_PERROR("cache_set: victim not found"); + return; + } + *vicp = victim->cache_next; /* remote from cache */ + newbuf = victim->cache_reply; + } else { + victim = ALLOC(struct cache_node, 1); + if (victim == NULL) { + CACHE_PERROR("cache_set: victim alloc failed"); + return; + } + newbuf = mem_alloc(su->su_iosz); + if (newbuf == NULL) { + CACHE_PERROR("cache_set: could not allocate new rpc_buffer"); + return; + } + } + + /* + * Store it away + */ + victim->cache_replylen = replylen; + victim->cache_reply = rpc_buffer(xprt); + rpc_buffer(xprt) = newbuf; + xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE); + victim->cache_xid = su->su_xid; + victim->cache_proc = uc->uc_proc; + victim->cache_vers = uc->uc_vers; + victim->cache_prog = uc->uc_prog; + victim->cache_addr = uc->uc_addr; + loc = CACHE_LOC(xprt, victim->cache_xid); + victim->cache_next = uc->uc_entries[loc]; + uc->uc_entries[loc] = victim; + uc->uc_fifo[uc->uc_nextvictim++] = victim; + uc->uc_nextvictim %= uc->uc_size; +} + +/* + * Try to get an entry from the cache + * return 1 if found, 0 if not found + */ +static int +cache_get( + SVCXPRT *xprt, + struct rpc_msg *msg, + char **replyp, + uint32_t *replylenp) +{ + uint_t loc; + cache_ptr ent; + struct svcudp_data *su = su_data(xprt); + struct udp_cache *uc = (struct udp_cache *) su->su_cache; + +# define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0) + + loc = CACHE_LOC(xprt, su->su_xid); + for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { + if (ent->cache_xid == su->su_xid && + ent->cache_proc == uc->uc_proc && + ent->cache_vers == uc->uc_vers && + ent->cache_prog == uc->uc_prog && + EQADDR(ent->cache_addr, uc->uc_addr)) { + *replyp = ent->cache_reply; + *replylenp = ent->cache_replylen; + return(1); + } + } + /* + * Failed to find entry + * Remember a few things so we can do a set later + */ + uc->uc_proc = msg->rm_call.cb_proc; + uc->uc_vers = msg->rm_call.cb_vers; + uc->uc_prog = msg->rm_call.cb_prog; + uc->uc_addr = xprt->xp_raddr; + return(0); +} + diff --git a/TBBT/trace_play/rpc/types.h b/TBBT/trace_play/rpc/types.h new file mode 100755 index 0000000..0854cc6 --- /dev/null +++ b/TBBT/trace_play/rpc/types.h @@ -0,0 +1,111 @@ +/* + * @(#)types.h 2.1 97/10/23 + */ +/* @(#)types.h 2.3 88/08/15 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)types.h 1.18 87/07/24 SMI */ + +/* + * Rpc additions to <sys/types.h> + */ +#ifndef __TYPES_RPC_HEADER__ +#define __TYPES_RPC_HEADER__ + +#include <sys/types.h> +#include <sys/time.h> + +#if defined(__INTTYPES_INCLUDED) || defined(_SYS_INT_TYPES_H) +#define HAVE_INTTYPES +#endif + +#if defined(USE_INTTYPES) +#include <inttypes.h> +#else /* USE_INTTYPES */ +#if !defined(HAVE_INTTYPES) +#define HAVE_INTTYPES + +typedef signed char int8_t; +typedef short int16_t; +typedef int int32_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +#endif /* !HAVE_INTTYPES */ + +#endif /* USE_INTTYPES */ + +#define bool_t int32_t +#define enum_t int32_t +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (1) +#endif +#define __dontcare__ -1 +#ifndef NULL +#define NULL 0 +#endif + +#define mem_alloc(bsize) malloc(bsize) +#define mem_free(ptr, bsize) free(ptr) + +#if defined(NO_T_TYPES) +typedef unsigned int uint_t; +typedef unsigned char uchar_t; +#endif + +#ifdef notdef +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK (uint32_t)0x7F000001 +#endif +#endif + +#endif /* ndef __TYPES_RPC_HEADER__ */ diff --git a/TBBT/trace_play/rpc/xdr.c b/TBBT/trace_play/rpc/xdr.c new file mode 100755 index 0000000..3a1fc88 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr.c @@ -0,0 +1,685 @@ +#ifndef lint +static char sfs_xdr_c_id[] = "@(#)xdr.c 2.1 97/10/23"; +#endif +/* @(#)xdr.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr.c 1.35 87/08/12"; +#endif + +/* + * xdr.c, Generic XDR routines implementation. + * + * Copyright (C) 1986, Sun Microsystems, Inc. + * + * These are the "generic" xdr routines used to serialize and de-serialize + * most common data items. See xdr.h for more info on the interface to + * xdr. + */ +#ifndef lint +static char sfs_clnt_id[] = "@(#)xdr.c 2.1 97/10/23"; +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "rpc/types.h" +#include "rpc/xdr.h" + +/* + * constants specific to the xdr "protocol" + */ +#define XDR_FALSE ((int32_t) 0) +#define XDR_TRUE ((int32_t) 1) +#define LASTUNSIGNED ((uint_t) 0-1) + +/* + * for unit alignment + */ +static char xdr_zero[BYTES_PER_XDR_UNIT] = { 0, 0, 0, 0 }; + +/* + * Free a data structure using XDR + * Not a filter, but a convenient utility nonetheless + */ +void +xdr_free( + xdrproc_t proc, + char *objp) +{ + XDR x; + + x.x_op = XDR_FREE; + (*proc)(&x, objp); +} + +/* + * XDR nothing + */ +bool_t +xdr_void(void) +{ + + return (TRUE); +} + +/* + * XDR integers + */ +bool_t +xdr_int( + XDR *xdrs, + int *ip) +{ + +#ifdef lint + (void) (xdr_int16_t(xdrs, (int16_t *)ip)); + return (xdr_int32_t(xdrs, (int32_t *)ip)); +#else + if (sizeof (int) == sizeof (int32_t)) { + return (xdr_int32_t(xdrs, (int32_t *)ip)); + } else { + return (xdr_int16_t(xdrs, (int16_t *)ip)); + } +#endif +} + +/* + * XDR unsigned integers + */ +bool_t +xdr_u_int( + XDR *xdrs, + uint_t *up) +{ + +#ifdef lint + (void) (xdr_int16_t(xdrs, (int16_t *)up)); + return (xdr_uint32_t(xdrs, (uint32_t *)up)); +#else + if (sizeof (uint_t) == sizeof (uint32_t)) { + return (xdr_uint32_t(xdrs, (uint32_t *)up)); + } else { + return (xdr_int16_t(xdrs, (int16_t *)up)); + } +#endif +} + +/* + * XDR long integers + * same as xdr_u_long - open coded to save a proc call! + */ +bool_t +xdr_long( + XDR *xdrs, + int32_t *lp) +{ + + if (xdrs->x_op == XDR_ENCODE) + return (XDR_PUTLONG(xdrs, lp)); + + if (xdrs->x_op == XDR_DECODE) + return (XDR_GETLONG(xdrs, lp)); + + if (xdrs->x_op == XDR_FREE) + return (TRUE); + + return (FALSE); +} + +bool_t +xdr_int32_t( + XDR *xdrs, + int32_t *lp) +{ + + if (xdrs->x_op == XDR_ENCODE) + return (XDR_PUTLONG(xdrs, lp)); + + if (xdrs->x_op == XDR_DECODE) + return (XDR_GETLONG(xdrs, lp)); + + if (xdrs->x_op == XDR_FREE) + return (TRUE); + + return (FALSE); +} + +/* + * XDR unsigned long integers + * same as xdr_long - open coded to save a proc call! + */ +bool_t +xdr_u_long( + XDR *xdrs, + uint32_t *ulp) +{ + + if (xdrs->x_op == XDR_DECODE) + return (XDR_GETLONG(xdrs, (int32_t *)ulp)); + if (xdrs->x_op == XDR_ENCODE) + return (XDR_PUTLONG(xdrs, (int32_t *)ulp)); + if (xdrs->x_op == XDR_FREE) + return (TRUE); + return (FALSE); +} + +bool_t +xdr_uint32_t( + XDR *xdrs, + uint32_t *ulp) +{ + + if (xdrs->x_op == XDR_DECODE) + return (XDR_GETLONG(xdrs, (int32_t *)ulp)); + if (xdrs->x_op == XDR_ENCODE) + return (XDR_PUTLONG(xdrs, (int32_t *)ulp)); + if (xdrs->x_op == XDR_FREE) + return (TRUE); + return (FALSE); +} + +/* + * XDR short integers + */ +bool_t +xdr_short( + XDR *xdrs, + int16_t *sp) +{ + int32_t l; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + l = (int32_t) *sp; + return (XDR_PUTLONG(xdrs, &l)); + + case XDR_DECODE: + if (!XDR_GETLONG(xdrs, &l)) { + return (FALSE); + } + *sp = (int16_t) l; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + +bool_t +xdr_int16_t( + XDR *xdrs, + int16_t *sp) +{ + int32_t l; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + l = (int32_t) *sp; + return (XDR_PUTLONG(xdrs, &l)); + + case XDR_DECODE: + if (!XDR_GETLONG(xdrs, &l)) { + return (FALSE); + } + *sp = (int16_t) l; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + +/* + * XDR unsigned short integers + */ +bool_t +xdr_u_short( + XDR *xdrs, + uint16_t *usp) +{ + uint32_t l; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + l = (uint32_t) *usp; + return (XDR_PUTLONG(xdrs, (int32_t *)&l)); + + case XDR_DECODE: + if (!XDR_GETLONG(xdrs, (int32_t *)&l)) { + return (FALSE); + } + *usp = (uint16_t) l; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + +bool_t +xdr_uint16_t( + XDR *xdrs, + uint16_t *usp) +{ + uint32_t l; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + l = (uint32_t) *usp; + return (XDR_PUTLONG(xdrs, (int32_t *)&l)); + + case XDR_DECODE: + if (!XDR_GETLONG(xdrs, (int32_t *)&l)) { + return (FALSE); + } + *usp = (uint16_t) l; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + + +/* + * XDR a char + */ +bool_t +xdr_char( + XDR *xdrs, + char *cp) +{ + int i; + + i = (*cp); + if (!xdr_int(xdrs, &i)) { + return (FALSE); + } + *cp = i; + return (TRUE); +} + +/* + * XDR an unsigned char + */ +bool_t +xdr_u_char( + XDR *xdrs, + uchar_t *cp) +{ + uint_t u; + + u = (*cp); + if (!xdr_u_int(xdrs, &u)) { + return (FALSE); + } + *cp = u; + return (TRUE); +} + +/* + * XDR booleans + */ +bool_t +xdr_bool( + XDR *xdrs, + bool_t *bp) +{ + int32_t lb; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + lb = *bp ? XDR_TRUE : XDR_FALSE; + return (XDR_PUTLONG(xdrs, &lb)); + + case XDR_DECODE: + if (!XDR_GETLONG(xdrs, &lb)) { + return (FALSE); + } + *bp = (lb == XDR_FALSE) ? FALSE : TRUE; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + +/* + * XDR enumerations + */ +bool_t +xdr_enum( + XDR *xdrs, + enum_t *ep) +{ +#ifndef lint + enum sizecheck { SIZEVAL }; /* used to find the size of an enum */ + + /* + * enums are treated as ints + */ + if (sizeof (enum sizecheck) == sizeof (int32_t)) { + return (xdr_int32_t(xdrs, (int32_t *)ep)); + } else if (sizeof (enum sizecheck) == sizeof (int16_t)) { + return (xdr_int16_t(xdrs, (int16_t *)ep)); + } else { + return (FALSE); + } +#else + (void) (xdr_int16_t(xdrs, (int16_t *)ep)); + return (xdr_int32_t(xdrs, (int32_t *)ep)); +#endif +} + +/* + * XDR opaque data + * Allows the specification of a fixed size sequence of opaque bytes. + * cp points to the opaque object and cnt gives the byte length. + */ +bool_t +xdr_opaque( + XDR *xdrs, + void *cp, + uint_t cnt) +{ + uint_t rndup; + static crud[BYTES_PER_XDR_UNIT]; + + /* + * if no data we are done + */ + if (cnt == 0) + return (TRUE); + + /* + * round byte count to full xdr units + */ + rndup = cnt % BYTES_PER_XDR_UNIT; + if (rndup > 0) + rndup = BYTES_PER_XDR_UNIT - rndup; + + if (xdrs->x_op == XDR_DECODE) { + if (!XDR_GETBYTES(xdrs, cp, cnt)) { + return (FALSE); + } + if (rndup == 0) + return (TRUE); + return (XDR_GETBYTES(xdrs, crud, rndup)); + } + + if (xdrs->x_op == XDR_ENCODE) { + if (!XDR_PUTBYTES(xdrs, cp, cnt)) { + return (FALSE); + } + if (rndup == 0) + return (TRUE); + return (XDR_PUTBYTES(xdrs, xdr_zero, rndup)); + } + + if (xdrs->x_op == XDR_FREE) { + return (TRUE); + } + + return (FALSE); +} + +/* + * XDR counted bytes + * *cpp is a pointer to the bytes, *sizep is the count. + * If *cpp is NULL maxsize bytes are allocated + */ +bool_t +xdr_bytes( + XDR *xdrs, + char **cpp, + uint_t *sizep, + uint_t maxsize) +{ + char *sp = *cpp; /* sp is the actual string pointer */ + uint_t nodesize; + + /* + * first deal with the length since xdr bytes are counted + */ + if (! xdr_u_int(xdrs, sizep)) { + return (FALSE); + } + nodesize = *sizep; + if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE)) { + return (FALSE); + } + + /* + * now deal with the actual bytes + */ + switch (xdrs->x_op) { + + case XDR_DECODE: + if (nodesize == 0) { + return (TRUE); + } + if (sp == NULL) { + *cpp = sp = (char *)mem_alloc(nodesize); + } + if (sp == NULL) { + (void) fprintf(stderr, "xdr_bytes: out of memory\n"); + return (FALSE); + } + /* fall into ... */ + + case XDR_ENCODE: + return (xdr_opaque(xdrs, sp, nodesize)); + + case XDR_FREE: + if (sp != NULL) { + mem_free(sp, nodesize); + *cpp = NULL; + } + return (TRUE); + } + return (FALSE); +} + +/* + * Implemented here due to commonality of the object. + */ +bool_t +xdr_netobj( + XDR *xdrs, + struct netobj *np) +{ + + return (xdr_bytes(xdrs, &np->n_bytes, &np->n_len, MAX_NETOBJ_SZ)); +} + +/* + * XDR a descriminated union + * Support routine for discriminated unions. + * You create an array of xdrdiscrim structures, terminated with + * an entry with a null procedure pointer. The routine gets + * the discriminant value and then searches the array of xdrdiscrims + * looking for that value. It calls the procedure given in the xdrdiscrim + * to handle the discriminant. If there is no specific routine a default + * routine may be called. + * If there is no specific or default routine an error is returned. + */ +bool_t +xdr_union( + XDR *xdrs, + enum_t *dscmp, /* enum to decide which arm to work on */ + char *unp, /* the union itself */ + struct xdr_discrim *choices, /* [value, xdr proc] for each arm */ + xdrproc_t dfault) /* default xdr routine */ +{ + enum_t dscm; + + /* + * we deal with the discriminator; it's an enum + */ + if (! xdr_enum(xdrs, dscmp)) { + return (FALSE); + } + dscm = *dscmp; + + /* + * search choices for a value that matches the discriminator. + * if we find one, execute the xdr routine for that value. + */ + for (; choices->proc != NULL_xdrproc_t; choices++) { + if (choices->value == dscm) + return ((*(choices->proc))(xdrs, unp, LASTUNSIGNED)); + } + + /* + * no match - execute the default xdr routine if there is one + */ + return ((dfault == NULL_xdrproc_t) ? FALSE : + (*dfault)(xdrs, unp, LASTUNSIGNED)); +} + + +/* + * Non-portable xdr primitives. + * Care should be taken when moving these routines to new architectures. + */ + + +/* + * XDR null terminated ASCII strings + * xdr_string deals with "C strings" - arrays of bytes that are + * terminated by a NULL character. The parameter cpp references a + * pointer to storage; If the pointer is null, then the necessary + * storage is allocated. The last parameter is the max allowed length + * of the string as specified by a protocol. + */ +bool_t +xdr_string( + XDR *xdrs, + char **cpp, + uint_t maxsize) +{ + char *sp = *cpp; /* sp is the actual string pointer */ + uint_t size; + uint_t nodesize; + + /* + * first deal with the length since xdr strings are counted-strings + */ + switch (xdrs->x_op) { + case XDR_FREE: + if (sp == NULL) { + return(TRUE); /* already free */ + } + /* fall through... */ + case XDR_ENCODE: + size = strlen(sp); + break; + } + if (! xdr_u_int(xdrs, &size)) { + return (FALSE); + } + if (size > maxsize) { + return (FALSE); + } + nodesize = size + 1; + + /* + * now deal with the actual bytes + */ + switch (xdrs->x_op) { + + case XDR_DECODE: + if (nodesize == 0) { + return (TRUE); + } + if (sp == NULL) + *cpp = sp = (char *)mem_alloc(nodesize); + if (sp == NULL) { + (void) fprintf(stderr, "xdr_string: out of memory\n"); + return (FALSE); + } + sp[size] = 0; + /* fall into ... */ + + case XDR_ENCODE: + return (xdr_opaque(xdrs, sp, size)); + + case XDR_FREE: + mem_free(sp, nodesize); + *cpp = NULL; + return (TRUE); + } + return (FALSE); +} + +/* + * Wrapper for xdr_string that can be called directly from + * routines like clnt_call + */ +bool_t +xdr_wrapstring( + XDR *xdrs, + char **cpp) +{ + if (xdr_string(xdrs, cpp, LASTUNSIGNED)) { + return (TRUE); + } + return (FALSE); +} diff --git a/TBBT/trace_play/rpc/xdr.h b/TBBT/trace_play/rpc/xdr.h new file mode 100755 index 0000000..2ccc5d2 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr.h @@ -0,0 +1,308 @@ +/* + * @(#)xdr.h 2.1 97/10/23 + */ + +/* @(#)xdr.h 2.2 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)xdr.h 1.19 87/04/22 SMI */ + +/* + * xdr.h, External Data Representation Serialization Routines. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#ifndef __XDR_HEADER__ +#define __XDR_HEADER__ + +#include <stdio.h> + +/* + * XDR provides a conventional way for converting between C data + * types and an external bit-string representation. Library supplied + * routines provide for the conversion on built-in C data types. These + * routines and utility routines defined here are used to help implement + * a type encode/decode routine for each user-defined type. + * + * Each data type provides a single procedure which takes two arguments: + * + * bool_t + * xdrproc(xdrs, argresp) + * XDR *xdrs; + * <type> *argresp; + * + * xdrs is an instance of a XDR handle, to which or from which the data + * type is to be converted. argresp is a pointer to the structure to be + * converted. The XDR handle contains an operation field which indicates + * which of the operations (ENCODE, DECODE * or FREE) is to be performed. + * + * XDR_DECODE may allocate space if the pointer argresp is null. This + * data can be freed with the XDR_FREE operation. + * + * We write only one procedure per data type to make it easy + * to keep the encode and decode procedures for a data type consistent. + * In many cases the same code performs all operations on a user defined type, + * because all the hard work is done in the component type routines. + * decode as a series of calls on the nested data types. + */ + +/* + * Xdr operations. XDR_ENCODE causes the type to be encoded into the + * stream. XDR_DECODE causes the type to be extracted from the stream. + * XDR_FREE can be used to release the space allocated by an XDR_DECODE + * request. + */ +enum xdr_op { + XDR_ENCODE=0, + XDR_DECODE=1, + XDR_FREE=2 +}; + +/* + * This is the number of bytes per unit of external data. + */ +#define BYTES_PER_XDR_UNIT (4) +#define RNDUP(x) ((((x) + BYTES_PER_XDR_UNIT - 1) / BYTES_PER_XDR_UNIT) \ + * BYTES_PER_XDR_UNIT) + +/* + * A xdrproc_t exists for each data type which is to be encoded or decoded. + * + * The second argument to the xdrproc_t is a pointer to an opaque pointer. + * The opaque pointer generally points to a structure of the data type + * to be decoded. If this pointer is 0, then the type routines should + * allocate dynamic storage of the appropriate size and return it. + * bool_t (*xdrproc_t)(XDR *, void **); + */ +typedef bool_t (*xdrproc_t)(); + +/* + * The XDR handle. + * Contains operation which is being applied to the stream, + * an operations vector for the paticular implementation (e.g. see xdr_mem.c), + * and two private fields for the use of the particular impelementation. + */ +typedef struct { + enum xdr_op x_op; /* operation; fast additional param */ + struct xdr_ops *x_ops; + char * x_public; /* users' data */ + char * x_private; /* pointer to private data */ + char * x_base; /* private used for position info */ + int x_handy; /* extra private word */ +} XDR; + +struct xdr_ops { + /* get a long from underlying stream */ + bool_t (*x_getlong)(XDR *, int32_t *); + /* put a long to " */ + bool_t (*x_putlong)(XDR *, int32_t *); + /* get some bytes from " */ + bool_t (*x_getbytes)(XDR *, void *, uint_t); + /* put some bytes to " */ + bool_t (*x_putbytes)(XDR *, void *, uint_t); + /* returns bytes off from beginning */ + uint_t (*x_getpostn)(XDR *); + /* lets you reposition the stream */ + bool_t (*x_setpostn)(XDR *, uint_t); + /* buf quick ptr to buffered data */ + int32_t *(*x_inline)(XDR *, uint_t); + /* free privates of this xdr_stream */ + void (*x_destroy)(XDR *); +}; + +/* + * Operations defined on a XDR handle + * + * XDR *xdrs; + * int32_t *longp; + * void * addr; + * uint_t len; + * uint_t pos; + */ +#define XDR_GETLONG(xdrs, longp) \ + (*(xdrs)->x_ops->x_getlong)(xdrs, longp) +#define xdr_getlong(xdrs, longp) \ + (*(xdrs)->x_ops->x_getlong)(xdrs, longp) + +#define XDR_PUTLONG(xdrs, longp) \ + (*(xdrs)->x_ops->x_putlong)(xdrs, longp) +#define xdr_putlong(xdrs, longp) \ + (*(xdrs)->x_ops->x_putlong)(xdrs, longp) + +#define XDR_GETBYTES(xdrs, addr, len) \ + (*(xdrs)->x_ops->x_getbytes)(xdrs, addr, len) +#define xdr_getbytes(xdrs, addr, len) \ + (*(xdrs)->x_ops->x_getbytes)(xdrs, addr, len) + +#define XDR_PUTBYTES(xdrs, addr, len) \ + (*(xdrs)->x_ops->x_putbytes)(xdrs, addr, len) +#define xdr_putbytes(xdrs, addr, len) \ + (*(xdrs)->x_ops->x_putbytes)(xdrs, addr, len) + +#define XDR_GETPOS(xdrs) \ + (*(xdrs)->x_ops->x_getpostn)(xdrs) +#define xdr_getpos(xdrs) \ + (*(xdrs)->x_ops->x_getpostn)(xdrs) + +#define XDR_SETPOS(xdrs, pos) \ + (*(xdrs)->x_ops->x_setpostn)(xdrs, pos) +#define xdr_setpos(xdrs, pos) \ + (*(xdrs)->x_ops->x_setpostn)(xdrs, pos) + +#define XDR_INLINE(xdrs, len) \ + (*(xdrs)->x_ops->x_inline)(xdrs, len) +#define xdr_inline(xdrs, len) \ + (*(xdrs)->x_ops->x_inline)(xdrs, len) + +#define XDR_DESTROY(xdrs) \ + if ((xdrs)->x_ops->x_destroy) \ + (*(xdrs)->x_ops->x_destroy)(xdrs) +#define xdr_destroy(xdrs) \ + if ((xdrs)->x_ops->x_destroy) \ + (*(xdrs)->x_ops->x_destroy)(xdrs) + +/* + * Support struct for discriminated unions. + * You create an array of xdrdiscrim structures, terminated with + * a entry with a null procedure pointer. The xdr_union routine gets + * the discriminant value and then searches the array of structures + * for a matching value. If a match is found the associated xdr routine + * is called to handle that part of the union. If there is + * no match, then a default routine may be called. + * If there is no match and no default routine it is an error. + */ +#define NULL_xdrproc_t ((xdrproc_t)0) +struct xdr_discrim { + int value; + xdrproc_t proc; +}; + +/* + * In-line routines for fast encode/decode of primitve data types. + * Caveat emptor: these use single memory cycles to get the + * data from the underlying buffer, and will fail to operate + * properly if the data is not aligned. The standard way to use these + * is to say: + * if ((buf = XDR_INLINE(xdrs, count)) == NULL) + * return (FALSE); + * <<< macro calls >>> + * where ``count'' is the number of bytes of data occupied + * by the primitive data types. + * + * N.B. and frozen for all time: each data type here uses 4 bytes + * of external representation. + */ +#define IXDR_GET_LONG(buf) ((int32_t)ntohl((uint32_t)*(buf)++)) +#define IXDR_PUT_LONG(buf, v) (*(buf)++ = (int32_t)htonl((uint32_t)v)) + +#define IXDR_GET_BOOL(buf) ((bool_t)IXDR_GET_LONG(buf)) +#define IXDR_GET_ENUM(buf, t) ((t)IXDR_GET_LONG(buf)) +#define IXDR_GET_U_LONG(buf) ((uint32_t)IXDR_GET_LONG(buf)) +#define IXDR_GET_SHORT(buf) ((int16_t)IXDR_GET_LONG(buf)) +#define IXDR_GET_U_SHORT(buf) ((uint16_t)IXDR_GET_LONG(buf)) + +#define IXDR_PUT_BOOL(buf, v) IXDR_PUT_LONG((buf), ((int32_t)(v))) +#define IXDR_PUT_ENUM(buf, v) IXDR_PUT_LONG((buf), ((int32_t)(v))) +#define IXDR_PUT_U_LONG(buf, v) IXDR_PUT_LONG((buf), ((int32_t)(v))) +#define IXDR_PUT_SHORT(buf, v) IXDR_PUT_LONG((buf), ((int32_t)(v))) +#define IXDR_PUT_U_SHORT(buf, v) IXDR_PUT_LONG((buf), ((int32_t)(v))) + +/* + * These are the "generic" xdr routines. + */ +extern bool_t xdr_void(void); +extern bool_t xdr_int(XDR *, int *); +extern bool_t xdr_u_int(XDR *, uint_t *); +extern bool_t xdr_long(XDR *, int32_t *); +extern bool_t xdr_u_long(XDR *, uint32_t *); +extern bool_t xdr_int32_t(XDR *, int32_t *); +extern bool_t xdr_uint32_t(XDR *, uint32_t *); +extern bool_t xdr_short(XDR *, int16_t *); +extern bool_t xdr_u_short(XDR *, uint16_t *); +extern bool_t xdr_int16_t(XDR *, int16_t *); +extern bool_t xdr_uint16_t(XDR *, uint16_t *); +extern bool_t xdr_bool(XDR *, bool_t *); +extern bool_t xdr_enum(XDR *, enum_t *); +extern bool_t xdr_array(XDR *, void **, uint_t *, uint_t, uint_t, xdrproc_t); +extern bool_t xdr_bytes(XDR *, char **cp, uint_t *, uint_t); +extern bool_t xdr_opaque(XDR *, void *, uint_t); +extern bool_t xdr_string(XDR *, char **cp, uint_t); +extern bool_t xdr_union(XDR *, enum_t *, char *, struct xdr_discrim *, xdrproc_t); +extern bool_t xdr_char(XDR *, char *); +extern bool_t xdr_u_char(XDR *, uchar_t *); +extern bool_t xdr_vector(XDR *, char *, uint_t, uint_t, xdrproc_t); +extern bool_t xdr_float(XDR *, float *); +extern bool_t xdr_double(XDR *, double *); +extern bool_t xdr_reference(XDR *, void **, uint_t, xdrproc_t); +extern bool_t xdr_pointer(XDR *, void **, uint_t, xdrproc_t); +extern bool_t xdr_wrapstring(XDR *, char **); + +/* + * Common opaque bytes objects used by many rpc protocols; + * declared here due to commonality. + */ +#define MAX_NETOBJ_SZ 1024 +struct netobj { + uint_t n_len; + char *n_bytes; +}; +typedef struct netobj netobj; +extern bool_t xdr_netobj(XDR *, netobj *); + +/* + * These are the public routines for the various implementations of + * xdr streams. + */ +extern void xdrmem_create(XDR *, void *, uint_t, enum xdr_op); +extern void xdrstdio_create(XDR *, FILE *, enum xdr_op); +extern void xdrrec_create(XDR *, uint_t, uint_t, void *, int (*)(), int (*)()); +extern bool_t xdrrec_endofrecord(XDR *, bool_t); +extern bool_t xdrrec_skiprecord(XDR *); +extern bool_t xdrrec_eof(XDR *); + +#endif /* !__XDR_HEADER__ */ diff --git a/TBBT/trace_play/rpc/xdr_array.c b/TBBT/trace_play/rpc/xdr_array.c new file mode 100755 index 0000000..d448022 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_array.c @@ -0,0 +1,175 @@ +#ifndef lint +static char sfs_xdr_array_id[] = "@(#)xdr_array.c 2.1 97/10/23"; +#endif +/* @(#)xdr_array.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_array.c 1.10 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * xdr_array.c, Generic XDR routines impelmentation. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * These are the "non-trivial" xdr primitives used to serialize and de-serialize + * arrays. See xdr.h for more info on the interface to xdr. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "rpc/types.h" +#include "rpc/xdr.h" + +#define LASTUNSIGNED ((uint_t)0-1) + + +/* + * XDR an array of arbitrary elements + * *addrp is a pointer to the array, *sizep is the number of elements. + * If addrp is NULL (*sizep * elsize) bytes are allocated. + * elsize is the size (in bytes) of each element, and elproc is the + * xdr procedure to call to handle each element of the array. + */ +bool_t +xdr_array( + XDR *xdrs, + void **addrp, /* array pointer */ + uint_t *sizep, /* number of elements */ + uint_t maxsize, /* max numberof elements */ + uint_t elsize, /* size in bytes of each element */ + xdrproc_t elproc) /* xdr routine to handle each element */ +{ + uint_t i; + char * target = *addrp; + uint_t c; /* the actual element count */ + bool_t stat = TRUE; + uint_t nodesize; + + /* like strings, arrays are really counted arrays */ + if (! xdr_u_int(xdrs, sizep)) { + return (FALSE); + } + c = *sizep; + if ((c > maxsize) && (xdrs->x_op != XDR_FREE)) { + return (FALSE); + } + nodesize = c * elsize; + + /* + * if we are deserializing, we may need to allocate an array. + * We also save time by checking for a null array if we are freeing. + */ + if (target == NULL) + switch (xdrs->x_op) { + case XDR_DECODE: + if (c == 0) + return (TRUE); + *addrp = target = mem_alloc(nodesize); + if (target == NULL) { + (void) fprintf(stderr, + "xdr_array: out of memory\n"); + return (FALSE); + } + memset(target, '\0', nodesize); + break; + + case XDR_FREE: + return (TRUE); + } + + /* + * now we xdr each element of array + */ + for (i = 0; (i < c) && stat; i++) { + stat = (*elproc)(xdrs, target, LASTUNSIGNED); + target += elsize; + } + + /* + * the array may need freeing + */ + if (xdrs->x_op == XDR_FREE) { + mem_free(*addrp, nodesize); + *addrp = NULL; + } + return (stat); +} + +/* + * xdr_vector(): + * + * XDR a fixed length array. Unlike variable-length arrays, + * the storage of fixed length arrays is static and unfreeable. + * > basep: base of the array + * > size: size of the array + * > elemsize: size of each element + * > xdr_elem: routine to XDR each element + */ +bool_t +xdr_vector( + XDR *xdrs, + char *basep, + uint_t nelem, + uint_t elemsize, + xdrproc_t xdr_elem) +{ + uint_t i; + char *elptr; + + elptr = basep; + for (i = 0; i < nelem; i++) { + if (! (*xdr_elem)(xdrs, elptr, LASTUNSIGNED)) { + return(FALSE); + } + elptr += elemsize; + } + return(TRUE); +} + diff --git a/TBBT/trace_play/rpc/xdr_float.c b/TBBT/trace_play/rpc/xdr_float.c new file mode 100755 index 0000000..9048665 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_float.c @@ -0,0 +1,288 @@ +#ifndef lint +static char sfs_xdr_float_id[] = "@(#)xdr_float.c 2.1 97/10/23"; +#endif +/* @(#)xdr_float.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_float.c 1.12 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * xdr_float.c, Generic XDR routines impelmentation. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * These are the "floating point" xdr routines used to (de)serialize + * most common data items. See xdr.h for more info on the interface to + * xdr. + */ + +#include <stdio.h> + +#include "rpc/types.h" +#include "rpc/xdr.h" + +/* + * NB: Not portable. + * This routine works on Suns (Sky / 68000's) and Vaxen. + */ + +#ifdef vax + +/* What IEEE single precision floating point looks like on a Vax */ +struct ieee_single { + unsigned int mantissa: 23; + unsigned int exp : 8; + unsigned int sign : 1; +}; + +/* Vax single precision floating point */ +struct vax_single { + unsigned int mantissa1 : 7; + unsigned int exp : 8; + unsigned int sign : 1; + unsigned int mantissa2 : 16; +}; + +#define VAX_SNG_BIAS 0x81 +#define IEEE_SNG_BIAS 0x7f + +static struct sgl_limits { + struct vax_single s; + struct ieee_single ieee; +} sgl_limits[2] = { + {{ 0x7f, 0xff, 0x0, 0xffff }, /* Max Vax */ + { 0x0, 0xff, 0x0 }}, /* Max IEEE */ + {{ 0x0, 0x0, 0x0, 0x0 }, /* Min Vax */ + { 0x0, 0x0, 0x0 }} /* Min IEEE */ +}; +#endif /* vax */ + +bool_t +xdr_float(xdrs, fp) + register XDR *xdrs; + register float *fp; +{ +#if defined(vax) + struct ieee_single is; + struct vax_single vs, *vsp; + struct sgl_limits *lim; + int i; +#endif + switch (xdrs->x_op) { + + case XDR_ENCODE: +#if !defined(vax) + return (XDR_PUTLONG(xdrs, (int32_t *)fp)); +#else + vs = *((struct vax_single *)fp); + for (i = 0, lim = sgl_limits; + i < sizeof(sgl_limits)/sizeof(struct sgl_limits); + i++, lim++) { + if ((vs.mantissa2 == lim->s.mantissa2) && + (vs.exp == lim->s.exp) && + (vs.mantissa1 == lim->s.mantissa1)) { + is = lim->ieee; + goto shipit; + } + } + is.exp = vs.exp - VAX_SNG_BIAS + IEEE_SNG_BIAS; + is.mantissa = (vs.mantissa1 << 16) | vs.mantissa2; + shipit: + is.sign = vs.sign; + return (XDR_PUTLONG(xdrs, (int32_t *)&is)); +#endif + + case XDR_DECODE: +#if !defined(vax) + return (XDR_GETLONG(xdrs, (int32_t *)fp)); +#else + vsp = (struct vax_single *)fp; + if (!XDR_GETLONG(xdrs, (int32_t *)&is)) + return (FALSE); + for (i = 0, lim = sgl_limits; + i < sizeof(sgl_limits)/sizeof(struct sgl_limits); + i++, lim++) { + if ((is.exp == lim->ieee.exp) && + (is.mantissa == lim->ieee.mantissa)) { + *vsp = lim->s; + goto doneit; + } + } + vsp->exp = is.exp - IEEE_SNG_BIAS + VAX_SNG_BIAS; + vsp->mantissa2 = is.mantissa; + vsp->mantissa1 = (is.mantissa >> 16); + doneit: + vsp->sign = is.sign; + return (TRUE); +#endif + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + +/* + * This routine works on Suns (Sky / 68000's) and Vaxen. + */ + +#ifdef vax +/* What IEEE double precision floating point looks like on a Vax */ +struct ieee_double { + unsigned int mantissa1 : 20; + unsigned int exp : 11; + unsigned int sign : 1; + unsigned int mantissa2 : 32; +}; + +/* Vax double precision floating point */ +struct vax_double { + unsigned int mantissa1 : 7; + unsigned int exp : 8; + unsigned int sign : 1; + unsigned int mantissa2 : 16; + unsigned int mantissa3 : 16; + unsigned int mantissa4 : 16; +}; + +#define VAX_DBL_BIAS 0x81 +#define IEEE_DBL_BIAS 0x3ff +#define MASK(nbits) ((1 << nbits) - 1) + +static struct dbl_limits { + struct vax_double d; + struct ieee_double ieee; +} dbl_limits[2] = { + {{ 0x7f, 0xff, 0x0, 0xffff, 0xffff, 0xffff }, /* Max Vax */ + { 0x0, 0x7ff, 0x0, 0x0 }}, /* Max IEEE */ + {{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* Min Vax */ + { 0x0, 0x0, 0x0, 0x0 }} /* Min IEEE */ +}; + +#endif /* vax */ + + +bool_t +xdr_double(xdrs, dp) + register XDR *xdrs; + double *dp; +{ + register int32_t *lp; +#if defined(vax) + struct ieee_double id; + struct vax_double vd; + register struct dbl_limits *lim; + int i; +#endif + + switch (xdrs->x_op) { + + case XDR_ENCODE: +#if !defined(vax) + lp = (int32_t *)dp; +#else + vd = *((struct vax_double *)dp); + for (i = 0, lim = dbl_limits; + i < sizeof(dbl_limits)/sizeof(struct dbl_limits); + i++, lim++) { + if ((vd.mantissa4 == lim->d.mantissa4) && + (vd.mantissa3 == lim->d.mantissa3) && + (vd.mantissa2 == lim->d.mantissa2) && + (vd.mantissa1 == lim->d.mantissa1) && + (vd.exp == lim->d.exp)) { + id = lim->ieee; + goto shipit; + } + } + id.exp = vd.exp - VAX_DBL_BIAS + IEEE_DBL_BIAS; + id.mantissa1 = (vd.mantissa1 << 13) | (vd.mantissa2 >> 3); + id.mantissa2 = ((vd.mantissa2 & MASK(3)) << 29) | + (vd.mantissa3 << 13) | + ((vd.mantissa4 >> 3) & MASK(13)); + shipit: + id.sign = vd.sign; + lp = (int32_t *)&id; +#endif + return (XDR_PUTLONG(xdrs, lp++) && XDR_PUTLONG(xdrs, lp)); + + case XDR_DECODE: +#if !defined(vax) + lp = (int32_t *)dp; + return (XDR_GETLONG(xdrs, lp++) && XDR_GETLONG(xdrs, lp)); +#else + lp = (int32_t *)&id; + if (!XDR_GETLONG(xdrs, lp++) || !XDR_GETLONG(xdrs, lp)) + return (FALSE); + for (i = 0, lim = dbl_limits; + i < sizeof(dbl_limits)/sizeof(struct dbl_limits); + i++, lim++) { + if ((id.mantissa2 == lim->ieee.mantissa2) && + (id.mantissa1 == lim->ieee.mantissa1) && + (id.exp == lim->ieee.exp)) { + vd = lim->d; + goto doneit; + } + } + vd.exp = id.exp - IEEE_DBL_BIAS + VAX_DBL_BIAS; + vd.mantissa1 = (id.mantissa1 >> 13); + vd.mantissa2 = ((id.mantissa1 & MASK(13)) << 3) | + (id.mantissa2 >> 29); + vd.mantissa3 = (id.mantissa2 >> 13); + vd.mantissa4 = (id.mantissa2 << 3); + doneit: + vd.sign = id.sign; + *dp = *((double *)&vd); + return (TRUE); +#endif + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} diff --git a/TBBT/trace_play/rpc/xdr_mem.c b/TBBT/trace_play/rpc/xdr_mem.c new file mode 100755 index 0000000..6b7fe4d --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_mem.c @@ -0,0 +1,205 @@ +#ifndef lint +static char sfs_xdr_mem_id[] = "@(#)xdr_mem.c 2.1 97/10/23"; +#endif +/* @(#)xdr_mem.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_mem.c 1.19 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * xdr_mem.h, XDR implementation using memory buffers. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * If you have some data to be interpreted as external data representation + * or to be converted to external data representation in a memory buffer, + * then this is the package for you. + * + */ + +#include <string.h> +#include "rpc/types.h" +#include "rpc/xdr.h" +#include "rpc/osdep.h" + +static bool_t xdrmem_getlong(XDR *, int32_t *); +static bool_t xdrmem_putlong(XDR *, int32_t *); +static bool_t xdrmem_getbytes(XDR *, void *, uint_t); +static bool_t xdrmem_putbytes(XDR *, void *, uint_t); +static uint_t xdrmem_getpos(XDR *); +static bool_t xdrmem_setpos(XDR *, uint_t); +static int32_t *xdrmem_inline(XDR *, uint_t); +static void xdrmem_destroy(XDR *); + +static struct xdr_ops xdrmem_ops = { + xdrmem_getlong, + xdrmem_putlong, + xdrmem_getbytes, + xdrmem_putbytes, + xdrmem_getpos, + xdrmem_setpos, + xdrmem_inline, + xdrmem_destroy +}; + +/* + * The procedure xdrmem_create initializes a stream descriptor for a + * memory buffer. + */ +void +xdrmem_create( + XDR *xdrs, + void *addr, + uint_t size, + enum xdr_op op) +{ + + xdrs->x_op = op; + xdrs->x_ops = &xdrmem_ops; + xdrs->x_private = xdrs->x_base = addr; + xdrs->x_handy = size; +} + +/* ARGSUSED */ +static void +xdrmem_destroy(XDR *xdrs) +{ +} + +static bool_t +xdrmem_getlong( + XDR *xdrs, + int32_t *lp) +{ + + if ((xdrs->x_handy -= sizeof(int32_t)) < 0) + return (FALSE); + *lp = (int32_t)ntohl((uint32_t)(*((int32_t *)(xdrs->x_private)))); + xdrs->x_private += sizeof(int32_t); + return (TRUE); +} + +static bool_t +xdrmem_putlong( + XDR *xdrs, + int32_t *lp) +{ + + if ((xdrs->x_handy -= sizeof(int32_t)) < 0) + return (FALSE); + *(int32_t *)xdrs->x_private = (int32_t)htonl((uint32_t)(*lp)); + xdrs->x_private += sizeof(int32_t); + return (TRUE); +} + +static bool_t +xdrmem_getbytes( + XDR *xdrs, + void *addr, + uint_t len) +{ + + if ((xdrs->x_handy -= len) < 0) + return (FALSE); + memmove(addr, xdrs->x_private, len); + xdrs->x_private += len; + return (TRUE); +} + +static bool_t +xdrmem_putbytes( + XDR *xdrs, + void *addr, + uint_t len) +{ + + if ((xdrs->x_handy -= len) < 0) + return (FALSE); + memmove(xdrs->x_private, addr, len); + xdrs->x_private += len; + return (TRUE); +} + +static uint_t +xdrmem_getpos( + XDR *xdrs) +{ + + return ((uint_t)xdrs->x_private - (uint_t)xdrs->x_base); +} + +static bool_t +xdrmem_setpos( + XDR *xdrs, + uint_t pos) +{ + char * newaddr = xdrs->x_base + pos; + char * lastaddr = xdrs->x_private + xdrs->x_handy; + + if ((int32_t)newaddr > (int32_t)lastaddr) + return (FALSE); + xdrs->x_private = newaddr; + xdrs->x_handy = (int)lastaddr - (int)newaddr; + return (TRUE); +} + +static int32_t * +xdrmem_inline( + XDR *xdrs, + uint_t len) +{ + int32_t *buf = 0; + + if (xdrs->x_handy >= len) { + xdrs->x_handy -= len; + buf = (int32_t *) xdrs->x_private; + xdrs->x_private += len; + } + return (buf); +} diff --git a/TBBT/trace_play/rpc/xdr_rec.c b/TBBT/trace_play/rpc/xdr_rec.c new file mode 100755 index 0000000..0047c44 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_rec.c @@ -0,0 +1,608 @@ +#ifndef lint +static char sfs_xdr_rec_id[] = "@(#)xdr_rec.c 2.1 97/10/23"; +#endif +/* @(#)xdr_rec.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking" + * layer above tcp (for rpc's use). + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * These routines interface XDRSTREAMS to a tcp/ip connection. + * There is a record marking layer between the xdr stream + * and the tcp transport level. A record is composed on one or more + * record fragments. A record fragment is a thirty-two bit header followed + * by n bytes of data, where n is contained in the header. The header + * is represented as a htonl(uint32_t). Thegh order bit encodes + * whether or not the fragment is the last fragment of the record + * (1 => fragment is last, 0 => more fragments to follow. + * The other 31 bits encode the byte length of the fragment. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "rpc/types.h" +#include "rpc/xdr.h" +#include "rpc/osdep.h" + +static bool_t xdrrec_getlong(XDR *, int32_t *); +static bool_t xdrrec_putlong(XDR *, int32_t *); +static bool_t xdrrec_getbytes(XDR *, void *, uint_t); +static bool_t xdrrec_putbytes(XDR *, void *, uint_t); +static uint_t xdrrec_getpos(XDR *); +static bool_t xdrrec_setpos(XDR *, uint_t); +static int32_t *xdrrec_inline(XDR *, uint_t); +static void xdrrec_destroy(XDR *); + +static struct xdr_ops xdrrec_ops = { + xdrrec_getlong, + xdrrec_putlong, + xdrrec_getbytes, + xdrrec_putbytes, + xdrrec_getpos, + xdrrec_setpos, + xdrrec_inline, + xdrrec_destroy +}; + +/* + * A record is composed of one or more record fragments. + * A record fragment is a two-byte header followed by zero to + * 2**32-1 bytes. The header is treated as a long unsigned and is + * encode/decoded to the network via htonl/ntohl. The low order 31 bits + * are a byte count of the fragment. The highest order bit is a boolean: + * 1 => this fragment is the last fragment of the record, + * 0 => this fragment is followed by more fragment(s). + * + * The fragment/record machinery is not general; it is constructed to + * meet the needs of xdr and rpc based on tcp. + */ + +#define LAST_FRAG (0x80000000) + +typedef struct rec_strm { + void * tcp_handle; + void * the_buffer; + /* + * out-goung bits + */ + int (*writeit)(); + char *out_base; /* output buffer (points to frag header) */ + char *out_finger; /* next output position */ + char *out_boundry; /* data cannot up to this address */ + uint32_t *frag_header; /* beginning of curren fragment */ + bool_t frag_sent; /* true if buffer sent in middle of record */ + /* + * in-coming bits + */ + int (*readit)(); + uint32_t in_size; /* fixed size of the input buffer */ + char *in_base; + char *in_finger; /* location of next byte to be had */ + char *in_boundry; /* can read up to this location */ + int32_t fbtbc; /* fragment bytes to be consumed */ + bool_t last_frag; + uint_t sendsize; + uint_t recvsize; +} RECSTREAM; + + +static uint_t fix_buf_size(uint_t); +static bool_t flush_out(RECSTREAM *, bool_t); +static bool_t set_input_fragment(RECSTREAM *); +static bool_t skip_input_bytes(RECSTREAM *, int32_t); +static bool_t get_input_bytes(RECSTREAM *, char *, int); + +/* + * Create an xdr handle for xdrrec + * xdrrec_create fills in xdrs. Sendsize and recvsize are + * send and recv buffer sizes (0 => use default). + * tcp_handle is an opaque handle that is passed as the first parameter to + * the procedures readit and writeit. Readit and writeit are read and + * write respectively. They are like the system + * calls expect that they take an opaque handle rather than an fd. + */ +void +xdrrec_create( + XDR *xdrs, + uint_t sendsize, + uint_t recvsize, + void * tcp_handle, + int (*readit)(), /* like read, but pass it a tcp_handle, not sock */ + int (*writeit)()) /* like write, but pass it a tcp_handle, not sock */ +{ + RECSTREAM *rstrm = + (RECSTREAM *)mem_alloc(sizeof(RECSTREAM)); + + if (rstrm == NULL) { + (void)fprintf(stderr, "xdrrec_create: out of memory\n"); + /* + * This is bad. Should rework xdrrec_create to + * return a handle, and in this case return NULL + */ + return; + } + /* + * adjust sizes and allocate buffer quad byte aligned + */ + rstrm->sendsize = sendsize = fix_buf_size(sendsize); + rstrm->recvsize = recvsize = fix_buf_size(recvsize); + rstrm->the_buffer = mem_alloc(sendsize + recvsize + BYTES_PER_XDR_UNIT); + if (rstrm->the_buffer == NULL) { + (void)fprintf(stderr, "xdrrec_create: out of memory\n"); + return; + } + for (rstrm->out_base = rstrm->the_buffer; + (uint_t)rstrm->out_base % BYTES_PER_XDR_UNIT != 0; + rstrm->out_base++); + rstrm->in_base = rstrm->out_base + sendsize; + /* + * now the rest ... + */ + xdrs->x_ops = &xdrrec_ops; + xdrs->x_private = (void *)rstrm; + rstrm->tcp_handle = tcp_handle; + rstrm->readit = readit; + rstrm->writeit = writeit; + rstrm->out_finger = rstrm->out_boundry = rstrm->out_base; + rstrm->frag_header = (uint32_t *)rstrm->out_base; + rstrm->out_finger += sizeof(uint32_t); + rstrm->out_boundry += sendsize; + rstrm->frag_sent = FALSE; + rstrm->in_size = recvsize; + rstrm->in_boundry = rstrm->in_base; + rstrm->in_finger = (rstrm->in_boundry += recvsize); + rstrm->fbtbc = 0; + rstrm->last_frag = TRUE; +} + + +/* + * The reoutines defined below are the xdr ops which will go into the + * xdr handle filled in by xdrrec_create. + */ + +static bool_t +xdrrec_getlong( + XDR *xdrs, + int32_t *lp) +{ + RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + int32_t *buflp = (int32_t *)(rstrm->in_finger); + int32_t mylong; + + /* first try the inline, fast case */ + if ((rstrm->fbtbc >= sizeof(int32_t)) && + (((int)rstrm->in_boundry - (int)buflp) >= sizeof(int32_t))) { + *lp = (int32_t)ntohl((uint32_t)(*buflp)); + rstrm->fbtbc -= sizeof(int32_t); + rstrm->in_finger += sizeof(int32_t); + } else { + if (! xdrrec_getbytes(xdrs, (void *)&mylong, sizeof(int32_t))) + return (FALSE); + *lp = (int32_t)ntohl((uint32_t)mylong); + } + return (TRUE); +} + +static bool_t +xdrrec_putlong( + XDR *xdrs, + int32_t *lp) +{ + RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + int32_t *dest_lp = ((int32_t *)(rstrm->out_finger)); + + if ((rstrm->out_finger += sizeof(int32_t)) > rstrm->out_boundry) { + /* + * this case should almost never happen so the code is + * inefficient + */ + rstrm->out_finger -= sizeof(int32_t); + rstrm->frag_sent = TRUE; + if (! flush_out(rstrm, FALSE)) + return (FALSE); + dest_lp = ((int32_t *)(rstrm->out_finger)); + rstrm->out_finger += sizeof(int32_t); + } + *dest_lp = (int32_t)htonl((uint32_t)(*lp)); + return (TRUE); +} + +static bool_t /* must manage buffers, fragments, and records */ +xdrrec_getbytes( + XDR *xdrs, + void *baddr, + uint_t len) +{ + char *addr = baddr; + RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + int current; + + while (len > 0) { + current = rstrm->fbtbc; + if (current == 0) { + if (rstrm->last_frag) + return (FALSE); + if (! set_input_fragment(rstrm)) + return (FALSE); + continue; + } + current = (len < current) ? len : current; + if (! get_input_bytes(rstrm, addr, current)) + return (FALSE); + addr += current; + rstrm->fbtbc -= current; + len -= current; + } + return (TRUE); +} + +static bool_t +xdrrec_putbytes( + XDR *xdrs, + void *baddr, + uint_t len) +{ + char *addr = baddr; + RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + int current; + + while (len > 0) { + current = (uint_t)rstrm->out_boundry - (uint_t)rstrm->out_finger; + current = (len < current) ? len : current; + memmove(rstrm->out_finger, addr, current); + rstrm->out_finger += current; + addr += current; + len -= current; + if (rstrm->out_finger == rstrm->out_boundry) { + rstrm->frag_sent = TRUE; + if (! flush_out(rstrm, FALSE)) + return (FALSE); + } + } + return (TRUE); +} + +static uint_t +xdrrec_getpos( + XDR *xdrs) +{ + RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; + int32_t pos; + + pos = lseek((int)rstrm->tcp_handle, (int32_t) 0, 1); + if (pos != -1) + switch (xdrs->x_op) { + + case XDR_ENCODE: + pos += rstrm->out_finger - rstrm->out_base; + break; + + case XDR_DECODE: + pos -= rstrm->in_boundry - rstrm->in_finger; + break; + + default: + pos = (uint_t) -1; + break; + } + return ((uint_t) pos); +} + +static bool_t +xdrrec_setpos( + XDR *xdrs, + uint_t pos) +{ + RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; + uint_t currpos = xdrrec_getpos(xdrs); + int delta = currpos - pos; + char *newpos; + + if ((int)currpos != -1) + switch (xdrs->x_op) { + + case XDR_ENCODE: + newpos = rstrm->out_finger - delta; + if ((newpos > (char *)(rstrm->frag_header)) && + (newpos < rstrm->out_boundry)) { + rstrm->out_finger = newpos; + return (TRUE); + } + break; + + case XDR_DECODE: + newpos = rstrm->in_finger - delta; + if ((delta < (int)(rstrm->fbtbc)) && + (newpos <= rstrm->in_boundry) && + (newpos >= rstrm->in_base)) { + rstrm->in_finger = newpos; + rstrm->fbtbc -= delta; + return (TRUE); + } + break; + } + return (FALSE); +} + +static int32_t * +xdrrec_inline( + XDR *xdrs, + uint_t len) +{ + RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; + int32_t * buf = NULL; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + if ((rstrm->out_finger + len) <= rstrm->out_boundry) { + buf = (int32_t *) rstrm->out_finger; + rstrm->out_finger += len; + } + break; + + case XDR_DECODE: + if ((len <= rstrm->fbtbc) && + ((rstrm->in_finger + len) <= rstrm->in_boundry)) { + buf = (int32_t *) rstrm->in_finger; + rstrm->fbtbc -= len; + rstrm->in_finger += len; + } + break; + } + return (buf); +} + +static void +xdrrec_destroy( + XDR *xdrs) +{ + RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; + + mem_free(rstrm->the_buffer, + rstrm->sendsize + rstrm->recvsize + BYTES_PER_XDR_UNIT); + mem_free((void *)rstrm, sizeof(RECSTREAM)); +} + + +/* + * Exported routines to manage xdr records + */ + +/* + * Before reading (deserializing from the stream, one should always call + * this procedure to guarantee proper record alignment. + */ +bool_t +xdrrec_skiprecord( + XDR *xdrs) +{ + RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + + while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) { + if (! skip_input_bytes(rstrm, rstrm->fbtbc)) + return (FALSE); + rstrm->fbtbc = 0; + if ((! rstrm->last_frag) && (! set_input_fragment(rstrm))) + return (FALSE); + } + rstrm->last_frag = FALSE; + return (TRUE); +} + +/* + * Look ahead fuction. + * Returns TRUE iff there is no more input in the buffer + * after consuming the rest of the current record. + */ +bool_t +xdrrec_eof( + XDR *xdrs) +{ + RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + + while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) { + if (! skip_input_bytes(rstrm, rstrm->fbtbc)) + return (TRUE); + rstrm->fbtbc = 0; + if ((! rstrm->last_frag) && (! set_input_fragment(rstrm))) + return (TRUE); + } + if (rstrm->in_finger == rstrm->in_boundry) + return (TRUE); + return (FALSE); +} + +/* + * The client must tell the package when an end-of-record has occurred. + * The second paraemters tells whether the record should be flushed to the + * (output) tcp stream. (This let's the package support batched or + * pipelined procedure calls.) TRUE => immmediate flush to tcp connection. + */ +bool_t +xdrrec_endofrecord( + XDR *xdrs, + bool_t sendnow) +{ + RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + uint32_t len; /* fragment length */ + + if (sendnow || rstrm->frag_sent || + ((uint32_t)rstrm->out_finger + sizeof(uint32_t) >= + (uint32_t)rstrm->out_boundry)) { + rstrm->frag_sent = FALSE; + return (flush_out(rstrm, TRUE)); + } + len = (uint32_t)(rstrm->out_finger) - (uint32_t)(rstrm->frag_header) - + sizeof(uint32_t); + *(rstrm->frag_header) = htonl((uint32_t)len | (uint32_t)LAST_FRAG); + rstrm->frag_header = (uint32_t *)rstrm->out_finger; + rstrm->out_finger += sizeof(uint32_t); + return (TRUE); +} + + +/* + * Internal useful routines + */ +static bool_t +flush_out( + RECSTREAM *rstrm, + bool_t eor) +{ + uint32_t eormask = (eor == TRUE) ? LAST_FRAG : 0; + uint32_t len = (uint32_t)(rstrm->out_finger) - + (uint32_t)(rstrm->frag_header) - sizeof(uint32_t); + + *(rstrm->frag_header) = htonl(len | eormask); + len = (uint32_t)(rstrm->out_finger) - (uint32_t)(rstrm->out_base); + if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len) + != (int)len) + return (FALSE); + rstrm->frag_header = (uint32_t *)rstrm->out_base; + rstrm->out_finger = (char *)rstrm->out_base + sizeof(uint32_t); + return (TRUE); +} + +static bool_t /* knows nothing about records! Only about input buffers */ +fill_input_buf( + RECSTREAM *rstrm) +{ + char *where; + uint_t i; + int len; + + where = rstrm->in_base; + i = (uint_t)rstrm->in_boundry % BYTES_PER_XDR_UNIT; + where += i; + len = rstrm->in_size - i; + if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1) + return (FALSE); + rstrm->in_finger = where; + where += len; + rstrm->in_boundry = where; + return (TRUE); +} + +static bool_t /* knows nothing about records! Only about input buffers */ +get_input_bytes( + RECSTREAM *rstrm, + char *addr, + int len) +{ + int current; + + while (len > 0) { + current = (int)rstrm->in_boundry - (int)rstrm->in_finger; + if (current == 0) { + if (! fill_input_buf(rstrm)) + return (FALSE); + continue; + } + current = (len < current) ? len : current; + memmove(addr, rstrm->in_finger, current); + rstrm->in_finger += current; + addr += current; + len -= current; + } + return (TRUE); +} + +static bool_t /* next two bytes of the input stream are treated as a header */ +set_input_fragment( + RECSTREAM *rstrm) +{ + uint32_t header; + + if (! get_input_bytes(rstrm, (void *)&header, sizeof(header))) + return (FALSE); + header = (int32_t)ntohl(header); + rstrm->last_frag = ((header & (uint32_t)LAST_FRAG) == 0) ? FALSE : TRUE; + rstrm->fbtbc = header & (~LAST_FRAG); + return (TRUE); +} + +static bool_t /* consumes input bytes; knows nothing about records! */ +skip_input_bytes( + RECSTREAM *rstrm, + int32_t cnt) +{ + int current; + + while (cnt > 0) { + current = (int)rstrm->in_boundry - (int)rstrm->in_finger; + if (current == 0) { + if (! fill_input_buf(rstrm)) + return (FALSE); + continue; + } + current = (cnt < current) ? cnt : current; + rstrm->in_finger += current; + cnt -= current; + } + return (TRUE); +} + +static uint_t +fix_buf_size( + uint_t s) +{ + + if (s < 100) + s = 4000; + return (RNDUP(s)); +} diff --git a/TBBT/trace_play/rpc/xdr_reference.c b/TBBT/trace_play/rpc/xdr_reference.c new file mode 100755 index 0000000..56fc99c --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_reference.c @@ -0,0 +1,156 @@ +#ifndef lint +static char sfs_xdr_reference_id[] = "@(#)xdr_reference.c 2.1 97/10/23"; +#endif +/* @(#)xdr_reference.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_reference.c 1.11 87/08/11 SMI"; +#endif + +/* + * xdr_reference.c, Generic XDR routines impelmentation. + * + * Copyright (C) 1987, Sun Microsystems, Inc. + * + * These are the "non-trivial" xdr primitives used to serialize and de-serialize + * "pointers". See xdr.h for more info on the interface to xdr. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "rpc/types.h" +#include "rpc/xdr.h" + +#define LASTUNSIGNED ((uint_t)0-1) + +/* + * XDR an indirect pointer + * xdr_reference is for recursively translating a structure that is + * referenced by a pointer inside the structure that is currently being + * translated. pp references a pointer to storage. If *pp is null + * the necessary storage is allocated. + * size is the sizeof the referneced structure. + * proc is the routine to handle the referenced structure. + */ +bool_t +xdr_reference( + XDR *xdrs, + void **pp, /* the pointer to work on */ + uint_t size, /* size of the object pointed to */ + xdrproc_t proc) /* xdr routine to handle the object */ +{ + void *loc = *pp; + bool_t stat; + + if (loc == NULL) + switch (xdrs->x_op) { + case XDR_FREE: + return (TRUE); + + case XDR_DECODE: + *pp = loc = (void *) mem_alloc(size); + if (loc == NULL) { + (void) fprintf(stderr, + "xdr_reference: out of memory\n"); + return (FALSE); + } + memset(loc, '\0', (int)size); + break; + } + + stat = (*proc)(xdrs, loc, LASTUNSIGNED); + + if (xdrs->x_op == XDR_FREE) { + mem_free(loc, size); + *pp = NULL; + } + return (stat); +} + + +/* + * xdr_pointer(): + * + * XDR a pointer to a possibly recursive data structure. This + * differs with xdr_reference in that it can serialize/deserialiaze + * trees correctly. + * + * What's sent is actually a union: + * + * union object_pointer switch (boolean b) { + * case TRUE: object_data data; + * case FALSE: void nothing; + * } + * + * > objpp: Pointer to the pointer to the object. + * > obj_size: size of the object. + * > xdr_obj: routine to XDR an object. + * + */ +bool_t +xdr_pointer( + XDR *xdrs, + void **objpp, + uint_t obj_size, + xdrproc_t xdr_obj) +{ + + bool_t more_data; + + more_data = (*objpp != NULL); + if (! xdr_bool(xdrs,&more_data)) { + return (FALSE); + } + if (! more_data) { + *objpp = NULL; + return (TRUE); + } + return (xdr_reference(xdrs,objpp,obj_size,xdr_obj)); +} diff --git a/TBBT/trace_play/rpc/xdr_stdio.c b/TBBT/trace_play/rpc/xdr_stdio.c new file mode 100755 index 0000000..8240089 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_stdio.c @@ -0,0 +1,210 @@ +#ifndef lint +static char sfs_xdr_stdio_id[] = "@(#)xdr_stdio.c 2.1 97/10/23"; +#endif +/* @(#)xdr_stdio.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_stdio.c 1.16 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * xdr_stdio.c, XDR implementation on standard i/o file. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * This set of routines implements a XDR on a stdio stream. + * XDR_ENCODE serializes onto the stream, XDR_DECODE de-serializes + * from the stream. + */ + +#include <stdio.h> +#include <sys/types.h> +#include "rpc/types.h" +#include "rpc/xdr.h" +#include "rpc/osdep.h" + +static bool_t xdrstdio_getlong(XDR *, int32_t *); +static bool_t xdrstdio_putlong(XDR *, int32_t *); +static bool_t xdrstdio_getbytes(XDR *, void *, uint_t); +static bool_t xdrstdio_putbytes(XDR *, void *, uint_t); +static uint_t xdrstdio_getpos(XDR *); +static bool_t xdrstdio_setpos(XDR *, uint_t); +static int32_t *xdrstdio_inline(XDR *, uint_t); +static void xdrstdio_destroy(XDR *); + +/* + * Ops vector for stdio type XDR + */ +static struct xdr_ops xdrstdio_ops = { + xdrstdio_getlong, /* deseraialize a long int */ + xdrstdio_putlong, /* seraialize a long int */ + xdrstdio_getbytes, /* deserialize counted bytes */ + xdrstdio_putbytes, /* serialize counted bytes */ + xdrstdio_getpos, /* get offset in the stream */ + xdrstdio_setpos, /* set offset in the stream */ + xdrstdio_inline, /* prime stream for inline macros */ + xdrstdio_destroy /* destroy stream */ +}; + +/* + * Initialize a stdio xdr stream. + * Sets the xdr stream handle xdrs for use on the stream file. + * Operation flag is set to op. + */ +void +xdrstdio_create( + XDR *xdrs, + FILE *file, + enum xdr_op op) +{ + + xdrs->x_op = op; + xdrs->x_ops = &xdrstdio_ops; + xdrs->x_private = (void *)file; + xdrs->x_handy = 0; + xdrs->x_base = 0; +} + +/* + * Destroy a stdio xdr stream. + * Cleans up the xdr stream handle xdrs previously set up by xdrstdio_create. + */ +static void +xdrstdio_destroy( + XDR *xdrs) +{ + (void)fflush((FILE *)xdrs->x_private); + /* xx should we close the file ?? */ +} + +static bool_t +xdrstdio_getlong( + XDR *xdrs, + int32_t *lp) +{ + + if (fread((void *)lp, sizeof(int32_t), 1, (FILE *)xdrs->x_private) != 1) + return (FALSE); + *lp = ntohl(*lp); + return (TRUE); +} + +static bool_t +xdrstdio_putlong( + XDR *xdrs, + int32_t *lp) +{ + + int32_t mycopy = htonl(*lp); + lp = &mycopy; + + if (fwrite((void *)lp, sizeof(int32_t), 1, (FILE *)xdrs->x_private) != 1) + return (FALSE); + return (TRUE); +} + +static bool_t +xdrstdio_getbytes( + XDR *xdrs, + void * addr, + uint_t len) +{ + + if ((len != 0) && (fread(addr, (int)len, 1, (FILE *)xdrs->x_private) != 1)) + return (FALSE); + return (TRUE); +} + +static bool_t +xdrstdio_putbytes( + XDR *xdrs, + void * addr, + uint_t len) +{ + + if ((len != 0) && (fwrite(addr, (int)len, 1, (FILE *)xdrs->x_private) != 1)) + return (FALSE); + return (TRUE); +} + +static uint_t +xdrstdio_getpos( + XDR *xdrs) +{ + + return ((uint_t) ftell((FILE *)xdrs->x_private)); +} + +static bool_t +xdrstdio_setpos( + XDR *xdrs, + uint_t pos) +{ + + return ((fseek((FILE *)xdrs->x_private, (int32_t)pos, 0) < 0) ? + FALSE : TRUE); +} + +/* ARGSUSED */ +static int32_t * +xdrstdio_inline( + XDR *xdrs, + uint_t len) +{ + + /* + * Must do some work to implement this: must insure + * enough data in the underlying stdio buffer, + * that the buffer is aligned so that we can indirect through a + * int32_t *, and stuff this pointer in xdrs->x_buf. Doing + * a fread or fwrite to a scratch buffer would defeat + * most of the gains to be had here and require storage + * management on this buffer, so we don't do this. + */ + return (NULL); +} diff --git a/TBBT/trace_play/sem.c b/TBBT/trace_play/sem.c new file mode 100644 index 0000000..3079f9b --- /dev/null +++ b/TBBT/trace_play/sem.c @@ -0,0 +1,149 @@ +#include <sys/sem.h> +#include <sys/types.h> +#include <errno.h> +#include <unistd.h> + +extern int errno; + +#define SEMPERM 0777 +#define TRUE 1 +#define FALSE 0 + +#define MAX_SEM 50 +struct { + int semid; + char name[256]; +} semname[MAX_SEM] = +{ +{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, +{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, +{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, +{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, +/* +{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, +{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, +{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, +{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, +{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, +*/ +{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""} +} ; + + +/* semaphore operations */ + +int initsem(key_t key, int value) /* create a semaphore */ +{ + int status = 0; + int semid; + + if ((semid = semget(key, 1, SEMPERM|IPC_CREAT|IPC_EXCL)) == -1) { + if (errno == EEXIST) { + printf("semget key %d already exist\n", key); + semid = semget(key, 1, 0); + } + } else { + status = semctl(semid, 0, SETVAL, value); + } + + if (semid == -1 || status == -1) { + printf("%d:\n", getpid()); + perror("initsem() failed"); + return (-1); + } else + return semid; +} + +//int getsem(key_t key, int setval) /* create a semaphore */ +int getsem(key_t key) /* create a semaphore */ +{ + int status = 0; + int semid; + + semid = semget(key, 1, 0); + + if (semid == -1 || status == -1) { + printf("%d:\n", getpid()); + perror("initsem() failed"); + return (-1); + } else + return semid; +} + +int waitsem(int semid) /* wait on a semaphore */ +{ + struct sembuf p_buf; +#ifdef NO_SEM + return; +#endif + + p_buf.sem_num = 0; + p_buf.sem_op = -1; + p_buf.sem_flg = SEM_UNDO; + + if (semop(semid, &p_buf, 1) == -1) { + printf("%d:", getpid()); + perror("waitsem() failed"); + exit(1); + } else + return (0); +} + +int postsem(int semid) /* post to a semaphore */ +{ + struct sembuf v_buf; + +#ifdef NO_SEM + return; +#endif + + + v_buf.sem_num = 0; + v_buf.sem_op = 1; + v_buf.sem_flg = SEM_UNDO; + + if (semop(semid, &v_buf, 1) == -1) { + printf("%d:", getpid()); + perror("postsem() failed"); + exit(1); + } else + return (0); +} + +void destsem(int semid) /* destroy a semaphore */ +{ + semctl(semid, 0, IPC_RMID, 0); +} + +int dest_and_init_sem (key_t key, int value, char * name) +{ + int semid; + int i; + semid = getsem (key); + if (semid!=-1) + destsem (semid); + semid = initsem (key, value); + printf ("%s semid %d for key %d value %d \n", name, semid, key, value); + + if (semid == 0) { + printf ("semid == 0\n"); + exit(0); + } + + for (i=0; i<MAX_SEM; i++) { + if (semname[i].semid == 0) { + semname[i].semid = semid; + strcpy(semname[i].name, name); + break; + } + } + + if (i==MAX_SEM) { + printf ("semname full\n"); + exit (-1); + } + + return semid; +} + + diff --git a/TBBT/trace_play/sfs.1 b/TBBT/trace_play/sfs.1 new file mode 100755 index 0000000..3c89a8a --- /dev/null +++ b/TBBT/trace_play/sfs.1 @@ -0,0 +1,842 @@ +.\" @(#)sfs.1 2.1 97/10/23 +.\" See DESCR.SFS file for restrictions +.\" +.\" create man page by running 'tbl sfs.1 | nroff -man > sfs.cat' +.\" +.TH SFS 1 "5 October 1994" +.SH NAME +SFS \- Network File System server benchmark program +.SH SYNOPSIS +.B sfs +[ +.B \-a access_pcnt +] [ +.B \-A append_pcnt +] +.br +[ +.B \-b blocksz_file +] [ +.B \-B block_size +] [ +.B \-d debug_level +] +.br +[ +.B \-D dir_cnt +] [ +.B \-f file_set_delta +] [ +.B \-F file_cnt +] [ +.B \-i +] [ +.B \-l load +] +.br +[ +.B \-m mix_file +] [ +.B \-M prime_client_hostname +] +.br +[ +.B \-N client_num +] [ +.B \-p processes +] [ +.B \-P +] [ +.B \-Q +] +.br +[ +.B \-R biod_reads +] [ +[ +.B \-S symlink_cnt +] [ +.B \-t time +] [ +.B \-T +] +.br +[ +.B \-V +] [ +.B \-W biod_writes +] [ +.B \-w warmup_time +] [ +.B \-z +] +.br +[ +.B server:/directory ... +] +.LP +.B sfs3 +[ +.B \-a access_pcnt +] [ +.B \-A append_pcnt +] +.br +[ +.B \-b blocksz_file +] [ +.B \-B block_size +] [ +.B \-d debug_level +] +.br +[ +.B \-D dir_cnt +] [ +.B \-f file_set_delta +] [ +.B \-F file_cnt +] [ +.B \-i +] [ +.B \-l load +] +.br +[ +.B \-m mix_file +] [ +.B \-M prime_client_hostname +] +.br +[ +.B \-N client_num +] [ +.B \-p processes +] [ +.B \-P +] [ +.B \-Q +] +.br +[ +.B \-R biod_reads +] [ +[ +.B \-S symlink_cnt +] [ +.B \-t time +] [ +.B \-T +] +.br +[ +.B \-V +] [ +.B \-W biod_writes +] [ +.B \-w warmup_time +] [ +.B \-z +] +.br +[ +.B server:/directory ... +] +.SH DESCRIPTION +Normally, +.B SFS +is executed via the +.B sfs_mgr +script which controls +.B SFS +execution on one or more +.SM NFS +client systems using a single user interface. +.P +.B SFS +is a Network File System server benchmark. +It runs on one or more +.SM NFS +client machines to generate an artificial load +consisting of a particular mix of +.SM NFS +operations on a particular set of files +residing on the server being tested. +The benchmark reports the average response time of the +.SM NFS +requests in milliseconds for the requested target load. +The response time is the dependent variable. +Load can be generated for a specific amount of time, +or for a specific number of +.SM NFS +calls. +.P +.B SFS +can also be used to characterize server performance. +Nearly all of the major factors that influence NFS performance +can be controlled using +.B SFS +command line arguments. Normally however, only the +.B \-l load +option used. +Other commonly used options include the +.B \-t time +, +.B \-p processes +, +.B \-m mix_file +options. +The remaining options are used to adjust specific parameters that affect +.SM NFS +performance. +If these options are used, the results produced will be non\-standard, +and thus, will not be comparable to tests run with other option settings. +.P +Normally, +.B SFS +is used as a benchmark program to measure the +.SM NFS +performance +of a particular server machine at different load levels. +In this case, +the preferred measurement is to make a series of benchmark runs, +varying the load factor for each run +in order to produce a performance/load curve. +Each point in the curve +represents the server's response time at a specific load. +.P +.B SFS +generates and transmits +.SM NFS +packets over the network to the server directly from the benchmark program, +without using the client's local NFS service. +This reduces the effect of the client NFS implementation on results, +and makes comparison of different servers more client-independent. +However, not all client implementation effects have been eliminated. +Since the benchmark does by-pass much of the client NFS implementation +(including operating system level data caching and write behind), +.B SFS +can only be used to measure server performance. +.P +Although +.B SFS +can be run between a single client and single server pair of machines, +a more accurate measure of server performance is obtained +by executing the benchmark on a number of clients simultaneously. +Not only does this present a more realistic model of +.SM NFS +usage, but also improves the chances that maximum performance +is limited by a lack of resources on the server machine, +rather than on a single client machine. +.P +In order to facilitate running +.B SFS +on a number of clients simultaneously, +an accompanying program called +.B sfs_mgr +provides a mechanism to run and synchronize the execution of multiple +instances of +.B SFS +spread across multiple clients and multiple networks +all generating load to the same +.SM NFS +server. +In general, +.B sfs_mgr +should be used to run both single- and multiple-client tests. +.P +.B SFS +employs a number of sub\-processes, each with its own test directory named +.B ./testdir<n> +(where <n> is a number from 0 to +.B processes +\- 1.) +A standard set of files is created in the test directory. +.P +If multiple +.B directories +are specified on the command line, the +.B SFS +processes will be evenly distributed among the directories. +This will produce a balanced load across each of the directories. +.P +The mix of +.SM NFS +operations generated can be set from a user defined mix file. +The format of the file consists of a simple format, the first +line contains the string "SFS MIXFILE VERSION 2" followed by +each line containing the operation name and the percentage (eg. +"write 12"). The total percentages must equal 100. +Operations with not specified will never be called by +.B SFS. +.SH SFS OPTIONS +.TP +.B \-a access_pcnt +The percentage of I/O files to access. +The access percent can be set from 0 to 100 percent. +The default is 10% access. +.TP +.B \-A append_pcnt +The percentage of write operations that append data to files +rather than overwriting existing data. +The append percent can be set from 0 to 100 percent. +The default is 70% append. +.TP +.B \-b blocksz_file +The name of a file containing a block transfer size distribution specification. +The format of the file and the default values are discussed below +under "Block Size Distribution". +.TP +.B \-B block_size +The maximum number of Kilo-bytes (KB) contained in any one data transfer block. +The valid range of values is from 1 to 8 KB. +The default maximum is 8 KB per transfer. +.TP +.B \-d debug_level +The debugging level, with higher values producing more debugging output. +When the benchmark is executed with debugging enabled, +the results are invalid. +The debug level must be greater than zero. +.TP +.B \-D dir_cnt +The number of files in each directory, the number of directories varies with +load load. +The default is 30 files per directory. +.TP +.B \-f file_set_delta +The percentage change in file set size. +The change percent can be set from 0 to 100 percent. +The default is 10% append. +.TP +.B \-F file_cnt +The number of files to be used for read and write operations. +The file count must be greater than 0. +The default is 100 files. +.TP +.B \-i +Run the test in interactive mode, +pausing for user input before starting the test. +The default setting is to run the test automatically. +.TP +.B \-l load +The number of NFS calls per second to generate from each client machine. +The load must be greater than the number of processes +(see the "\-p processes" option). +The default is to generate 60 calls/sec on each/client. +.TP +.B \-m mix_file +The name of a file containing the operation mix specification. +The format of the file and the default values are discussed below +under "Operation Mix". +.TP +.B \-p processes +The number of processes used to generate load on each client machine. +The number of processes must be greater than 0. +The default is 4 processes per client. +.TP +.B \-P +Populate the test directories and then exit; don't run the test. +This option can be used to examine the file set that the benchmark creates. +The default is to populate the test directories and then +execute the test automatically. +.TP +.B \-Q +Run NFS over TCP. +The default is UDP. +.TP +.B \-R biod_reads +The maximum number of asynchronus reads issued to simulate biod behavior. +The default is 2. +.TP +.B \-S symlink_cnt +The number of symbolic links to be used for symlink operations. +The symbolic link count must be greater than 0. +The default is 20 symlinks. +.TP +.B \-t time +The number of seconds to run the benchmark. +The number of seconds must be greater than 0. +The default is 300 seconds. +(Run times less than 300 seconds may produce invalid results.) +.TP +.B \-T op_num +Test a particular +.SM NFS +operation by executing it once. +The valid range of operations is from 1 to 23. +These values correspond to the procedure number +for each operation type as defined in the +.SM NFS +protocol specification. +The default is to run the benchmark, with no preliminary operation testing. +.TP +.B \-V +Validate the correctness of the server's +.SM NFS +implementation. +The option verifies the correctness of +.SM NFS +operations and data copies. +The verification takes place immediately before executing the test, +and does not affect the results reported by the test. +The default is not to verify server +.SM NFS +operation before beginning the test. +.TP +.B \-z +Generate raw data dump of the individual data points +for the test run. +.TP +.B \-w warmup +The number of seconds to generate load before starting the timed test run. +The goal is to reach a steady state and eliminate any variable startup costs, +before beginning the test. +The warm up time must be greater than or equal to 0 seconds. +The default is a 60 second warmup period. +.TP +.B \-W biod_writes +The maximum number of asynchronus writes issued to simulate biod behavior. +The default is 2. +.SH MULTI-CLIENT OPTIONS +.B SFS +also recognizes options that are only used when executing a multi-client test. +These options are generated by +.B sfs_mgr +and should not be specified by an end-user. +.TP +.B \-M prime_client_hostname +The hostname of the client machine where a multi-client test +is being controlled from. +This machine is designated as the "prime client". +The prime client machine may also be executing the +.B SFS +load-generating code. There is no default value. +.TP +.B \-N client_num +The client machine's unique identifier within a multi-client test, +assigned by the +.B sfs_mgr +script. +There is no default value. +.\".TP +.\".B \-R random_number_seed +.\"The value used by the client to index into a table of random number seeds. +.\"There is no default value. +.SH OPERATION MIX +The +.B SFS +default mix of operations for version 2 is: +.sp +.TS +center; +l l l l l l +n n n n n n +l l l l l l +n n n n n n +l l l l l l +n n n n n n. +null getattr setattr root lookup readlink +0% 26% 1% 0% 36% 7% +read wrcache write create remove rename +14% 7% 1% 1% 0% 0% +link symlink mkdir rmdir readdir fsstat +0% 0% 0% 0% 6% 1% +.TE +.LP +The +.B SFS +default mix of operations for version 3 is: +.sp +.TS +center; +l l l l l l +n n n n n n +l l l l l l +n n n n n n +l l l l l l +n n n n n n +l l l l l l +n n n n n n. +null getattr setattr lookup access readlink +0% 11% 1% 27% 7% 7% +read write create mkdir symlink mknod +18% 9% 1% 0% 0% 0% +remove rmdir rename link readdir readdirplus +1% 0% 0% 0% 2% 9% +fsstat fsinfo pathconf commit +1% 0% 0% 5% +.TE +.P +The format of the file consists of a simple format, the first +line contains the string "SFS MIXFILE VERSION 2" followed by +each line containing the operation name and the percentage (eg. +"write 12"). The total percentages must equal 100. +.SH FILE SET +The default basic file set used by +.B SFS +consists of regular files varying in size from 1KB to 1MB used for read and +write operations, +and 20 symbolic links used for symbolic link operations. +In addition to these, a small number of regular files are created +and used for non-I/O operations (eg, getattr), +and a small number of regular, directory, and symlink files may +be added to this total due to creation operations (eg, mkdir). +.P +While these values can be controlled with command line options, +some file set configurations may produce invalid results. +If there are not enough files of a particular type, +the specified mix of operations will not be achieved. +Too many files of a particular type may produce +thrashing effects on the server. +.SH BLOCK SIZE DISTRIBUTION +The block transfer size distribution is specified by a table of values. +The first column gives the percent of operations that will be included in a +any particular specific block transfer. +The second column gives the number of blocks units that will be transferred. +Normally the block unit size is 8KB. +The third column is a boolean specifying +whether a trailing fragment block should be transferred. +The fragment size for each transfer is a random multiple of 1 KB, +up to the block size - 1 KB. +Two tables are used, one for read operation and one for write operations. +The following tables give the default distributions +for the read and write operations. +.sp +.TS +center; +c s s s +c s s s +r r r r +r r r r +c s s s +n n n r. +Read - Default Block Transfer Size Distribution Table + + resulting transfer +percent block count fragment (8KB block size) + +0 0 0 0% 0 - 7 KB +85 1 0 85% 8 - 15 KB +8 2 1 8% 16 - 23 KB +4 4 1 4% 32 - 39 KB +2 8 1 2% 64 - 71 KB +1 16 1 1% 128 - 135 KB +.TE +.sp 2 +.TS +center; +c s s s +c s s s +r r r r +r r r r +c s s s +n n n r. +Write - Default Block Transfer Size Distribution Table + + resulting transfer +percent block count fragment (8KB block size) + +49 0 1 49% 0 - 7 KB +36 1 1 36% 8 - 15 KB +8 2 1 8% 16 - 23 KB +4 4 1 4% 32 - 39 KB +2 8 1 2% 64 - 71 KB +1 16 1 1% 128 - 135 KB +.TE +.P +A different distribution can be substituted by using the '-b' option. +The format for the block size distribution file consists of the first +three columns given above: percent, block count, and fragment. Read +and write distribution tables are identified by the keywords "Read" and +"Write". An example input file, using the default values, is given below: +.sp +.TS +l s s +n n n. +Read +0 0 0 +85 1 0 +8 2 1 +4 4 1 +2 8 1 +1 16 1 +.TE +.TS +l s s +n n n. +Write +49 0 1 +36 1 1 +8 2 1 +4 4 1 +2 8 1 +1 16 1 +.TE +.P +A second aspect of the benchmark controlled +by the block transfer size distribution table is the network data packet size. +The distribution tables define the relative proportion +of full block packets to fragment packets. +For instance, the default tables have been constructed +to produce a specific distribution of ethernet packet sizes +for i/o operations by controlling the amount of data in each packet. +The write packets produced consist of 50% 8-KB packets, and 50% 1-7 KB packets. +The read packets consist of 90% 8-KB packets, and 10% 1-7 KB packets. +These figures are determined by multiplying the percentage +of type of transfer times the number of blocks and fragments generated, +and adding the totals. +These computations are performed below +for the default block size distribution tables: +.sp +.TS +c c c c c +c c c c c +n n n n n +n n n n n +n n n n n +n n n n n +n n n n n +n n n n n +r r r l l +r r r n n +r r r l l. +Read total total +percent blocks fragments blocks fragments +0 0 0 0 0 +85 1 0 85 0 +8 2 1 16 8 +4 4 1 16 4 +2 8 1 16 2 +1 16 1 16 1 + ---- ----- + 149 15 + 90% 10% +.TE +.sp 3 +.TS +r r r r r +r r r r r +n n n n n +n n n n n +n n n n n +n n n n n +n n n n n +n n n n n +r r r l l +r r r n n +r r r l l. +Write total total +percent blocks fragments blocks fragments +49 0 1 0 49 +36 1 1 36 36 +8 2 1 16 8 +4 4 1 16 4 +2 8 1 16 2 +1 16 1 16 1 + ---- ------ + 100 100 + 50% 50% +.TE +.SH USING SFS +As with all benchmarks, +.B SFS +can only provide numbers that are useful +if the test runs are set up carefully. +Since it measures server performance, +the client (or clients) should not limit throughput. +The goal is to determine how well the server performs. +Most tests involving a single client will be limited by the client's +ability to generate load, not by the server's ability to handle more load. +Whether this is the case can be determined by running the benchmark +at successively greater load levels and finding the "knee of the curve" +at which load level the response time begins to increase rapidly. +Having found the knee of the curve, measurements of CPU utilization, +disk i/o rates, and network utilization levels should be made in order +to determine whether the performance bottleneck is due to the client +or server. +.P +For the results reported by +.B SFS +to be meaningful, the tests should be run on an isolated network, +and both the client and server should be as quiescent as possible during tests. +.P +High error rates on either the client or server +can also cause delays due to retransmissions +of lost or damaged packets. +.B netstat(8) +can be used to measure the network error +and collision rates on the client and server. +Also +.B SFS +reports the number of timed-out +.SM RPC +calls that occur during the test as bad calls. +If the number of bad calls is too great, +or the specified mix of operations is not achieved, +.B SFS +reports that the test run is "Invalid". +In this case, the reported results should be examined +to determine the cause of the errors. +.P +To best simulate the effects of +.SM NFS +clients on the server, the test +directories should be set up so that they are on at least two +disk partitions exported by the server. +.SM NFS +operations tend to randomize disk access, +so putting all of the +.B SFS +test directories on a single partition will not show realistic results. +.P +On all tests it is a good idea to run the tests repeatedly and compare results. +If the difference between runs is large, +the run time of the test should be increased +until the variance in milliseconds per call is acceptably small. +If increasing the length of time does not help, +there may be something wrong with the experimental setup. +.P +The numbers generated by +.B SFS +are only useful for comparison if the test setup on the client machine +is the same across different server configurations. +Changing the +.B processes +or +.B mix +parameters will produce numbers that can not be meaningfully compared. +Changing the number of generator processes may affect the measured response +time due to context switching or other delays on the client machine, +while changing the mix of +.SM NFS +operations will change the whole nature of the experiment. +Other changes to the client configuration may also effect the comparability +of results. +.P +To do a comparison of different server configurations, first set up the +client test directory and do +.B SFS +runs at different loads to be sure that the variability is +reasonably low. Second, run +.B SFS +at different loads of interest and +save the results. Third, change the server configuration (for example, +add more memory, replace a disk controller, etc.). Finally, run the same +.B SFS +loads again and compare the results. +.SH SEE ALSO +.P +The benchmark +.B README +file contains many pointers to other +files which provide information concerning SFS. +.SH ERROR MESSAGES +.TP 10 +.B "illegal load value" +The +.B load +argument following the +.B \-l +flag on the command line is not a positive number. +.TP +.B "illegal procs value" +The +.B processes +argument following the +.B \-p +flag on the command line is not a positive number. +.TP +.B "illegal time value" +The +.B time +argument following the +.B \-t +flag on the command line is not a positive number. +.TP +.B "bad mix file" +The +.B mix +file argument following the +.B \-m +flag on the command line could not be accessed. +.TP +.B "can't fork" +The parent couldn't fork the child processes. This usually results from +lack of resources, such as memory or swap space. +.TP +.PD 0 +.B "can't open log file" +.TP +.B "can't stat log" +.TP +.B "can't truncate log" +.TP +.B "can't write sync file" +.TP +.B "can't write log" +.TP +.B "can't read log" +.PD +A problem occurred during the creation, truncation, reading or writing of the +synchronization log file. The parent process creates the +log file in /tmp and uses it to synchronize and communicate with its children. +.TP +.PD 0 +.B "can't open test directory" +.TP +.B "can't create test directory" +.TP +.B "can't cd to test directory" +.TP +.B "wrong permissions on test dir" +.TP +.B "can't stat testfile" +.TP +.B "wrong permissions on testfile" +.TP +.B "can't create rename file" +.TP +.B "can't create subdir" +.PD +A child process had problems creating or checking the contents of its +test directory. This is usually due to a permission problem (for example +the test directory was created by a different user) or a full file system. +.TP +.PD 0 +.B "op failed: " +One of the internal pseudo\-NFS operations failed. The name of the operation, +e.g. read, write, lookup, will be printed along with an indication of the +nature of the failure. +.TP +.B "select failed" +The select system call returned an unexpected error. +.SH BUGS +.P +.B SFS +can not be run on non\-NFS file systems. +.P +.P +Shell scripts that execute +.B SFS +must catch and ignore SIGUSR1, SIGUSR2, and SIGALRM, (see signal(3)). +These signals are used to synchronize the test processes. +If one of these signals is not caught, +the shell that is running the script will be killed. +.SH FILES +.PD 0 +.TP +.B ./testdir* +per process test directory +.TP +.B /tmp/sfs_log%d +child process synchronization file +.TP +.B /tmp/sfs_CL%d +client log file +.TP +.B /tmp/sfs_PC_sync +prime client log file +.TP +.B /tmp/sfs_res +prime results log file +.PD diff --git a/TBBT/trace_play/sfs3.full_speed b/TBBT/trace_play/sfs3.full_speed new file mode 100755 index 0000000000000000000000000000000000000000..5237e99301bbf36d98a995526c3757787e6c530d GIT binary patch literal 1004158 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�Zs!T+H;Q)-je*Fg<0s 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�v2#Nx9mDLJwDoJvYgEIzN2k`s$RQ%T8* z#TQgka$@l%m6V)Vd|9Q5LQX8cqLPvmi@#7w$%(~Zs-)z^;;SktIkEUFm6V)Vd`%^g z6Awh62|00uk`qT%krPK$krPK$krPJ@krN+8Awo_Zq2$C7Rpi7GRpi7GRpi7GRpi7G zRpi7G1G+K*Iq?lt@Nx?VVd>wJ6Su{!X?sb@iKWwM4M$qvDm*w_dk@lhl9QZRTBnyT zB`21u$cd#Ya$;$SoX7%%oLEwFV(C2HR$j@8r3+M2a$;#c*F#))Tn1}P7pbJ=#L^`r zBu&YQrAtX-z%Iz)1z5L~E}J8gk`qgpA0ZO)By3vK8#pKuhoSPU$@_Svv*?owIq@fm z#*fmd%PNO-_4hs@;)I+yvO-S$BUNdS&$MP1ivrpZZlYZq6~J^PjbajwRB|FSG`plJ zh;60Cex;lR5VAMmAmqf7k`qf+<iyf(Oc!!u>5cOc<gDHp-HK5EJvi_Oile;T+XxCd zvE;~!r9QD3{VU|e(njRO9$ehZJ7SR7{|#~?pM0jQk`wK0whUDWxeGh%i4=L=i=ias zL_2Hv(u0r1cKfVz_(Ax@pGjVH432}>ZmeLrbGVQbt&$UM6*<vXkrVBqS`Xe+_;|D> z<V36FM7t`57>;R@6YbF*A}Kl1u2xCOiS`(kl$>bSXuc+IfPqnPFpKV|4zH_>$g->> zaj;g&iFP?#!OBQZv@29na-v;HKUK(y_E_fQN-Dc4G&dqAvfUl;BT>kS<^RLv#1=Ry zRaSChd6lmHJs~HSk9KznUa+|+2|2O6F*&h3L{2<RRFs@pUO|TmIkCKwqf*F;<<)GY zkQ2+tv05*?0Y@PxmX(}XUbae{B*3=v@vN3ZJ$N30hO&|q?eW8*Bjm*LSxlh!Nlq-E zJp=cKoW4?6kCrzgCw>E3StbfOv8?38@&#WPbtNa3FZ_<nOmbp*{f|XT2|2O6;T@5b zoLIg{HI<xLzPJwE$*C!at8DpeyNIOZ#PX#JMdH--GE8XNfMR^Mu$dttCzj8p51Q%e zW}ewP2MvYhr{Jf?=uqf4M*HK~%G!M?05MlF=HbDZW(E<>&S;EohtNdRb%tV{Lv<ki zCrqdL8>REKs(gp=KGV0YP9L(2UEIu6JxoG~b)q2^a-u<06I~#m4v5LpP$=wy*xbc9 z2syFr$cg2{#5?FvAt#oX`fLbCK*z1nJp;By{r^Nxd;$qVPVE0Da^j~*5OQMw|0FrF z2}_h~afcA~LSZjOXhzmgOCkKFha>0dT@ip&-zB`5Y*krVrmqSM%l)Ul$Z<i!5v ztWd~_{i|4om%R-~At&}LIkEq+%IvZ|24(+JwiT;g0{!||vqIi01TP|x>Q{1N|EX2x zomMd~^=DC1a$^5lRwU%a{&hS(L9HF<$cg<*PVE0G-G`2lx=v>m(tvHC*WRz>#Qybc zj*t`kH?Vv!yAa8*48uc+ekCXNU%-B=6mt=4VKLc95fE}>zmgOCuc(-P62@HrmCkG- zC-z^Zl9ChquU1LPiT&58gOr@u|4o%ZPJ9a$*nTA^_TNmigq+xa3mf2N+wq7~$cg<* zPVB$2Qa{Ow{Wo!B!cb}atu&U8T(ZX^myi=jDLHY}o|RmUX#J=va^k2ea^k4HXP{bi zUHHC%=01AyQgY&`87vnw2u=%XWxoM&HF~9Cl#&xi9q2}Z*KseB__8}1__yT5E%2mA z$cY0=P8^u8)p|ls99Y0-OlX#n69*Pv3T>{wNKPD(pGna`k`o6G<%29f19${>3@p+5 z3qnpDs3Ip043QIAoRAX-l$<!Q>?x_h6LR9f@?VRj<ivp$U%JXa#%rO0m8z-a#DSwU zP05J^tD14+s@`Gqn(oIz=PQPUoH%ee{nGp>WcctF{A-Lj02-q!5pH6|KAqak9Eg36 zv8-gMoT_>8w@7MbwSFZ>4TLOT5KU>q&HI3o69=xZbU(?712?Fo<ir6tpp=|AaFcpa z$%zA1<ivrSX_b%@Z$kw&u)ATjk`qT)krPL6&2)Aj$%&)?nVk3;5`~;NM#+g|s>q3B zx+|GUP8?H3P8?HE%Rx@u1EYW1LbQIYk`u=+)O!FgBsp>HL6t&CP8=)0$KwEc4x@i; z6*+P2A*u;-;sz8c<ixQ`P8?fBP8|C}rAU$!$5xRO#}1Jbe}Y1UoH*8z6USDQ6UWy0 zt{fpJj@^iyI0Mew0c|dfb>zgc)#Svn=lXh*V4q8z^<&R-VoJz~W2?!DV=wUa^^1}d z$6n~Oc#KXVCyuQqCyw3V?=Pvm{`2t?uDEDNuo{ttM9GQCWJ;JIz36lV2A9JiC*B8H z$cc%P6O$@(VzTo*qzgGQ+4~wP16Rr2@l;F5iOK&Z<izt~Q15<dUGl$8PW%CiSX96z zE>Ut~a@#tb#c-LB6O-Fj!bRmO%ws}MOq86Me24B3a$<7#qX;q&X)8$;IWegsCnoo5 zc}h-9s>q2+6*)1fA}1zQ<iw<koR~bQWhyx_sUjyP-=m8{f>e@+4@C<=ZVQP@Ngh#2 z$%)CMDuJB%C6t7mm?$|hsUjyPRpi9v8Le5#iOI9BZCc$=v`8f<CO=gP<iu`#!7w>d za$@p0-7n<C<h42^c-eh$<WJzICchMNVsZsXs(k*KyxNaMx|#RI$?G&#uJ>dlEFFP| z(}|K3lbIZ{LQYKn>p;yT<izB8_M*TIledqNgo2lS9DyfLWie55V)DmI9+DH2A#!5! z?#-1vA0Qy)#6-!7$)8*vE&L-k26tol!f67AO`_z)<YQJP<izB&PZ0F7TOe7;iHVXE zlfPElNOEHGIqP8?3rnDwN*p;c86qbpp}zx?;1wY!CL56xFG0MJ6BAFlXNm8-(uJIe z(pWu<&;1$!ZYD60IC5f=@m-5#pQNOkoS0OT6O$owVxbO?np24*CnnY8#AG9K;yCCD zIWckM#H7_%uVJI^5N&VwX%xwcJ^Mm;TH?rwNvF>`dbwL|Qt&4r3``l0LQYH^IWg%T zY%9r$$jl1Gl{X=fN*p;csU{~T8<7)(?HxHWapc5g<e*O`ccRQ{a$-_VPE4xFiKve@ z3OR9W6fES##E}z|_c?@xE0B!$8<C9ul)YAy&3zrGMK8Aq@j^~a962%BwxY{jr;`az zg6aiO6>?(Y$cf3071Kp^C#PEQa!)~3$cc#~CnmdAmAac7UrJ6)c6at*kK(@|r;rm9 zM@~%kuH;nP_ZhN%!VdVp2e+m+apc5gPQ@T0Cng(_6O*}qGt_bV$c|mfM&!gx5HIA! z#E}z|YI0&yO-@V>_H|LJ<iz9<Ur%5qCnk$sJwSoIhJuBhm^gA`a)i55dqPf3miTOf z?825D=~EqJNw!~3HYO+DfrOrDB34<6BPS*+d|Iu;^%(zMguPq>je{_WH6@Omm>fMg zT-)&^9htd>o1B_wL3ej7ty>dEPE4xFiAgm%F{vgeCe`G`WQd&Db2AEVO&mEfS?kj% zHS=tL1H$0);3(w8#E}z|GY8E)g=U`P(>gh;{*FK@QF3B(lkXO<WF98paZ=><bWNhx zt^VkF%RMNwg3<F1@seA7d1_bK3F#9PB_}5L`)>Inr99xIf;Z++XbCwn`JtBdpwEKw znin=b<TKfe*5G&r0`m48<iu+s3pp`y<iw<!oR~c9vv%?M?-3AkV&ce&$&)_IlfA){ zXZ$-vQOSwPM&!igS^rr{n(XCTcf^8zKAY>viOJ6@UJ-I)^1`57a{QFCV;*%KIWc)< zQ1>VBQSysH-LWk@IdWq1rvLvhIkBYV#FCN|OG-{Gy;Z=l8d*|uVreFxFl{ojq~yes zk`qfxPAoZc;@G{=qL#T_#{KV*6Zf9%$cc#~CnmsdqeS`LUeXdAfH0?1S(^<D+<lag z6O&daDmgJ}bE1+HlTDnc<V1{Gj&a$KFo}aZrC22=ChbmCa$@qT&(0TeV$u=3A(~1~ zOgf#Wk`t4h6P28pyy$b}2{$gu2QgX}^eH(p>2jiy6O(Qyf}D63)Yj~eZw?bjPE4xF ziAf=#hXrGp6oVtAR9y2PL!b@=v^{a;#H8%3Q9NDJ@9I!;Vlv8!m@dCS*0pnRJ|vEu zm{gM!lhFaK(keF%-b7f=tBE_~yuvtNl{j)@GA^JQLQYJ!2+~MLUkN!eDFy6WAtxqV z21Q8%Iq`4=2-2CFIC5fAO-@X<3-t7iys_s(_j{->O8z}L@gAfT4wy<DIWegwCnmd9 z#tYtmK==1>b1HG<#H5;>nCvyA+cd?I6B9>HOsdI=$ybJSCqQ>9Y-~szIWegwCnj@U zub|6TL6?BRhQyH*lPLlHA>_nlL9i>*Io4&tztHs;@?C@Y&I6dwkrR`}gSv7%aOjZk zUb{GQV&ce&$+Uoe)D!54;8v|e$cf250bMAc&m~6&PfC)K6O&m1Jtp7JCCh^Uk|ZT3 zCI<#|ke*4)gMUbpk`t3f0sWy3SP^u<7VI-C<izB#fXx?jVzM$AFG(OL9)W<66B9>H zOpbR009UZHkw`A$%*2rslat*Efa~<lNSv8Ca$<67z>-EUTk0ie1Q#Lb<^BstAtxq| zoS66lr)t^%IawFngLJx^<V2Li%4C=~PIctO#E}z|YI0(7{*b#5g07Gg6Gu)=E~=O> z<iuoEz}^#bVsddX3hEVdVsc?X3x%ARToOz{l9Ch8MQ998kyVK!Cnj45JZ0l)n81>6 z1k0rmH1H+_2tZtwIC5fg^<ZMpTS#1$IC5fgML<i%J=X`{Lr}<x$u|R5*82tKk>rNp zDTKXTc31pZ0;R4=962$$w%S!U2CP_IwIy_goR~OrV)9d;S89RvB)0}Rq{GEt?kEIS zVd#IHIC5h0y^5b?`~KwnZi<*J#Eaxk5tW>HA@T}2F>&O?<X)$YQScNJKTaGuF}XXS zMbgZNg3AyTa$@qsfc0xL9}aFu80%jgxq;bV6Gu)=9;j~SBLORxX7=pn$cc#~Cni50 zw4OA?<ViOnVBxR_bcLLlIC5h0^NO`XPE1~Ox<FGL23;X1CXSq#{Ia6!_3)RQa}!5S zOkNIXgtY9<;3x!zoS6J7V13%M-vnnP45lBBLQYH^IWc*ox@B(#tXNw19s=B=ZF1ts ziOD;amU+42?)U-5fwaPr6O;EU<_S45*@&E&ydR{XF66{X2nabbapc70qe?!n=Qt$J zO&mEf`7odb(yYG)yCF%)iOI(S>(*v{7A!y*#CjYbnU7~!i6bW_pH?^P^MDmgvwnks zkP{O}PE5Set@`wa*bj9Dpyb3P2z6mG%`2p*@gm{KiAgm%G1-WmxDa|mPD~s*F{umn zDyQVcB<0Ffa$-_VPDBN?G3Zl*N3sz)@vA6U$cc#~Cnm!x1$#aBAo1|TkrR`qkk*R# zI>R#&6mnwH7ScHNUM{>EVO;-k6mnwX$cagNSTzCWLsm@hjg9tj<iw=#@5qUT0i>rA zM@~$t$%)BE<iz#Rn}Hsin>cb}Vyk<hT-^)(m0l2XVlpD6(Lzp4Mumr<unIX5b<;+{ z^t^)r35IJEM@~$}hi-<{8_>;NsY*^vws0jWIWhTx+`0%kG1)SFMoI-aG1}9S6B9>H zOeR!H^?J5L;#xeU6>?&-b;x!~C+rj&Bnde&**>IY+6j}xBI1Fp#!<+L$wVb=C6lT< zVdsz)ODCL#fRGcDe@jm6`2o@=B>RTFBnl@pLS9;goS57tP3k3BA(<Io1wAkKA&x>$ zOdL5e*)yayx-8f`d<<b;3rS8yp{!g^q><AdIWckM#AJ5pCUqevCi{gnM97KBoG=4* z!r7AL2z-KNQLCRga$>SW!1bfR1e5vU=Ax<O#AJaR;7U$R7K*DwAt#;(Js~G1j+~e* z9_)xse~d)b;K+%|qLAiFPaGLuilC4alO-Xo(Vkcqeivac`4~qfCnk=Zm>gc+70W|b zElw!xg&&P!{k1l6<izCYiWBrv$!hVwH2eVQDmgK6<izCo>c*Z>-PjX1YV2{yEab$* zkrR_shZ=h)646*kPE5WU(kyB0S>aj)g`AkI4QYWk_U!O-guUc9I6jSeyxmV6IWak- zy0PbktXdjt_IBjN#E}z|3tj7!oS3W+b)pA3acAfXIWckM#N=xg#|SwwxhQ1aLQYIB z4a-m$C!LCbk`oh0PE4+-cG8vAPP%F%CtZ!qLQYH^IWhV6kduCiL^#Qj6O(KHANKA$ zPO9quAOCFwi@>r2!|t-f&d%;^J;QSDh+R}rFxVR^3J3@ame2&n7O|ITM8%jW8q@5? zSYwF=E7ll$lo%yZV~dHJm>7-U*Sp-AImqW5li#O(e-HlPa?U;Ho_5RJw)YuQ6M2xX z39m+w<;3{MA+<mT>Dur<guS@Wp12K-m5f1BIWfM<9i;0*s+tFBLu6(-F;+P-zOg(= z_1Mben?kv69PPDEN8WL<%8BtUAyw3t8i`DIgj-U0%@~C5<mJ8W*_ace853fa6XTzS z$u%&^i71FF=T<z30DaGNS*&tmd|!F^sf_sUkSdpxv6bOfyc~8;x;G|rVytpv{IEOx zkGR9Xs%rRiSooyrbZ)6~V*F%f_$MI|!>@8;{8&g$<l%oV>_!^PiSaK&YJm*@^WioK zd-185ikuj$oESgj4*v@wRm~m$0Rr^F)(Nr7iSa8*$Gz;mNMt!NRyi?#t=z!uY9w}X z>s3yS-zX=x4xLEdQaLeR6H+zY`1is$5M((qek-I3r19^EpCRmJ$6~>SINB|VRZfiG zaohffkSgZ3AAr33An$}&<;3`-O52Y`Viyt@;ei6niSb{{&C7lVi7Y3^DksLDmlIom ziNr;*%8Bu(Ayva|kD~7*$Z}%*MMxD$+f&i)2;*Z(nBIaBSrV(97zdGaV$dg|in;CG zd*i5yv&Chx%87CRNPi9Z#`FM<O#o<&tSl$SDksM2DEV2YFLcC%H45@)De|(M7^|EZ zH@JBl%XwSxK;GZSDksL7h?>OxY>Cc9kmba<DWdA7pZVx=guUz=n6jK0tDG1&yZvm9 zs2$wT^gb#l#wsVqol$c7NOEG_r5(lj{#MA#a$>A<Vmu;B&I3tKjC-~5INv`6d09@3 zRZfgIbn}iX=WYD~@;(u(oEY1P+Qc2*B3g(b%Zc&E5mhf8-7-28VK4g>rVnF<J{_x^ z7;olwbaX`R;EuNA@hi)T@fbn(;<4q9elZ7)-!{_oWR?@-o8@e2r)`k5UGz6DM&!hJ z9KYVhr)1MmD$9wn%8Bs~T8w;uE8a1ZTf-tJ#ye>$o=RMVMF};miB(RFcZsNxa_fHg zh|+PMBDe0V<;260kWJ%yj^)Hy<-~ZO^7wL__tiFwoEYyFQB#@Jizh{^xNJQkcjd&` zl@sI1+G5CwBT?sp816~2%8Bvxq)xAuK5V}vRyi@A8eNJicqPt`HbqiDS};B^qISzl zoD=Peu$R36Q~FHwak0vY@j>oNJS3uuc_qGw0LzK7%8Bt@cO}kCRwBEC@%-|NBRMf% zkgPbC6XS&%#pb#_4zVmJ#wsVqN4e{9iMt+;PSzvKiSaQy4k9PU$3}F{Cv4s~P%@#0 zHL=Qx@$nILLss+Bh|<fexmr%#3klhJ+(=+KF;+P-J}nt?>EY?+)l8MdCr8v1S<Poe zH*ndynq4_DcICv_l@sH$BdR%(6XSED;T$D7k@omO+`;TNnE5_$5Gp6e=O?Z6T9+X4 zxLD=H_}u6eG=?{b??w}l#ByT1JZeK&Hi(O(X$a%_2d4CW@TIZJiSb45260J574rtM z5&@PIW0e!*%aaYl%kF@!isi&u<;3{MN%L4vjIYqOC~{(arA9?gjIYwD$cgdQ8WlM) zzDA=WC&t%uv@9pa*YUO`o4~<nM}~(<<;3{Lq#a)C_mFsYta4)flZbl6!*qKz4@oR1 z#y3aQ7a68IqSFxevX5iRa$>A<VtkuBOm{|9F%Q%45nwqnRyi@gw=zsCa7jtV;2E*X ziSdI;^H@%dA4>KUmJ{QLH7asq{D?+HPK;M+ROH0?QH_e67(b>_krU&`H7asq{Dh9F z$cgcjkvv!sIWc~UGmD&f2inYXVytpv{A|)@uk}MDo)N2@7(X3R?|DdHjqXPh%Zc&x z5%pV!^f%G35caZkhnc=Ke|D^LV!YZN($^xYn1^&r<YhTARyi?#qcWs7A(7?8Smng{ zouqjzC&uq;$3;$z-_xkbiLomu#;%+gyK-V&B`5v@)e?X>DONc#{&P~T*II+iqWQ7P ziSdUKb%00Zv*;xRSx$^UiKyu^DxXImAnau~!h$R(#wsVqpSq*+kBBPfQJIIlEGNb) zC&pnaIh`OWDvnal-KbP5xf_+e1dEb&cwDS<V!S~rIgR2A{QfDqXMxkG+mVmu#8~CT zxGu@-wf>0<Q8_Uln4&guM;lTr5o9?r9-N~3q@#@~x_9nnn{hfyaKO@7<-~Ys$~iwA zmZFNeqdOok%ZahdiLomu#uYiyUWmMt^Yn1%UzZa*Yqr3_qpMTp#QfbTt0q=CF?Qv| zczR0Sp~ZugKO-N@iLuIw@y;pgAIpjH)YM}L(_Ijf6Hy#BkzVd?ZJ16E`&CYiXQrsW zHbW<Ob5j36*vsyQ94sfsDksMKr)V@-PK*yuwIiKMCOHuWQA23NYEQ#_mJ>@NCzgJv zkKS2MEWM%6J6TRFy(zI8{EapiYcHvsSaRjWk}D^cTsg4>^30>k2e6!2>h-DVc%7H! z#L`Ip*y(U@!)Y^cA2KW{nOJhkMAS`9XEL$0iBEaaA0`t^YmteiwaCQMT4Z9WnoOLB zf<pY`$Nr68-a8OiN+l%|OD>sMa>>M!OD2|FGO^^6iKU&iqe3Q@cGgiD&SYX~7menb zOf2o{Q=5oPY;8k>#+4TMZGXg?EY0)jUA4BqP<TGSSKzgdLyn21#eUm@Ud%m$UyEux zfx?S?dU}iN9poT4<Mxu0i71acN*f4~iL@|Bg-k3R$?r}Gnb?Zs1vwkTOG+k|j`3+8 zElXr#=~%yuqe3Q@Tr#obl8GgkOf0!%VyTKuEG_ll;wA~1SUS-m6HC=(BK2-iM5B=3 z8!f3RDVbPu$;48HOw3=2e03!y6Oq3caXu9+UF%aCb&kly(sjCCA|?|{*K4$f$;47M znRoye%l{6O0VO39OD>sMa>>L}g-pzkpGD)VWFi*7o|;2sV(D(5(x^E^CYJ8esE~;z zmrN|(r<<6NiKUgv7KhP!pIUbWimE9onOJhk#8MTR_&4NZGO?s&VriZ}LJ~5u<dTUc zmrN|VWMauB6HAK%+5d%1EG^ckkcp)uH3~9u>w`S+PlsbYZP**0w-8@I8!5*TFYMkK zF{|G)2%p~-u{inj(0P<rlGp%m)Jzm|J?<v>3w<&MA7y87qi1Oj|5+IPLT}?fxD5A? z<M;0=!>>8hCO^lb3uny5d-*($H23JIh$-Ck@Ohqp0XvN69k=P6Lp*=EM3!v!7mS>L zp~Mz>o3B9;{v%Y`RX#0yE-x?DU&*~e^;`HOss5X|e#)_ebJgY##y!iUvBvz{n&y@~ z|7Y6$rnwt>{_PTpye;=awwI_1*%)Zy)m*@6H0&NqMIT2WS`H7u*03MuZngC?O#KF` z`%&JkX|cP&%lLb5#DL@~ZQ-UUh#*H4w8-1~PFiX>E%h-kMN1G$gQY&fF6e)xOL@^_ z$hiWG@u79Nx7p^jP(aJ4!au^c`A<U@=@SY6Wys?Ho3i4TxR)|0nm8K+Qza{2)fbx+ zqnZB>vUo0+c_F5CH(<hRUywzLCdlHgoNGpenx77F2W-~EJmU#W>t4r17NgKQki`!< z@4AsiUdz~=ynl);jw)_(2z8$}zJG=+;!Cey-8V4to2W_jG17G)i)T@;AGx`xi%Kyn zviMWV^%&)%eL%{hXquvEPc^c*n)1Hq<V}#pxoF7$)m3~2yO6qyZ&FZP#XnI{T*c2Q zxEx)n>*7s7T*Ya$HHfRYn1bRec2I5ND$)m1)m5aw>rz*7PYV7Zg~E2B@1cI#Rs1#; zCaxllp}LCK(OhvA>5C!iD&9|X2VyiFSMf=jdwPJ$oR=uLn?Y?~(Nz?|mN$;?PRC+j zkysR0kv>!XRb55;605q3f1pNg=A*U`BI@yfv8z~vUqSr2H~khCFp;f`j_hfgc>Q+S z8^O>&zCW6BMvM!Uo}CXvklswm+)tm;?l)&VUYTt5>HFmUFTwi{^_$YidHWMtnET^c z>H?vR!?sz-HPPo28D$(+K^a>sC}V2{Wo+G!2cA&I)^W6zGs@UnK^a>sC}V2{Wo+H; zRLV;zV`~LvY^|V-tuwf=uhk&KfgB{1v9*FSwpLKa*125R_LO%X2MJ|t6_jz~Q3zHf z#@33&*jkYon<^3`zC=i$E@X+Zt0FOWcKY;b1C|)Or_o>1v&6X3MEVp$mBbkTCbuQh z=&wuSH|cLvSYnJnr$6gpi81c$)1PLr#2DB4^fwnQF~$X-{%V0G#yIxrZw^>uj5qb^ z?+RFAjJNjbPXSnBjK}-*em_f$@h(2Sz|Rt6Jkh5&^jTtzr}^~GJxh%7F@7`#VR}6~ zKF7b7!t@qge0)GJ@Ug@gpB2z6ZY(jz7X|cI8cU4vH37Zb#S&wDLqM-<vBVfZ7|_d5 zEHTE91@wLsON{aF0(zB*CC2#efZh&bi81~>pm%XtVvM7ZUaw(^G0uhbmPx<8rlUQj zkY4s+i820KNH0FH#28Nv>5T%G7~_Q@J%?wBF<uhVlXaFD<8wlKWX%#|{N0eAE3?Fi zKh>hAv@9{k4}|o<lO@LZrH~#UvcwqwI;01JEHUD5bm+kxON{ZKLweTM?-puXO+*j9 zSYnK8BYKX-5@TG5=phkHjB!sy&wW^8jJJ;HnF>pc@%9ltCSi#&J}jc!^ei#Piz2%7 z&JtsMX+(ExSz?TT5YY`-mKfuoM|69VCB}GVM0XrnVvN^BbdQZC#`vv>Zkw^h7<(zY z%fu36+&4uxj#y%hvnjeM!xCeBSgHYCVu>-Xk{C->5+km}-=RET+Z0zGyZLmb%o1a% zN@6S><<o^1ON^y6e7Y24i4oUUbS=gbBQ9*{x`-u4Tz%|>bL9SXV!z=iy3p8+o(lR? z=$ttf*6j{okxqL3JHiDt<9E~h)C`LLb`bL2L;3EagF!0HAfFe`!zrovDK66@|Egwu zvgGNg<~f{+F2o90Pb03U5%<CgMS2#U(D7N_+Z6gcO*rJ!>Dxd|`X7oP^5}W`<-o7( zepslM7WyLsqx)<Vevbe9ES`*vxC#$HL`t6{>C_K*pTc{vNS`INNzz{)@7V{jWAxH9 zd>p0rIaXqI{kNyI2@A1YxQZ4!{#q1^Kh^CYP_~_}rG%*|1i$ehCp7jSOWAgPm=dV@ zeNOl_Ck*d@A7=I0Z8aqrDs|~!I3XV%K(SM#HSOUaX?Z%Pp_=tH;(8hppLxb+Bwxb~ z$gUre+9>=xw!YMcbiG9z_et1*Qllsv-Gr|B24~uM1I}bA(*_SAEw#}WNbqWJ#q@?@ zxZl<Hrtraj_}h@1X%3Y$dK1KMAt0W1lDn{AU%g*ZgFTFjt);{t;otI_kqxVcAVUqG z)zXcwJ8+WPcVHRJx(7Q#-&&2<G>pt3I!L1f8Uh^s`_^f+t|7$vRNu5lGY!{_LUgc3 z8ylX_BRWK*>uE$<JyRN{kHesB>(j~z3e&NE?!x+E{N{TG$Hl&n4E*(AtnPt-!NB_i z4;_hY1GmBBiqPw`4D*A&eSVDp{65iTC}9PHe7lDZr5|B)if*Z%JIV`wg?LTBnc**n z<BWjM^0CgF79R9<yqh2gD|}(;ADrnh$#jV7U6Scgi6PTaobWH*pe1|?b#9QO7)tUY z(n{zbH>1VtGZmX`)VG#CT~vxucmJdM_osHBPX&dea8j|JM#RXirxEcBzMe+>$~0p0 z<=BfZn}B^QEhb^QViKm+Bzy%~YSLda3DaY*LIQTywN1kGwktUSJGVE078R2)?U;n= zib=RNG7U(JNtoVE3uTiqy}OpgCSkgRZjG=>nBJou3(?P8@Ers)X)y`Y`#MWc(xurX zO#eqFVS4}hT$Y%G>B$nqH?VHN>?kcJVY*@xK8M*gX)y`Yj!BrVn1lxRL>8sRButN{ z7@LIY?P$E&Buwu}<C-IrFuf;@6PtwTDKr{v5~dHNuCYm&K7`tWPlKZzI=bqu_}UW? zXOl23CSlq!3Db^AnBGi|%VH9y52bG4c;OAW9fd^c6I<9MY?+L0;n<dY$K%z37B&f6 z9>xuUZ?rUj?Mo)%pLoH4!z47dRO7#45;C8dUdtrhpG#Y(NjQ&_)-nlK@XW7h5<beA zzoJR_CuFWkt4Ww{_CH6QPKC%MOjntNG~Z9t7L1=)TBx-HCrcZp^FEt|U`8p<CLv0r zCV0`tm{VT$FbPjUd<U$hF=;gk(?fjOC&VO7yCz}UH3?BwhyrN+7cM~{O0U}_e2CHp zi%B?S2a2&tI5_`93X(}Uxa}VZq92drzs!r6Bqrfs%i%Ny*(6LKMw@W*Ex@-hdr(Bn z)b3NKCgET)2?w`Rv*_URU@vNFp;HMq2?vkJAuFAbl1Vt&H3<iAI0=bmlaLC{eisuq z35T?bNjRj<r`FSoA(L>3YZ4BrG70a;qA}Lp1{pC4Gec+&7G#q!Q!xpj!E9pLc)!Fa z`}^U6I-7*}h7)0inMP+p$0W>DOu}|7#wKA#Ou|f#=Ab?=qA#_VPlrOUqXQ>%ViIQ9 zB+OKsgc-*q%s3`t#xV&qj!BqtOu~#~5@rllnV5tb$0W=+CSk^I%sIs*%(y0D#x)5u z8;(cbvPqa3wYNmYB+P7dkVIh;Qkk?Wd5EDT&~oJgfk{XU(rQT3X-QfQNqlj2ewc(2 z&V%p5s+p8wlQ83$gh`A|!i-}QCNVY%Gmc4^#MmUvI3{5d6O%CGn1mU}B+NJ_VP-7# z5u?r~Va72DGmc4^aZJLDV-jW@lQ83$gqdoSFyoknNlZ+_jAIgJs!hU-V-hAYF$puf z&`2jHVa72DGgT%bZQ!+|kd;lsp<)sabxgvc6_aoRGDbt!G6_$>Og0IJib*)sF$sq{ zCgISEN%+W6Tsg(q66(KX64rMLs3<03{eNT<*7w}Ref*M1Sl|0NCw$2ytRMM1P7sr@ z-pYx}mrTO?xIY?4J6&H~XVsUaHTm#)97O9KldyiHr*Ra<cZT{OgzKT;`+QZjJ$hBY z*{)o~mrTO?EoXCrn1uC?Nm%cgg!PU|SnrsG^%awFLOs?=!~dX3czqK!V7*DW-XvUa z60SE1|BFn*x3YAQS#J{l>n33xe!{jHg)5{+F$o(Tldy5LT%O{CqZS#zH3q}nxK5LB z5|Y^@Y!s8Q(J=`dD<<JmWUL#eCgCvGBpg;{5?+mbY!Wt!N!a9=giRHb@MUCVlQ8=g zO~P6{&SaA?D<<Laib<Hg;7yc9*AiqBW|vb|M&WV@tiW$MpSZ##oR0V`WL=sSlQ8=u zU2tuaF#F@pxC}7~vsci9bcZCGUtJ<h!q*Xxvj2@H;lVjI3A17nW*w8T`IA0KU)v<i zzWjIX%?524pVd?bJ)I+yFzc9vS;r*IIwoOujciq964K^Sdn1ZrlQ1hLVb(DTvyMra zbxgwS`?M@wF-P|yP-POXMl{N<+ax5%kWIpD*(9V}RBRGvi~ieOq}P7|?rzuZNoRrl zQKjq%Kg5E#%I*KF6jHXM(?I^FQnuG`<`jrxvk+jDFsmkE)-?&URVLv%$jc^SPE5j_ zV-n_yl+K&qr3jP>xPiDkgm{!&r%5;~kEE9WH%!98t<={4S4_g~&{{SLTf`)6Ikg9K z*d)wXOv08ka+piE2Wn16psqzs!hFRfY`LEj*d)wXOv082F2Gzi30ofgA%b2+Gj_s1 zKIp(Cq`5PaIIgyDMwptw>A1o16vAv0wunjC^3ZmeLrtm8VRvPdutiM5mY0*`6`6!B z`%$rM61Kb|U+EN+uw^x^BD$|bCSl7ODu$0<BM~TXkPux?M0{J^Ivd_1CSl9(lLIN4 zge`9;g<jr;*gMo{DwIsZmUpQebP#zPR|+lfNg7PTYfvPcge_tcwtOTRJvIqjDkkBx z$jByPtC)nX<D}7I61I+~bkycWzeS*Y5Qj-P0M)Ze*eWJr>rTmnWD>UStbO-tw?oFy zaNrxzDkfp;o=HYB30wEljBFCNR!qWUuo#<!tzr_kIwoQ3<YX~230tQ~3`dYFu-F%P zV<*$1CSl8AzIL5W!j>wN@E6F-CSjYHgl+AoV#}c)Ffs|-DkkCP?bx8&{yR*<NvNr= zO-#bJDfH{iCSluD3VOAdBAHFXHZcj?_M`dKVloNa4xomTN3bvMFt$zmA;-idY@2>H z$0{Zvl{F}$3Tx{+)Ff;Zld#P(3ELc#u+1?E+Z>ay%`pkvDkdTMhjndY61JU8G2H*d zw7fyVBz%JNh)LLX26vrI!nQLxNG4(1SsWyju<e^1B$Kf1Yz~r1*me#T!X{x`#UyO2 zn1pT1Za{z8By2m6n!zSv+xZk++azqe;8x5nn}nYs!X{yxn1pSo(5TVSlS$aNoVr8K zNKLkrHWo1n+isyU*d%Pbl@c)G&tgvPjtH|!*d``n+l}(8?y*VOb{o}3yIt)Z?DHRB z6VDXBWD*u8?}JjP1@o};7p6$;OD17q>hYXSOv1te5)+fKFr62wn1qEHGNWt~77pYY z6_c<qQ)ZM+!on<>Q8oz+2g!`GNmw{oW|U3B!fcsQHVF%Jct*t}EF8%*)Fdn%B{4Ax z3ri#hlkjGYPE`0$F$rHqQEU<x{;5fLevc<6Vc|<AVd1WKP!6pVG6@SmmzbD@g}Wst zCSl<oiHS*AxL0Cg5*F^0n3#lxl@zNo36mM9M@+)P1G-Y#BrH5AF);}X4@pc+!otH6 z6O*v8N@8LX79N$Dn1qGLB$j8Bu<*FV#3U>{Au%xt3%`(<n1qF=B_<|e;g=E<ld$lN z#Ka^lJTEaZ2@5YsYz&)(g%>3zCSl<viHS*A_?5)OBrLovF);}XuSiTx!oq5a!6bYU z>x@mpb}<Rt9h0!#F$vopld!#F5^jd~PS_-D7n88vF$vopld#<}3ELf$u-!2U+egTm z0ZhVuQ30ET9qTj+Z$WZnhnR#NCsS)^OCyu8<CId7j~Hwcb~q+shhq|UI3{67#U!Kx z*d*)_ld$8Qsa&L(gdOKfOiaR#^UmNjF$p^^keHZ+9T#54X<`y~TyzJ=#3bza?sFUy zld$8GH#tV|2X(D(Uql6)p%9yd9cR**BqrhZm^~=k6w^AYvXWO`q&yDu*d**sOv0Iz zmD=M|TAGSQ0pFsz)UK)mC>=>Zq9kghn1r-IT9fWYI72P=lX6~0q&7c7O~MW_2|FB< zu;Um?XOpnw)jkN)QGHO<iI6E_LT?wtB%F#Mn}i)|5_S~0#aO>=5_VLXgcl<(n}l8e zhDmrY7Ov?Mldx-l>KUqFldx;D#3HZxHDr2X0cD(&Ba^Ue3e}mL>2*z|Brp0Srsdym zoWXYCe1lEGE-?wa9FwriF$ufoNIj6Ej=^GV5_XA6*tO_H%%N?XOv0|k=W|R<!mcAF zCMIFmQ4$lAuxp7d2a}KjL=aNZhVszwD$O{Z%6bcv?k+J2yXI3bs4_AMyB0`HOv0{( zG){?0*mX257pO_tRb>)VzlR+>5?cYzNuuumW|Q!nsJNzEOv3I(a_;Z3N!YzuuM)iA zC!C2*!tQF5u)AUs{+6?dN!ZPQe99(a_d?n#*(B^fk~+yIVfQgqtyfEbOvNT)x0r<8 z^WNt{VhXMMSgMvb^<W3&8_+E#Vb`(WK^`^<yU(Nq8htVeyU%)pV`36^SDA!EFu#@( zm*I>k-z_F#_ql022cm`F#9_UAS%G6wNG4(Td1E*xCSmva(>W$4VfO`+Q%u6{<x4qD zOv3JO-^VdA3A-<Pg=2JR+6}eW^+(jF9;PY8CLub7lr)WWn)Ylt2311zVjmWN9hnA2 z&tRIN+I=nna~crIY*C(*rXXh1$`};UrE(oLU5-#xXQd9L({r#oYJOEZ?N-Tq2nSR8 zEpB?HWwcMEDODzSB1CnfBZ*0P8ZA#lAb--u^;0@BJdC;7fh9EwyVWG@K9olX>x50h z?!$cQ2yFqwI+2%6!k)EE!flYiCSlK7CgFS}uu0hS6-~l3u>hNdJ!_eSKS4rGkC=o# zb7iNHU8HB8#IT7z%bCO^>~T!Oo+D_`s24RKawahed*)MxY!db?qAI-FZd~HCN!TMM zVb7t-(S=OHp2MiGxYI^dUeA$KAzdp3yI`rB9x(}fPI8)ea?-q-!#R_fggvKFMQjrG zoJzZ=SGycDUS5EmtVc}3o)c*Jup+pw)2IsWz|9CW_J~Q?a~^evO~Rh@seG^Y6-?PA z>=Bc&=UiHENii2tEmTbH=ux;uhuad}Jz^5}tVo(oCSlK|+H5undoGihn1nswlbD!< zJ(tS>iAmV=eTl&&oQw*(d&DH{xt5y6CSlKY)B&&dJS4M8*dr!k&s9nNWD@pVO<N`! z%ALQS8cVlaYCl9^I?j_CjhKXH*JLR&35{bC8pk9wd+dd3vFg|)G<(XAmzacRFDh5m zJ<3%(WFs{RjhKYy0No0_VH+ZeO~T?jO~M<H%qC$`Ov0jL5*BCBJtlOEO~T@Vbyxv( z*N05PqGJ*k=g^HTx(9GSwvOT<Qh%OJ!lGjm7Aqzp6~`uFQB1<(yluGxF$s(FCvi+n z!s3D@S~;17#f6enOv2(3k|ri$anV_vlf*{qULC=Na=fAtn}o%~X<X8y5l5~L{5L3~ z4Pa1Ik8mAT?9-s8X$j1oKy#^*N;%GP@c<-cs9HZ+;v0z6{s|K{35#M97OzNFKbeHZ zD<vi-VNo|IF$s%T%Ls}|SaeLn;<eN&HVM0NiOweB2r&sqI40qU%_*H$ADM(B);0-` zKq8xjy<!sfIwoOnE?JmN!rm6y9BaZp`04A-OUq#rK8!-xBpfLw;Yi0M962*7giOMb zj!8K3ASrS{_!JJ=BM+9GFbVr(hhmd(q?m*w9g}e6Gf9zT5{`6C!jTn|aC;QOCgDgm z2}imn;mDM)<*-ROvdScU0*kUqI8sf*k*-NN(lrT3x+dXB*CZTyt}l;Y#3UTK%%|cL zlW?SK5{^9Ie}+@(_n&Sr;TM<UA7GNVViMYM6vF}1i@rdhd^!x1a1_=un}k+OLhG1> zcKl1o&nBVWV;Hj1adp^(2(U?L|CgAAed#vazUZC(w@tzrMa;_MC(epVXm2<bdohNL zO+s5S2^(-6W0TN|NocR3;b4={-ZmPg&@$xSSjQx^j!9_mkkZ8@w2nz=9h1;HCZTmq zLVJ&tk`Je0y<5j5w2nz=Z=xY$B+#yWof|18p}k*XViMX1BnFdk3A)K9p%s(RIwqlg zOlpprW*;)kvyMq<9h1;LDMgA&XrGc8Ou~y$!8j`>p?!#kpG`u$dMOgT+Q%_vlhFQ( zO+veZwp2C=?aMzzA`SBx{BGD^Q&VZXtleNsH3_YlgmxlrvTPFC-@Yizut{j2rnSgy zp#9w+IU$d4<zR6(39Xof_MK!IG6`+PB((3gV+Jm=yxQ3aut{jeB(#6hWu)*AsWX(Z z<}_rSWyK`4A5&Fq653DaAqXcLQ#J{$n1uGPNgv51w1201sE_$I2-E;a?Y|0lhi%0q z1fzO8=Mj?-nyMTr@%^;g(b&cD!!*FENoajvOJ|dis8uaKI|+H&B(!Q0+Wx-whzSPk znuOLh32l{0xD+MTSTzZ)YZBTjlkhs^W0TOTNoX^^j2b?H9ioqoJ~fI>!lw|}*{Vrs zn|!K+^)#FJZ$;S4eu^oZgjP*Ln=AK~OhPP774lHF;bwS^Rg=)VCZVk|36Dj-oiey< zVAUkFUF9($lhC>*p><6{>zah9kJ`v4;Y}!*O+u?Cp?#k=VKxcvM*bN{##YWBtG65b zvYoc$`8(#bNodt1v|A>5>3hz0w8oINW@|MG?O4Cza}1E(HfcI%-A=RSz3jHg$|j*z zlhE$ql)9sCUt$v4owPmp`tMvU$tIyylhE#wEGcc@v(olkk(W(Et0tkHoHU3{LR)1L z+A027$d28INeEkI64q{m56o?c^Czn&p><6{>zai2AYV>O#U!)``?3RzNoZ$lJwUeh zM#1d|(7vK3p><6{dx%e6;Ln`dxjtpbw!|Mcw$&z~K|<^Cm<+IL650hmwOTgUqx@M2 z!veyTO+u?Cp<P_wTpRI32@6j~7TR31!5B3Ot(t_^H3_Y25?a?Jw5~~Lt4zXn6r6#f z+>f3?+f#gMlr-}!zdzEv?CzMdNodt1v}cr?c_KCQn|^>SyjLBGK#diX&|dB9$t!7s z_8N`BB>Xn#5|a>_sRA+y?R7riwafn;Gsajk3GLm!p1yD?_h>BdjeH%s*d(-K5@G== zm`y@^pHInN^e0SLAduP_6PSdg7P3ib)g-j8NoZI4R6CFP4CG~#(5gvjSNT*Pn}qgp z-yj{kXuqAY$=E8B&_3ah=cI97_8bJZpH73KCZYXhGAe8m+GonclDz|Y*(9`T651Eb zd7nhR_N8)O`^|0DB($&n|HLGG6g^^-(Ed9n;bS-+ygLn-rdCZtJ20RzVw2Dg4qiZ* z4ySx>HYCvNC^iXgMx$a9+Mya1lhD>{R7^s9y-(XXOKP?u=tZ@3YW{MB)zhd^F$wL< zKCOH<3GJ|8U(P8ep>5KfViMY{M#Utw&-t|FvB+kJ2g`UtF$rz6M#UtwIgP?3494NJ z<@@1PdaEX(bxlH>4`_rL2(<0Nn_MamEL&l@zSuw;t(t_kM_VKCn>AX8n1r^dQ5-H8 zV^KB<t(t_^H3@BRK&_H0OTm5!^L}*)0&EgmH3{vgfSSQ3q1_}ni8G2xXgdR1wQLgF zO@m802`1qi2(U?L)g-j8NocnUWcP%Plp61eNodz?5|)t8CZSc6(7Gm}-BC9I4A)fT zWs}gVNoZY@(C%KzdnWQeus`klY7$!4B((cf^4^NPY!X^E39V}q+9|qL(1!PsmrX*e zCZXLvpz&an(9Q_%M>=imWD?o~b$wyEVcV-oXw@XNv&(tu(>HcbCGVri`%wqJ-(l4x zv^xhhj<N%NJ?KO_9s6i3?4AJ)A-_In=LTarNlZdJDWDPK_vh@qU<xORNoWrUXn<r- znjaj+Nn#S(SpkiQbYMYnJ|~GuXb%mj`)m^0g-RpBBzzNj*d(-S653;R1Hdm>_%-~% zprI43nuK<#?g03mZb#xot0tj6DWH<*E6euuU;vW5>=aDtyULTSnuOL5=upijp*=My zA)SVsOhS}HmGNeN1_CGIkTk%mNoZY@(0;2j+^dn7O+u?Cp}in!KAVJgQ9x^tO+vdo z_z~AJt{)X|mj%>9HVN&8!MzBJNtl{|yB$;Tx3E@CLc4iDyDXc8_Tu1eE(9HHLq;|U zt(t`Pd*#Gd`lHlER!u^?BA}M?@La(%BAbNv`vFzPAECBa2HPU+WlzVHO+u?Cq5Y9N zR96L5F%Q*G5nz+hs!3>{VqcgU9D9ARoHL@(_Yhcw&H7`jCZWA487DRg?ag|K5R=f} z!cj2^`|Y46p;eR6-l3VX6>N*dkFA=7_O^gp#NE6v7>pz~3GH10)i2##8T4R2&VMmw zlhCS3Xzy{mi87IeyLmAJzr&6;!Kz7UA1Sw<OhUUVkRPUTUY7p!mrX*eCZT;cX)T+C z_BqW9^x?P2%O;^!lhFPu$?LTa-_i4?STzal3jsBPd-huJ9)fHV+E)UqPkQ!x5MmK8 zI{{NR39Xuh_EooMYXYj6dv*}=vPo#wB(!fOJ@c}cB9To(t0tj+FKHf|gtp2gwC@Ke zB0HOecOt+hp;eR6ewZxhwSI)eDOOEF`$0f0;BNgbcmP2*3GK%L)h*rnG<X?dFWa({ znuJzOLi>r^t-l9UF?VZ!<Ykl4s!3?^#USkZw7b(+;H@9Z8Gx9CHV9??!6ZBhi`G~* z39V}q+A5Rq735=+(5gvj`-bu>Cnlk-(K5v(w5~~r3aE`iL2L|LWfJ}s^VuY{Y7*KZ zNx@!g38%w{TQv!7T}Z9v(Q68QB(X_ohlbQR8NF=SNN3R5shF}!Xw@XNjiGY@93E1| zG<x=U<lPaQdt}#b68;$JHC9bR>zahN$|T%i7d#!GPODo@Lfh@Gg&ub;m}D)mNoYGl zYBZaKwix~f;lw0F-PA?~aHgXYHVLhogm$A)kC1W!y0MljCZXL#OA?dN{){hO*d(-@ zhT~9JP=HBzJObz8Aic<{NoYqWrFyNmA@LO4q-B%PZXQxUc_nNYo`xWsgm&wYS|%%D zTzDzMUUm(pY!X^E3GG;SC5#WLVqOWsuDJDtBW7gRZ4$;vA8pknw0nj0)50d9y_LJf zCZU}eHXxsuor5WxgjP*LyK6|Tk<)@b_-li-bC5}hLaB1zkuFDI3v3E!STzal{-GY! z*(9|4hSU%?3GL+Y6kZPO+1m(+Nodt1wA%!9{>UbwogV&#bBal5XXpklCZRo$hgwX+ zKD((&Xw@XNv&$=@GzN(o->OMyXNA;UUK4Y}I;62lXb%agHL@n=F<a=xUq`B#gjP*L zd$_wQ=7&@@55jj5V3W|QNoW@*gCMs`j^xp2Ztn%;oru+jW~oVNk99kHoZH#st2+A; z7G{&ss!3>1s&uw@cP>>;LVH3;&En3U8Geo+n}qh1kXj&}JqsTT#LAEN$5c#0t0tj6 z-R<l*L#moPdm1wT8RuMMteS*&nf6XhLVI2)2YQ%<_aHBugjP*L`|V_4*d(+Ugj6@1 zg!ZEF2MF^Z{T%@@39Xuhc7;1gm%4*=S=Asl?x7~3Rg=*Eurf${A`wHcCZWANq$ctp zT@$t;jZH%P<B(b)gLG}UIp%xu37CpWXw@XNSGj|9T}V~)AYDsE;qWlds!3>XEDusW zPIK)|p<Fk@B%}{;j<aeK+FL@Zh)qI!M|cZ`*Nnjeck=RHHnk^C1Nl@#O+x##Fu4XM zlMn?_<=l#`ke5wDt0tknuRQ!zhP|7={wXJ8E5lY~=ixsF0Wk@!nuPXYclaN1hksSo z@Lz<5Pnu3gMl}iTla=BBB@!|GY7*MVLTVxp|8wE@5oD9l{vxCn$nZZO-j1*r{{vGo z39Xuh_8E8hUkIsc?syh=$=M{dY7*L4l8$>>`eqB8gjP*L`&zky*}ak2#jRJ9(7sVl zY^6847g;q4?V6CP;l{rg9)KX5g!ZkFDv-v%A1+4N%if78n}k+PLi>)}_CJJFF}M9i z1lT0BY7*LyDsBH5iCz4&qb8yKYq@#Z^hE5Hd|jX>q5Zs^*t#PU7g;q4?WZAC!)=eE zCM2;*Xuk-l0%?0HDq+5trSIL}f)QC_)g-h*<eV7viKt?3JL!gO5?VD0ZU0Dr4fw|N z0F6!XvQHu_n}k+PLYt0~pJg%$?O=@p)A<|nvPo#wB(x1~-o|p?)(!W@wrkZSv>65$ zxt}djfFw2vZBs<mOF#3`FwFO|2Vlx3p;eR6HoN_7ji?>m&$Ezs^T}YCteS+jGfGY$ z$t1L0+EJYE--f(w5?VD0?T9Ej4<wV&_G;sCzP|=}*(9`T650*jyrar_TeJJ%8I@I& z(AtRF#2wutdKW=93GK!aRWBXgG77MWm;D;14`YQsZPg^So4Fkw9Z@^Dqn9G@U-=Sw zjF^OWY`LRk654GeJx^wn(B3R(OXU06?bt@dS1RN*YaGAc#iwL1p;R^rt(t^(2Q5ZS zLc3!mw}!<ev^!}ko=Vi=c`%!VR!u^?OGJ&7Tlc$1l#cThxpiM{625^<*|U(!CZSc6 z(C$+nUvBfh+Ga5c?OqWzm9aWIDH_XV>j}AQ5?a?Jw3D^PFbS{3QU_wVCs{QK?ewHh zuk|$~F0pD7+Nn`J;=B@PM?Xc7O+tHMMD3Q9I461pVJ|yilA458O+tH+yAltHsA67; z2O%$;gjP*LJJ(%_^OBXwVxOH~UU6g++6BpqW0TM>)F?LBdr>f(gjP*Ldz8B#m$>Wk z=wv;zNobGJaS)Tx9vjg)pMZ3qpky`)t(t`P_=vh8t9fZe>E+d2Z4y3#gzULEJz<m3 zs!3>1ONLx}czSs?QziD~h<YNc`HZL=OP8;^T$9kcCZTmrLVI>ZH76#aJtw-5qhu1& z9zTdXn7th{-{%cNO+tHq(mK#!NIcG}NodcFK1aiNgZOT=5=m?l+U3zD2+Ia>ar6?x zc>aNNN;V0tnuPWucZ0YjqKbKg*aCUkB(!Q0+RKv-!pq)|L^cVnnuPYpN%Pnwv{&d_ z6qC?isZlWr?Nu5Tlh9tRQ85YaH5wI@&|b^YvPo#K<84bef#1+cG7poQg!aay9bRiA zAAHp$v_FZcM?6foN54mqO+tHfM17HAx+D4mVJ|xl3to=UGOH$`z0Dn_J0q%?hiL}# zvPo#wB((QdhG{5%XxSvRY7*K9ljgBWXdg=U6E+F$!x|Nn&_1G3F$wJ|jfzQVAJwRs zg!VCwib-f6*Ql6;_6Z$RF$wLHkvv!slh8iJnZ+dB5N&3Y(5gvjpH15AwH}7VGpw3~ z_UVXv&qMlZv?Y?*B(%>*)NdKm-$Z*N3?>#+HVLhogm$$%q_0I(F%Ri22)u`a$5}NA z?HiH)aC+GyPD$A$v}zLCcar9@Noe2Ij*CfX-_xj=gw{0)t!olm*Ce!6CgB8B%O)Yd z<16<X|D03{9|4K;t(t`P!-zV-qw-m_2a?z%w4X%ObQzV;qd5qB+3PW7lhCS3Xg_sF z<sT7M%%k#q1lT0BY7*Kom7GrS-KZ$#+>J`5lDkpa41S#1B(!Q0+6_|4X%v}+wtq_Q zS>QCPguHALS~Ur6U6R*pJrs$@Sv3jmz!bHKJKB)i0!eHV+QBKRPdeI|+5=%P`)y3w zB(!Q0+My}u{BT%`D&~&fj{uv5R!u_dnuNB>B&@}0?&Lh(-}wJ-5^jRBYOI=s)-?(3 z^pw0qiw7xlk&jJ6t0tk{IYs?rlh960ZHIKa3qmF#ilZjd%e}435U8_i655$5s*g=V zJ12D%!d`Y2rfd>gH3{whDH;tn3GKnDOOZ|`lSznzs3A0BwO=5>CSfclVf;H;^&Xpq z@f#A0*d&bKlvs_IpE(`Zz_FTyv1<~>u1OfXCSmNFgmJG=O~>oJY!b#JeJYyXIPG(L z69zTf@HdEmCybRQj9r=#O`skyO&D+DQ(kn7X+rQFxYnTG`@RjAZiOqnwP?b4Et)W{ zrU^elK_}xMKm7+yxO4_?zQ;-v#x6}5yEI|!(uA=~6UHu081JP06`C-1X+r2C)CGE# z<@LGf!LB~FiD|+gph4qer3vGCKE1NWG+{j7H)F9->x0NKF;<!|K7!wlVwx~s<kLf3 zFY6tsG-0eXA<CnU(spwC)~Ii>Pu1WZs)i~35IvG#p%9wz`&gD~!dPiSC?_<JmL-}n zKGwgIqe2tLE=?G_G-2%0gt1E###J<7ywsn~#R^RrpXktpaWzdyy&DwKDCD0<uqIZT zFm`FexQZt1nu*;kR+<p`ug7wHMi^hK(4x?Uu}c%iE=?F$(}b_0i2N*+J0Mn?Fm`Fe z*rf^MDw^;iEH*q=nh=Y#lt?sTe78?&G}c5D#`kDcXu{Z~3FG_xnY`5qO&G7#Xa=M6 zb(E6-2$PyvX~Nj03F9i7@aS1e6UIst#`E+^lF)>)OB2Q}O&Gg0VeHa`@uENu140wV zi!~}VVSJ=UM|h*YhgSI0aQ*0C=94%=@(BF9!)VT>ytlyhi*7)!71+jk{~hjad>SPO zv}`JD1Yq)?=HJnu<FEJcf?Egs7>A!m*eGu!JaJI8`9YZRpXT2!;xdoHv~D>jd{`9! zj-rWwcQNN8Aw>PVYk9_vnASavi7ZB;b@+ELa^7|Ocf6LdIsJ40u2gK9P2H!B@1Oa1 zJ0NG>OicVQFj(@(b%d6vY<JRFykIlipGUdQbaPP`UH|Sv%5?+fqJ2QhqG+0;Xb-KQ zetmjT;zEBV<^83TH}UTXHQQwB5F8Me5fkD2|5ps}zF8RQ^b(ZT{x}5%!+U{(g5kYR zLBa5Ndk_q79Bmqc;q6aB!SFUf)r#TKmpK)~qpxQwhDV>}R1A+k#Q9|m?^r5KFgzMi z#qiFfpkR3PsS3sLuB5pGF*FW_cN5JO4DW6V{_7YXZz<oMjv@J%Fg*G`@>j+1=o6rd z;XO-@6b$ce3jP;kc%L3Zo04}ZdN&jk+0-bMrU{a=_-|8o@P_nkjm+%e4Y{8_-OLVN zy49y|VzYxcbW{2uEIWA3f9#}{PYzz=Hq%hXpol)%#|~a&#lg#09K3wR!OL&QV@?iU zzT)8JD-K@1;^5^g4qkq@Q)%s!gO{&3c=?Khm!CnqPJeRn@&|H|9K3wR!OK@1ynMyM z%g^I-$-&E49K8IAJoosGF}LF2<tq-}u!@6+uiw$<^4P)atT=cboj(0JJv(?^)9A0a z*}>azVjjUN2haZIqAid{4xW9J{vLuIJo`EQDFHipwy#fr4!{nct@G(Ue|GR}!KXLn z*}=21Pw%<2gJ(DO>0Neq@a)z;y?)LPo*nPgJLT-)*<E~kX`CHAJJF}N!r8&I(|mf* zn;krRj8CsNvx8^P@!Q8BOmAM<;{$qWj2%3CRzR;+v4dwX3g}HJcJS;q0liPe4xYUs zpjU+0!Lttr^a2h$c=oY?-l<^+&;Bl;*H_rVvu_9VMoPc?r=zaV1A7019XuO_^hyUi zcs3W(n+@#X*-}U^7_ft9zZTNNc6RXW)R3NHvx8?BhV<;09Xz`vq^G&;;MsFRdQ8d= zp8ak}&obG;!=DGylR<Xy>;oY^WMc=<z7*0UD|YbguS0qW#SR|+E`%Niv4dy-9Mbb0 zcJORXL=Rfn!LzjyJv(6s&lVzjFu@L<?TP4FL%)ZqzgtIiAD<mOyM08r=GnoshedR2 zm>oR3D5AT)?BLl;Bf3M#4xar%L^u7|!LvV)=$05ecy?t(ce&WXvuh%{ufq<WeJi3{ zHSFM7FGY7Y*uk@XQ*^U}9Xy*&(TxCh@a$nJx~^ph&sI5jag~FI%f3U=4R-Kw`Sk&X zFTzF?S2=j`Q9hmJvx66(!MHj*csP-#6MuH_a28D`;q2hyRQGX&H~SbXKmH3J#C(6V z^RTJK8wB+G>-*6=xZGOd;USE-**7qjab55m%t5x(v1P<V0{&xp-@6XSs=)hs7IzW+ zC-ay-8`X@#3F(ViW<7(qp21ts;H_uyNDvhEknO|1IKZT7f|H!>@Ly&(I^rZ>wYUVQ z=|gYD#2*yxjf6qbY)psHp-8SN21QFSqmIx*IsK|^V(ah~`RqTr1h|nFB-GHyQ4tpg z0Ved3MwI*nC9QVK#N0KQk@*A7<;<A#HgeY%Q@A&VKuw?P!`1k|&riOFqHr@J+zt)z za|0EOn{Z6p_4z5~rhAyt76`0o@bb6<ThHL(8ldkO-hS6Jc<ULwk#u!M-yfOKf`bw3 zK=gSJ`V8)%SdJ~3a9%xPCCS+s?$&|WhID8pXyZG$RWWFk<ji})d;M^ZHfa0s*1?!P z_H|sq;o#JNvj)TploswnWo>&cg7k@zRk%hPH14k)8_@rWX2iDG0!Okil|yj+Gn~+k zqu+-J4Lxxrj)zgr237YLvtH1Gcr<92a6>9<XK5u$pNrXzgQkW%bz}A<ogHSW3YE>G z@W?jA_D>qKXeeTnlL5I6%MY3&J;29wPUTu=g=5BI_ROS~Y~TKP0|ftu_q)gMzkqJX ztfaJt8&Foid-*eObgQ^yD@5<pXw-1g5JXpMw5H)06wvQ}-6sb$)ZjqX?*Wb0H9UiU z^?OjGnTE-oh(4st(s#P)BX8M*QN(Guo^I^-xj%f`v?X4@fA}<yD!(ws5?^Sv4^n4( z{k(wuC9L>{-7p6Ie2vy3b%xh32#(}s>%52pWvVIg`b@%lujyM$e|O9XO!`X*OuF+- zsW^NJ4NCt<2u!-?r=0Ce2u!;7QK`KD<@Eb?=BXIZa3<DUdgK~T$W#M*-@xdk@mKoP znJ;-T={SRSCLT<>Bn=Y}Chd4I>Ho-sNpH3<7x5(zCcWi+P7n_!?RYS0$Ad{b9!%Qt zVA2&2W)F;3H28negSidQj-tWqJ(%?#%z6)Iy$ADO<H0ol5hn)G;Q!cz$>nfI=l5am zZ^JzA?J(DgU#WM(+;29*Uy6shA7SD5!dxRBxW6Cf=&QSb2y@$Rf<NX9bN!m|rI9E% zZDY^dCCXh`^1NN6+*>0&Z?`D7FACc|%3X)sp?gHRc7t~&qug8+wpWzf8?lK|?oXre z<vA>eAJcuJ+^fSqZ{H|)MI+wrjB+zk-+oc9A9C&=<(8nn$x*HwXDCym+&d^`YLvSe z)gBP#PQk6`X;E$sV$-AC^=SNzC>P)?=)fp<EEb;`<zB<D@vJB(9!zelqURkP<(A?{ zcy^S#9m~y$a(^A|d51)~OOf-?D0d{z*bl?&BExXfz#hyH@nD8*NoN{#?Kcx`7*g?I zHbcg5jlnPvS*Hgx1Ig^c3=t1zh~vQwDSI$2XCh-=Mm?B}>%nBIJeZr1k3E>7;=v4c zJeZ*s59YVX$R14nSM*>S@RWi*n0oPG8Y&)4{RJPOH0nKhF!jqRD}x+e2+%b<KYoS> za|q(IusWC4iw9HxBVBN952pUdW4R3RVCt`+1^Mdv=;{*T!MuZbRR1q~FuCvGq}uay zeJriE+;P}cAcb6jGZ(KW_tP=>uP*mNFaFEt_NAO-ay#Or$6M;v&Ks&8Oucw8^^OPA z_{kuoukFFqzZ_vM-3!<N+gAN*Dud3r$%CnPJeYdNgQ<5snEEw)p$Ry%!g(fb4z+ip z81`W5#e=DLJeYdNgQ<5snELl=S)6ft(W3~^-H-a6QMlLq2BJ~@x;>bkak;=AOnupd z`5f8%j|foK+0;0uVd{(ihg^C>!_WyK|80By2)_YkU?7x+p)*4M)Assazr-nk9hM-# zG)%qHF!e4CQ(r~Hd>?t4h8ZR_%rJ+B8CIlp-V*8EuCl}d8iu|)6%AV_4YLFnNKw=O zY8ocDtR186djmRgP>h<U4!jRX#l8dI#z6NQco(*+{sR|c3mG`@AgqW%1L<45LkG^n zoQ8p?;{Z5p;B%YeZ_5W3#^8&X1Fy#ATyfx;C~5P7w<FK^fy;2D-eusd&2hXQ_$sb2 z_Z@f%&g`e<PQ_B#TVBQGsUPG%!_q;JTZsLmCdl<i3~94RaoR-uhnXj$DcG-yU3len zh*!HW>R=kCNobg+Q@6q#reU%b8m8%t4KWu-6MW?ifx0H4VX_q(rs;l4U>YV{p<$XH zxDs=jhG}~6X9#)`&6tLNeEb0oLvt4=anLZ2AWTi*bkHzs5M~;tNobg+hxWxBYD(>f z_}O9_rb%d+rk9gL7tt_H`%$q>!!*6}880I=Ow(#wMYQV^4b!xSis1v<wg{9rNPJlP zLd2PdX%ZTy>G#Pom1vlzx06DNhG}|-8cl@~4b${4b%Ty4M8h<_CuyKz?m>}E!!!vE z)AW&K6dI<fLc_d;jBnslcEoU@VTO;BMhguyd_1M2HZS@Ff%5SjG)x{9G7U3aXqe$U zB?}S_Gkj<5JJviheug9HfZ;;J4Bs=!co=?{hwr5snT8o&p<%v-#h8W}E;P*W{r94+ z3_mmjuz?MqoGeB(%<w4^!x7|8EXFiUlhQCvhxyudreT_@Xqew3FVis1Lc=t-(={$t zK{QNrg@)OMo^Cb&%QQ@`wif02IWl6ge|&)>9`=Y47#{r6Y=+-L{L)O=9RKBW=TVk1 zUd`dCsjgXQnC2<;L(Mcy^Hd6g0Yfs=FwH{4H19|Asl}^Nc=G|&P`YnKG)(ifpK(lR znC9tsbF4zcP+5Z_s<5^lZFzGn)jTh}4~LCrhlXi(XqaY)hG}+am}ZBDX|B*P)Y-ab zp<$X&=B`H*FmDAqr8{WNYn(@DnC3IM>qNsepUFX@VVck4;FcJ&=5KQF7HaI-93&d1 z`5Y>QX_#hucI<6%1P+$X6&j{_*#qb=(=g5FQ8So^X+EEVYtt~z7d(o&Wg4aq4PY9k zS!kH%Q)tv^=!u4DUQXS?(BmCwWQ>}HhH1Wq%3vC%`BqB6h`)t7wFe-~G)%M5FwHko zbE&jlMx(Ubs5aW|YLCO|Ez>Z$FVQf$$%k?-k7=0P6p4L_hRIDmPYMeM<JT>BfW&I@ zbQH=>=Y^Pt$xp;NcW#EvAR4Ci;6vtmxdV9y(=e?^9E}+>Wd_kOtqYG><mG0`45DFL z=N~c0%N-;$h=ysMG4n7lcd*PL8m4ve!r5MKw#*<JCjT{R#~hwfp<!}I@(iV6az{x_ zXqem*iGhZB1fvt>{!?g}4^b4;Fu8w9!(5S98YcH88YXwwKTr;>N1|bJKbM%$FuA)W zCNxa$9*GGJle<@9Lc`?llUOR>AIF8<N{R^$lYe8g$_&&aG)(RRU8zjN<Q|lm&@j1& zBqlUW?qP`u4U=0XF`;2{k4j8vnA~F$6B;J>xWt5p$vq)4p<!~rkeJXgxu+#2G)(T7 z5)&FG_l(4ZhRHoIF`;2{FGy?*(=fRgB_=dX?j?x{4U_wo#Ds>)y(}@IVRElXOlX+g zYKeh{c^B)9X_ywFVOks-rp2LQS{xdtr9#8(jPo6)VOoTSX>n+n7KesuacGzphlXi! zXqc7}a%KP;<}g&iG)#V-G|Va_H|B+g$)8NEp)HMQnEWZ@@smZT1rzb3ns;cJyhFp} z9U3NIp<$>1reX3z!{pCdz(ooTlRsBtLc`?GTfu2U!{jfJn9wl!3vc2yp<(hDJ;^bl zVe;SoJ;#KG$zSp*$LRaZ)U~?95S4F!Q>bmyxIMl6nKUK|4YNOH4~llew2rE*<dwHQ zmth{$Fs%s<a};Hz_V|>RreaaR4`?p6tEvD>N7C(-M2+M-`@BF}lSs+SP>cPfoIfB^ zyAjUAw#E^*CNDHh-l1Xg$51-cF!@)T5Tv8}plAz(3ga=Mrv~j_b^(G+!{n8Q$rrfA zSiele<f~|ypCB*OFm3+^4f8A(u4xk*rfq-f8LD6!rfsssBCq3PWMUeoZBi%EFl|$) z&d!-$+f+*Oq6j~$D-ht{Zk)kxi8#|RZ9>DeIW$b0L&LPqk$UjK{%>J1reWHIhG|=L zA?DCFO*Bl~;wv~NG)&u(5)&Gx?I?)}4b!$nmaFr+DA0}prlJd!hkjRS#wAqN-!bWK z6B?#%KJ|hsBO0b{fy9J{X<JC+l+ZA3N7Hin6O^#Fgq&404E1~1F}O@(8m92yOv8K+ z71tDmhAAwPbAOL%n8IScO7MdFITO<`g=!k6P@!Qy;VeSK6c+G#Qx=zjg@v?LG7VEW zk~+yWOyL-+)~jv7MFZ0?1)*UI^L)&wjS2ci;aIAcHuYdC@(m~m4byh)br>+FVG3tb z0*yY=Fom;T<CxGeg(@1R3-fC!k!hHM&@hE_J8&sP3lj}fShgw0goY`cH<4pP!xYY6 z#4({^3KvLDp<xQkmvfrXFokbF$1$N{3KxCAF*-EOMD2Agi2BsSG=-RkDV$AXkfxDN z)1EEIph{>uF1NAxpOI-$^d_bms@>-TFo&*;GrN}Oq$!Bmv@!-oTOm?MO_w7S)mf<n z>HAYUHNPsIcB|winqw$^m788^8Li?prOM<fgs4t*B%xt0rsZh}e9p`DQ#vxdjJerX zoS8qr74j66hAAA%ql0zAG)&<zpE^QYz_2ZlmuZ;xwP={Vkiaxd`&u;2DM(-%ru{3@ zFqdKhreWIGqG9ewLQT8SFzs_?r;uHweV)XyiN3{|gobH%XqfgRXwaw^H6$_Av<nT> zKA$RN8m4^_RRJ*t$xOqv3k}nLXmWHR8m9d)>MPE6Y3FD^k}9NYg<u93Dl|;{Nlx=l zPMTM<lrsqp(|!t7#57F%skD1~wO3&V(=hEq!?d43!>2o9`)O1Kci<5O8ry}2X+Mv; z!!%6$`Bc7F`vIme4#Bl_yU;N0=hAvhin)Mlp<-%x$L;gIaa*FhU1*s06-l#+hH1Z4 zo6R&#`(+Xn8m9ew5)&Gx{c;%~p<&v;FEP+C^H4!|yU;N0*HW{XhH1Z!I^fm*7|Be- zv<nT>epOOG(J<{-)0T;b@<3ltjip;IwS91ZpJ|v5p<z09O_m}Wro*9OIvg6NV-MN` zu<DqG>DW_#yo847*o(@oqG7raJrak`0UbiabR3{tfj4Y>Bry%sxlS790VFdG(<wAe zr$fVZ&Y*it=oZs3od>p~4!Y|@G)$*M!*tG}8(DM@fM}S`L!|yZ(=eS54bxenVW>E! zVLF9|>72I@SAh3FuoHLAKb&Ji!*nh<n`1R0(J-A0C8y9ZokvKT&@i2gF5{f^S9a9B zx*ZXf;}wONhUq+<#wEQw;>bnk)`KG200u<{;X10=r$J5A5|}%M=29h<a-8GhLL_CV zT0dFhd_-zfxF5naOsCK=omV8QpJ<rQD<vj0Os8&8Lc?@kEh8v2Os7M`bY4rXVj5;N zDqtF>OK6xbhlc6eoYHCa5e?I|g=|qZ=nuVXU3)qbnTF{W8m8N!VY+k4!bHP#J2Xsp zURn+s=4BMZG)#}sFg*?p(=#(EglL!^hlc5KXqcXZB`0W@7QE-fG)#}sFg*?p)ALMH z<Y4^p^f)w3PlblrAB8Xt)1x#@k4wY!q<k%hX_%fW8s;@D$}~)m(l9+P4byWr|2kn@ z8}`SES<g8dt!W5w%<XY$n4WWec@iTuOwTf(ice^m9+!scIp2SiQ|b4gZZF{%m*OE# zOY=r(7&DGyI6&e79|GmmVbCyNL!4<CBQ#8cP2umGa11l!-$PkU!<apKxeWYDzJvhN zFy?;=4U@&;oM{;IZ__a2P{gb}em9NKFy@9+`B*A6jH%EtMXbJAlkn%<Mrasw4Gjm= zFy^-1Q3{5cX&B?sFvg)_j6=hiJEg9=(BOb*92&+rG>ma*7;}#-muDKrI5doLXc%)7 z4H45YX62u`kwU|m`z0ncjCnv}pkdBNH<^YpLc^Fxbv&9D1A{RR4Pzdcv>Iu@L&KOS zrAVP+%u^Br4Rb9j7-xirF%Qx3GYw-_FGqq``x{J|hA~3Jm=(08G7V#1{uvT!n2Cll zzow>Q$MR}(_<f{5q#9#{hA|UqlVuvl{Pta0hG`h{G_6I}Tg>kwEQJduuXaxan1(Sz z!<cuHWr&6`6&l98yIHc#u?R2?V}yn=f6`^7@DHgoRCvwB$T-Ug4P!p0s+fi`pPr1M zSNi~_Ov4zVVa#8XJ`xRM{!aB!A6x!{K#frv##Cq+6Z%hZ9-(1O6%Eri3cL75_@y^W z!x-P!(wT-qX;eKGpFJFTnT9b+!<hcQ_K3fPYg`(}xHOEZ&@e5_QBsXj8pgOZjH#kw z?n6GNVT{r+CgaPf2@QiHs8K}2bgw~RXQMQXY4WL#o}=bqfb#yM2!p{vKbeLxO2e33 zxvxaSU}36|hjJ6-tuac&7?*}IRWuBJn~Z4~qcn`^Dvt@#Fvg`}j7!59mxe)o)JCRZ z9!7v^7^5_dd7n06reVxR{tC_~G>qBUm+cfcVE%;pOv4zZVa%3EUiz$_8Lcs79gef# zX9v)ZrZkM%HfcI%-A=RSz3e{7O0Q^6FiOLi9h_2k)a^@X7_*bM2OnWO2}`a)yT=)& zVay)MlKj0>vuCC4k0LMAFh*$@GdXDx(=eurhA~t8%a9$r4}UVwRM9YPc#_66j8PiK zxHOD$X&7^mFDIo!!<d78*@1<IF|)NESO<roV5VV=(lF-h`b+KgqYBL-K6QaVGhycX zlpWg=e_zg2(=b~jq5C{c1{kGb48Hz>xNNRR`SePVm%STPreTcIFlKRib0r!E3(rFq z-ZkrS%|Lid#wZPATpGr>G>ma+7~|3~rb5GXZ-#<1MrjyxicgJ_W}fA@AkE9p!jx$k zqcn^;quk6BshQvOsdc<porOS+5gNu^?d!=ap)TecjYVGfHJs~u-%OkDAv0BAK*N~p ze7<Yf@-$|QF+#(byL~-<;Zp9=Sl%oC8M&B-F?UHx_xe<D&yFFAyU(X&FG}G&wtU6| z8m5Fe(=bM97~|3~W~EQH^O!G2UZ!D;(lBO~PvtQUV;=XnMi@UtOv9Ke8pb@~@5f2w zyzKW8*nT<<iqbIVm&vFw4P%}u4@>q*<YgMhC=Fv?Ea!c43_A5vIq!(?;fLWLlh8r) z+W#;OlWVQPWlvXb^*~%VcI95j#nzCn+?qT#tF9b9tQy*tn}{s+UAaD6W9RA0#rR!r z?8==`i|%&iJ{^P?0=m2rzebOkhB5yR4f854FqwuiO2e3e0gVyUFlKP@4#HSh{0VC_ zB+%<9reRD*qe8=&p&At$#?)(6Xc%+7Pun<y7N#K>i)s@Z#x!bFXc+UdPb;5k7&9z5 zjB^SNW12Ll&@d*eQK4bXb3Sc(3}cw#!H;-Bp<zt3MumnkIgNsbsmEE$lKt@dp-~#f zxHOE(2Q<R$w3znbQ!W+1`FmiwzSuw;jnXitM_VKQhA~=)&@iT`Q5-JM#G*{Y7^Pv1 zOT(DnfLbM0mV&P%%=^`o2rvy}l!h^*0%``+FlLkB+niBo7}FWhs%09+Y#Q9iNuXi= zh5*wrMrj!1(lBPLKz7f_8!>FOCp3&%Hw`l$=}f~IrD2Rq!<ZeD?FG+2ke6u~qcn_h zX&AG6CGVxk%QTEp8pgOZjM=A>_fh1XfWNRFV3dY2E)8R*=vu)l^Kn0uX&9q4jM+Y* z@n9Op%m`jUS+uPa4Py?}^@Zhnv0T$sszYfQGrOFZXc#l6l9#>~z%-0e8piA#&^XEt z^z~p1q|>pF#=`6w&=B(Lb7pR^Hzx@VV<rVOV*LJ`nHS9GB%xu<0Rat=>`C*3Z*r2* zFlJUj;~^bb5M04YLc^Ft1L{80FlJ$}l9NEge2M_mFh*$@bF6Lv_yudgaffLbqcn_J zsyhIFr#D05M58o}IVqr$3}_g0dXPtwmz|F((=bM97~=<YsAd|*oEnTrIt@3`FeryA z<IQ{p0!+ggrD2Rq!<cVXhWn4m%QTEp8pd3ZG@of0vnZgo$25#t9^B4#2n}PF1=K>O zVa$cWvj__fGYo%w&oqot8pdoM&@Rg~jJY`YoC`q*H$g_GVT{r+=6mJD?s-UDWR!+6 zD*|dM56>0BHb`O`#(Y1Z%6dM-al~91?1QkEy#!OHVT{r+=11;OT@_HpJX8-Nz%-0e z8pb^3(=RoDjoe%xT*Vnts5b_!!DjujQ5weFl#CO9uF>4AhX|oz%q<)h8m1Y`KFmi* zrD4n+ni*TcK1lr7C=Fw73#dih&HI8*Bry$R?h2@W>E_B{D}-_Wiz(AEMrjyxkK4`r z1FD$2`4a?~hA~RRm`BR3zpe!puF?YnP8?oFUZ!D;(lF-Pq_s@LnCCPvzP<Yi@-hu$ zl!h_CO7eQ$@mRcfZj^>GF9g&G?%8XBha{$9%qs!aCp~*TXuy1sNSHDWW0Zz5uev>3 z6Hvw6v!juB<H>XtpfrqmE9se+y%C8_!x*Ju%zH`mn1(S`G>myaxDeTyhItABreTcI zFy_N#Ij_6#w$v@9Vax{swSc?zx8OwtnT9bR2UNFo>(k&52z%L4Sn&SoxQAtwhB2SG z-THe#6?3=dA}`Z0Mrj!1g?iQ}7sP%jX8=OOm>`rBi=DleZ)4FKqcn_hX&6&Q!+d~z zOv4zZVNBmpe&vLQF*RDI&@jfOVNe0JF(}BFs7)0OGXNJXOv4zZVa$-EV6S^T5)U^@ z!<f2|TFax?6b?la(=cXeNR5-x%Z4K`AHRQ?G7V#thB1wya{wG3QpGfSBhEvBX&AFk z8m8qAq}LdwVT?<|m?|13H%@67qcn`^cGp6WyB18c7MO-H9U(QEX&6%sKjPNy&hRYi zrZ)0-$QPj!reTcIFlM7rkC1W!y0MljG>q9qOA;E!{ERPMn1(T%hLcbj(=g{Dz%-0e z8pe!HO7*%QL*gm8Ny{{h**v6v@=Dk)ycj{IVa(PcwM<sRxbQ}Vz3g8wWg5mP4P(Z- zD`9*{74u3UVS;HGvrZbOdmPe7o4v!H<~r2AS4clCOv9L4xl29t<ybQ@EFzzmJq}Z* zVT{r+X4jBfBc}y>gnJ;JehZ0)L7`MR??|^Gz%-0e8piA&>Oq}p7_)Ck4PhF_Ob)-p z%T4flKSw}l7^5_d*(RX#N2X!S^zeSpDKw0kp&Pi+Fy=rWYN278wpSX)C=Fv~msiBd ziAc=&Mrjx`E2QS~nwT55BaLYob4W<7ku@<d+!FJ>-V-qu8pbFMV-9y$#r%+}=0Ug~ z0j6P$(lBOmG6-_3<VYTUVmEr<L0+L@jM6aXShussxt%?}s<VB+rZkLE8pfPd>Fih} zqO(fFm=i*37I*f{FpV^(VazEZwLm(1R@jaCUhiB?g@!Ro!<f_E&VDnbs=2clBk*VZ z+Kw?w!<c2-JE39Bd7&KWLBl+Qyle2QeuhyR#(X;&7^Y#&1tHbVG>o|@yp8L?APt&; zU+$^g9i?H+3U`n$bqDFPszDloh1*f-BBL~n`C(;{4n`sdNog2!c}Pv<LAoZ~1Zhme zm>-AK0vV)h!(9;edN06KXc(h3jJe7kr0YVeng{7#1ek^~O2e2N%Y#Jwj=3q6>&DSu z_xs2@&L|CIZV9QPo)jL?m^;E%6kan1;X8SGFpxXoG!R2G!6*%5eikOz!1N1<f~azC z#U9AZG>lOi#@tsPek#M<9a80TGPW|@7}<Fn{T2d3!x*Ju%){>RKjIGms;c3?77L#= zorYg&81rOh_}@SxhF@tI^H@kt<l%oVycI#FVazW=YJm*@^WhT+d%c5qOlTORG>m!1 z9sU<Ws+v1)k(p^2qcn_pCF!`A-5H5Y!x*Ju%xmQaW)DGP7q?z%81qIsvHJ%|Tx67n zF>6Aqh8zE0xDY|6Va!`0RUnOjKRgp*FZ&dxOv4zZVaz*j+y4+!#oYFH5nvj|C=Fvi zs<gdkCp2#wwL)na^Vf3ovK>fd8pbFMV?Hk@b{~MmMMh~D^Jz%caNDD3B$AkhF<*pK zfwVmpjYk-q2Bx=QM3xw(VN4J?CkA~Ys+ik;D*{7twz$kF4P*L8`fI>9rUz(jf|q?A zS(%10O2e3Rl>96c4Pypt6yG!-xU<qQMrjz+;O1>C=k4Aed4F$|hB29ln#BEViRiD) zn1(S;5mhh!%tyVL?`0Qa$~25o8pbrc{cMe>9o)~$5MUa{C=FveqvZ6FXc*I_9mV<n zW5~-ij8PiKjEIu+K%!wxuQnd%`+q@RreTcIFlIwH@2GO#Zo3PfQ5mISjE$&G+|ezf zFA!uJ#%vr>_0rKTqk1gjWhZ0$FjnZ(MrjzcncLCP5w(LmdL!~O4P(X#4P(ZZJ4!T+ z**4PiWTs)v&2qN1(>6%jE+Pjnp<&E8e!YuN$=<_!reTcIFlGlWMratbV<fkRg@!RZ zX)2ydwC}1kj8PiK>=IEU<<|Y~5vAiiMQ+_!(=dNSrtD=%-46%8B}Qo&vrl<^xy}1( zn}vokdqvb#Hbcy$Xm2iCPsm*w#<(<$nXD}a4RaTkIuOG>$tVqDrYCiJ-5(=yiBTHH zOpOfUyb@<e4<g7kj5#o(cFRhf6TO14m(A~{G>lOi#vJ6X#6u#gm{;P_$jda0Q5wd~ zbywoNWF<13V&<1u9MLdlL9*hQhA|5@ip}*|6wEY?Q5wb^<*vsi?s_~rS&vM^m}7Jt zgoZK4Ms&_6E=d2~@iJR2T}3DjV~&rg8?u_0MwDJ&&DAu_Ysi%SAyS!!F-pUj(~=>V z9-dxa%~XjwIijA(YCa<x&1LIqc4-*n(lExQVa(YP)tt~U=A7tijuH(+d;B2oVD<^j ze4jT6rD4qZN$b4s)E;<vXq1LA=SFE{<_+Sz(ens04P%x^Hy|t<#KqBj2;=z&7GxU6 zC=FvSayN)eBC41-h+UDFX&9q4jJZ77AiV4gNMstuC=FwNoHUPV7;}ZLMWJELl^PWq z#$2UQp<&F`8WkGGT%%E;Va&B0Ez>aOI^MQq6Zi=2$nY>J4P$Oh+TnGN*b^?7Q5wel zB%&ViFx?(~h9J{0=H`g{BExh?Gz5!y*-4l(4P%ssF}JzHbZ0~r^Dr$&UZ!D;(lF-U z$}sipr8JCD8pb@BG>>T*^H8#%Fb!iK)~L`h<`Inw4P#bmRA?CUs78f`F^_3fXc+Uj zMumnkPw1Em4P%~+<iUc_Fy<-FEHupaXfx9=Mrj!HY|>`0`vfGOVU&h3Pe;^y9@1B% z-H^mIjCnqye#?;lCgS)0ve#k`(=bM97_-_P($^xYn1^%~0!+i;1&xNC4nhrYREBh$ ziAuv5rD4oFN%NS7G4E=}g@!TjX;f$!<I*t3rD2Rq!<Z@>W(ulh8pbFMWB!~}>vdm< z#Q8>P81rF79pF*<EIJ5DOv9K@B5Jyf%IDE>2z%L;nBI!Zq(w$)81tz+D*uS6Vjh*x z5MUa{C=Fx6RB}2&G>nN-&fTa~D!Ch#?cQ5y7^5_d*&vmiM)3uH|CHRbz-iQY<YgMh zC=FxklDu9wePQ)Dqcn^en4&guM;lVRB8h1jGdM-{Nk<z~2O;cbufcRN&iR%arD4p_ zlyiPKEJYP_M_)jIX&9q4jB#lgQ=wr-wC$rbjQQ7Tm|V>kICymBmN(JkNv~x`lvQJt zhA}P;W2UG26t#UPAs^E)MrjzcbBg-MG>n;=+860`7lddS6h}>@mwUT^gg~898pg~_ zQGGpTC^F4SeG_3XyBbrbVT{r+X8#n82GcO+;M9#sr;>?=K|$0I8nN0T`zj4n6dI=Z zJ6ZKY!xZ0;n9wlAHzii%wH$@T+KWoV6kQso=+ZDnmxd{h&^IFnG+cHt{x-kYr>5g| zUZ!D+BYi5miiWWtA^x4Ps5DH`rD4zn>H*U*#Z7$5i*7LuQ(TLNDXvAs6xX6*iq$ks zILY%aLSg*pKaF0?a>OSTm4+$0G)&Q@VTvvdQ*>#VqD#XRchdd}4O84%2WNQ0sJ<AS zT{J2*OmSDA+C(%=_ibp<xZ(o8=Z{#S#d$uxvexq#3eV@)3%u@^kYi$TvEQ?x7juu` zx1)MapztD}9^!i03=SV-(>Tv7Dh-43sH3!<5DkNcshWg_DIUqMPzVh}haRS3ib}&2 zkMU_9ElV^^@mT*ZjtUJ^bZMBPOT!dh8m8#dFvThwrnuBUmWve{rs&cz#cCRcdN(Md zQD}J^!J4AdFh!S!DOPBhmMw8tr>>|p4Du6}$7h7aYkf+i&NVlpbJyv5i5f1)c~|jz zjn*`*!G%e&nuhrhMYJrzvIB}r!xUW_rs&cz#R?76@+&Mhyr?t`79Wdv1JN+WyM0Qd z<`4~2yho!#!xUW_rg)!jVnV|dS0-B=M(0G7($W_P*P5czFh!S!DOS-i=OEvlL-EAB zs5DG*o<2zu8m8#dFh!S!DY`UF(WPOEivl?e2n|zQtWlw1ibra6gcm%4RxBSev}w^1 z-iS2)0TM=`34g{tVz5MNn&vM4x;J77en*j17cRi%@`wyAMSsN*twvx4l6c2H!3#E- zf&+Q)tgv+u?g34cg}nYvV-TA{e`yy!Ou^|N^72u59A@>-kXTLla2;X?${)}S2=~WP zzIUd?>b&@Ll)TLET@-$MHs;UMqQhIMg!vMS!bcB5YyoAZKX!NqquaZXzONn>b_nab z5j%o@r^CV%fydunUy(F@j2B&reC7BT(9jWI;`bgKPQp)m?@>vGy`vFZl2kZw7Gg&y z6&^!5kD&!op%;CMzzWn*!sWSF^f5XIV6b~HkuJ~)9P)Wm{?epGF5^Uc&7Zokh)O(3 zVgver)Qs378snye=Xkv*Q+c#fMrASo6p3YI5cCgv8^c~o(?94P9&&rnkn-~UN2a}J zNzLuyRXD%vJzG|LcR0Qtv2Ts$8shN(u=gHdQdL>s?@3i%P4(#j-E^p?=>)y22@;y7 zK_rWbB%-3A5(UH|35tlAz>I?8V9q(`F^q~ihcS#fJL(w5IL2|j|NmOMy6Vh$pZERV z`@Y}3-+kKW@vzt4`@i<u;hZ|D&u@=bSR1wGH2=fFur{eR!+&Bptj%i8^uNOG>ON=a z>YM8iN7wuOUah764`X0$QEQFAKU+AHI$lA@OT~T8QuAcjmvKH@&5bU0E<Uyh#v>l) z$$HCo2CReaevThzSC4u8ko+w|HyZdebJwu4w<$6323m)!Rp7L^@6*28W!;yg{vd+m z-20e)xi9Z`F@NZvNJ?rwo!J-r{Hr!NYdr<OwC*MP9BNZR>t!sx)R(2Yskrq{nwR<V zq18>LgYTOS^9o<ab<?W0Rd{jRz0zt|I74ngv)%BM?LO|iOtZzmJfCg#;r3RyWvxeS zGX<^Bu$f0~GsQz!9PGG{`TWZ~Z&RtZ0+Kh5uHL2<Z3`XuNnbm^v~AAf6^{FqH4kiC zKJOsMecI>W6?>Z&48D+VpYd-)DE_98*J9v#Knos*O>Z*r-JL<sOjPU5i2mWr;hHh? zQ~sIJ`l&tc`>}@IpEIN71jUeYw-8s=&bGz$;?ZHuZ~O1$O!3<u|6`n~pSg#g;=5>K zCnitrfrDofYt->J^4qJ)cTnVQT91t6P0T%Ak~m>-yx(&wl2^x&+#(*{_K=iaA^A3Q z&yfo2nR~t@Pht~S@C&=i<5=NpY3)qrUL&owvbF0acNZpaklbn}Zxs(0#F5NJPMti9 zKj>%Agv<Xn<kUOj^FwTl9WHyetjH;v8RS$P>7rm3g15-2a>?k1ZSeqXRL0bAAg9Ji z-ES$z6|WN;v>(PJ`4=V!%Y-BKA0wyw_Bu)Ya2@_f$f?^<v-nwT+(qb^{?RUvpi$)1 z>#X(nXf1Xzikxz>FBGS^L&(tZ7*RsAgeC?h7dh33b+?Pu%^;`lLm<QU>4FoD{|s`f zZYD-ZkyE{yROHlPCKWj~n#s+GrMOAfgCeIcW3D2n?q*VvQwO7Ii=5(jfGu*0ALzBn zDSialBB%J--+xC=eMD!9oZ^65<kT<BRpb;ufn$+VU05M>uo>UR{p(#$_KX_lDsn2# z<fLYVzC{-Qr$dlDlOH}#&fE^kv({5Q0*k-+zT=pC-6|xv%1Sv4P5x(*Qx9Rs?bQ9B z^zaChVb9y<A~_Ee9QI$$$lRewPTb7oVIz^;{ahwbLcGNv@q@#bJ~e{zDt&5qCjW=& zQ_JRYRULwD@yXces$;5z8x);}f3_2#RK=I4F(3k^x{SCA*+l`r`6)oDE?J;dDhrfK zWr0$uF*2eAN~N+usZ<symC6F8Qdyu>YSMa64}nstEKn+y1xlr6amNs#R4NOUN@amk zsVq<`wOE{u!?>rGNOB@hC{tOWR4NOUN@amksVq>cGz*l%=QC$v{}P~7T^1-++tcMo zI0PtFzaPK(AV8^YrnVrN1C(lh`<nhJ<KICwf5^Y05};J`FZ=@}0ZKLJx%?v{0ZKI& zyY&N+=AX<oH@NRH%|CT%?&tEaV+1JGJlN$U7y(K(4|n-TD*}{i-rnV3q6ko`d4kJ7 zH4&gx^Hi6AOCmt2=KWm$b%+3^npeC0g9rgiHE(eFR|f)=YChZ(Fcn|0Y(CNB%a{U` zYTn}U{Ye2zHDB#58G$t45Ny7|<BNp?lxlv++XG8pfKts*ct<nM7uK3T@c7c30HvD$ z>hV1`0ZKLh;_<C90ZKIoJ|BY%P^!7c=lf6slxptp-^nyzuW8=F=PNn_lxp6`=esWg zlxkk?^NkS!N;R+Y`3{HxrJ6VRe7Qn^Qq5=id;>y&Quw1nzE2=PspbcLKAIPxRP!r7 zA9M>)s`*WykFEtMg})}_BV++eHUHh`Q(*y0H75c-o)w@}b78<It^$;5ZV31|Q-D&< z>3~lt^B<Xs_=g94`X@lC=5YZZ=m}7&c~QUzN&=K>UJ>vakpQKd&kOimL4Z=t7YBS) zAV8_+KL)&!E<mZ~`vcw^7ob%0djT&d1Sr+~QNSC#0+ecYV!R(JK&j@u7;kY3P^vi< z<LyHMN;NNv@oty^rJ8eqQoVA3Qn*>P7B@-+D21CgyqP0Fsa`oisa}V;yf+{~sb0ss zyjvhZDO~>Zc7Xt;aP7@YcL7S_0+~$r;bYNwufNH0$#sW+i=ph5!AlJ%UaD6HFO^)0 zM?byGc-5TzBaRn+=JMJzS%Je<pTl_VmRwPV<T<?PDti}8+#L~zZ+A9Ns@<5s@rLtS zMoeUm98!=o4d<HMevg50<w7;)e>@Wz*Dyo=mOkcxh79MT`8aWMg6=p8cE7`3KI=U_ z(cJ=H9lWZ$@Cz8L<7o>oWj~F<s{gA)E8)cZ`Zz<06&#DwAl@@vgHk6<co^W2h#W86 z9yxya5zE6_$d82=ARP}&Q86!EfNdhY6?O8R0#bg0cw2ZFgl<@hA9T+tEP`ej8YzvB zPA)}Od>V87?NBFvz!gv+sN^?r5Z|W@^8A0GZhX;JDF~A1(3;1}{&8&fz*nUpp%k9q z$psuf;}FvEVj_odt&LyBGBi<4V2`X?WdhBGD?Kw5VA0PsCFMdU=e@}T;5>L~J^BP6 zdqG}E(U(XJi;wVs!N2j9)1?u||C*5<%+p8Qe)aE#ByMjxB>X81XSKa0;6KU04^yi+ zIgVvJE{D#4jH1J@fm__DNqVfd3kgX6J{*p<4@p5qaulmgB;iP|B;!ZCDFxNZt;mW` zBHzewrq?6ClY&}*Us~&MjDh1u|4ZuEt7ZHG#(k_>z5SI?#E(;}pMNSXd$XZMhxlY2 z;x-HjDMy7gV+gM?KGL5=SEuKpeJ-p!X>C?3@Nc3emrJ}yQclO`YBL4?+l>1{wTk_o z?Dxg8*cwV@dB!i%<yqmkup?LKyw*CMgQDOI`0c_Et;R1wx7-BaO)zi;44*{-3yJ~~ zQ;LF0<P>w|=o(<)9lWXzUKQ4AMfpp$ICypNs&EImgIDF9#``;d2d}DwSJlC*!g*W= zuj>DGURB|bxEm!rlFrH_=^Wvabk6cfDsY)~%1E5GcK%Hs$tV;HkEFBmNIFM&B%RxN zB;6LGVsRJCBk2<5k#x!7k(`El!XqhF9!Y70M^c*Qkvxcs!XqjBPx45<hd_8FWy&Mz zn&pv{o&GEed5%dQN!eyr6$(Tit~bxcX`no&0FPuR*mJA#;!K(HNXjm?!hhwFlwDSb zsyuQ3dmcPqP9ZOcg1vG*f=BWM?4ay_BadVfev|*0#8dDx<&l&{cqC<wUm&1=<&l)V z_JPE&Jd(0E=!0k7<dKv`cqC;J9!Xh*M^cu<BjIu=JR5F=M^dIdlClVor0f$tfe-v+ zd!v~MkEHB#YVmzNC%6g;o|W@WaXfi{7-mrR+dPuqxD_ZolCpLl$(yMDuRM~nUheba z{oi;bWxd_+rR3juBxQZv5_snnr=}qxJd!fYBPomWNXl|}BuAmH@JPy)M^YZ)k(Bph zxva!ZNU)oBB^F!@J1GB69?3M!N>K5C29G4(nX&$#!Xp`sXoW{op*)g`_1%yoJd(;R zkEG)G&d9~l#7P{2L~(`kNGh{Dl8OgdAUu-FERUq(!Q+rCJd%oswjk*Q%ou_HW!AwX zVeal38$6OLkY)%{4j#z^NDGgoLU|+=4-ZBTLn-Xs1=p<Dk%}vnM^f=x=FmkRNyT1t zD?E~l*WVD0@<=M);1uydMjlDUdvqfQvU()i7bL#syb`wXNGg;^Qt@%-m`WZ=#a}Z{ z$s?)wghA6Oc_bB|vKu^}kVjJSnU;Y^az0!RMOJBr@<=Ma(Td6=smSt39z{jrkyI&< zq-v}Ntvr&d?OBf2oPggdZ9kraN8;cnlkiBYlt)svOGZc@N!57Ucc*YLDhiLJN_iwz zQ!*9FBdOZWRumpdRhCDx7&76JR4I?7D#9bFnx2u7M^ZIIEgV74hD>-Q6_!U*vB<Su z7amDP4v*wP)D<2{N_ixyM&4fHZ`rVVJo(7-NcxmvL8bn8@JPm^rQ($GNK!NSLoGa# z)ILl)g_}?;Jd%|1NK$(-pAo-NfQa{HpnRi+Jd)IYTf|ZxNowYKVr6+G^i>qF!NQ=c z<&mV82#+Kc;gO^wJd#v|N0N&0NKz3VNh-@DsXzn8Ddmx*j-llQ2HW-pRpcbLN*(2q zq>h)alSh&|L6YQ=q)wD1c_gWmBuO4g>SReig!P%)KqtZ@No9E?sZ)1Dd!3fx*q+*W z2^7L3No`^n!XrtY#^k^9NK&U?h1_-?$(JyMN0L$=NopNOjYCf!Noq5@!yBB5qH-=4 z<&mUrr4Qkeq;6vYM*LCa6b?sPcqA$1k)&>7jPy2P7`)xi*0|dh&Zw|FlInlskyKCL z8D6Maf<3)@hFbr|BdOkJnN$meM^e47TFN7-o++X%kED8*X0-E2s%J|^mPb-OM>E=a zB-L{@qn$@mJx?>*c_h{QYeqYdq<X$)wDU-+7f42yM^b&TWLO?a^&x60kED8)THujf zfzb)7|66z@&%@R8ILHUp{~?d$SPb_$SdTNR|BXjdefL}N!#Vk&26OU9wUkFveUDno zBdNYuE#;9^-=~)HNUFE0r96`A`)TFyNHQ5{M|mXG586o;9!d2>YAKJT`eC({M^gQW zTFN7-eoQUpkyJmfmhwodpHNGAB-Kx<r96`Ar_@p&N%ga8DUYQ3Ikl8WQvD~jlt)ti zyjsd5seVZ<<&jkXSuN#}RKKj2@<^&*QA>Fw)vv0hJd*0y)KVTv_3LUWkEHqywZJ2J z5_2Xzl5WZ)=@#LUbc^svx<z;--LgEA-gp^McqHAFN760ABk305k#vjjNV-LMB;6uB zl5V~A%m6%+UC@B=NNRqQM{*^KD{7QSQgaNW;YuTqq-I?YTwC$9fIO0#2#=&D!Xv4P z@JMR1JQ6zi0p}F8HOeEY*|4j)R31sqscI>Yq-N6^DN`Ov&FN|>kEG^|)1*v!BsE*E z6H9p{HD^60mhwny&UsBNe)N}JE8YdBerTGhdh$qWPT-hicqBuSjW79QTg)c2b@k!V zQsfDbq<e-(vL~xD9+zb$bPESZGnaAYIAA%7&SVjTR2~ThCAR1mq)QpGoAL7ujKWeJ z34}*dqdbzD2#=&@HOu8y(VDk@LVX_9i-Ia-HuS*8@<?_?^1DND+FN6JBsC2ZG3Hlz zBsDoal2cGucqFy|1s=&Q5GQJtM^d{tdxj>2M^ZaoE$~QQL?z*o)GCjpb_Ux7kEC`V z7CFHO*q)1ooIK+UHid&-%W}L{UaLHk+6a%NHo_yRU7+pYEAfjV6CO#e@<?h|tV9mi zG<hVoD~}aRc_g(5tED`W+C$V*9!c#g)qzLC1o0zu-M~8hU1i2$^z}M6rdD|*wM*Fx zHbx#v?J~8LM^d|-<CNi%)E-J5KH(`mlG+>|3Hx2SH=b<^kEHH@Gmqp5G@PhY9!cE_ zJ@<EnM^d-aUL`o*#ZpOlBz3twlDaI9<YlR%Jd(O)a<W7oN!@a;O5u^z9n4M&kECuj zn{^6*#a4JEb;={DTk@_9lJH3C4r8-i>fUy>SQd54BdI;?WYiHJN!<x7;OLV_Qg`B3 zv6M$rm%}5;Lw+F(g-24SJd(OoW0*3EKg9bBbsNjYQXWa&rWUc3M^bm%RI!vtQg^!6 zR31s)<`q(=Jd(QK-zt{!Nb0scD;5t;W6*l>uh_WkVF^>hBdI%?V^G48F5#Z7$DnMA zuPj6U66zNPk6>HM)?IOcoFt6WzU?_BOd^|;Q51B8QOwZw2*ozDZJ>N8%Nc%7Id?0G z0p)wM{K{x~HZo3e2}^ZzJyL8F9m()WR#VR*&<C{AKFd+zF65?MTp+!)9qQCs9!cE+ zGCG(O;gQrWa@i5CfXXV=6&^|bzwk%~qCj{g_5Z>nnTrD9k<|Yuc_fEJAUu-#f8mi_ zjDkeH@<{3z>rSD&Nc|GEutXn~O3EXtkMKz956T>}6CX+?<&o4cWrM;asb9e+oWg4S zate>6UU?+-2V{;e<dM`bVqYzfr2b$w$ZG{}6jX_N<&o4M9SQH4On8ZzQb~Cv_3PM> z@JQ;{bN6%#k4J{^Na~eGQhx-84--KoNByyEg42+=0*Q)x<&o5HVt0f`Qhyr#JB80; zD?E~V<&o5%%6ZGUIi0Q0O<})A%Oj~*9!dSVnXt(tsXxz#Ej*I?^VL!wN&N+CDUYQ7 zLLDIGk<?$L7I-9+(158|9!dQ*3`=+<_1CfkPT_hK3y-8;c_j5$X4)r@r2Z<dOav;O zzmCE3mP_G>NX*1}QbmLENE#+)ROFE~M0g|(ll59G;a`fS-mt4$$|Gr*qCZ~BBWc); ze)*R?MFDFS=Jl{Vk_P3GH0*0vfm7KNMZzO#{7oLoB`6jiNu%;e8s!}-9^wukjUTXC zyvKxY36G?4c7Qd-yFTQRH13}Xum}f$#s$2Q#d`qcku)yU_G^Vl(iq{9G-i1ubSFHL zM&*$-E@_nplt<FIbi7!~BWYZAplzHylE&p)Q+Xtf2Wgq|NE%nHm6{wKcCWY@rXH`D z5*|t8fgG2Tey~xi68{thTmVHuC!~wnu**R$p#r(Xn9D}8ej>-kDJUvsvu;MQ97ZAU za0!p3QF$bdmuIG*Jd(yM)KVTvqg_zSBWb)!M^JerjS(J6<28&*cqG-hH5VI*U%DR3 zBk2+0k@OhCa!wz4Bt8C>M=~FU!XxRaJd&Of9!bxdjF>!<p51k}6F!MTJ!>^`@JQ~0 z6XB6GDUYNn!Xs&#lW{^GNz>enlV`E|n<6}trv0@hcqG5VrSM3alt<DO;gK{wpK(bZ zNmGPJ(v;<qkZL46k|xU|X^Qelnqsc?BRrC(93IJ5NQFnzWO*b_Q65QClt<DO<&iW+ zc_dAzy80wWc_d96UAo7<TkhlErYMi3=`{BdDdq1!Z!h5&m-hSE)EecHn6b35hdRMG zNSurHDW}8Wku+gyghyhOM`G*|MR_D<`=?M}cqC@m5LJ0xt-J#X;gOjCkMKx-MnJ+N zG5=*ANhAKSU~Vmb;*9c0%nj=?N<65|!0k$Nqgwb;N%lg;Y4{srqdXFGHHSlZB<A*h z@IuW#cz$FeJQ5S(k(daN#6)-`=5B4P)GxrVl!@?2OoT^bB0Lfk;gOgKkHp-}Arc;m zx&I{zQh6li0kxDzVjffrJdy*^P2rIk<&l^OkHkcHB<4wtSa~ES!Xq(Ht4rmPm}k@i zkK`mYFxDuK#5~O57aoauV+9JF!uzll9*I#Ni8+@mRsM9)ytV~}9A@%J%$p3A>$32l zNbo)Ukw$qWW-6Dg@JP)2PpU?EB<4BJq98-e2Omg5Em-dGCOi_OJQDLsMnfKn$?{0d zrxmD*i!7&bIugPoG0Gz`Us;Vh|C*g)#l&h<oNJUvV!mZl!Xq(15l7$@UV^RgNR0AG z%nzA9l1E~GW;^U-w?8A1FqTJRvOE&wyVpt`<&l^i9*KuL?7|~4mPcY-*LoKo3B0j+ zx=)QqUEz@!%Of#K*Y-$0ziOg95)<W-m@JQ^+X{F}7|SCuQ67oO;gMX3dcq?ymPcYr zT^%)i{?13=D_jO8Jdy{H7;h|(#8kO#L&yoH*1Za8C-paMg-2p6kHplp_mw;nh}obF zW!Jup(DF!3lt*H6cqB_uPk1E8@<>cm`<ReNVxl|}6XlVZD31i~F-GB$Tn@*=BQcgo zVm{{*79NRdao3<2t6V-*X$HBvP8*%npO7y+5@UHJX4_0%e#^}avlgoU165xr;ErZ_ zBxZCbbg4SVR;|S+sc{=acqGR1NX$+VuRGiIr92X|i;V|g{+bP?@JNj1k(gaGN{xL= zHukGfS9m1G@<`0|OhCdTF*!UEGs9hr>ezkcbFe0dN8;hgNIXcXu{;tJ<&l^ukHpM# z^`umJBxZkCcVOj_nEAFHd|-bkIBwh*Cr8HeNKBMRVivmWf_&S+EOuEPYe_z8W^#EX zH9T}L!=}Jk9*J4zGHP9}hq%*_c2ehI%L72Mu{;v9vVFOdM*{IARFPftJ0y0&S$?Up zJQ5S-k(elt#6)={Cdwl*IXsez{&Z@2Bxaq<pft=A-CvQ$-+7^i@JNj1k(lG#!#s*% zp5!t**{cpf-GosdiMh(PC$A)ln5(S?9?6MPOL-)y%m&CKG1t0s*RI=*$QWsqM`G@A z?dgknxz}2?POq0xOL!zkc_a|fvG7RDR+q(2@E2^`&v?KiDZzQ<Rro`dvBvU9Oq54r z?swU`jQQ@UD?Acoc_ijBm;QuDVxDw~55g`gJQ9<`BQa08L!=0w89WM!aWgq6mPca# zlo=J_k(lS(hb46#>I#p<SRRRaxxMbwBQemgwAbx@1kT}wM`GUj{|S%e9`va53{08% zukc9jZLvHOV|gSd^f*SsBQc%5Cy?ghRL;%1c=kF<cqFFOn#v<FW!6+4iRo%h<&l`{ zT&{7UD424u4$bn^{IvknQ(;Zzk(k$9PQLI+Or^Jr)Kngcsj@YdM`BXeR33?W(dEh$ z8iT3!)`?JgB&M4+l}BP~tO*{;cc>;j5@UHJCdwl*wH`-U;2@^adriFJH^2WNyuXbF zT45}Y#H4LB3VkrmwhiTxm|oVz;c_9Q!Xq)3M`EHp64S?HRN7>JZvv{zesvuZ!Xq)3 zM`8wg3`2M%rqw%GDk_h}^z=Bj!Xq(*y-iXC9?7dnJcHG;qp>^^6XlVZ?L6H*@m=&E zQTOKkxUznmN7AF!@<@#3k(elt#O$0|FTl5;uJA~V<&l^ukHk#Q);%0`g-2p6kHkcI zBxa9n-K$VncqGR1NKBMRVrJM`!Ibf@-h@YDERV#D^Ee*DBQdkQ+fmN7P9BMwZRZQR z5ci#gM`A3G#LRE6OCE_?kga<U>I#p<SRRQP?{OS;2l|~?g>oMII2LA##~~CRiCOFo zk|O1im}wqIOn4+_i8on_lt*Ir^*BJfCoT0BNs;nM%v_J-p&eM}9V<o3BQXbf?7r|w z%yRD{DFTn=H6(;bVl0ou9A*~)e!;#+q3}qI<&l^p?GC`b7I!+P8p|UwM|<?dFRU5- z;W>(&)MRXhM`A3G#JC<0)xsk&>%AT*=Wvrp0zYg_miZdw3y;KD9*K$aNX#kO;eHNv zg-2p6kHnmw314_5W`)Pu6CR1#?42o1Dv!i$^cbP=NX!}DEl4Ym<VU3XVi#FqERVzt z@wm$hkHnnqy(v!6!LGQ-5FUxKJQ8z3d!d<x!WG8yNX)q&BbDK~-0O}a;gOh&JT@jD zpfgu^t;ly$hhZx`5@UHJ=F;d;UFor58LCT>5FUxKJQDMa%U^2w1h%=(J6<Zn={raW zkHlCWiMcs5PQoKGx7b63@<`0BVk(d17ifh?Vl0ou+-WOg-db^k@>^qhB<6OH5lJ_< zdT|s9kHp;Vv3>34{a!cZ<NOy};gJ~2BQf_zyZL~}hNYXQpsw&pjOCG-N86((kHkD? z4+uDMxC?cKM`A3G#JrG+R(K@lMOzmie0>>pFUNW7G-G)r=G9DH#}wo8b%wD#67y$| zK}gTu@!mmFcqHa^kL_vC-u1pm+DQ$AP<SN9@<_~E(Vo5Mv0>@iUZ^WP5@UHJ=A%r{ zoYW>13XjBC9*OxZ6Q1x$Ob(C4eD1A8b>Wd*kA(0@jOCG-uQNKwe1yUo#_~wa7ak*! zZvE)}0ZHMJm~TC{t=;;`dj@GI)n%CFkr>M(G2chK^|Qx@rCSqGS9m1G@<@#1+p|8s zAa;E{15h4`@q9h881HmD7}A8XJQ5S-k(eAF$+M^@JQ8DhBqq<-UpeKGn1uDIJQ5S- zk)Q#_=r!nfaZL`7<TK<8kHlCWiRqGY?3f;SBz7RE()n!K6#I-;Mz6~MJCed9F=al3 z)6q-$1(4wP4_o1p7|SCu6@KIZSnac6j$ZF&s4F}Y^P4=9ZfBu9VJwftM0q48hez^H z)Z1+)r`z&KjET-dIywu@nOP7XiRs}pXyK8VUj7S6dkxAXLEDT`K7KuQxaE-;%Of!@ zzCA+f1?V8_Re2<))p}AMiTQ(Ex(JWN4EFh11>un_Lqd2Y#_~wau#8t+5utD$Zqf>m z#0>G-Pnm=<{%RBnkHif38JSMPSbr1J_!cm>!Xq)3M`A`rCt-V^4a+2ahlKD*%y08Z z8b{y{la1w(nB9E-v<Q#H+$LQT9*LRiCs7ZKB5Z|6Vl0ouO!OIzo)+xt_eVK@3&|sa zQ#LL;(q`lfkHlCWiP_t?2X*0*m_2<4B0Lf^-CrecK%96J3FVO(%Of!(Jf1%akHpOM zFP56hBQdk=0#_c1nJq)DJd&SK@0I=dQ)YQ2W`6rb^lcf5Yhc${9*LRjGhCU8#eRS? z;gOhyKBLi@SmLLUk8j#xt2`28c_ik*=u|BA*|ZG822>UviLpEqvobRXdaLAM8GT_r zJb}7X_m;3MkHj1n?d+OpXAjTm>_-qk2k{DHc_ilOY-j66iC4=bF-Q0eOFDak{}qzL zBQfiIMxdQN(N93*^qGjQ@<@#3k(lG6oju8C)6&`1sQmZ6cpYzfBxa-So$^S`CSMQq z;E~*ny6++0<BjE!nBQjxMtCIVbf0YtkHl>8e~+{b(icc5kHlCWi8(hqNasZd>HM5Q zD&XTv86?XiF_&ZqX&ef%A}o)@T<9}I8KkTIt|${8iMh;Y1Ug99_<fL%FArj?JQ8Dh zB<9NKAYJRTX&Iyo=nAWHtg$>2b5r{ub=@AFyV=+4M({|UM%}T-@<`0BJ{uAqiMi9i zlIiy_^yV(ngV%^0;gJ~2BQbyQGuOcU5DOf!afzZo?xhKj#8@7Q+1ft*^kMGt*|?sJ z-7ml@8UDr4DUZZh9*KD*I{c4DhySsh;XeuDqi1rBS{{jcIy?LiqY%Syc_iiupCQWd zzv!QVr0_`0vpyrx;eW}$7HOx?*Vx`S1DCU|u{;v<e02E#?6Ya<c+ptPBQcgoVqVX5 z+)4FDq3}qI<&l_o+5=4Ogu*6?-ttJyU)l@J(I{MDERV#z=d&3J{xg3HlENb~ANg!R zga6!Lh_sWs9$Vp&7|SCupG0H-o6m+N_9u}L9*MC$67x+q_K#60JQ6%mD97>GF+a42 zmx^Iw36I2B9*Oy-z0eFt;R<7UB<3fd%}DG)P>3Sok(gh7HlVS`f*#0sQU_prD@J6M zu{;vv1(6ejc)*4w_A`(W9*MC$5|a$<uYuf{F0j^)_+B@v3XjBC9*HRlGC#}Ll%Qjs zt%(n5e~!9)?~65QERV#LN9$I!*EK!Iv7Y6Tn9_hDNk6*>-ytbH5>pkhdF^Ly5JKXl zreG^P5@UHJrdzb1bphj$ey&Ab;gJ~2BQZUL%;_U}B&NxB6zBU_qpt8sjOCG--a+O( z@VGM6?PG(-`Tn0#S9m1G@<_~pXx)MBbxqL@ct&L`kHl;fFed5f(BN$(g-2or1#DhB zx^3_s(oSky2p_?OK4&bC#I!{_IxJuu(oufFM|dP=r1D72sP>MMM`A_?_B>g5B<2=9 zTO!-Tj0yIKI>RF|W99WOIVF1v`NAVHmPcZCvTl?|Vs;Mn*0AzO%r3SRPbGpKEsw-l z9*LO{Fi5?1KRIAI_6xmrpUWe86_rwJQ7SwVV|gTIkM{AEnD?|XE04tN7BEx+RhVhP zAn|Qa$fG<G6XlVZ={92UNX~(3Himneu{;tpGt;JHUPR$4V|gTIpP&<LnZ)_QrAX#e zXl4hDTPJZra5vK6ckX0)B*yYc%)IC%E)3YPOyXXsD?Acoc_e0WbP|_jCQ)b|W@-Dx zkw;>dWhPE|Bxboav0QI~W8sk)%Of#|MCWl;bRG}Q%%ku~%xXIh$|Es{1w7|d7{#aX zEIblpc_ilWfZfn(J~Ci=`!whBNVcLNb<ECqiG4br!WqjWF~??xTzhz2`!ur=b4<XV z=rkW6R734G*lCXPNKBMRVxl|}b8^6zy@nO#O&qul0cliT19>Fe<BOz&scVt>xhx3F zBQd9CqI1k&P`Jie9*H?M_zHo^f;cO<4MoBuF`I)^k=6xqcJLI^c>b}A<&hZ6BQaZ| z3*wxB4a<TcfJ%5I#_~wag_#9`zx+X=@JNj1k(kRe;R%n#TyAGkc_iivYbuY#Txm_^ zk(jHjsXP*MwKbJTVy+RhokwD>m9?cy;044{D#K)XB<7|}9F8d%4<3oJJQ8!gAhl$e z?g-w1PIx5dmVkZHVY)N;25BeN2BGjsjOCG-+oQvDSHOm4n07~9;gJ~2BQf`7hbe!8 z<&hZ6BQXzU!V?~ec{sD52#>@(Vol|dm`AOtJQDMmHI+wV9=E3QNX!$~R33?W(wfR6 zF;CesRUV0XI?x9T$|Et)NM+@b^hC_UBQcgoVqVC^?3n#fc)YPZ67yWZ-pi1_r60ee z(7Y6|-#Vmk2jh^&AHrcPJQ8DhB<79ikiHYJVHwgZkq{n<u{;v<m+X*spJ;g`#_~wa zCz<esM`Av;9akQS`OKQiBQa4PiHY(^Oq54ra(E=e(CpuEW;xAR9*OyTrdh|VMB!3n zc_ilRfE|!g`A4uliiAgEz7H6>j><2=45XdZMcCekAzNWAkHq{G9hH9uY*<F+4J3p| zVl0ou__56C1bHMTh(+#3#bTMeQK`fv%Of$CM`Aj~GN)1Gk(gvm?^)n9st4){kHlCW zi7C$1b<DmfTw^Sc#Dp=%Bpoe}^+l2JNKEG#+tZF##I{Eoe-eqU@JNj1k(jbr<ovKQ z#)hS%w<94u5@UHJCdwl*IXn_?vgMJO|HnL%Zq4wOFqTJRqC65aGp6s*;z7!6)Ds?w zu{;tpKF0nDkHqW~8;o+^1zC$**l@=X`EsvWheWZlJQ6b}#`c6qViv>}A?>98i0zfz z;D}}{kHqX9<7fzv#Oxp2gmQW&j|7ew2uG~&8zgS(iWd&j$|FgCU?08v5ATLH{$ihZ z`mYa#^`Tk`r`w*p;u<(@c_is5k0c%Ck))$Ml5~_ulJ4U&biB?hJd$)@m#%YpBrm`| z(@$F-Njl0SK?v-D@JQ0FF6*LO!Xrul3y&oIFFca;zwk)Xxjd5h;ph<jFYh~6INerE z!Be)h<&mVLJd$*jN0N^6NYYUrNjl0SN$+C&t2~l)lt+?|@<`GXUB)CllHVhsv1!XA zNiT8v%9`*<(o5y_0>|8e8dKAjN0L5B-i{I;NqU9LhqzAaThtI9N!s#A;Ex^UIynwE z;2>r*86HXcV0nc?c_e3GbqJ3nZFwZ=)h_d>C66S1n9FaJb26H-<kC?dNjl0SNk@4k z=_rpRox>wZAL&jPx5^_)M|mXaTpkH~R}^p*x;+j_B5ipj=_rpRo#l~qOHIXYmbN?+ z)V~NiIU`J8<FbsMBab8<<&mVLJd$)SkK}o{=r#@7g0$t4q@z5Nbd*Pu&hkjQ-3eKB z+VV&suY)Z-lJq?;%NP!MB<XvtsXUT<+@@YL}SuNYeLbRvboW7QA%(2%ALO@<`HA z9!WZfNAkPfEsrE^c_is2_DPcRNYYUrNjl0SNk@4k=_rpRy~5MOfbvMvE3K(KlJvpW z?Ctcs6j2<sCtiR5oxC7VqCj_{3iO}67`co1_l!U=^nZIAtOHrw+f;zAatG=6F3is< zK3K#Q?w=!GkrCg-IxAUQ#eDs=U(FtPlE8u^MNHulIpVb$aVzT_!P+Y3OOySk!>nP! z2_mNO%pCD?8S&Ss=N`}6D(0)f1I}XRx$MwsqNehb9QDSG`f0YXiPcrj*R}_|O!WY& zFA_DCi5CfC&>_1ZqyCZV3*}vP3|YTIm@_H6T4a=5@KRQGWkz-t>s-a!+9+Sc?6(Wd zE9h^lh$)=-=dAeNjQBj(xsSC~%-0qB9S`#XdVNO36rPbIekvobV4bH~Tg80!vEOqr zkHr7(D<Y<_?B#6Bf3{-z-nRLLFWC0Kgd_Aah3e;ar25f$k`4S-{Lshz96ujr{P1;S z^Y%%IyZ<;=|CmDc^H2C0u$-Br8PJd7h)y2OarAwrH`}t}5AvnfO$7sG{r;rucg;ZY z{Ihyq<+S{nRez<=5?7qk;qI@nmxE+CC~~dWLE^R8wQl8u<K{!YCf)ix^_^Y%@;0q$ zl_t8lnKp`oYf;cL2&b6t0@m!MEwPpNb6QGATWZd<)OrF3wYRIYv!?YO`sm~8AfpRs z)A4TX$M))&PLFss+l4Vv$79<a%gBs#?a;`_(oLSrH=J8Hu#p{H`t~;QvUn|0EhV^F z@0PJ>e{G7*-11tsskzan=4G0akFT3zm#;#%_F$~@T{^>X$+y@|gUfe|Ti3B@fm<#` zZ1y!YTNLmqM$6G`{7#O=N!mCYKKJ!(<0nKLKe4@WzC3LDxqRQa)iC;#T{>%@s|`7G zwI>Q%K4(*}v#D#eDK-OmWBaPOD%#Z5?M?A@Vl&v~E6c6Vu`}1YbjGG+-mYt(H!x4x z)Og(ea1UTpk7!eD=ER()9*Q>gaHc6;7LT?s3prRm=IYTB%i<<+%n;sD$8=ipX0{J+ zMIFDLaV#IcH^W`N9o>2h9lYz(8T%lOyw~1HQE)5@TB>nP=Kh0y_*t7`GneNy^<%WD zpW2(^yU}KQmv2<JZspAU!=<zKrhdt3>MJ%y{*$|rL3Q<He`PZh-pcl>)Qjw|WuDz% z<+J@}g3C9uTkm00<sO~2H&x+fhOQ`>fr6H=+0+f})c|dZ&Agt|RKI9b{o9-3%h+bB z%Qw1PC$g!59-Xx>f)+2c^WqaPY-(3L;&G2)Qxmi)HnZ*9*<S4uZEAd`DP05;z09#e z=4VoSPg>rh;}7Y0zB;DUo!-qlo)>kzf5x%U49tEm-z{&Au@4J8Izt}>ZeUise7m}J zC8uJc$5(1_oM>IirVjArwd_qR1~0~GgopDslr6<>d=hFE1(##nG6@d}+y_~Es|LoP z_`UbcarfS6VE1JLYn6l6{a)sv#n++@cHsU9W(NH|;L*1pv<4y-%){nf#2{B7)y}|Y zSj*cTxiNpne<uWq9~A0<ApPG#klw{ZR($sx!~Wj~K^ioZKDWiTcz0~{571`?_#04~ z83@vJsl{vyK{`+}mSbCd95yOr>Ng-rXG-1Qh9Jp=JNPfw#zmeyJ&xoMA>wR~AvttC zb7$iep}E&PGZ`w^;eP}{>NE>+HDcp##gOTj0(cOMLXi5g*3Qvd(sO<_-N`=(`)%G5 zcMsND%32K2e)$CEZ$XfbW?h15vUM{Mq*4T*9GVZmb8gK;ay1U3`HNm<a`#nGH5|-o zEn%|pC?*eQvd76xD)(p$lbe0yHpvoD?$J-oRqjzS`f9mHPcrGD8^r_ui`=7O^q|}$ zj-llqO=0pR%u(@5ern5dj}|aD#OOr0M+Y$%ub(=}1#6l7U*{gl;y4Qf_h057{W2R3 z|F3h8HZxx39$n4k|1kIHPTck^3hu+U_(g1V@i0}w4T@gFKWzd#>OA5@q>BRnnUlbd zI%i==m08$PWfpc+IYx$&u%pT>?5HveJF3jWjw-XTqslDos4@#Xs?5TUDrd<KyF&tg zXG?NYF_Kx>QDqi(RGEbxRc2vFl?prB8#`v@TJbPwAd*?`QDv5URGH--b;)v%@R?qI z*EhL-G(M=B<sQ}abosF#;U3lQ$8XCB_o&y@QY4joBp4xcxW_ld^Z8!1Ink?VLz?d| zn=RfNrupKpx!U8)!-5eqH+Xy>STI87A&+m{3P#90;qe7p!3dcTJie<c7$NglkFTE! zM#%i)@nuiJ2${g=`<sFhGBrM5V-$>#>F@JxLcs``9elo7Cm12KkIz@u1S4dY`z<4p z<||lcl|PGVzRqMe_<ScyFhb@mpD!*6MhJfk%-4VfBV-=*`9_Ukgv={G-%k;Yka^SR z8z+Jh!XJV1%@4r{nZNtjGkq&|L6ZphR)t`MOkuzmB?Kd68UntBAQ&N&4)|h0{v)hE zJm5on!3ddg0iVMQM#wA*_#9X;LS{w4N4tU%GUo++94Hteb8)~Ye1Z`&e+>BiOE5y_ z{(z6P1S4eL3-}O6Fhb^|fX`(FBV?QyA7cnc$mGTNWI`}PCKcmdf58ZuMKRvQ7L1U| z!3d>uFhaP;yD#o<3PuR`T6qsvFhc1Zj8OUzm$&o;Ba}Yg<xM`p2;r_B@A3&o2)E35 z$4xLoxUY2|($1qt;OxqIyAEeo?h{RpbACZAF&8IpkKtI;0qN<0^bprm(gEqgJ>?Ea zk0tdG>QllEe*4GZpHh;5+|2i<&T;X(b{jTsQLsA-ih}vrcHybAo-h{$tB_Glq>`Rp zWS7`)(4XYgHgkS^6NN-BxwGL;0aE;&GCV)UB0`t4K9Tz#GD`o(T&awlzoK?wuNb~E zg+wBLo&N^@jbG2xY&<sb#~|?d4U7m+Xq2^dBWv?%V=xp6;+FKQ=^pv1y<;u@&(OpM z@S1`ij^<UvKvttBu~sS#>Liu6VWmzFqb$}!XHMa5*xpcyryzMZ`w!uaJnt6f(9f_| zShwzulX6CKH-ve2@Wg~(asLjr3R!q5{@<MFTmD!VR7gnY`S|0`BAB7xn8Bl_dq=@6 zv}U5bFYch_6<M>O+^c|TZ-wG{qmT1}yb@blT7ET7gz`FDvjft@8JJc+gX25W4)=`m znPXv&b~zbd!%WQ2-I$+BUOHZY=M+KSqv4yKF?Zp27<e~40M}byI0Cnod?&t<bD9^w z4F9<C;Cx89tH|9AuIq2GoC02@wdW3WJYK3L^5^)^R^!)vmaYnX^lRSl@Zy?U__i65 ztBL$YT4|w=MT&)?l@3q~mCA6+d}OEY{xfK^Qw=Rtasm=z`M>?FO?)3L*&wfw-?i(H z`$UQ4A;~1;?tt|8C)Z+{Iv_nAkRI%c9gv<5NRLa_JPxAOJREmGdO9FIyS3nixC7GD z0qMyP?=HdZzi>HD)x&UJ8O|HRGy7n1gmaNo9v+9YtIF_2+y(3rHjKnmhVUv}5%daA z!0Gvr@D9}3KHS*baVCUwap!MR_*M_c*)u!`cl-9MSr3)t)x6amXOUjbKcM!!n&ovk zRr6|+uuwLCpp;F;Kgc`^p*TTJuO_^F+y$r>oSvp}W*2lodV&r}kNkl79gv>lnjNvH zfs3$-d&@{CL1s<|q{r!i^x%ObZp(B)dVF^k-o@>J^f(=m9{dHn{{+rBIv_n)<5!{s z(u1>2B5s73V_#Ll?aB^F5AOPg$Kc_A5H{7~4r}-j9<#^87NqkWuLIK40qJq74jSOp zbwGMLAUz$B9-L!$KzeZP*#YU{^_G7-Zjp6BdO9FISPUJIo(@P)LkFY>-{9_m^fWa3 zpW-hhIv_n5gAPa!&KXl3kREx4Q{DmT!7kAO>A@g$KzceLJspsqE@3Mk-Ia!SHS)cS zaUovj4!7XliBz}`UYD&72NmMheRw4<GrNbg@s96^@P!)386Dm@if>;Wi(9qh!V08! zsHsDplBSwB;G%O=&ATbQ<K9&B9`53nHr0F$O<7aTR8;BOR1+VLw>z3@`r!<vqN(Nx zv{2bp^Ans`H95UIAUzEokRBYKJ0Lv@+v$MxbU=Fe`fLZJrvuW{tr!Q!4oFW2q{r!i z^i0C8&;jY`fb`5h!0CYW-~_=Ei8>%XOSmFBAU&jPbwGMLAUz$B9;XA+<8(lJf_#F0 zIv_oPLWMgZJpn!@(E;h{fb`%q1*J1_WaxnOU=TVWJ>`AyC&L|(o(@P)2c)OG4Ikp_ zfb{H!3u(MwTRshstvet+9gv<5NDuad|0YP!H@oAz_Bi`;@0Y)g<cC&+5BQaQCC+5% zF9YT*T?+b(CC>CO#P@aGt?Rj8ZYmyl5Q-j948ocgE>HJCEn3y`EL~e`#h~E*Ah8Jl zw`|!P*>A{xjSu%b{hIdxv}<4F{VGu~g8G~&e#wAWPNVLhtgYCVA^2ilE8!M*<KwVy zLg5fpjuiD^A*{qRaEQSo&l4Jk>WL?By&C$BjMEZYs68%6-%e;zeX&b{>dE43{RHL_ zGoY{1_Nl!%M_&<bzfwP32yRbn<DQOlKZmkjl~mo6qpXc8>$1vyp<K!~n^j5GD>+IN zRi?AbGoid}FDM77lB&J_!Wq}m>K9e^cPY@`5=7Yg3d~t-*}`C`Jv&F=4ujFr8tYP^ zdh(xJNv#>p(VnF3Q~P?3enPbUi58tgFcfK)t<#_{<81GxdTOgb%*MZGR6ot7K-(v0 zz4c6(KjD9Oj_Ro$l%t;&)z5Y*P(4|Ot*^i=Wd9ebp4y2y`h`*b0WJlqC!D>t0%QLX z<3C9C)b{^48~?JXez{A5>dAs@od$FAbm-4eJ+)`&=ueO8H@gC&<!U<(N}nTti7Ki3 zHb;3;RC%#Wfwnwh5L$j7=8<gqdeu|A&tJ2VUmMk5=Te~hgQ@R~op%;{{zuhQdw!1o zuBiTQmjcz3W7#?dX2^qt1=&!W_er+>c94y(yoX(3?y~16LwP-0eo<Sdsy0XYe6-~k zTne=1+gs7{Q!rcbzx#&jsa>9<e=Vwi-K9YFRfC}~z^HwI!EirTJ+%+z=s%3=KXNHh z{UGYe@VSref2(?GyMCJO`PWhXH!cOLpG^J9Fvqh0j<QgwU67;yXH@^IOM&VaQU4Uo z(QLm!_0-;#qwkbqsp!ZjJqlERtn?rIOIIG}DpXHx!Drd{yGHfpUI7HEzexHI^LFYR zRZs0rIr{pjzQG&cuIE#jwzFWmm(jsMbwKeGIS%?~9OQoaUOO|uqgsPG9I4h<u}O0> zw^d*C^L~!6AsJt)AL>z{dWXUEWE?MY9gk556c745I~t=h4st)eug*q$RI9U2NVV>d z&EcHc$?A)K&d>2RG2=`1lROGke+z@T1*Ur<gV|RdQ2axVgBckIx!?X*XZv_mtFtXg zwSI$5Gdc@ikNk|&Ps!i19h;x=rTPUP1*-2p1bWhRe!|Lem#LoG<{bT!sD7zOf$CeK zZ#$CxsH1~J)d9sDavU6zagYly&|p@1RI9VONVPppXQ#3^$Eq_rx;)3(x^`!$qn4!~ zsI&DR)#~gfq*_Bv(NgX}r>HOb@xI88%*h#Fs^8#Ip!)bw=qJE@fNSn7)l-|w(Qk?B z&-5rzeK+Xa&W7oJIt`nP)B(k7a~xcdagd8|&|og~s8(l0m9)vP*E^Ux_j+|kNB8A8 zySCjKu}zl$pw6!Is8(mhu(USe2k8<{*&XVOew;6}BXfJkm+JrEQK0&FI5P8LUd*X_ zK=ssC=IFOZ_4j)esQ!D7%&jop3lNF>ggT&jO^$=dG7fS978=as9@XmXOr+Y}S@6@J zy?I%k(a{+>&R%MFHVCyW0Yjbr*`r#W{RPfiN5Ndkp1rNU=;zNlzTV9EQvF*V1*-qU zw$L91^NYQq|Eub${US&IQB?o2M}g{}fxhh-nC`tCp0Csa#Z$h@PR$n?2e}9j4dzRa zYISxqQf(=G9^GBT-uzRY(b2Uz&i>KvjQgQwfT*)yJgU{%uW;5n9p<&%GduZmoS~l{ zUuOeL`1X21_4z&ps{i{i=+A+<nd`Sg_0$f}(RYpN%Y6z|U$7lI^D#`fH#<|S4k*4T z$3acTK`z8ZgX!*5t<Ii8s@2TF8~hBWkNTpYyK;Q>%J@=!Z=V9yzrbKt!gMEbng*!@ zipPJG9gTq*2e}j#b=Kljt<LsBs`WN(UgOA)RbTXTUyiTQ8DFX&<5Qse)x)9xJIoE- znI@~A+E;S)6QlY`J_V}Z1by4+xzHZV(U_(VDBkwlY-je!ILO7oXfS*FRI9U|NVOh~ z%}nk9^VAppOwI8%C*w=?bA1X_-+u)355OG4S^k~sseL9#zbL9d(5FE4tDtWyng?ww zJ9C6Opt$Jo+0GoEagfWg(O}m4RI9UZ;I?%V%&$4JC#Wy_S(M}JxQs8=AMaD3`d}pV zr^0-Z`pv4Rc3qDCw5a}cp90mVpl^EvrVusVbJYRGALTeWJL4c1#G}ES<5R89wj$M* z-XG2m;mBO2&gjT|mmQfa+MVT)eAL;MKGo_hM5=WmHj_Ecx2P}rS(oGMri?Gu-|SPM z`mZ=LSHa}@x_htf7_{f*=>Hhi-{VuD`ot)7<~x|~BU~qsr~`_f@3WnGDB~a(MWn$z z>{G4IUPG$2b-v?##UJ_?)EE7X%JKE5j4#zc?^B@q-!qtFV7hOyH*cx~ijU55@Os8U zE(=MWz2Q@>&gLW4`XDxqoTZP{7yZ1I<LfUOU#kDmr$F^xM?>!~z?Ui+%h#%>_WK<D zmr?y!J_V}p1AW_MnC^TA@RK^Ac<&#wBl|<fK`zWmgZa^?TAj5Z)w&Uz7<YiYKz=mn z=bRj0u^{rJ5f3O({gv#@b1;A9p|M2u)b{%^8%t4CUmQ@N`m!<TOt*#5{)wGwQU??- z%W=>n;~<wtrJd;+P_52fq+0jE<^wLd{_2Z<p3d>LO~#k%`vnxJ{tN`udLGQR{LL7q zdTKw+(GQL4w+$#z{k81OCotW+<%dEYP`uMm+0huCagdAC(qP5}RI9URkZK!z0Gyq{ zMZc>$qodh5&L*`xI}5cek4v3R4yaaVDWqBt!KRXd?WMlx=dc`KduDv8ep*0*>Zgo_ z{tqx4I5M+TPwnO${eDsX%zy&bFND4=SOo22uCs;efa33S9L&!+$OVdNFbe{z)!D~z z+qNs6kt*mOtj_4D_~+~}uV{C+2WnX&nL1k;P_51eA=SDGn+Ez?tG?(bo#Sgw#+T|3 z4=7N5;r7t~3Fa`4?6In+c65$@T~xn5pg{FKpl_==5ZW6#w5O;8iZ9D?aB{{$E>cZ{ z*$_~z&OD^r7SUM=&$Q1`XLR&oj<d7cot5!STbXR??CgMQbv6m9)@!i&l6&T5>WhAw z{*fK!OESJxe`!F0>c8X2d<t_9?u|F7p4tUD`s<?l>jMf@AC5z3+I|PE#{=b^>VV=W za~%93;~*C*r@`D2P_53Yk!o8{XAf~?9#Lm>RPal-HxIQt8^V!MDxEreIG|dcEkvsI zVQenqls%`u=x0KXuV*s8RR3&1f$HBz&st-P9p@G5UsFA`r{w5giRxbsC{X=(9GNLF z-7)OU2kL<0uW}r`mvNAb)ze_!52#jWtw^<9Kxf}_p1x9NbTs#$*^&98-PsSQWx0In z?8|^^b#@F=tvfDpoNn~x#pLINe%|^u>&uBneoovN1<(&!=(v7tHwU+3H(h(g&{uIw z*9~I)ETy+;#n5x;F&0~oVi1jo?#9DxJodh!x#LiNQN+!Q@u7paY2?sf@dIN`f72aD z44I3^Fo{^OBVFHm1n!q#ie&5YXm@um?p_)wgPiEZ+VA(Lqk%SO0>$|+l>2h>hNzON z`8mqLQDs|<0v)2e(Q;c6p6s}{a`Hy01B#!>aWEp|AeVip<2^D)wL1F-Zrf(i*+l+G zOi*WZw1=DR$1d&8rtn8X;fd;Oe2i*!HWI0Ae~-;U^ff)^(#J94XV^jg9e3}T-Y-uK z!zT~htpaD;C!l$phrj(}F6-Zz)y#|Ooo3Z!EZlcKX_@0pq~;)vnVNn<Hs)oSm^IGj zF$(M=Sz#B+mxt>jSs7g<2S*plA<;#$D!NG2Y+odY#y;Ii7s-%WD;#%qY!hzJ`<otH zJ7oU_j(b?_;t@1|I2=ppww<wro<vm7a~?0#Zm<g%=5*tdXg4m+bOZbUa>w~Am(Z=M zr0UKb<;_v$EinpoqMky_ZBt;n*YL>rM|D7Pbu2rAcV!&pVls6!?~YNe&RUUbyMWHt zbJ`zMXLK|u$Jrz8&Q9PGQqHD2do)J1Iy(lb*0-@4$Y0av)EE8Cif03RCgV%>&&DWF z{TsVL-~Awbx1A&NvFfQ^oumISs{bfPf$G13zU@Gm?ztS9FVz9XZ{|4oTgE{yC{%;_ zB1W}3n~YT3opd&fGyD&AMn~V}IQyyH**w&;q)~PDbBt<rb{<l#KVowf0}JAEWTc-> ziR{RD@yL<Uk5i!fZ6-iJWCiw6&QiVVsl7T!UmMle#VJtzIOyAsh3URT2hHk$;xIoO zfXO(>MWt$I(s8QQ*#Stky+CIlb7Wf886EY^an{oA>@$vxvR2jEpg7g)><>t_c3FuB z&Ga=~ebG;^PT9bQWqhfAyEp}^-+m(WyTSZ~hm)zQr*>?Peo9ooTbu&b&w#$|LYVGN z9GMyFfZ{819PFKOkPC~|V5Y~ZR%h#wYWtkd=Fr&!btaCI+1~8m?rc74S=y{Rn;)lI zo&5=^);<S2&TahdJ4k)G@H0Bc*RqT+)h~}zp!&X(p#L4ru8iey)l)k?M}JsUza~zB z>bHl!?Jk(^GEU8UbwKgGIS!7=ILO82YB1~KRI9V|k!s631b$rh<}`IiM{ndf+t}_b zhFX@ftIjsXsa9v7!ddGMFfZVopRK;==jR+>XJ&k<{;W6!svkTV`r}~cabzx0J+<RO z)XhBZzbLA|I8K4;7eU|lB20HV4^%g)1B!{{%{aIr;~*CvtijwEr&^uefmHAbHs?AP z;#cdm1RQbiHRQ(pBPYib$&WnLuO~n=*}`PQk$FgNagl7~Nl|hVb9)d3m^>(kq&bci zhQ*Ogi|k4!n+58<7H+&=;$bkfUhm!LVOz=u5Q&w1iGBwjoJ4XQ{SJ}bjg$?Q++_ih z$4PP;D{SaN4?i>a6aovAJ282xgo9Ho$J;1%-=W`467NmSJzbK0nEbuOa5ZDtA|BqR z-!momZRVaM$@R=VUy>)Wi7O<s<5=NpY3)qrUL&owvbF0acNZpaklbn}Zxs(0a7b?J zjEv-sl-<7nq2xHOScQyf%(zRcU5SxQ^d%-Tc~>ox!<o!Kb0Fqzdskvzh>TdxW9Ub6 z8N9_BI?-o2a^th^5}#YKEq)CfWuDH0_Y#^VAyR1b9jV3a<Z~G2_}s4~<9lq23sz@j zOqH+_6d!Rd9FBJ4H+4gHQ9xj6atg{~gZ5*re_?X4OgK_btW7#|I{F+NB6Gi)Q4d{@ zvgGVuctF`}p7`N9EIXqKZRQh_nYV+>{+11DVNTcK$l2FD6*Y^m!p7a6onR~_e9x(b za8@V(=uxOO-@T2so{iR$o@=dhwn42W?(3}ech+Kn>WgNEB+L29k7JSKEqC({L)~(0 zvUQ#0fe0@#?JNY69GZ{1bI9OKuI`KEBH}lbyJJF&8-B*#lq_MgG0qu2oXH-{NbbyJ z&uS(&qbtQtvII_J(j39uz0s}W^chTEgaIt>JDII{%pGtecN4?9!%txHDvU$%j*l~W zF(NCT@SY?ooAw+B`$Og~yr0f$(6{2n9K$3A!zo_zGILL2-IZ5$MY3-fBoF(XxgoXV z+qj3n3lGT|KQQ+=#E@Lz)5D}@)ZHTM{nH^xo+-0Db2}u@T2Ju^`n^vM4A((hyj2#* zSu>G*K=z}@aeiC;nCvD!nS4T)%(49d94(jRLzsq6-H9trK7wS}^XN55&O?vG{;L_8 zI~2)@o0&XpB$B(I%j8My@kdJwkUW9OPb(R38$18SHcWONgcaP<3A2~HuM?8v&S3Iu z9G!|LB#@EpiE${JEWOx-z7_4$ow@haBRO|5lMnYqa)AW-^8h4&&vD4Vp+7dyd&3V$ zPB0eR;=QoZ#lutyHz?W<|7@2Wk;Vt2Ml3?QD0q&+wxy79s5m_nqg1)A49#}iA~{-y zaaS}|IYx%@rd~*nmAag&%AI6Hhq3MiIRH;)-H9?MH?r;|=}jlrohp7y(T~dM;`eBD zw{n&^+X4Nk6jW?-QZbTq#cvnZT_ApsV%^2!Y#i$@k>o_yT_%3_#`ILK6=#D6B6*Zl zI2=P>d6GC=!{i33dj=|WY2ZOEzZFx^rO_RQ0F&!S<11VHv$_25up8C%bbE9|_EWh2 zsojqtgyb?ze6OjcNS0L<Vu+JP)9?%a_DSeza#0T?KfI60b$cTD%j({6bRiBXCeQsd z)0LP=Q|$6nkon)hlWA}tV0xEQr2DyDxi7E5p~nn%moU8ng=V;WInys-rp)&4pP1&X znF;Pcn0^oAZKk@d*s=0&MTXhWoz3(dG-y`4%do2R55<g|4Q>UO$wZ7}Iwfl(|L10; zw{`d375O`06{aV-?=!sxdqsMV))`fT^dasYQ;>fc`j$T4{g&yctB}6IeH;Ux|19E6 zKj8Ac+<e}wNI&Y{hqUwXa{RhDKE@?*&p~Y8cEyDw&K;he<v5>XB^P7LTyGd^CcF(o z{Pi}_65{W8B;Z_$zpfLXjcEz;@)j;$;ou^WlN<OG^G{&zY?o%IDRAeHpUvbU$o40k zjATX_@PN<%g&+*f@wG_1aqltwlkg|`!#yZ=VuNVie-Z!s6ZaMiAAILZDTvi1OYyIF zA}#+eG#0CVPU|P1j3H~%B~*QiWUSj?tu#K60meM8z~Wd#ayOKEvL4k#9gq8LQDpTh zOE~eqkR@UT$D%Zd_YBuS=7b5v=!T?+d*SwMG5m<-;Vk6G!V8d&hgeGBY%jnz5#EYA z`Az`==s~<KJdDE~mg4Z}IfX^g3_~NO@zKep$cj&6j=vr1#1FUv3Ivt>1`gu;bU~i~ z57dn>+A0M>@*G<8SlK_0%^vuw6eJW-{yY9sdJP4BNXLswWA_@0akwc%6UAhi>#-@z zS(l&Izd}{=8^rbG0dO9?v>ttekG&u-oQtYoA~7sJ!v6*T##c_4MjZcZMs_fNfBAX# zdm)Lh(pgRTQy9)_c~cp|J;}fiQ>!>Rj%7P8XOFlf;)h=Yw_ZbO(qpw<2#5Fg;c%>d zND3;FqgZVs;qN}fk00@-6jUd-A}c<L=y$)FUXT1v3Tpj*X|2;}8vQS+U$2(&3+Uol zwR-z2p@<)+RzLq#S|_U2qC<SL4sja>gk<+ZnlXge7$1qnjOwT7p?xl_J85lJEAVfk zCFh&`V0k$mpR0}w{I?nRg=!W1J=yPzWwAAs%JPg~qRX?wZ(&ES(0Q$OItN9;7x3GK zN6z>q=$4xR*B(Yuz;<!cCTX4z6$O>ZDdx)2HISWonSRzU;RZFw;Ga@9;>zg~a=4Pq zZo|ec3U)_9Q7|9dF5In)WCt$_Rw1L9v-Ph_>^BycoO@+Xz;2?Dv+2%;yAoNOj2oVx zViD&v>l3-}A)}Pz<4R@ZaCi!PVHS=-A`!pNe*^!<uV=qG6Jt1{H_$Pc=FuF(8(AAO z;RHjG;7oSqanJF1ZreN7;{OayYyeMs>4BQqKvttBu~sS#>Liu6VWmzFqb$}!XHMa5 z*xpcyBUj$d{zLgVEZo8z`We;=>(<?Ig2PDehA{6Ao%aN~LAQl0<caa-MBnnqx}ZWr zIuCXEokcK1eWko1D8F|U%tC7>%KPF7lviZUf^x3{X0bJk%l%<6ORQO1e)T|@ovm3> z{!%T>F4n9r{~AAJd8O8@Ew889)tZgvkKxxSuiToZd@r0x=2cj;cllo!WR<Hu?pJ<4 zes1%sUF~v9`IttSHMU1><z-DU>#aGgybDVkT|KyuEMJ_2X<VMucnxFAUuJ*PF3(`R zhH>RzkA&Imo+9QB6}1@fylczv=!}2*y5~#oj^*3VfjQXKgZ=pOXF{0U;ki0pOf7$j z=5T9HE1$v8N7`s+l+PRsbF|CJ@ET@fet4l$$!IRX?{|>*X!s@?bHeX1@NRejerLUK z!~h(8ocKoMdwKE8@Q)i07)<*wAlLObSWW>iBieHZIv%ej68UrdXRGn!F-up4Q$==k zzMP+7#W)_%jT8Bcw9-QUAYx%?r32JLrLqKG81K|we%L$J&_X3A;3+HrmmlKp_<g}B z1bKxsk=4H++$Il6V!$2mH2Iw!I2Gqpt9U)Yg?nNftV1&`6!HxG>M_t1jPqHsU<X?< zIR|Un!@DjRh6m7xf*pC@E}@gyab!3#dFV)t*<h*h0Zw1TU05IiK7s^K=xu<7?;?E% zBQKolUsJ<<!;Xqm$aDECv04&EDgSYUF|F3v9RC!|Nl`bg2rMJd^c!$$*I3l-pV|dQ zy{r$vx(t@wo8Z29dO55<jDW}IlWSr1)tUwVj@@BxqgJthOdYI#dTc56mtgf5_1Cfr ze^L)v19W*+`)6XZiUw-UTF3hgq4X;n=ikN%N3nD0b+U~OH?Yi~KwqP;L6R{%hCQcf z><?lUB%i{mZ_!Ym&G<14MbY-pOM$^^`WmUSqp<wkfEP3EYbxb<yo?EoCinyBYrO5b z<MEQFqG%s~7mnOCo9(CA#9nFz{=xOI_Ra*ef`c_(%L@G4p)Z=DJt%g(_0q~*f8;1+ z&&jls%EM}!jsL6j?{$;Qctv<W%gS$nul)O57GnU)du#`Dt2Kl2EnQ&VZ_PycYShYq z!0wX;<q1qo{)5&mE`J{V%74h3rRCFm!hG0jD=;*CzL(k`E{?^XUXlNcTfN_YtDO9Q zy3AwaXJFyy|7y)RO6TCj&C?@JLHQ(%LB4CvLX^&O@;#4NGCZvcm|#^|uO80iutO#C zx-}Fy&WhhTy_4(wb=zaNv8Ni2{{$_2suB24?g-1CY9#zUc7Sz!W`Iu_2kQj2iv0&! z)1HQu`fu$F>m)6!@Jm^CvRc*tuw7tnP^;Ged?#2Nb;oG*U!}E4Ptc50I37_JBcS5s zh5klNT=Jsc2^karHFPO?v04eIW(g{uk5+<``c7jIWl015clr~wC5>F_xHpC>iIOqN z-u#J`VEl#XLCL5?P{2jL1l7EC>rvPL9<EA8Z<GQYtDY6<&VCox-;p&?-@g#Cmh7Y! zDutN6f|4ofbdq(do+evQ$<r__B|X?H{~R{4>o?Lw!tv%qU0O0j)q6(N)2!OBq}s0i zM9!T?e>cXp_XxP)dG$$*Yr0xk_-m0Jl+5-+4138e-R)8F5@aVz7W$hxMDsHTrb}tb zEd(A86F;KTijo65y*Llq!)SGye;|@$INqIB&O&l$j%)Y*malM1rf{4(^<+CUI2xTk zU>|3*Yn`rShzl8u6XX}>T)b5~g%e>9LEnxnY4I1bkArk^IQ}|Xt!f4SF7(l+R>JQ= z=LfJG=(JOC9GnCtN7den9;~jNj>5xg??ZY`?e+-h@Y+W(Dr;-IVQm}{o{M}Zd=u#- zor0|*+!B5S(+$7I)(caR_@P0yAe;zcENnm|@h}gidEpe~C!B&GMR;vE9z|~WD>A*X zGb;Gu_GleKIEjUmk&cIbQ713F4_gQe@e>}D+#e=T<c5v#?>Pl&$$TQbNy7L7HNEf` zdUFbfOVLZ=T&e#a8*&O3N#<MO3z9iX>K`kuFU2qg;WfyKh0Bo>cM8sw%3p-Hpe;8% z4H7S$AeC1l9fW_wHWt1FN!+R1ipq(S?r!;Zs9n;{eF`=YLs!zQu_q#dQ_6g|gew>a zF^cNH#ipgC)@_jWQt8re_$PeB8$)n{pg3ph3!K6Uuy;a-MkX&W?~i~=y12SaIOPM8 zT~cZfn?d;uPJWp+6XoaAtaYD<S5AN9LO2Ra(r)-icyYpgAa=uTFaw^GIGtsk8<TyZ z>9P~8PVXYwxpo<oDOBxT{}htw###8M^fGL4+KvpT^pW;-36sfZIS7lmf-{#Lw;{VI zpr)`9w?k^+?&Qu@{#taqvxLQCOZKLre+fppbMMbkpFio>v6}pB`wi<{e<j>^9?%nh z{D$LQrw1-&=rlDSo7dsFpi7-wegG<UsdpJY=j9J@*rlO;pk}eOM`oa$!i|vjLtdxS z&i=djx3mj$cmO*CgHT$kzazNFxDeUxzZAYx_%iGQ2#QO~Rag-b%1I{A0e;GZn=1XW z>f{uD4cYf1>*n`l3pLC^drsitq@w-g&Ubo#1N%zM*HW*vu4>L9PH6*6QMKwI7>#NK zRV!xAcS_~C4x1Z+5i9MfWd(j2rnt08tz!QVY|p4w>R-m1X|*b<_B&{WQ`*cXy@qOk zn?)#-=RaP<f&NX@_12MC>>tHZ+Q#p~noIpXF}$Vy=!H|_Pckt3t9i2PzliWl2XsMQ zzhR^6Z(!wt4QhHiG%W+w^l?uLDNd#Q80R?wb#x_m%n2yuG-S%D<TPaLF}d{zJB82V ztp099JFRp`)%=5;(xKV~$A61mx0YA6KZ>^1qQKvVMb?T}%|p>JElT)DvNP7|R5cex z+i6jOza5LLRaiA|?n0+@xQ=Wwe83u^R%z9|*-M?$k!n?-4Av;Ms!_JeDIKj=Exf=Q zqgG?pyg9JOs%5I?&4RVPTD|?{9M*Aa^{bk<e7RG)gIX<B^A@6o9o1^9nuDNs(x8X= z-*Z~6HQe8U)4H=(9O-YzvR%{~TXhg*<5jk!e-L)GjJ30W6?;B`BkeVe_xGZci7K01 zHD~z%+=NhTDvknN;Ds(u9X~|CiL#Wx1cO~xZGUwALy=q7e%fEq6%~WBhOj4EaKk51 zh+A(Hka;dL3d#nmx+SBorZ4-`82HB`{<2m*Z%^P7f)}{`uoAlVNcQB3%rQLi^G_dy zn+84if=}-7yt3@tWM?~x)*SCx2Je4{v+AztBc)nF@(Nxa9lsujKYswSy7oCs3UCq@ zL%|7BFo%l1H%mdOzZR#sUANKml#1lbthQdNeT83#uKiw>YSn%ot{S`c*Jx^eUf6aW zpwSrr*Iuw%o<pNvL+|7~Ttj#l$@SMb^r~xHHTvf@^h@r|g3G1g1YDGK-FA=^wD|9D z18ba~5VrYu;8L*b4r-0`@97C^N40kJyR)U;?G*ZHT6<)su<&EJy9ehr#pNgX)A4Wl ziOk{Py-({TwLE_vtqmG(;O|cB6txomb+k^`bGQO0Ud`!<6)Z)#K}A*gD=ze$@GETH zZ~+G23y;Ayq93loy^tV$B8|O0>`}#&hNDtEY4`#;iEvkpLVozSW*o=EHU05J6uvkR zizGA{#4!9MAL}lBgjZ?d<;W=x{Z{-!g});9&f#6S>(Iq1cnR);iXFmzv6P+gIb1Kg z;bX}2!h<?7fIs$N0OK)MvGA#34B!?tofp1>5lV!6j$#0Z;xOJRd;m=+!^>+KKmrRP z411wwVK@~(i$&oNNd{1e`6vn3<1(;w_#}eq5)MMxrQv0Lv2Mdu3r0QM1CJ8Q!!4*& z5nc&xWmqs8oRM(QApGQo%@~&I(8HsyZsDQ0{;CPbVVQLg56Qz5m+(dmMO`=<163c+ z!nPs24Bi^U7qIOSz6Wj3a2(cMQ~0-HJm3iT!)1IrY(Oi`;SdZ_udo81dxuTvZ=dj4 z#N0RRia56kheFja{2p=k56?k*K==Vx@xbtQ_-P66#gq*SKg0O6hJ_f(!Qr?*coib- zj$MC9_#S)?4O1A&ZNp})!C~QCjPS5<Z;Zxv;YG+99=2h$M})f###v7oqUNYDAO1&& z2O_F5VQ;iDHtb;>09+o&p?_S+$4onf3G{l$@GFelPT_ZmbLVg}#(bCXR;0&=BjJBS z*n*#jiQywfjx#Bogyl3jJQH)bYd8|4Iwkxgme6kDeB@6J-@-WT9=?da?-8EX)p7O= z&qbWm!UNFyUSSMlwRd;|TAUuPz|xu#4n(i_2_L|i?;C!P(cUk-65E+!33@Rr{8K$H zqQiHvofCe6?cDIdk&ZJjJPf0>e^?1W^TYR=9A`l|6frCeD{ve*Ap8KUXi@mt0LM8n z{0O_l@4~YY)#C6FtcNAxd5CRk_!dTBS-1fUczJjVB0MPE7j;&IA;w{4I2ElN9PS4X zhlJ%A!&Tw_i1yHM9k#2(9}x3l;ULsm6SiS?4i8JP=GTVJeR&OVCsap<Z=y#>g||X` zbhsG1&oN<djNZENIpnVo_wLP$@*R+WT=)ch9v{9IcbpT#I}rbg;a!;PlfrpenJ0&f z5bcI=Dm<SOzKnh3)bMqT;l^++Mrl(x8)I@>crfBQJ)DQ0ZVu<6&Kco_@cjGmN35|e z;cL*I8Qz0^<g9QVR^!>>c=Y<5Fo_vCH+&THdtUeudVPL42BUgGxHq&HhI?SFE(&i( z`r@zz`{O0y1<+m^PQbWb7QT#`y*zvhBX&i&5UcLWumG)G6%NJfx;h+!)p$)f1FP=Z za0L2$U3e1qo$JG=(XSiA(=b*yhNbX-Q+P(&ac&M@LTtB$T`_~VhCf4fTX+*@<o56w zl>Q;y5jF3?9V{%?pyCBLd<H#q!c~}KH+&qc+zT&4tA0}rG#~p-KcaJg^_zNPZG7T4 zy^W52>Nj1AS^CUxsz6|$`%N#y@8A5U(OBSL_)Ym3$?-wceuJ>kgQhbO%fz7RBTUew zplMGyn;bM<i=o{$Xlg`XrUXrk;cT~{X?IvtgQl+#$nMZ#6!r+3-oga#88lsvePLSA zGzabN75rc9eR+HoMf!JlS5KHp(qR%3nlJ$-2Qx_s(BYDsAOXXnAfO_L+&7W{0VB5x zii$^sUB&xaFT7n9PdxEh*K1wZTg3Z7ch_4{-|x4odL|RRc7MC?`+Gkh|465vs;8c- zo~o`sroZtJI49ci2I!k)$4lYQWIO%=q)f5n=R)mKc6<W{W2znB18SNbzYfMvx8oMZ zb%q^Zh3+%$_-@2!mK}c*oU`rt0L;HRc6>b&V6GkCfqwJs_*WRf`F8vQa4xXpE0Ayt zaTBmNA~y(D2J`!hAJCNFZyX}XGtW%ekl$ZXwvm4X7*9u5+xb(Re_)ghr++S^ycbcx z24X#$E1)l&f3&=3l3&Z4r<B8cCizPg6*LO30%N#siRtoYZrdW0_wB$|z`MC^i#3U@ z#WUa=d;koKbP|U_Q>4P*kvxv@M>?xL2n@3jvV07TT~O{EiQzY$+c0S?XAY84I4{C^ z+bP1d2si~;GI6QU3mwwB!HR;jGP)w`+O^|uK&(UB7Hr8vBds2aeg#{}Y86|CJq(5O z;k4WmJ4V(f(6ivc`hv>@KIdT+T&_E2JZGUx=Z)YM-|$rOhiOuVEk1{mf-Be&*As?) zPE(1;7?~l&)GoNzSq$$?r!Tr%&K)qxc20)(K_{5U^V{`sFzU2GHdi@qF+11C&nbi| zGoA**u;Qzc0EQjkg6U`k<2P2}UpW2={5GQTBgk15AA!Ic>y50_q3~|-6%@Q64u>lR zFX{kiR0eR``?3q#GrFQ^-6Or}48sauDn>1@Ak#6{1+P#Bua3S%tP5UMLxP*a^z?Nl zEi6W|-)_~GD~i}}kIE|EhGaTqy$CUP!IVtwS;t{g7QCm}4e<=zD)>Mrp*WGEK2%gt zWMZZk>}6kGi`(y^z=vb%X;q0GWjx!N&e_NY(^<nbb*_(NSd7Rtw0(}XB(tF0%($-_ zF`C7FQ$gMQSw=x8GaJpwfs7@P79Pv}Wx;Z`RG6J)i=FWz!V(_B4Q9bywsba!%9fxJ z*^I(ra4J~P%X9_fa)7BUHr#QjFX*jFOuk#e8(=1OD3j^ep<L}tRr+=4#CDl}52HZe zboyoP25oof=Bz?q8%}i<J)GMY%tq#BjItf=;B19l!zsqHVmRjyCc_G7x12akvYmr- zxYIhngqd{}9L#jy!m5<*e2itGwQ~o?pq;Z5{AJGbaI%wA37!gP5W?5R`4AGiIyd8- z)Xk~G^3&ZJ2|0b810kWG6Gou>I{_?ilbszfY=%=lfM?=;7_CLl<*4}~ekS-0)5z?L zWU!-CocqAt!g(JV5OVH<^bBWdE12!fhVGWmIY{3;XBKK)=OipD`Ob4#7dkq{kX7zn zjp6O&oDAK4oE_j9>YQ0&U{^8=Gkdi2T1W0O&O>OX#y6rDrpRl^6Vr<CLvPE9FGaY5 zRy+d~+U9nbZR7C|mB+ypOr}oodQ&SSYay&H!xCvnPs%8Ors&2)QIk>EfGuGUIYq8T z+@hO?p%&?51h=CQjy{|*5TPop<oKl@g!o1uU_-{W>?bco(y#81+6V7KZTbrcP4uBZ zp=j8wScX42>MaeMwI};DuH#=unI_0~+&cLJ<+q?X9Nj4%MS4dcUWgi+k~Iv6mWWF@ z`l9#*NfUj^U#|*#f;y67#hV!L=*wZSBI!gaT#dfMA>w>L73mb+LossoIvj;WLbfon zt_FP|a-<;ouJ{OJ6n)QMNIOyf`@YbN5zFWYG@3%+M6c+Fbc2h?`#9G|KT>UNaF+p% zeNmMg{f9V`-9A-}coPQc68(Y^=d*^aZ^8I>jHjO=;w!9}Mb*k^L$J1JDBGdUurm<b z#3CLyvU-4?glq^FjS%HXj-ru%$M#67qEXs+j64|kAvl>uhbqRgKI6g`U>v6z#Z1h$ zqVeOTVK{|dif)%7c{7WS6dxmDiYBTiLu7UbHOcSRo!urY3M<Hy==MW89baI&U9d7o z7n<62cLHWabdh|wNc#RIc<<m@vbb1SRk$80QDMRJbVRkdqk5IZHZrGUBG|<h&O~%D zoev@2awa2PZRc}DGw5W&<FIorreM_Ri|JD3{2s0A<5}3o;q=mjQ^AV=fFZ_=r~org zFTG(KPA@f>X*j(!lBLQBu7{>@@lol!U{>*Do}JUJTok9UXk<N%<|*h=T0B8SVMg(h ztmi5B6$mdriiW~9aRdTaJoQhKii-Q8v3S}uk}5IsjzkLMCM9LHw5c$wH-`EhEOX)F zT5%tOR9vSnt_`sayv0itWs8qpP)qgAh_>^WE-{LiF_Eoe+<H2Cx=~!Os0@*D3^<o7 zD$@w^3N>83A$?I4)hCcL?8!JL5~`&U{7mv}O@9HCrFfHcy}lI1lV$NnWKi)bve*v+ zC_Ysd|43su%i=@Gk>b;Mt)IRRp)Ee0>5{$-BUXHdEY>2##b^Ev{-$5b;ccWD>1F6p zd=`rtC#S)a+d1gzM`DF9-tsDHjjVQ<KCf^J6|WV2`=fpX!^N0y#oAoFm3}atg8ji{ z7vGk?kHP;VJEtE(j$7G)P}9Mzg(zn;X^L+YmvfMBqH&aVFlFCHZJcjeC&TwoklVQ> z6Eiv-i16)_8?Cp&RWj)q$z_OIO!1P*in1~;$6O6RvJvvyE}OjsKlQ&*2@^ROx00h2 z6)Zh4AJjD2$<D;tzjQn@sbspU2%@5F&iutj$qZRxwqV7wV^J|vRiv?EDelac%u*Er zR@5z<XOzrV6*eoT&s=Df%uyAB70Z{-HA?2H3JVpb2homsvciJTnZjL&iWRb=I7^sV z;!BQER9K96Ks6{T$H;sR(XmU`I0s|SnNBARm*pIeunK1~HgUFdGqyefXU!m<sB6o3 zf;>yk)F<I;$a$&-#>QEJjL$Ige}JfGvAWwOH#y(qgl0HHFnLYqn@S433TY*r7uulR z$hxH*mv>0S$t8DY1TeS54>QBX->P8ST^UfraeNCivgB?>nc{8?LCHOevP3rIl-#Q* zA-;y@lAVgO#d$$c_bDn+nt^OBxu2A2Bc5rcZ^M4S0(#O!1ld#apdM7g2TmmqDJoOE ziM%U$SW#iIo;K`KRIV7ownr7!M$~i%^_Zdx#B8i0C66mADhe<YOa7#&5;2->PbjKX z+=|eaJf)}#(FTiN$<vDR#PA5HXB5>#Y~jc~tEk>$4Q6b~Ulr9~OvO|wd0tUfqBlMI zo1&@(-<c?RK~Xj0ag0pKi;5a0;uxEfmlQQd1labnqQ;Atna!^#YJ!pV6~=501P42| z6JNoFj_v(z;w#9JjvW+biCh@nF{&tAbngVJP*G_{){!9lAV+dK#zYJ&NJo7~7H>-; zS2~vZLN3GDcP#UTypA3n^{zBc+=7|gv6EU7gQ5y+a>oj_B?uY$4N&krve=Hzaz<jL z4d+F;Z#pMqdBWuQE1KKHP8Qc7da)B|4KuB{9@K`h(8B$|cqDafqdI2=#kGh@>_kOn zic6s_c9NpP;t42->30HhjVucI3QI>cwn1#c8WlTjtrTg9?+}yN8H%#SH&IZHSIf4b z*amB3TNITk?j`lRow6+~zQevCcJ@n>$`xnOlx?3&s*QMJ8L0DGVBqmg2dg4o3pb!q z{<tukrSb{YV~yC!j7c`%<^b1RRJXJz;25ULB(M76#RaI#xC@8)Y%YIb-9T2_W3o+( zMePl&rCm(~upLbgvx!E^*F0p0Y~8dQwYjv|^yPeunykU78Hm*&7<)xn_#b;!FXb6G zU^2#5vR#}$2gTRo(p>^%9E4J*u{iJ*k_scT7Dc|B9*h;4?sZ75Sh2Ji<Lh1zmRN~? z0}dDKcY(JIrvkh5ActS@G@MRYLQO{wg0m4p7tRSV#CGCQE`JYUrl&bQP;OyZyU{sV zI$12hcA|75J%ft2i)xQCN+&7GHrkuG*TlEugQXKXES!C;Q97AAJIpjnr?3f+YjHdu z1-Wa+*0lz7kEPh9mChEuvAC7akw1M$a^6NgbJcY$UQ_J{YM#=A%^hDJ&qiA0mM#+u zFgZ)>FGUS=dJ`sP>GE47Ws6Ud0;MYy6%?^dP{$}LQ?!M5r46cI*yzkc1p-Xboyfz% zV8vCG6{OhGRpM%7bZH&Ept3JuLg^AkWr_=`KrLmQtl~Ce6c)|WW7*Ftj*2WSIHfC< zHzg2Czw=K*V-_|ec3HI(!NkFMU>cdubZj~-XGN6baV*=N$1zR;=Y0rCa|Fhzg)^)G zQFh9Z-65x~%hS<T1T4d;hTu%+NQ}JWoWu#>oPf|}Idjk#S9919;Qf$mp*UE!LXKWp zz1s8}a%9Vv>&pYY0VA1Kw#BdD%Nq2kw8f9$%Z^n9-W9QNl@cslm3|BIu51YdmL5T3 zlr3derhmiQ6?8H^4x7tXQmv6yflfEWtIV>+;$^I9Wwm^6z+_sw+$dW`wcNm13&59I zwmIVwEMcXq?tow7X#}S1WHvDRTe3i%vR6{J2(s-|6=A-#+5`1jY&;WYM9WSQ7b3mN z&gh0Xu=6`OGnSoMEh$@UNA$}Yr%Ng*YI}e>>o`eeile)M+M+naqCFyBw)GO(mMbFJ zpnm^`q}qt5T7f!ypQN}n)j@l>0;DOQjIfkZ1aHbVGX~j=bT((UT7!}$-mXJ;3;cK| z6Rf$^Ze~*eYI@^e?xaLbHjAj{V6?PHfC$rcwL-B=QU}`SvYqBPY3H=^Uu`&v?Y!;V zq&;aFhd7(9>hLm3kV!{c+O)c*eI@%d1oB3UG+8=|rgu>r>4Iqf6*DhfHs5r2A>GRs z$mn29+&PT<LX(a#1M&|7?@6%DF0XN>Anu0K3z|$Le;OM0qOV<ku+s~|4CkZ4vY`<T zCm{Fj^07__Iiude955ZZD|R-)0O35A!x{Au=1;)LzXlyHZ^sUkoZXmYhO-va2;aqh z3Jt;XIyptk7dM?E<+X_^@|k3+7nvB;@}(;G)RJAkj6tIp!B*WVX+`>j@Swbo3ezt| zN|x7Cg^@KJ&3l*N^jtn)42OZ`3;fk(ARH)PNMEtJ<>V+|L4~|luog?98<cs+r<iww zZ(i^;$#i1+mB{t-4OEoA6LY3~Bc~^BfT7|gXwEEOD{jFAEnmm*>4{i=B2`ES_|&mY z`IhuE5ZLlYx|4ny&LZV!QNEG2568c?!rh1R)5Slds6T__=8M@vEfkYA1*3Zy&N`*# z=ZO=lz<0iH_F4>h`32f+@fPx^{6a+q#T2$(q^L~6@9>phtO67k-#}~mC5kFAf~%pR zwEVjC$LPy;nw7p0`^xfb>41@SGn#YZxL1CqxEuSd@~eFP*WwIPel;@_hRQ%+M`L-f zC94%Io>q?lbUIip#cI{*5WiPT@OK)m_C&V04OVv=GY*P)H|27i7&;xQs7$ff0X0^g zyuzX(8`L<;#ji;)Ey$JC1LO*%dS<67;%~^qPDkl1F!INtDJKspT5-6Omjh<!KCFeN zGouO;jI5{8JRWZLsF*6Yz=(=zO06N*V2~=N^X3xV5^FL+&8Wl)fCf%RkBT|Ifh{o^ zD(3MH7BBuEfE^X{mHwzGL2xP-sNAj)vq&velxJj790j+jm?5@c;jXCVT^O#f0i<|E z-LaCgMI6(!V#zkG{4<<HE0!wGOmQGmvSOKP3k!#w_1h&UqeJ(?M}So85lb0#w_*|F zl05}9xQ5|hOPdMM(mnv?Fcq5&YBqbIc0OyVBq=9lU0jEzT&gww9v6Yg%7MSb!Z?9c z{6QRw6KTa2{_t<f19hdMaEA_awL&KpUXP+nSE~r(cSUsR8WpoP;(AD{*iNgsf6W>W z1%d7u_|6B26)1P^tuo3G>oF*u`>>tEcO3TIo%^Z*<LQlDSzFO~8&WOYr9jk1aeKI( z-+3Qa#V+mr&TnFC-=%}@EYh$Xbcrg<@jl+W5He~8yze?)3_-W9GkhUSuy%Hx=?hs7 zd%Di@g>doeI$Keh;>j+c<|t0w6s&-;TL;3fu1||gaH{Y6j4$%_wxFK%Mdl#my8gu% zc^Y<1U7u5&nRx#VLN0=q%&r3Oza5A+TW@6zcOa&C*MO<zxa*O6UDNcfecZ&`i>`y< zbF}Lzrn?sb>UwJ8F~C){XS4qpz@3OCx9e$sSJ%Qgc0FB_neG_sIm1-XUBd3IY(3MY zc+6c_l!9#3MzwKgvh^(UQ`yS%KkqByj7vHUtx<2N7$L9#Rg=ObG3+P`=OaJmcDRF) zH6HZE6L9_MjTWPjHlAKl@MB{Pnm6=o$P&8{L+{YOQU=bFZ=*2n2nh2wIGg%Im~$tN zrgI+VrR98!xe{>pwBpY35TuCX3=LscIO||+j&n6yTRE9nRdbyU;LmeTgHNrU*;sWW z&M25z;OtA|dl(sz(AnY3PJ^6nkbj`l7IDS<3b}k=;eCw%LC!4H%rx>#A+$&ubdj@6 zo}Kx&ujvefO3SInm<#6?OjX-y!0-l~D={O|oN*Za7EUJ4F<5mEg@{?OHt1b1+CzhP z!$vs9b!9TF^lnrXPAa`ICubpG)!wZc)ahNraEL5qu6NrMNMWBTEkWI``-nL>-FrI} z6>Q6AOWqxd%52N$N#31`3JXG)cy}o(S7ah4-rb67Bhm{%$!BwXwQ#cw>R#0r6;s)^ zQ&A;?U+MDhQ&efjEO_PJ%n*qY6H&b1f(Lm0Nwl8=>H$UJrUydoJ*cSuM%FekZHIeN z?@{H=V@jT(?0;NQwzB_EiV7<GpHNh$Quk*i68l)Hds0ztjNlF^sP<mXU@X0d8UFNb z7=7=ROVD6seS{-_*<9njn8950&S$2|H!8iC9zr9-TvdUFS81v|-*<uw{0VfGw<n|f zEHsU0%8F-3qxj}ms!s-;@}A}>3aRg{Z1lnflaVzQg<Vit;=SK?*8HW0_krK%S#W!Q z;W{DsK{W5fp?)8x{}Zi|5#HaGo`2{*O87tN421`;1mi64+YB;$pHWphf1>Sub0&&7 zr{YL@p7*8l@hjiQ6XAmQE%ngHLV@%SdI2-zQCxj^&(kJXPH|zrD0z&6MY9(hp6#zU zMx-mc@HgN{CsQJSdZwwRyAv?^AdTuNKEhX^_#5#GFWq!y;4)0@k=uVJcr*3Gh^Tw| zc#w`JOWd9ayqBfPQlsz^X=oeMEyPUqTI&YGt<MMP|7H#v{|vq-k@lm!R;DXQB-d0? z(~h+<X%v3;W)yvb!YHrM46%dP)})T&W9Fe8HJ?Y>h(vG>;P2YUc<r>W?GwH(Y6r$b zlL}=hhk!Teb<q}9q*~ZHaT6nQKKS_i_#R$N$0VMJNp)MuEY)43Dnw<P)G;a7WT_gb zE+%cn{4*kdL4iNEulBl|8O%IyFH_j%=HBaJUM=|y6_lQ)%F_}f@;&PLyZjojujzgV zuf2XgZ$W*n;q})PSj(`^{srq-wKrI^9_X7cS%+xWs1cb7)~CT*<JD-^;VDu_==>^C z_Kei_lp2vU(39T)sP@K~%JxJ3o{D#D()Q=UE58xoO*9MSB%9<L6xz`pt{h=*hDYuX z9Ms9?cCcgms9c-k#}z4E1w)769OBI|U4FsPn<;l$SQK2j`pwe3wj0MO!kcZXO=-}T z^LLJ^CUB<GGgs>g8<7<dTyhlW$|BQ!92+k0Xg!gP5EXj!O}gMd$)?37*^x^Hvuo!V zUadBPo28LxD7p}bOs~NVQKGlRq}3{2k1;o*Y(yT%k)KHjd-Xb9mnYJ-4X0*<xf(2T zY8K<{JQB|2dTUIV@9}tRwUgMLaE`4@_$*Hj>opm4e@EuyG@0pMRRHpMO@{Hr6oPZT zQ_T>idmBs|rOZ6VtU#L)S%M?K;Lyf9Nt?MTVdil(^Hh`8$ys$a3PEo>eqXlS@UAxX z=CxqzBAh8TWgA7eORnq8E>r6aFjGO7sfgjVro3xc_$n%@yq!wQJ*M8iNGbPfDr$7K z4#X$L;Z2oymy)#8q~Kz?Al_%P8NbQE@q85Ij%Sb&83_97Bblyun68PlzjvpOh2g%y z(A;lQyNvnq;GI30lj3nR#7^F$CgrKr{gXKoW#n9F6bJYQxn!sed%~O}o2re-^(YLR z#-KcHx^iprj2{)Z9hMdE*+f_(FM&6IDtVtb-HSON{+8hVa}~%J61*NigZw=f4cpso zItI=LhLeT;j_EAL>ECkhh3J5D64s|QryKlnob!-q+0OM?IdYu4I&d4-3kyxIb0_-d zIl~-o!_Gbc!|hDLPPV}5i9J+%=c*uIhn>-q+pxd1=Qiv{NE_;$h-Gw`(+1^(;)USJ zj>lhth@5!*jR<7N<9nhI9*_SMee&Y*@nC5kkEad9<cY^UnAj#BUk45O@%T3o-ZqZ& zEj-FWv~5pV&bi1d!%2rdrqc!I7Ry-&@xpN+&vvFjN5DzL#xl+M1{1P{5&5thulFH# z<GpMvL@}OYF-ET3n&ntuqRjO{?#)_RDlu(WZq9Nw8I*7SczK%4l&|}Etu+}AZJh?O z*O|<5SMJqZ>kz2r*8C+KO>Hz;5aK9$FPR*CSMJ>Mtp>?i;>yihTg_Ra)~kpndxV%O z-g721&z1YN0_!H(vA-*KQ0+8X?aCcndrgiqBE>`Taw>9kf>&gzg}2z!cTqzOj2E>S zVRt?AxWsA!cA4Zyq0j<a+jymx@~TW*V<?s7T1QZ+?4(I7m%m5X4U>>8URSNsOQ@ts zud_v~l*&qLrLXcO6lTMudatJyq5!YEMKj!`eNgUYT_zcg5Z&=&7KhgTnp5=v>t5MZ zU_{b}VdEg@W<Sf#M{aoiecf(AfE-|{>1jg*cyES2+Z${-OAuPaA$E)D$djrO8H@Jb z6FKi{Ecefxcf%91WWJ2>^99d8!22HT3VMfV-qAj<%APSv-fO`7pp-ja^B$Jql{^2# zlf2J^w+1$5dJ{G8B%fD}%49t%wh_rX2$K(TGreh6h>m%~EXG6GKHYi~W#+m}pc#6+ z(62B0wVlF#bF_}R30|39^OC$DfcMjmc)QD6WQ7<UZ<NJ2stI(obr9OQ_AwUTSc@T4 z{x7y>$fjV3u6q+KMoi75T5FAL3Ww;aca+5dDevm6b7WJS5FPVoS&WBrV2O2$Y$^%S zc5i`2_f={xwVsqso)KwLgLNJ4)!s_W9XT6}t8@ZjNh?8PXBax(TW7iLaQWx0*AoEe zY5tOOytl~;k;6OQqNFah3k~lit22s5WDSll9L1Tn!3uHB_3+I=PEh{9%iCy;MLWYi ztrX-Yi^}B8x*CP!<dAN*T)rLPot7BVGx~!(JsIxrz<WA$Z1&F5=5O)M4{;d0dW)my zUddC&R_kG@qdG+K-kBCHbZ^CdKJRzdZj?pwdeex+hQkp|k$P{N72<sJ`dFN@?x-r1 z&$ZIg4!e!W5EQ=0G_3b7v{d6oiN>PUXsq|HvO*N)oo~@n8J;VwQD_RWqj!l#WySkA z>94eAqHIL2!m%S78@$Ub6{^cqLv@u!#WGaSpg<I>8t+<|N<~kaJX5<Picr4Jx>Yhl zs51iAAVWU$ZqjkO*^iSN*jw}xQSFv-#rUHnMe+HFM>+cL!Wd8Rc37(Q4$X`#n2E;E za6KHNbniBc7D+env$~@x#E#xw7WFGP@3%&vjQuZ;pCBo|^6t@Y-ka*?0~QraH}67$ zkW@9^E^YlI3G1)z00ocg1pym}cforl!Z+D_R_E+rd~03F`<&*TU_|VZcnM!-=nI<n zMW5Fw8i2;h-X1GNY2Nb|jgX%0w)lJP5IcG=ThymKd&4S0y%Cv@<7G&jY2K^av)58R z+hb9&^z1|w2q{(Vy<@otF<}AkgaMTKbuSu^hL*|RhnCxjjPpM7%?s^hUf!hkPIBY6 z*Sb^+g<CJ9@F|94g7**I@1K4@qbPS21%7LVDB1hOq6N~eudTOG46&p4nMK{ot#7Pv zQ8pq~==i`iy7h&2>&sNPzO|@Wy0r?t>mh%P_k*P_hz+6LQdfJXP&<HXb&+BTwXqmw z6kdj|LC+ED3OGY-gl?`9@(MUp@X}qX*ayBFk@1VXG~rG}$h;Opo#pUzukj#*TIK|G zoe>i1tUg&?P&$G(TE)}UI+~^R%rheS2jkZsNVW-Hjuza?7i<)bMdKo`K!m8;3kzB+ zqt{jxp((_UUY?+FDtZyo2lY7r;pibEb)GBKtZpMx7Qg~Q#dOWP5QPyCWP4HJ@WH&{ zeC6`Iy9hfd%P8E1_Mq2Qhp2OEh`J<0)aejB9mj}G_ToZ~LTTzKl%<YBxjzaaYWF${ z8tpEKqufat5M~vRQPJxvXd^y>V?>Ta{c8xu2CuttcXk4A5205`H4Z(s)OvSe7Fv60 zNe!WThVXWI>EfQX4CMpFY^h|m5xEeBMx=PX*H26B?@Kj`{)Wa4xJetLUaya!pK=g} zh$~SHv7<Lo&@wd$)#6^1jffbH9|ys;O<t8Agu$tUFjP>n9E4)<&ca-<z2U+c8{#0e zsNf*nIe>#ubST>Udxwc)d0reRcxnkf%(&euT`HC?jTgPaXGBiMaR7$D${Qm>6zd%# zXpPzy94d}NnOS}UmjDQrB(@&+qtI7ce56o!2__1?sD~)eJ3`Qqf_Zqq!<!`jAp6xA zmFZ*fx(ULNYkE^fh#Nj{kj4FDLG6qMhBr+-B{>bX_)pgfUgL7z@n*<Si{gv1_{SmW z1vwCN@YON4&P@zNx9MnvhB^_VE^n5gxpE{Hi%PVG*wdRYXpI_)T2X_tQF%I!c?d%r z(_5&AVo~Z))CsDVQ~gd9h$pqdtJgtT?gv5b*jLEtTg7nvOYn||V3@U1C}&rtI=ed6 z*)>g^%{>%%bI`foJ5D=$eA3xN&<LrgiV)>{>jce`&YmpVqb<aq-UdMnl(VOZo~Sn} zSK;_L1`E!fsGU72)!9=8RZC~DMB#IsG^@Nbgj)2^)ZXETtNoy%QK*H!zY%#IynCQ^ zllMCvnBV(>2~mc(MNqdov79X)lsXWkg0Xl4i-5wN^K_8TPYu!qsX@B1Ns#)WbBPSn zr8-ELC4;mKjgWe+2vNRwv7m`ENY{uVXbZ8Y_Xj}>RFJldi6|SD7vs1PeGsH8b&#$~ z4bruOs%4O#Kp}+Os`hRa?w`^i@}@+PS|iK7n}xb=>~9o(1Kw)yE)ik}?~j6tiUVCx zzC-+#<vmp>-zocJ^EVFLK$ubE-6qsE@a@9C2JX(Y%w2-Yr4>hk_pjhR)7z=Te_tZ} zl;PbYs9bHv?iUAwUFPU{D7=S!K%gGh;op@S{zp>7|7er&^LuZ{Ph*Zgp~L^@WcdGy zMo4{Lgec#8T+l=r{^#W9v_kCZJtb&?3jbflizpkF9dOFriDd%ee@2J@+0^hqFQ{5N z-XF|_bgJ=Q66&JiW#4flawHntK~lZ<x;Aik!obK1G{&X%Z|TOj6OBbTpt0WjScKT! z+ast(8vl`4hhm5wy>|r_DC75v-=mBT4~|PQx((j@+V&4pZU0zMv9$dw6bQOh<NZ_H z{%O+oyu)A~8td^uL9KRQCCrO-Lt~Y6Y@crYF40)D5RLU-njK<y?;Ali(stYKho%ra zdOrv%P__r`u_zmnvvK?*9Bc4QTWt(1J7r^#W>c}W{Q(pRi*%-!ZmWCP8MZzf$Q#p{ znyN7(AA|KX<luW=*ml3?)j+oGpO&|0gUr!ny%A}9IDTD;e0k4nt$E#4-Zlx|qH*AT z&nvV;6z=8PG)el|!7fHqh;qEPHq|RXqjq1^8<BN5reX4b<P~T?+ok$hXwweq=XMkb za<tj&XseA&%=WjBPqv4GxONo#{l9^C36wtJb+Xkyu)_BDfhXmGx3e}L8)I<<zJCH6 zp71I)Z?{z5?g`$a{@{JW>u-lB-0N!7Ch2HjI}=SI%JF*IRIeQEXBVU1h|I%r7Y6ic z?*Q#+?^H+o+q6SEdM^sZESlg|+3N1-;Dn>{X8wV;-Y17nU;^Euc1t4%p*+OiBE^hS z+pKE&dY9aieU17vdVs~NvDJ;T;aUuST{j=~BW!hRIH>OAkJPPrDp83mvTfLd1ijIA zNXj_arjhE_{TQ3=*afI9^`Y|nXmq5dorZ?Ub{vjGUNm@z+3HI1@I-v2%|~dPCxocP z8)ws0S3ZlHV9$`U^@jXNdn1Br70*-NP1NKfW$`3!ah(x)6uo93+!MU1TIV!hr%_~L zk!tYf+aX%zO|cIEvmC^^_A@Ak*wLF|({440^X&If#xG~laR)MRwKq!-;_TExoNrUH z9K;jBOYEY_-qCsx7pD$ltv`sa3_@LE;P`OXTjCF#`wdU;OErmf-3_6aw`3-*(BpVa z>Nqx}j^nZZIJ!BlA!((KgXhld0CJVhJ)it`QKSu$cW)Mld7T}i3U7@~H`Fk%x7nT; z=Hr?S^IkATZba+zGB_J`$WQb`t~@*`G0arronX@wHO!mr;Zn99=95#0`IN*k%UGPM z$vU;o+iX*_Rb21J`#|hz_HB}EK>BgUx0DV>UPR?ynGlWIy0d)ijG`QA_IY-Q_Iqd8 z?ZGS)Vw=s4REQnDt@hn0tAse${u*UG{{a6DD6RK?uM^_z)Py+Crec{8lfX+PqH6CV zd>pGCBwp+%gb{fYjR)WyvDv%aR=e*%_~yAeE5Un(9>odneO*CbsmW<>r@kPs(&Rk% zOk9(AS8KA)jr0R~jV2r1<pV)(mn83MxrbJQzE<XzN&y4wNv;gjjoOZzd^?PyK4{$R z-D!ttsdv3ik7Ss3*da89*wMSirY|Z?ci2(X8xh`vx)^ad)4NrN>9*7`;q%*IkzqO> z1;P?ld-v)v?M#M=_`4e+Xp{G#Ht!+dJonsuXn)wBC+^jQLGIGzWVgWq`G_W`xx4y< zd{mRO+yfbx$22+5eKrX4aZN6AA1nj;Cr#G5=NEu{LdUe;y`UAyKilfT!fN-tD99%z zGd~b!M8?79K^XT5-m}`~zxXyAMVryM$=hv*=#}@hP48t$U$ZBpDa4N6Uv2uWLi)PB z3}qv-1IHK9xY>JIhxC=yknXmrSca6lQQ{d@dvDolWA=73q<q}A6^(V?``Ww@eDmDr zbD{l1?f3+@NZxGH<Ye~-#$m5s2Bx{Cok4!A$ysh^t}dTwa-Mq~HyeM~<RaV-gW8Yb z>;&&Kt@d+Ytx-e>_&V=<J4D00f7*0FMrEJ96ip#^^uDxdx{Ato_Q@#Y_n$c4im22B zj#X`&zDbSB_cj&FsBq&?ETb`=6;Rs=5%9MY^7RZmka9OF5b*CtMS7y^9gwry3kB5O zjr4%OjglAm83A?A0^6vu;B6~SYN>g{KCe-9DjHXN`GF9nc}{>fNk`qlBossJ=;Z(# z#dow#fIC_vayyR85uEj2Za_Jj7f9J3<_D-)I{GFGmmy+!AQN!!E``M50d*QR+<OW^ zjtKbo&%CbF@Vnk9?{6FuaCWwZDrY7345q{HM_Nv0KRzm~0)ylHvk#UEXAb1GcJ4>b zpm?w^YU1&&=+-)J6do~+I;RKRXQ^{)Lgy>gIW3^xp~c;dGr*StN5kIaK!}EVqXP8L zeUA5-rUYi89j6_4Z8wn912mB@_ZHoRLfD%h2(hy_GeCXC<=ouO3!H<p5qTfStFT<= zdb0x|y5&s_FdD9WJ8w?lUbIv4);4IGAD|(OSXOj8KB9t?WoG4@_$}p3qw*~^^oDpC z7dw@2>*t-~WjI{<j-v2=4t`>)q_V5&J_vc0W&X<!Ztr}M<^D@V?yUYGJ83fL7FB?( z&}622;T({iO`49^dEH|=fb3#YG~YN)+kxN)?e4}5+>R8L{Y>|xQDErl>k8FHKz23h zf!hlA-6{bn1YWr1S~#OusxK;Fv2WpCFaWgrq=G2k+`@gv1KmeT!l&_CxWfm6?yKpo z_|beAf-Z+(`SiaHUP}U9Q#sm{+sDcw`Yj*au0o_MtF=kC+o1$xjV6Qc1d1K5$xQdl z!5~LyGVIp&0y$Fqo9o7~0#%OEtp#rP79bDSWYm3QF~~zq+GG`%7)1}lpz6vcX7S%S z0JSDxSu6g6<vRI#fl>4}IL22lH;b2GwW?fZ^6jYNbu8DLe28mA3TNWuDYz8cQF*lK zcIu9nB_<tZo}5I}mzz|>Plr#&xwdkJe1$?3pM$0T0rVxRMnUDV>W!Srl_u-hcPM05 zt}-8$q%H3+SFYBc1l{53XkDYpOm_`OeXS<LZdbanPLsKzS_WmFc|OdQA2zOBZ*G!e z+qi89q3Jl;WEJPTuhYWgHQ5$>A$r%+W)up)KrvW(6<*oGRaxZ~TDswWiN?w+HHo+6 zYGz@YRo-BR-o=an|C8t^cZ8MOO}5dwb~Nx>zmIz{qFQ;KCWG!C8g;!UGok+nh$!5M zzL}MGn(j$Vm)mu-;g%JE+>wyA3wy=NJA7G2;XCM7Q2Brv+DDytnumaPTMS3}9+Pb} z=VeZUdo^jh9Vv0AFT(v8%Sz>aI>j>GGAt{V_xl+K>rRK1!ra-6&Li5ohZENEO@+!` zzI8_AT=30XfG6ISi>-nNyntF+tDhtp?x!W-t<z5_ZMO%rXNiRs1V#qs6-?z)ORdD2 z@&QQYGEIivf8dF2Wxb`Afn0YWXZdnXM%|qpqZOL0FnX-SqG7hfG%$}b3CYsAB_Z8< zUcs7rsjxBFvpqVQ%jHtvnAx)jsTKT%k<}RPwVIp_6a%QxV|ooId8{UDd(9xZQj>Ef za7^VUleyHZlwwu|Wycrh_DUz`YU&V;&&@qxCO~Z0DC+6C2P~Zpd{T+&BO0HW8{#ah zJjP@)_S(gM>rRzo9-Z6k!6xKGa}ApfO!wXhgYkpFAdN;%p8<2xs}Fhmn<ibM-?wDb z_r67}?x)tI#$dnJv~z%|e0cAee(x^_J<z9X`_F@=gG|1X9}s_^mKN+^&zdTeDn#R3 zwf$tg21_lXad&Nhx^|!xFB)H|?UzCMLu6iv#usY)b)oud`Gyf9Q45PHdqCAZY}wd! zwkdgOEOB&T@JFoP;v3wT>$<sBC0b)<Um4in>DF*xY0dAYHCAKpz-;!t-@Gp;5bS5s zl5;d?eZN<c&E_^u*7ct=Zy8XwO}@HL1lfKsv*-CH?IO;wg#Ggt26%4mw*m+A0`n~y zf=%nB%NI&XqH)8zekQwKB!B-U8rQGu_cTtu=EXV$`_}fmh3%JW`m41F0k>E7;ZVh~ zlxL9wqt`aFS;J5p5SRZaVFUR!h>ZUhY~XOD`2T-cfsf4R^1mCg{?B0rMzH$f#DZhE z9}aT7CEi~)Ngpe4u;iMKsA;Ugv9e+Uj^U$mP~BMi5v;(qlK0240_7+pos3_^3hdTt zmz2Zg`z5Tvzk@TJz7Td@fsm=6cyb*}#R_Z<hOn2)MHe-f#tQ6DuEWX2l~~ClnN1R* zBaDcT6*!N)4Jo`nR^T@<WYCmWI4zw?+!NFMS>V9A9AgC>xRgZ&9Jq!>1su4E#jOJB z;xZo;aNvimooJ!9%!ED-9C#Cp7Hhk$$0XOlfk(1<HQWu?Y-3RY2i`1;X{c=q99TpN z3OJAf)xd$hSyaG*uf|a90`J1B4q+`W*HeK5k7Vshupwg}zGeLH00+uU*)|O-ekE|= zF6;}P*hgV#`;};c{I;z|3tUMfdt+$BpPb3!e;F<ClB1bQSK=7H8wZtYEM@b6rU&q^ zD&sp&!2W|A95-RR->U5GMJOIH9K{FtdGgRaY=W~3O@8)0<Gc}=C<pMP#~BmRul?Wu zOh*Vfm_H~RTkn=OKOQLo2lJDFgM~@J!NMfqVBruMegX~_Rx|N4IK+iXz`?>K;9y}A zaIkPR_eU8794t%%4i+W>2Mec5VFv}lFhdpzI9Qkj94t%%4i+w!!iJHzRu&03Sf~I8 zdv-_hI9W@S!NMfUU||wvux%1$5Z_^3PwzM4oEA@_48~$6zwaPX21}>%a{>}&u={v^ z526Xmp!fO?Y?39)p!W`cpd(QRz3=$j7Kt+GwJ`bn6p1qEg-!nOM4}9O#U_8cAyEcB z&*Tp;B+8(7fXN?MNR&Zupvm7uNR&ZusL3BWNR&bEV3WUMkSK%Rc#}U<kSK%RRFgkE zkSK%RO7p@hl=+UjcbYkLFv@%t&|72iEyfVv-SbYdhSs3WckH~gExwu;;>&5?H5OlR z3-RSM?*@zStcCbCm-mpxH^M@ETg!Xg;!9p3z6<5OW$}Hg5Z`t3-naOQROo?eD1T@1 zg`^N)7V>PtcZ@=OO~z|4_^M549C<1Q-*^e}%?<A$!M8s`d?&)2BKTTGi0=`2O9fv> z2=O%muR-wjf)F3Md#4FLw-52=uD4C_;d+QqnelryKB^A!ev|j0;8WiaAAfo;2tGp% z@#&`bs^C-65bwj_he~`p8RC;e?{mQij-fx&wxG=?e<41V^RjF{%nR`;msf1_Nm+<b zvAi;y55GdY$Uo5LgQXB3`gp@^J}V0GIfJ**=Cg_rpCfqnHXlEPc)QuVz~(LM5N{`Y zm)g7wEpg$!yKUa14e|b|cfZZst`Zg9+hg-aV~95ay?1TiJCvB<o)O@!wh(WFc`X9G zdnOUFy-0v}ra}a?_Kuc!ha}QuWfP=H+<Q26I(QDkwifpQcyB-=O;&28Ng_((s*zWj z5>XPDY`o%=h?2Oj`UvI0)d)c4$8xPQ&B32x_$$BgsaY8G${$Q#S)i>EsaKh9@e~Z| zJ+`^>N($81Fr%`&rVbnmL(Ea~${--N-f%&WUUg_U=kfyw0r3zvn8uacuy-?Nlwvb# zUW}{1n=VqD@IKJB7w4hhfGh*Q0a+50Qtj1#1F}4kCszK<^*10(Y<h-Z5&8|t607NN zK$e&kzX4ftX_$t2@f(n($xz_=(QiPO>LcX80a@~EPaD&*(QG;EHy}&wsDdprFV8?Y zvcGFL3|B?#oH&k#b0MyrOy@HkEhmf`d}#hVu<b)dG}|@hE4SHg<fQ?h@9=fC?0j`k z8xJ<Of-SqP$sT;LvW;X!lVSG?k_DP<<3B-dAJ5bPlsO83GA9W@nWF(HXMiP`vmXFu z&fu<SfE~>ND02?1kq!9#-AS^m0-(%E0ieuD0#H5-rpz1#K$$aI3zYzrIb*aW2|$_C z(FUgkpv*aRyHo)H$_@^;Avp?wGUtet-V=0h2|$_iQvj4X6Tg(Q6aZz;Bt;=%dqJ|D zqW~y#k^q#GP#w%s0F*f?0F*gN0Lq)dRG*^&D0BLgk^q!BLl|!fK$$awag_j+Ib#_o z2|$@MnbD8{lsPl#ngpQCnNM2``+gj`2MvtE0pDFp!$ZM7xQnnpM*&dgqySLn^ium( z1wfgT0zjFwfNtQEY)0lF2(fdHYbOCH+fBkvj8*L<0A)J~K-q2=ZeOfz*A7>n%>XDH zWXFF4fbu5U@!tTT<U>$9rx^g{8?y380VuzaP0au(i}C#5&iR!9lw&0GuLGcrHh?*p zqX8&$+L;?bbDr`BP)-v7O4gg%%wTL6*s1VZ9D3wL%_0I&=H#2Ci@wKDKpIUj?7MMH z3_U(9m4;h(!-wKtW=@VN5h!z7nQBfb1j?LL1j?LL1WKqn1p+z##YK>0=lnPV<tVnb zQV5i}!%0a5%2v^G79-H#s%RsM@MA3g<sOBDk3hLDQO;&jB2eZmWD3jcNpx9^>XtV9 zWW9<*dx+cIN+D3TDxq0iT$&+JwyOApY)}Z4tx^#vTXlOKjq(+~5`5g9LL<>^jX;^J z5h!zuOj^%@Ap&J?DgtG069mfP=qeE?^ArMQUMtq1qeP(0OCnGniR$%u7$N5+U=)Zq znTbIU=P3lryf!HvQxGWgk_eQ1^;{xQ<|zcqy!NbtJ^(qOzz@)C4IQt|!7DwOl66*| zM4-%Tia?o{f<T#<f<T#<f<T#<f<T#<f<T#<f<T#9PL=#j6n~(cmx4f<mx4f<*Y$46 zsSqghJetiRkqDG|sR)#L-JSq%0)aBG`zwl62$Xp}K2jtQC@GUeDFc9)aM&*q5Fk*p zBZt9nXHO1;Pw#8W4+P3nb2S2Go<yL`OF^LYDTzRtmx4g)QxbtPF9m_prz8SpUJ3%G zPbmb-yc7h=yc7h=yc7h=yutJlQI`mmc_|2#c_|2#c_|2#c_|2#c_|2#c_|2#c})>0 z^HLBfeM%uv=A|G|<~2p2%u7L_^eKfvnRhTF?ITd;r65q|H9??cf@j?cR*68_S|L!j zPC=k-okXB~3XFE^W(bsNd3d6ZIg!~~AyBqXL7;4%f<W0ii9mS>9#%;NN_Rg5N;j4R znVj%Mpmcu<fzmDOBiZ&tpmaNrl?~zM2$XJ@g|a~*P`X{!MrA()O4mC}vMB^gw^CW7 z5GdUg1WNa(5GdW=f0ZKkL!fl~eJmRk0;QXRK<TC+P`W7ylx_+FrJF#Y%zO%Q{C|oQ z*&{*&e#43U4JYz9oXFpBBL6!$k;RJ(xXAo8PGn5sM8*_OWK7{i#$NA1oXD8MiHs?n z$e6;3j47PRSO^aaN7*rj6B$!Dk+F9xh!Yt*A|LPP+cAX`87nC#PGoE`b~NMcn8JyS zDV)fd!ikJ2oXFS}cuRAF9h=#kIFT`h6B$!Dk+IVD_)(S}Q#g?^g%cT5IFT`h6B$!D zkuil68B;isF@+NuQ#g?^g%cT5IFYd(J@FQ(9aA`wu?sxnM8<ra$g>f-UxgES8yHWo zLYRLXC-QYP{~DagXp45(f&U7eNDq9!3MX<p82=@lNPZ>nCvhT=MkBWt3MZ1R5~%MF zD4dTgD!Fk5PUO3wXTjwpPNeR*KTag!_Hg4><3zF}?gJV2mrW%CCvqM}{@=ifj47PR zn8JySt;RJJkXjT@Wb8(aml2LBoXD8MiH!L;k$a)==WrrVL;L<Xk;kK!-YA?%%78cj z1Dr^vLsst&|2|G6`|^s}J_rRu*eIOHV*Y^er*I;7qOBQDByUmqIFWTyq>mHHU7(K> zd5UcDaU!`5^l>6Dmn}hjoeqV=;MA|diOh{6qq<>;egY?w?K1sJP@r!*{W1rE{t2AO z583p;8z(a6<3w&Nq^<v#a3W(0Co-mRB4hii5L+vza3W(FpbRUfa3W(0Co*;%u6{6? ze4I$)Jf-49-iVt0aU%bKTHYQAa*q5cP9z)l$BAr<T8$G~j-p|+;#mljqYj)%*1qS{ zxY{PNAWe|%xWRE4=v16YnoLu&{(z=boJfDYD#Y0#dL+eaoXA?RCUGJ;M4aymCz4|1 z>UAp$iG&2$P7q2C<U~o}MEVP9C(3`{7pia~X*7i@oJb9jsBt1y8*m~kA+j${ze$`( z#ke0%<U}yOjW=VGIFZU|A19LS&}P^RP)ID|z=_-fdJ?8<5+~B{sBj{+??%?+VBCi} zoy3Xs85K^XW^9fVX_nx(ZJpWe$8jS4ZVD$-QCLAbqg!*FNbUN5IFW~g_gCRW-Ur5i z11B=3a3W(0Co-mRB4Y|CGNy1MV+toS=Ho=Z4oyj%NS>WFP9%#)R!PU^IFYR9DfboR zPw`PSlu!{0C$b#ec#N%aB0WhZaUv<JrA>udXQTMeU~2v^;Y4nSf+S8PDct|VF_BQf zi5$oGvx<G3Na?!5iIhc!6Df-dCsGy_PNXb8)CvJOjY57FC$bl`YMe-#p>ZNv+#e^h zKZF@+1p-kFWSs!wl{ltj0xptKW9YXIfaa}qhY&EqOTn0gi=+$<7s&=hd?Nm3J%hu~ z!9`LVr(M=3Q2CQ^k$kn97AUw#Mg0g|qzxHdF%(>+qJnV+7b!bQxX8GIi&Pbai!4=e zk+MR<MV2bKNL4|&$WjFtsVWE;S*qY7RR!T9OBGzCsvulsse+4C6@-h7E4WBm(G)IH zRwUpe73IT4DhhCseM=GR{|sE@Y>4`maFO}AS~(B%?nmJwmp~53M8QQW%7=?oln)oF zC?76TQ9fLxq5|;@oJC9SC#B#b;|eZPRX~pq7pVtT!$m5}hl^B{4;QH@A1+c+K3t@t ze7HzOMKxTcqI|eWMfq@%it^zi73IT4D$0k8RFn@FsVE;VQc+bJE>clGT%@9WxJX6$ zaFL4g;UX2~!$m3zaFIu0%>Ku4kvk#ezX2Dib_Rfpd<_bI1}?H+Cw#Ce0T)SYm}v?w zatGQt$^UJ*ND4@Wi+oFp^x+~E<-<jCKg5d{1sAC(A1<<^Z1dqF2|s~PKWn(iNs{v6 zB9E37F<IzZ_%$394iHOa3NDf{@!=x>jB0%RgS=Fk<W=u;3e=^-MSeh5+GDaUn_?j# z59KiJY9fH`XzIi!8mZtS*&$mu4MS}%EjE2Q(?MiiiNnvqMY3JPMV^KtSM`?mbtskZ zz=0pB>|{jVLa{kqB-LVk_k)Wp#IC69KrDR!4qW6gbWXxWY84tTQc-}5oC~H@xJc@h zaFJ{>>|=0DoV&4u<>%6WJzS*H12`Fe#Xc1-GKd=Hw1SJwmy{0|sVE;VQc*r!r0N$o zDp=@*08?~1dH95x6<L(E7>6WWB)y<A1sAC(A1;z{O2S35A9qsC;UejG{$4a@^+8nr zZ^K3Abj4UE;3C!D-_UT8`YOS&dP=6Bg^OGuS$+gAl3A(YBI%@ti=<j3>nt2o;Uep0 zkThH*)iTwsr%_14MRov>hKpnaqp#p1$KlQzm#-l9qfOx=&jnW&8&lyT&ynmtT;w&n zvx19!L{dIn<hzpc;UX2M4;R^@l1hEJ$b%&1!$r=J6qlywVM6#Uh%xD5HcJ{Vk}=3; zq_a7*)f$v6@plR6J`emY?IUr_rFK&aK+T1$xj#{p%_6Ef7%lA^L4;|#TA`>jsRQkQ zW;@Mq(#~n+zlX4w?fp{Qla_H#WV2Nr2BSosaKwj;bWjy$2;`et(q!o<7!F2lWGfEM z;Ue|;`*4wTgc*>39e7WIZTrDR?n1+l!bN_GhJOhzGK~8`KMEJw6AekY$fi@Iyf!gK zCQ7Cshl^y;=tb~Y$>hUDQlW;6qzWVJ1{_o2BK_4x!9~(n>~<AgBo*>n!FmpbV7U($ znPT1vzInk<C6f;qNktkilGD@3iga%d7s>EpW=UNqQiXJ&FZhyhk#tAHMN+<zH3QA5 zaFHB0U(6P2p_r@(QAmZ0^vzz2oGZUTo2}s@73IT4D$0k8Q~~ngA{7O=$URVygo~tE z8ZMF!7+G!b+#wY%($}xxBAJ;mRKZ2kSl)8UIu^ZB;UfKBE#dvY0vD-HUOrqT<zfZF zv=~R@&IOqY7pb$r$iD+keAxXHaFM-mr|0M3B6*JqZfUs4v%$^X*N?$P@<tZ#0X%^0 zsF<(xM}HnJlHyX~A|I0qe7MNhCFR3K{-BjBxJbq6!$qn#A1*SxmmeLv7rqAvwO+BL z;UXEA><2+3hS%a>OPdMM(!K!YFcq5&YBqbI_9@m<Nm5SAy7)Gla;etzdjvpa@d44W zFfQH`aFPD-E4WBS`EZen^5G&?1b-YZl2-BKJy|zEK>&9D6kH_RIeb3~7x@hue-19v z@BCwMk;+!UMIMY_>-`*Dq%Y)`;UX0$;3Ch0$e)9Y^hM?%<9-q@^3M?R3viKI&VF!_ z<M5F0KM5D99={~uA}Jno_b1>YkCd%E|MT_|&baWyT7k+WTqG$hkcNFC3W@D7;3Dq? z{c~`UQ_-&BA}^LQaF!g29pwKixX9CC(EkuF@^*-r6~!s;m*65V><h*T{EnRu7s+sF zxX1@l<VNv-A6z6uq~Rjx!2|w|PU6EnpeP?MQc-}5`~gh=8MsI#(ua#w6yPG;_hSrw zxJZUy!$r10gON2B$5gmTW~zpZEJq{5%<E$BRhlZ#_l>Ath<lq!xJag~hKoE(_0e#V z97PQmd5mm`8d(pckO~*+_t_6Fa=YK>V-!;1B6S}P7fEMuHD&~z{z#hyTqIR#xX3S1 zG_rc3ITbF__ff$`QV)HM&IjX9z(o#16QZ&oT;%Ui-yAMdOZVX-sh;8^ucOc$E>e4> z&@p~3Tx7HbZZ!wJUxth94!-7akt%BVIyvrVLj;W?Tx8*4;B5*QNgYM<6TMNhKgxi7 z!0}UzZBw{N-&YM6Nrf_$SEKN2;3BPo`@=>0F{y3~3;#v9NZN?`XGHoya5K0_rmzne z*%HkdPxXP>o~Fvv5+gDT_08cTHLr$?R1{dx0Bdu&NNu`?i&U&pBk~wno5Mvml{&&y z`IUf+RQBM*%%7uYbGS&|Q`tT?X?y=c`@=<QgMJJyG8^ptb)!U0X#yAdJJb`s@z=ve zs!eGEE>cb41YD%j1N^gBAov&HBI$w;7fE*H(*AIfH=&`>uEH*>30x$tR_S_-`60?i z!~@4nEMiUJBK>sLaFP6Oot&B{gZIAy7g@L+f}6rc(kNx-Ddt%y<3r{+HiwJ!&D3y_ zv`)^d?@(w07pXU|8ZJ_AUX8+bc!U=O@Lp#H7fA)3r%Jd;dDkvF1no`WBK7t~!$m48 zYIK<gE*~yZO4?~saFK?KWV2y6;CMa?^65F?A}<I13viLtE@S>43jBaYQ@BXV^Wh?I zl8iqJ7x{#2sx~6Ixc@n98r@C8Me3;daFKpkB30le&|p)zNT2u5h!Nl-ecrA)$p8Nx zaFH<u7a4oS!F6Li_C|zok+D5dq*Xko;38w=afhjOJf`3xV+t-Zrr;uD8ZNTy7<lyW z!$lriy+2$eW8}j{PC=Q=sfLTxarEIL6`6pGRAd4!QjrO`NanZ#XL7ASO0fyJNJS># zB02azT;yw#GXWQ=I1_M@icG*oGV^@6NEe=2#R<4bMJC`P6$!Y=lfm{2aFL9#4;Oi~ zl!|lyLnyRBf;NSVRMsTmB9)E=T%;ngT<$|xqCNf$TqLdX;UagVEaz3#(EZ^eX@(CM z8A3b8%7=^O(E4zZC9(-{k&93udQgq`lW>u0dfG<U&EUNm^XjMIB6py@_e9RSpM;Cl z`GV&k;Qa--$OLbA*e}9GCV2aUw+1%;3|yod6%1KDc!{dm6fTnS@Zlndqn){~;Ud*| zq2I-*Zw?pf^UBMCc~*jV41Ubf94?Y^^x-0}Q#yRONQTgdi+ofzCEy|%F&{2c0a+&C zA{ihbF7kWXD*+eDc=&LU`LG2a*Y)8d>Anva*+Vt~F7jv;n!`ov_1TD=f<}J1s3}~e zngBRYZ%1QOxJXLstl%QIpokCS<JcT7l1sJv==DZx2ih5K4HrpeGR<4o{32XrBHS~< z+Z-;^KfP$UNRFNl7ugBy*c1D3k+jf<iyVd~1sAy)r7oBvP2nOrW!+I&zyKFnCxyU4 zemtu=T%_MvxCf0*;Ua0N49^wT%_#bCkyKW+59<ivA|FE;pr-f{UURs}lu%tIzuF~3 z#jn3Lhl}Ky+J}pbpdBGLBFj){4i~B8<ika(C8FAgizKPwBF{wM=5UdU8Cmcc8k@pJ z(jw{RebzP<eYi;KS8m>K-H0;wzc@CBi%fCz0gH;In}s9xhl}*B*Km<)LBPi0VDL7F zi`3ToaFL1^cpMAB+Z--3;n`(qYzh}iBcx}$tz{_saFNufJbS}B1!W`h6ps8vT2r{l z6wmfpR4hIF2nBvZttnh&V)_w%vN>F&Ht)yaB7<PJis9B^6q>_DCfr(y#-?zQv_QJ` zwKW1wK3pVqE4RL}rlV{`uE6nuX>=<I7n$PLw-yykx86X3Uw~^07wHd}{6N1c)T*3- zi&SGW%7}(W@r3fraFH{>*Bma=A7>30smJ*z;38?GRjgr4n!rV_gW%?Hk%{Q-Kx0$5 zNLnkS*H&yo(T9tqaVmNdaS_Tm|KZpiE;1#01%iqhy{`7bKMNNbtw8&)fs1Se-#Cm| zQ@F^KQ7B6tg>s=s!H0{a(LP+{90+Ry7fBl>HqpB%G>41Si=esy?Wv_E;3Acz1Y9I9 zU3|F6KS`;8i?k2fA1*Snv<^UHQ@BX_DF<PQaM0w#Mba`g2-Ttl^+2w`u{m61${-9C zR4fPKWE6e|F0$};v=c6}NW(?))Z)WM(xoB|7fJj{Bk~E3&EX<xjSm<35Xw9kYPd)$ zmlG*IdVjb`y{P+eku=1Ii_8E!5$U`-6u!V31njsZTqO68K3rr^$(ewQR0*Ddi)5&U zgo`{4e9hq^{eh@_1dY&;go~uPawHauvr+WnB592piCS?z%D|(=aX+}ol%c2-R4s!L z9kV}Nqz;0*RkA{;6FVHA0^a@LB2%1QEmE9aBa+UpL}!9NHie5!IQvI5!r3HTB+Zh} zo-8(?=)*<Q0_E%};yjcM?+qOJ?aCCm$P{N!6;v&qbq?JhE>d}yfQwWMJ>VjTg10$b zq@NucE|R)^xX3cF%j`HFh5g_nQ-X9sYLG5$5~PdJxj9^9B1kWy5kX4AMbbnWq-(_0 zDEe@bv_J)EySNW!!%G|cBXE%^LAq8@wG2`>FcWa4DO{u<Bn=m-t{eLsh10;>6fTmA ziZongKbH4YLH(UFg^b8%)QBm(K1#wx`q#i3E|SWn6%V1n?~OKvi}b@!8GwtVavv`8 zD%lS?YT{AyesGZ~;eRAG{Es#Xe|vO3ej0N$2^X0N{{%E5{7JY-nkd8noG3+`4;M)b zRQUfY2B8e}b{zMEi%bds^Mb0S<Cmh)94=BjZbWvXu{m6%Z(!sVG{&X%Nw`SAu`q8u z-AckmQjIkJBk?whK3pUfDC75veJEqYgN{p)4o%@AQ*8fOP_eZADDWN*-llMo3ENkp zF^<NjaFM=wk#o>U)W>=+2^Z-%7CwW<rf`u|BW<_sOHlOTBB?;x9<X<yjE^PZ_(w#f zDO_ZV?P)d@OWR8i!>WnhVpF(CZM%kxR8)--84uRxaFN<rmtW`rT%;m_bF?13&EX<b zc-tg+3-17LQ@BW)B>n7QpNygp7fJQX&!~Md%0}dE9Gk;MrubQC(+=rp_ThL97k)N{ zi}W`xPqv5gaa%cx{r&*(HiwJ!_kkKNQu%_7@qF+$hl@<%?XDf2U=&^o-llMov`ISJ z*ItUE4;M-G%F%xI$tVNJ3dda-(57&aDUSBHX@_*Q<cR&@B7H|ST%_J7`*4wRw<J*q zhuB|BF$uUxwRr=A_f$x24j1X)n$mEQ>Sk~PE>gAPsl?gn+8i#DM*47(Y{x!jf4Im+ zXozIv32$?_NI$;P<|DMt3Ajj_>cd4oDrM^p`7gmmDvJRZ*#$ahAlyyiBDGGVa4s5~ z!bM&H6><>gO6158I|43}cB?_0XCH#H5!r%cbGXQqL7Z<>u^hyYP-qSpnKFpAwjM+u zF47-34Hu~g&WDTCmn}wQ7;e2Zhl@-Z#|ArP9FNs{N`1IU6^8^|Bu_C4D{w6&H;0R) z8$Mhl+Y`gw6fSZc8X~Q+MQRQgnKH~LC5BnUMbZ-=F7gH`TMzTU2p37sP2eI6BuThP zs&8r2!N?#~?v)9Vgo{ihL<1UEdr7#+4KPL~#5TJIO+H*?5y~nd&b6najOQOXHiwH$ zNr>}oDwYXxKMKv^B6UI-k>Oa^n!`o<!P9V&YK#+bk%~;fMJh4@7pceuT%;lsaFHaN z!bLK-R0_<29l0`0Nw~;Fm@blwZxSw&9?3B6uxrue!$r~;6{b7v6HzuIf5NdjTx3d^ z?zE{`hUq;Nn!`mV!gM}Pw9Vln{V-{`NIg${xJX4N;35^7fQwXQ0xnXK3Aji_Cg36! znShH_F-^cls+*4qxJWW9xX3$Tb91=JL`eUM#-?zQ^j?PaHTwZH`EZf+TZQy>`$d$E z2=6dAhl@-J>28~fWk~yhw>ey7BBVE?k*JJK;UfKzYPd+{cmgg`k^e)u$fux|sE<wI zA`?*w;<BhITqGTkQQ2p|fT9l<Nz+wSzOz3;*@*N&$L4U6DN*^}reYbDTJSc9i}V+0 zc{j=qs3F#OqXGftIGzrjhpx@xBK@tAhKp49EU=B*0p8|tk($>i{0ADF!bQ?1>8KmH zA4MN7lKPaRZ34V^ZbaH)JGvaB))X!>#nJo#6-!5lgZEd$MRxricqc{qaOYo#i;M;P zV)2N_G+bo#9?1F`xJdO5EgqzN4!-7ak@U}pi+mhq-UZQcku;Gn_ZGU-_^>|-7fF3Z z<#{-F&kKByvJn{#j^=QYjD`;vS%P*-)^L$Dgb~X+5%sBXk!t9DxJX6W8ZJ^%K_mJB zy8Wl%B568a=k?(t)u&JkjBZn><38li;6&1NA19K$@aKPy6IlyE0)O(ce;Xre%)ph> ze+MU0Ir=McB54zGA`6RP5OE@lG)^SnRV&gsk(|?jr~wCYB8xOmBwvdv(m0WPc!ld7 za5Tq>q@zp_jT1>VO>iQ)%~Cj#M9yoD6UjRE)i{xHN&d%iBHxuJ{W4A@y=!SR3em%0 z$*;hPj9v-8rZ|zELHjt7Y@>7kB2MH{=oWnohkqR>GCFh??o&3wiM)>HXq-s4(HxBv zsmPzgiBu_;z=_luhv@93b;}^?*Wg5c4Zh|$k$RQ*PvAtV`JcdvRAd4tQjrx#*1*|@ z@sC9qr*54^<5I)u)`hn|5DHN`3RG9(t6&Kbf5q+KZl0p>6DhV;Dk{^k)<DSC?s;wN zml@r&`ST_8Z##dn(LF~I!M2N+A8mARr9_9tlJ<ttJ(s=s8wmR}6wZfMxjL*ftam}@ z_LwCKTjJM&Q?)%AZL2^{=I`{xE*7U<Ci~lBbqT2HiVBKdVNf&F&u=ot#7>}QDk^Mr z?>z^1Im{mQ;`eh=U+cSiD<#w^$`+5!2epK({HesV3qUQck<@SzE(NuWD}pF~!ZK{u zCrqz0><LZjAB^tjf^IN-tP&HD8a<Bj6?X0qs=-(2%mQ_+uW%(fSF)p3JlwFiYl%G& za-)-p=)m-?J<d}utn(#G`Rjd&QpR!qXw_5V@p`l}K5Yl8o-uAaXP(jH1j^&@KDtLx zzd=#CDhL}@pEja1+fGzefspoYQdCs_M775$N^^<03UYgFR>NK@hPDQE`d+EQ6NmQ# zb*AE+Al^9uRHLFMi^m6o+N!8&f>@_Lem8m=Et@3>b=u?iimDS|4FPqwqE?GJ)Nl^_ z$q%dYi>y7iDRQ$ZWSq}c<e4UB=!b}={T=LKoy@n8k#*oa!*~O>jq0&o^n&!Bf-kFa z4s;+Tf1+a*SBVl*0rtciE*=hnN@HK%{B9|wX59|5AJ__dhLosu#jZtVD9To%G8GjR zWfbfvDpPDZ5LA|;!s32XEftlkl!mDVKQe{V8kDjskg#{cv4UP1;#*Yo%G@YzG{n_t z>E$TO7ORlTy|NS)6n{sLUM&@sDT1WJiVBPK5TRb#ipmu$DJ4fy_(sgbAYYt`=MKI4 zi+Xk&z>m@}T*KIHprTCiF}n>?lqIfbTa}`On1pfaHH;q$=HhZM4Le9tX<{s8>Mx`R zl?(a{>6yxu5x$wyp^@55BlrmvR`r^ge!*-AIN}~eBK=twC$J&?HkjP&NXE*rf54I5 zWCpY}vPxk<H_XE7UWbY>)sIy|@wHRb_L@kSxv}fH0*q!BB$>CEL}@+6B~ojzx>{hU z-Yf^HF?c``H2YNM0gvF6Y2GY<tI`+-zM=U?#kbn%c^jooBKQ3!C2h}_(!g_{meq%g zk@<j}YmKY>oJP$L$~oHDVD#;bD4IiIueqCD_eI#9D)wj<eYg;qZ>sJMO4wV9s#n6^ zR>JD~48kpa^BvW1QJ-8q5;5OZ)Vw}F;AxNfo}y+M{miov6w@)Oue=>~_BZorXOs^B z3|4*Dp49^)i%@$99RwUN+gSfbA=?JZDD{)IR%39W3{yW-#VK>p@l-KDe$d}EtKiZ= z&AGY?Cl&zn$|a_;Zt#<g!BlELT5_13jKO<YcO&c8`*jBzFl9xDK=5Jw4Kbf(&=kfv zIvCY&p!VP>TSm~@@qRbM7-~Mj0ldgGY4<nM_F;37%;ps)saO%cIcO8aPodC9Op{e} zij|YtV4AxWE<t0kri}d_HOUNA<ux@DwD&QSKhqVBpRF<MbI_7FpL}l&=y^0c5Tn!l z#*|Vh_uZJ3%_ZObT8AZ=aTO+0unQqqs^$QR_*;{G17i2sv|tU_Q*)nW4T!f`GnSpd z(>3p*hB5wL3tL^YfIk1An?GWQG7d<<>L-Oprvo&trTrkRwa1}`Cd(7l`$GO}6)*<8 zLDQ?*tG6Wu(d=*H(sZ7F%>yizQNiIemd_!n5;Jok&moD{u=J*nrM&q&EroY|HS_AK znTFZVqJFD!RrQMy{}ujfWNe0O1@(AHe%)fotf%17${Jb{EKgc<NUAl5B&_+4q43G@ zK%P=SD${eNabvV`pD|94F$oUU?W=2u4xS(zYWNCCf~=<}W3{5X>Nm;)!8BN;jGwDT zEUJ;=n5Rjk!ibk@Ata6Iz1TA8!yR3uA3c~SM{CC5z|(1oPwJ@pq>5^-HvB6X9vu#; z73OkFzFuMasN{I9jzL?hq~cm|CKG3wCe<taX1)A@8-Av2RMpmDC0uUN#DFlyr3I@W zfPuHeKqlBmZQSabrObd6HQ7)zf!_FJea*P0t#vgoac%Nj7gf!vG|ZE%51_(ooL4oc zTauckVz)`lord%zo1itLkRz^<b`M0`_J|9XK;&4ucB5r7ey{gP=E4oSzmB0#W~xNJ zL62B?piB*)%&pl(CvK5_10s3^lHpD&_@k~VVa>PXxmDMcvSvK<>o#3e!I~+YG`H&- zk2T|XqS&Epda&j#4(=VgrZ;O=F=lt_n*OXA&1LW|T~oyxk8<wTHPx*7lD+QHH8rS_ z8GWx#j8UvWlJ4}UTa7#)-KWV0mF@S-?p9;H%66ZuQ`zp5i>hWX2HN`rmfSKm&Z~Y5 zQD5o#`TnpLGE3$AE=^(x2fWX*x}2e`6>=rvSe@6C<E2Jqu~17#uxeTTQp2nhytKdq zyS7RK%r6m~tyo~!8Uyy=8b_`1tCTFt8Pr#f{kcr-mA)*MN~>jGt8sOeT%^|M)`l86 z_G>j+&*WaGS?d_F^}1zIja)E}({1x=S}@?pYjRf2)6{u_Ca2Z3=aBj2<Qh4JeR4w0 zw@i!;k~tv8aQF;HWuxvszIs{jK=3X64KYh7=o;C;6Pz*N!)2(Dr@dE|<8=DOK}pA7 zNp<`+Ewo-a{<?0hQ;zTU-J|1gXx4dje2;FKr5t}#w@p)yzop5^%JH`~IYBw@ljGGg z@{XX<qVc`8)z9`11TPtgT#?JGBe&$D@vXHg<9vGeT9tA>{mNP$h#c8L2EzUpGMNu@ zLC7->It!<VTR1Pq*e2t#yE++@(Y8)3l?;cfOM%7)6_c^LwO++!oF?lSlku8$5o2<g zZqcKExNe(O{ng+=@bCi@<JFKfbL71+%iP1Y;Za+imPg5x+9Q(Z;J>rwF<CYac^kZR zgQgD{M)ODi0VK9nV{XF#fEbTlF-8tpjy|<axIMPol8icwV&0JEje4G^k2j>qL*c=j zN|D?_z9l&hg)48%(!p(+ckgKR$3ZSlpz1CMN%dbT^`mVxS&f-v!t(#>F>U4lhA}bu zzkZA-|JNBKZ$&{R?PtEbQuC(?&7Z3hEc-(1eFJ@{n>wctmwLBKdBHIk%m1s#TqFNC z7$fsmK>n$m6m0@JoKn6kv25vCzA8a}TX~IV95NVvsDrYzMoRggY54}4uqcd4m;W1# zu?tWLa(W&R(4m#$dnLs8mL=nm)4@o=6u3qS9VdkZ$4r&~R~sWALcwgyfg0=^Yjbd_ zq+u!#RG9;Hpf>mK=tb$2`l*t9iIf-|eVzQDIpz-e9~urm2I3ab-jTkB-*XO(kg^Vz z>&_@u<|Vaxuqvy~k1UTnSe^>DC<k#T!Q2|)wS-|Davp@!7#i`wAu?_PRZPd0^8e~F z5&6GiOkDo2ubvCG+weE0hpea@Qzidne>w6ll+12CZC>P?D$To4nqeIBH?&hDHGQeH zT`xN$<G*CzVRY=ql)g9UzO5SY@e=T`U+>Y<(hvN;-O*+i$Ww7j-`jNG(P(FX_M5Bv z#z4YLW9$YLp5;P+ub&Ol<a=b-Q8F{`No2-Oof$Wx6NOUPeM;bTC2;i7@_*)-W95I0 z-#aLncQYuDrf7JiiH1iK8XnUca*oBz&FD$#`D3KJ4@!x_(ND?$nPXm-|5qF1e+S+> zxH?>tCg%(JZ#|TKY9`UMOVi{cDyDIZfGoUB(%b~dM7ms)*aUE=@&`>W^3kFBM4%>f z7@Dp~qbls=YIaiHm1(1=k(_|*iv?&H|27WWnEj8XnJm+YZDVQ3K&k0bEjp;|dR&qL zF-zO^C*8an@_1`@e9x7b(M*Y#m1C4;kE6Wd4fAIi=1ZyaUeWUAX?d?E<Q=pSlDcu0 zzMp1NLc8%YJaHQPfp%kF&0y;KFyYmRr_js1i+S~Zn#nGGjz~K5T|&7`k{{C4`Db;_ zN7%laMhnK=YHX;vm_kiW)~mCprO7(uprcpei5TZ?rxqr~e>Xv?;J}q@w&1`4G_<^h ze2&(bS(8JuqJ;|oyqb0Ntg|EoViCQ^?yZY%o=~%bgVj~b8eelSJA0ZO!-S~R#HbqS zO1Boul^SFGbFgj!`LAsuQ;XJBOq3ShPo>*^3)P5Tr^(e-a(u4WWJ66aEXL*ynyjyh zGtq8rp>7P!Ge(SA4SMDf9G+`oQhvXqsDrls)$dfbAW78G!RHeW%H;Z+W*lGhD9?B= zXmU(d{n0pOyx3wz2FIhO<{sMpl5VXw#;=0zx9MWvpmdR{^QTgMO%FWeH2VehNE#z< zq?w<y|J0yKp0}s@W=;#ruLd=)uDOgOH(iqr#`wMHG?)XtHYiUw>~rUA8A7>UuhH`L zj`;tu_ulbU73<&VT5Hx$_R8Lq?1Y3Bk^nn}okD;W2pu6nfY76&QbO-VYA8w(6+{#i z1P>O(uBg~iv3IOTJsx{6c<g#CM?Kd2{m#se=DhyyIq&bj@B6u*d;Mdt{X8?zJZ+wt zHEY(anG?d}i#ZNh-#;&G4e7ELy&ePTqp;0FV`nMFHZvwa4y)H0+ZWeeO!l9INel}I z>N%Fr6<-dd+=0m0jc`=ss5aJ^TYz89pzu6(v?94?&sLoO!TF;!Pjg;Xx0kY$=;FGI z*o`I8IjoV0Cu~ZcQP-QKNpwohe9Mk{+H(!om^UGDHY>f0mPlzwD2X+VsGX6X4uw_L zQAHFn&a;`jSWyhJbLrBFo=tqYN*h+W4oT~1_cYID+D?_uh?#E0uCaz4L8|Ym*ecH^ z#Yc0K6?WXOROgWDSjxS|vq^F3d{PaaH3zC)?J*m-iaJ53@ALL3bWP2SnJX;&I>Uu4 zkoc-0L(NzA(KA|qRheWKT|kD1O+En}QW-z5c`DYJe$`^;^?iiNuNrpYzUq$ss;3)b zv@c(6AXlTYzI?X<RBM)9_W{ekZgSUf82gr|AKg~fehd+@h5DKPL&LtYw)?T6@GJP$ zEXD)+C!X$C79&K4Z>Eli=#26-Tg^C*P`Z?*Q8-6m6=~#cjk*?;v`87SltzqR3Y24q zb?SeOb=U_0>?Ksbf10w`24Q<etWB&@Y*D7~Hn!RtJqf=(Y8`g<df@M}=n!3$#YQeu z&bpMLG&s%FOkXZ(1e$c#RTaO<jd}b$F1X&|S9Nuh#v!XNtqQ!w_Pr-~{QfL>$l*6^ zb=(lc9-1~%=dG=?IUzVKjeFjS?U$|9Gjqe!*y)Pxr>?EzyAFGV!N0>~C*oMC9}=t3 z2U|nlKGCwSV~Ea9v#EH|YNh;6YC9`ww|a=5lg2qS+>yy-uJOTQ{pd0;jePKEwNgLP z&rjnk4`{!#ZWA?LkoKj@KWAm#M!N7=#aV3Mxsrjf&|uqE)~N_vq?n8CTUORx#;946 z=A1-s2Nu-5!1!L8mZk6y=GQ$z-!Dt!doxI%TlX4!j7C%b(A>H+*eaJB{H?ilud`BC z82q)l10I1|+g63bOO8XFsi*rFP3B8;t=eA4WBw066@j0pF#^SQWLPfEVQ;0TCx5{y zdepF~`@+#6)R2u$b!ROsw+J}v-rz^un4+}R`x|<^QhEKD6(D+$8mB~TXM`FTg%r&Q zb@wo^>X9{k<l*Sv(iI7UeLsqK$B*1ru2sQ42q*$~^x^71I(nk|kJaX{356Gcq<)@C zz$k5OpOYcDiqhLDhU9S0iR7Y(U<sAV5t$l-l^S%hz8{xGbYw56iyRFW<q?jpI0{pP zvWkcvl+CF7fz{tWqDKXFbvsxem5LQ^CydB(-&2otlC7f`l(4mmLVLu($|DBW_b5&C zJRTM8wMv^2>iI&`jQ<pDJuXWR(p|1*AE}zz#gTKE&L!VLvCC!SBUKZ-Byt1OxdcD> zhv-lGZfqr&MyTFM)$A^du-REh@}cQvC7gZhqsSk%c>_MX0zm&2ouX=DI8fHaG3q}$ zdb;|Ljb5bwvq!I1|GA?#ssDlj%faH%NqDH+fLgi?iJZ8oze@q%t*L)b0bj4Fe@g*> zUt`r)Zw!Sa5LW+LG6{Q%Y=qKRunB%1QGJ^;q8FRcXG-6Z%87s1unM;?G_0KdrH0jT z>nr8Zk$m-Qf30CvvHwsNd$?*j-)MSO8Q*GHRls)|Ru%J4Rn{7{6?)N0bj}H<D+lcc z%6@sT`d?GOU;VGDzfb)))<3EK7uUa}{uk6AQvY-6|E~UL)c>gdr__hR+S%SvpRNAK zTE|qLf@U!jiXRhA#?#U2U8!zW23BSs9c2`cP-bd)WG*9gtV$m_i(JM<dB+(!ls)A5 zD0`lfSCeLfrg@n(4Lbc<(oBqU0q{t^<+dkjntgQR<S1_q)~G9>9^FAVTzQ6a#rZ%P zD_5%j=;)i(e{A%<>OZ^w3H9Gt|DyU|T>mcq5gu#nKUct^we>$J;HA~|so*ZQKexKR zg#w;gUEfv#Pp-Cxd;sXeX;9^ys7=YQZ>RJLgGSY2;2QnnOw5m|V7W-YS;n|gy>GbO zpqQrEv<Zt&*e;$o&$Mg{FQ|t`_DlxCFv~y~5x*E>H>xA=B}F+>x&Gy*$IDj~G;$-m z_E$B9ipSS9tX}rKuHf;<L;m4v?A%5@5uq_c?0EIm1J|}X6`z5?NE&wIg*2}Id!XpU z>Rps{wlA(vSN{v@bJhQx`Xc;?#8~u1^&M5xjQU>ce@cC=%4%R%y-I4RZ&3eZ2c$qy z!>&+x(Ya`T$Ew6StM=geq41}Bkg{5()Z)HT4#K9}M1j?_4<p2(o_MuZpAwF|??A62 zzSoXa6D(ez3UHSVump8#MMg8V9S<*!EDHfF+eCssDFDk=vAqN<cmhRa9oRVPK-oxl zlyxdO){XRLvCh4~GV(n6cHvV;WH9;mRH@rotd~k%g680CQs7t;Y;8vlLz#M}(qSU` zZc_wrk=b^o<c&<-p}-CV|D*(8O~Jc37LUA1W@?>9<V~iYuLw3Wb&mp1r-&<+I-5vv zwUTxYQ?F6dx>4G7Ds?!)>s4w2LG?yFav}Y2OBSWwz+$(ax0(I(&G2<(EEDcfg6@c8 zrmZjEb9(1=K@APEm4b7T5Nh)%l#eU{i%@YI8Tm*}nfk6`^dYV>aT@B6d@`qkWhT)~ z2?&Bbz}U4BNaPtfG-XDfN{Hhc>xzpmMo5(f?fb0twUCo?<6uSmH4Ecql@;nb8ImLK z5$vw27LXO{p)A+|Lqk1Pob065y*45*GOay&z4m7)K8(1Wvr8e<yA08iI>e^EIbG2Z zZLeitq)f68fHL+hF7|OSOm8C1&M<QbDg~e9^#|2#`wdck*PKdOuBp!N1gb^0dpfAv z;G%7$U718PsV@v=rFbcbjMSz0R28%}&1kL20;m@r`v4SaSTh6Q8Am~F+irM%8jv#$ zGO-1aZ5oO6^1yQXezt|<aaZ={zfABb3~XY>y;$X$1WN`HTuZQBJ;7lF6B7vTgqE>V z6&`01EW3xP<6(WQ{5gUb!_Tpf8=zFgVQOcdO&cjAZC`Gw8@USZiq#!K@Ddmh8_A<+ zBbTt)*k@pP<Zn!!`wuc}0~f>=($x|8%!)Ne2%b*b<vgq<(h=6hRu?i=vh0W+VO-w^ zhe$&uQ#V0}$eiv3M^}J$mud?i_XK#33Wh0%0X%mj3-=@6dsR<yEiJxJ#n8D^06w7F z*kk<wKB}5fg5cvSvQEK5f;3-&2T@xx!4twGGhwEzI-cs-n0%0<b`o4&1MsNb1W%`# z@ADAc$Yz2ckED`4Xz}OM31)Rg6jY~q0Pjr$IQVRWU!sr77#T)FBmv_y>Xqu-VNk}z zwoJXd2;hu`1RsWrGUh1Fe&`JFPc+`Uz6zITos+jB#odf+Y!5Cv2nl8Kf~?E%H_;n; z6jB7nir{xDEX^)|WD7v<4U+u;4>I@W48o>Wg*H!!kDNaYn_PA0@yL<!a6p^CUqnm# zm19${eqVzh4qY!mLXUhT9H$gJ2ktHGt$cb|Z-4`o{||?{g#(rUZ)^{+R?)JK3x}$z z=tJ6(sw=N2?I=~5H;{I;@<<wK$0)wp@I~PS#rGt*tZ=GgHUz#XoTk9hFr#pW;@g6> za}?hbNxM)n8%)|o3LHh+C5ms?E&xwdscTVJg{LbHYY1*t1ZRUFub4fh*9~=tvmkkR zWMtz2e3?3nGCUq5kx1A)Tq43l9Lgs1EQ81rL`;t{Jd7eIFCBFg;YOgrn{8lpWPUq< zf8*|;5$=~6{3QE;5x2JtQf+RI>2dqYAZBydOOIPY2E~}M06ZMk5(GAPee}5NV$j{@ z&W;|pQVjaq+>FuVUWvgVn>#Fe+zc@oX`e}$+ZzUBZ0?ch?MK%UOt!h}p~t=Zg5zx$ zaqV$~ykM(+4Pov>6s&c)g_6gQAA-{yZZhO?gP&lR!+n7~?u-*$?Yu+y5a}OxxRZ>> z4OfCg4mWM_x{~HYhx@R24<J;6pB(Ob;&Dfkz!luc#N%EZK^wvSJiH@GQzf{qhR5w8 zf+2!iOnAKrPZZp{!Q*BH!BW8;54_b(UnRJQfyeLqgRO#_19<!tKR8$L3x01tX|5Ce z9^T_O;=zN0pO$<4#yWUOj71E2{DeAqU2G)G&zFP03w{Fa?WaCrm!A}S{0ufIclq_K zcaZ6QU4EJBeMh;2U4Crp@k7O6zRQmtJ${H7G`jqL(c|}O!38eAdGq)^T5yTW&)7VE zpcUNZ@*^*gA7BOdyZmm;<5xz(J1)Oe^7s`|@V?8Bk34>76IdaBtK;!InjkgA&u=__ zmJ;NL_-TvBk1vAxAs;GwA?kLl8f$u^T3A+9zUnBvA1VO%wm%}xF7$;}({(!6;#aM- zYo{TdYwN2{wYiAh`*R-P>us)L_nt<)VM+KQ!ta&=e#9P*``4seJ`-OG4`R)ea1T!j zPg*t~@Z}~Yij;n5q5`Y_ZVxyM8Fq&I)^?P;X(o^>c`hZ&?Y-fG>V6KYzv?S{G_<qr zNtJW(xA!!iUvk@?>V97%Y^uId8ryci83>%JUu^OM^>&1*eIak^;CA1wd#Wc*hXM9Q zMvIHUf6_Wsp?xt|l!4}=>mhLyd9-lU$rE-zi&(b9vl?vM{R+x*R&=t}?=oiOI3FnW z`+Y#nSBoxed&+36;;FurQvlH7Aa%aN=zJM%YM|xi&S+)R)G^aBC3Kn5DNJAeb1F() zVp1%mxbt`DVKn64O^0-IrXU3qDqMTsxeM3AN0z5!Rd@~;q&MUu?yE}`13Pk;1Tuq* z#3Q+A*43-I$U4%9TC6^wtD_rELW`-sKx@<RIg9QwNO#n0^@SR7Kc^1(O|}{qp#rKe zKBQD|Q+MKZ1WZNxc^OE*<VTg>@blq7v|V<?ezdY`UHx_g7w1-AfeT$a3dOmsc-jcy zC*-9-p<Cu6e{XMOU<fpy#8+wJhRx)8wJCWj2srEco02n_f6a1L(%p-{n|7G|Hy@3G z^E8lGUwgLZyosh>XE?X&0_1vwa4~1~4F=&_&gvTt!lj(mYNaYv_s+JgD|a*#a6C)i z{Eb$i5cw<z&x~cc{VkCIlZMmbw-}BoXe!mWa`p&0qlnyQau!nX0fX?iS$(@fmXN`n z23bSacNyg6zCi9yl9d9v#~>5h0pTVMl(V-pkoyesW;T%f4U$$2<N<?R+6~Bq2I+~u zq52_%L?S>QHppD&95hH@s`IEpj=+EQV+Pqp->88WG~h75`U#VBA?<n6AO~sopAGT? zbDlED5av8%kSgZ<#ULLLdDbBO5~2F92H8R6IfML!hFAT(K{io?7YuR(S-)tI4m9B< zgA@~a*&q+F%&P{O!5r>jL65%0oYxI<8r6BjAQOqaX^;T_)o&SOE0OmM^723+-)`8U zxJ@Ou?+h}T$UhBo3%Px7keiA8V34<I!jA^2B=VC%uBD<s8>E0jf6++4KamV;qLkEr z2@0~4*aIx(B(ax?i6r(FDcvNN$~^rp5`FM0@=Rh4gV^IvDm{m0Ww<<Yq32cTt?ZqA zHHSVPZ`68zi{@{itrPY!A#ENK{-hIbV8TFjMD{M7u%8KkBad@*!T}~6;+^AMop2Wu z^2y^oop2u$-kXgC_1qm24<W%l2&HzT_Vk=~TdU{O?5Quf1<9D4AjSItSv_hJJJafU z0u{aXMMV%tzua>jt-Jo?Wh}8C33X?XMH`#5WZ;36exD3R%KK`z#<r(_#{9o{oGHis z<}KWl+>z)0Rt?*lUPUQwBYlw0`_}$F)zj6h=D3<yMA8~e$+#Pu75l}SUWQ)NE{rTQ z#c-pU-hoL)5x%P+ryKTETcZ|ZCct_K_ificy~eVBxF{6fkEx<NaDV<ZfhP#wEgg(E zt+2CIJcWN}D=~bZV7cs~oBUy0DboZJQ&Z<+VWJ!2Jf!7{QQl^zPP2)o9RYTN=VMrD zE0HWlZULAq=+gWADgO#9WglQW#d#EeVKG|tbx>j`1@h~N=kPB^VUVOgC2%+vkA~Vr zvhnYnMoioRVWEO&G=1cW5Ryh;%2FQ#47L8y6iw+u1w#(U__0uN<VfT?YQ(QO6!LMM z5>>GxT`X*+bVRXmD0&KV-IRn}gJPBpLq}VF2FZ>bM2Yf!=F6!_56KGwr%3d#R;onJ zW9$6=pz*Be`HD#oxti{l*(oS)rDvc_9|*}#84y{Fq?EBt5r=^$W!{x20i_~efkDc| z7Dy96f;MITeJaC^oKI{fiNz+0UGTEX2#b@*>R3)nV2QDi#1@VQ8IMB3p?nY#I}#T) zhRP(AIRmtMM&v_6Kk%X;8^zRoPh=jLyU%ZgP2e#Oh0<B*?*RIw^b<efKV|s@CBzb6 zA}3`9r%lw?Dkze&O4n*w96@!C=NOI3Jwe4+Yb+KS%)Gj#$P)~Sl(pA@Ek1dRI3yc> z4KpUv9qS%a8M%@EBpY>*8MHU$gx6I@LF7IprHp=!85QKZ{u`AM7n6u>(At!Uzq9;C zjg^TgS)8J=O0gUzQZ{KU5N8rQO=H#C#apzCd%z*{P%@op2<nxj)F7Baz9SX#8CbUy z+o>^E+(_&k%_=N>GTyDRsCbilU!<{^NYL&}RIuUrG=^u&r8+!wMKuk%QrBzT%5pQ@ z&%w6^pG{LPg;{nu!(|<2xRjgWG9$y~G**V&3Mny0j*bB;uIpP>I_Wcc0p&paWmAZq zJo1c@oO25<=-!V)Mus~Z*A{Hn8LEM2xT}y5V{QFwi2Y7rsVOscLGc?|khN(~13Mj? z)Eik(GK=;3D^H}pgM@7Q$5zBhp?lKX!&4^%2&Y^tUc-ONb+nr`F_0d;o<YyhR4wHO zQu70Mw-*4`WJ~sYmcuE@%22iV0cAp+Iaf&zEEDQNGL{LiQ$$_U6j3J<r9F(iP&JuZ z>9^o|eJk`_sW*v-Jah{;GlhKm;MSPBpR;?^<W3Y!RbxML&2Y&!otd1^+!^lZKiUEW zdT|U&`?qHRm3rwUzB0I{KhSi8hW(D{fl@OJ8uguApfQ8Se9;GJra`m)tGfWrGH9;< zd>m*CgBJK-b^@Ah(70bjw535y{72gZ^$l9)k824u*PxaDdsH&d))oi;{k?z|*xKZ3 ze_#pFHmW+E;vRlZDbON=_VHUVx5U<+dyT&k<ImJGn^Q&nApAwzTW)h=$SEG|e^~>x z!ahTxLvrJAc<Mg?Ko<Tw+Iv)Lo!@&p(C)VG>__-d;V~)oFk83bG5+&J`x<nt-$2!C zj5ZDalv<zzY*vO-JO%Z``Q=vB=0ZG8yQz=J*CEW3-@x&<oYxU#vR`L(K32*$q&ul8 zm*dY)aj8tx9guPT6~f8ov~E*s7t7(?ZrGbHo-V-6W2){AaL(M!V=Zr*#&F`}&x&Zi zCYs9|A_7Ab&C?i&a>ACiKCP{~v8S~m1|rMlY$AW^M%)(n7lf>vn$AbvssOShD>>k| zoU_z@w#yhiK(6BaECctn9>9)EN=WDA=+y&JCOTM<FgnB#My4aCot=uq1JF8J$0=2Q z7U<jxV7(j_Ij#mi>#isc^#(Yc8A`!}065ob6ij~`@BwO`K1N*AhHb-m#Y*Qi>s~}l zI3r&?RtBFIXx%Jv2I?fEb<$z!oP;gL^HNDhg*dYXvf3LSq96yDdU?dQxWfn5fg13G z<}Gnx9W`ZC)Zs}uqm#yB;^abLf$m$f#Uez1MwQOX6{Fh$>#W18K%9fh%IKmg<CgOY zR0%Q$i(9B+e;Nm?BRwd%n0aC(c@4M*Aa!^YEhnS)JB39fPqqfui=!D4f-5oxJ*zUx z5KUhK$~h6?XS<wdZi=aV%i#>On=w*!Ca)33a?9atb8f~&F`S+oYm!AiMU2y!D^?T% z8=q9Bk<OZ+^P=Kbl+S3;7T~1CjY`T4QG*7WF+C|IKNZn54Zi~K9y_vR6}s~Knde^* zUf#Xx3}86GZ+95b`wZ&(yIKIf-=JatcrfrDFl{pGhfy)!g9eTH&%!S6A%kZ76B0ll zHf3|+nzNu;{w%OK1uZ?-`^hetJb4v10JoV&;b$Z8y<ZHPg52pCxH-DViTb1A1J5>S zI&!C4p5t(?n8PZU07+H6IvB~JL4{MJmm%5B{HgWeqcGPYOL4X29yAhLevPXmV@MI$ zeGp_nA|amX+v>=N%v@uL<#SzCW-HUDy8cd(W#*|BgNO|OT%!2~jrms)Eih=Vm2w~t zYPfAmqn5R&4yt4or41Z{IsamMKkYAr0G2RBEoT}?!dU|&mAo}>VYZk%7}@<hA%nrW z2xQKNjm-EKtg;5wsSLE{O{(ZH(Sr1KqyW9RXecoAAu@;_22nKY2+ee~VX8UR8%~k4 zhM`D17nI_Bia7ilB_fRAMV)15HR#gEG%G#UloqX6+Qe))@&h}cGgQ}dB=s8qt>P6G zr&F&98biQ#17|mDnvlbhJXJSWl$?O%aMoP0lP;Q_>`*Qvrryf18w=kEqQ<OwY0u3> zavx%8vljpyNPnj-p9*jo{n~c&(nc%m2>K~4K{*ZdM%p3TIE|*I?WKxysf*>_k1PEj z8i5PWJWNT)Gc;^lpH(g9(#EdLrHeNZ>!vYR3@48s8ViedWIm5(@H@k3KQM8#PHep& z798Jt0y0;(z8CPC)`OtX+SUi*m36IKBQ{QuyOD0m*8#7$qN`NFUGjaPw)_%TN9Lo5 zkYylq<tP*k$zo`dB2$r@Dvv;V*oxk$3ht95kY&qXkm$%P5C}O4(oqmhLb4ukitGrQ zRCzD1D0om6ykFvLEfg#Pf5(cxtr8!XH!3wg2c;u_A~!4gjmmmn&QSF4P>>ZZ#{JvP zdPDwICH7bJrzq)*;U-sJgOre5ij)*9TCa#dmp4O}Ezd#`M~+m)%K^LcGh9RRc@#;p z;;TR$&T4D>hk-h)wY>p2yN|s@+n51_Yh^Rt&SV6m^I@UFeYjL-#qA;$FRg6iML&Z# zxKRbm<%_tcs*hUvDKX0$3WI90vTVN!8fCSxb(^sKE=bSHHr<5lH?a0|3>x<L5RKcX zfg$U^xCorwta4l41umAHh{Cq)geq{X@CfF$D2a4Lne3s&(%uHxBF=Y&Y55>+QM3^t z>^K5{*^_X=cpC{;_6bdJCc((l^um0GaPoO!A(As(mPvmdmo{LxrA3}t2fJI8P%U;Y zoS#T*%Mp#>yQ3DBd=blg@wFsV_gil6CYFPJi_UK%Q*EDDf@csi&5vhV)~nzg%`UY4 zc_7LzvZ*~Q<~A_QE^czvROYrzI?75PilPD1(sHuI+xX9E!4x*Hv*CoCY<&a3)4@0- zH+AvCO5X-NiUP45Ul+`6R#1%&dGr1rR^{aByG)vu&TU`6RmED11SPa#3glVtodB9f zWx{H=52J&<sISFNPGR2kl~ztMb3vN73`mJa+`PuAv#lJpVus@S!DBfIofj23sN$Sb zjm5-mlvk#)Y;id$%QcppH+fm3l~X~HIB8X#RhXkz);Prr#EmRhsXeh!oJcQqx=T?m z7RSK7IRUw-9Ysc$0j<&~b`*II>gRNR611Xtn=Q7IxXWuAb+l`$KhY=_nUW`~QgwZ) zmdkQvrSOpDvLLG=na`4}h9v&uZ|S>R>8EE~)}7FHY);R-*~_e)UfKjpyg}0q=H$&n zR&Sl<icZWjSW4bZWcATmVX>aZ7%VMs2C@#*Sy6EqvkaD=H*>~ZE2ppaYz#br_0w2( z-ppx>t(+Q-<suJQe~lF&Z<UoZKx1)m0X9%$C3!Qa1FO|oS>8;1>Xb7`W0hhl-8xuf zLEg-zORbzC8mrEmITsS@G}a?;I+Px&mF^?HWwjctuNcB=9i|Cu#9_=EuCdy@Whgd6 z7poJ?(9n|DFmV+vA4yL;#UsQxG8v_d)#puL+S$q(t+6ra1sLGzcYyS7P&nK&Uo3*N zTNaqRt~d^<Et|&cQLaUBTNcX%B-rwCWMa-N1tZ4YNQk!VqDxmNOBaxrxi`Av6zJcw zn;vlEVCqqPG7S(3e!ED5Bbk#qViP;MVi20h0#7!0J`(z+rnV!HW;v%&dGQH`!+trJ z!=N*bT*;xusT<M#iOxv!I~<`hVv&nN$k?nhrn5vxu8+a|l~{+dsozNtRB|IPl5C?Q z`vSKIKR8Q~6^K+k?)g<(o4DW;soz;^Qzm|C53Kqb2*vS>k(n6$I~S|b&0tvN_jq2h z2qNQ|afQm*jHd~|H&>}R#ns~7PQV6N!WukmivxIu^M`1xM%<kMR;RH#(Uy{qGF2$b zi5;D+!syfRqnn#2f5CH<CBMMcmUGBdo{UE@A=hB4!Ih7fqfN?o7<{J46Z3g*{~Re{ zc{p_S<YyJ=ZRMIO+%DvET~L*>45mu?k%#Dz2l)^ruRuynif*`%$X_5cOWuKp`W9BS z8tmNMA#xIe!;;V7am<#FBF&M@(x|{)?Wn*ASQL^^_MrkdLv*Tq33i0#F?cNT<Z<Y1 z)8qpX9g$bWsX!RjC1rb1rpqxHPh`mNB2*w9Udxml@syP%pMWwgWLK!2EidngFqiq& zh%tFI-iG;d7ie<jUX*PmqXV#FM0V|p8<VVnTMDGp6T7j?<M4RXMh-?Ww3RDU(bdTt z;EF<74@VWrskj!)%fYQg{uS4D@*R{-$ib+=Qu$d7OHAZsJSCUQVo0fwJ>jDEG8ddH zWhw0KAfJZL9c4@C*-7?7sX%@UJ*(vTfIG`Wh_x>AR`98o_n^wU%1_{*ZZaL7>@K-@ zrH5>b4z#Cy2RwVpe0Z|AtU#pnku%`oK5{&~ahSXqDSc%Rc)OoGvO5MRQi8I-^uT|B zTmY>G%1TJ7mF>!~AB=nqjef9f2M-UCVOU)!zkuI{%5R|OFj)_u510D^kB~LsKT=k= zz{*y6FauwA%Fzg?dU+1&?r_ONtB#O&A%u>UvynbVz5zcRC7*-sN6WKXTGlagH}o7U z=Rx{78G^6I%gvBDK{g_^8e|t(JyAXYpHGtC!rPPOUR<ZhOjt2h{-p@dsPZjbr_0ZA zogo+0U~62t8eW<uTY=AP`A#X;1Iu2}VXn+YYn>+#A&Tb9Pdel42l+ml!?E%_XthwT zL_92#7eKeg@(p-kiQI|+UMkOkhRft6&@@U3KP;DHAZ3M|3=S)$4<D|Qv!Ly9as#f% z%kQA`YS|SuYh(}9&RUs?m|rI=I&!FfJ4&sWufw7f<$jbsNiIb5IayZ1dmH34NZ%;O zSMr&62+}vn$HDVd`AP~Fr^^G-|1^0A>iTp!6Op+^&WE;J<rr{2L%xVMa;AJ0KHMhP z!AslaH2CB!xdQs^kTYTFPB{}aXUmJg`A_nnh_PMr6_h<k-i<bLuH1lVJWq~*)#u9y zYGk*31oeA?d<a(Wkptn?3*~r}y+|GnUtKJ30(^<gM0>ncUWl@n$&v8e<?=<;>=p7! zc<f3!7g4uYMj_=Y*$dHiwd{##yhb)4>h{Tgu=iSdI@->4^3SmAdU+Onb%V?X{~P7m z<yamdpNDQY%a*9Y{qhHtx<%fI8o5=TjNIE~9Vic2#a!v;=Kj@|Pr*V<u0kE#@-alY zBQJ(jk!XW5ABe<1Vcdrzkw9#GBoc4Ju#ZLJGSt#1B9RNlJ{5@<!S^$f7=Qr(TqHbr za)g_h+!cZDCeDT~quj*%sG!kq;utWicN6>I+QZ#M32ZsSO)LbnBi+PNz{a?VFQCX# zC<iYb?IzxUmdCh>E6^6kx{2wKH_lCXpd9ZeRzcnbH&F(A8r;N3;4;xooCmR!+{6Y{ z#$-3~4zMY1;#w#_)lE34*J*BIHHuGn6K}ykGu*^epq%L@4nzB!<tEl60A{<111LAg zO?-z6oa-ho0OdS4u>t`%A3M43f=>OQWw=!@@e8tA^*$0F<jr+DbZFH_V>pNXRuG<n zsCHXTlwY8hEZHZUUj7tbz-^wZ?J6NJ*6LWX4V`wYMVy-?pI4x7t0fx49<ZtCS7KP6 z=X0(!ugK=K7|04ZS(;aDPz(S9(Dm;Q#q!&WqaZ22GC6K|9PZEWphpT&jK|QoLdCcR z6>=P^-<G$c(KvDzf>FqqVZ1Ag&@4i-09_^q^k<_$N@r+Ma8{%fqONso-m}FC2-|`k z$0Czj4??+uog{UNu@B<q0CvM@HE@-9bp(6{3|L=qsla!4R>5VaVB|RmMLKK*t@xg| zlFRQ^JY4Y)2r0ON1u^Qk+;4x&aitZVgPL~>_Q{2?&X&DU)RDJCC0A~S^<f##;r;D8 z7#NqSh~^rZht|11aZVvb*@+Y&mXlb60I=M|4m3wAoVcL|f3d{pu-l3!jv-}DVi*Kl z>#g)pA@DBH6%@QAj)o}(FB=C(szSK!eY+L;kxl@eyOlK^pjg2xCn1&3d1<KYg4f7{ z&oJM@*9EWZD#4s9EqzmSi;3YZ_m<Af7DX)gj*cqM@-ZCJdmx_fgep;tERRH^Ecif^ z<2ZGgRq&AsLU9UNeXOyth@zzye9E$XVs?iB;JYXNGN^buP`9A9Eq5UrY`K<UDz7U= zwcxB3<oyGENwlECj@(xZAI;ztRl#C>A79YkUaVMRX(+hGj^HRzoi$ldX>*YntdG15 zx5Nf=7^se#Ea+f!NgV4n|6>40!KiRSH`^EVOLz0^im5@Q7xYLz*;?t@aYPg&v2D2u zzqS>6^r*wHZF}ac@GAj8+f4XH`vG^`c9yFV*OsiUp@p-1f!K<E%&d4@xf6UXS&VMQ zlDp9$ZMg!{9a##MTsbm}Bdy(tvas9>1EcZ~dZkSH8M=j*@^;igYstVZmoLD`_Oc2z zm9ih)*HL~94xQvpxF>a%i_!gbk;B2KmmB~Ny=4rJ?ju9!+#2KoC^k)29L78GkEpE$ z@-n3SlGp}%%eJEDA|l=RM0p>mQ{{(<08jFfBO;f!fZB2<WM{~85WYEb22y;v37twS z`5gL%cCr|}D&$qD-u7}cWcQQ@Kr={gtF-ua6I%9Y`9?dAGR}u<CMPzc6q?8zs36-( z{D{(ylUNEB!cHOr40*G=sJt=wL*j{01&ygatUk4cmHs$L%h5%;@lBBeNQ!UdcD9kl zt1u+2R!w9d{1!j;a-^aQvBGR4vG~K0zHn7x73(i;2>cs=fEkf}ET<k9(yr=*)CcpB zn)VW06Mv`zpye{*Suj(zo?*F6<<a5_$J2H%;8a23nBX`H_#p_6#qSl5BD~`dQ)Q}> zemSx-;FnnZW$`(JCjLsYzbfnsY#iB&cTwT-R~La4K_|*!YWy`;5!?Ie2&ecvWTSeo zTL3f#WU7@OhL8b>k%IX9;uF+S{DWji+MfJBOftO)zKnmQw*hv=yC@a^m}amO`4IQo z_$N9K+i+GPvlo)G<6nqzEcTTq#6lB<OZ*#poKyMf<3addg14W6;yd)1MYURKOE9%) z5c6Sz<<0}p)QOi`={tZ=Ks1DLhIa*mqiA@tU>gKg(FkKZY953?!a32R!!_X%Ny7Q5 zAUx6#is@)=MPrUsis2S!qb@H+@J5TqiO&!)MdNjrC8Aw`O-L5&!eR{?Ll4pc#ePYn z;qz?2BYNieeA}4rk41}!FHlQ&l<h}@_IBPSi;IO*gXfVF?G{V)fme&$>6IF;6}=CH zZgHg?kAGW!4EBy}K)AZ{AMj>aro-Zx+=nI@m%Y$jYUH1gyFQVQVH|ER)ocn*;ulmg zT0|v?aeL_j-Ee!ULrcT$Wjje~tnizV5G$UPb`WY6H}LM9=41n$NYF|zL5rM-5@p3> zMI34rk7GJ-xvznF@gyn=)5I}wT=C=zP&>tOaX%y$PYD!OV&#lO2xBUdyfR!0Oy33Y zZ~ZBGk+=^|DqgG~fi1BNw8cv_=8DgJU`x%ELEhY@ORVB$3}mMm6GX>OwTc@x77>w^ zpj@u8s1?2j0%FA*(iX&#d@?c2|KZvcP#ISENW@#LcxT#6Xe`C2D$^Uw0B%;`4Y0HL zGzIpC1By>q;C?E*MS%|?MvAwRN!pKaZSfflm$YRtulP&_E`p1Tw{?Tmv`bjM+o?uc zIby!}EP|2EDX`==R(jev^zg+y_~nk3Qa}Sk=_dnujV-Boo#@pE*&FCJx_;+jz;@Cc zMpgI{5W2;;rv1n|*iRm5$B^O{X29d)@t6J=TrwFu#W#q{Sl2gFM{*lU-nUX3n_c?n z5cxSmJiBCkr0oEB=%5;Jy$7n239l<EODsZ5FKN)26S)ixF80Vq@XK2^bBR?l(FG57 zLs_W4l1Um1mkwwJY>F!6MllE|9fN=>nW_`QNGO|y%~VUKsRTQf3CoT{!gQUG!i1%m zMlG446GBW_yljqDGE*nGOqe=-zEv_yCkQ4i$L5YDvvq=lgwi3@V~$F2pk`F~^O3MZ zB^0L%8$Er=N{z+D7zu2Z#<Hv^pNia)wQ?kyo-Ny>)*N{>+$v-P#&WK_2_vA8T-%TL z>qX_fPoAY3>{Bq+lTV}nNs}uO_z|nsOt5+uoxEFeqx>2Dwj~Fm4cqejDl*-R=o0d! zT;yBntt#-8hj?r#xg!!n6OTR2Ko>98K({-WfDh~O5Jq<;cWKNPccBVO?$($iGQp?h z9*qg{Pn0ORS7WX?KMd?XjfF}h2-=eSiP<jvnNs>5^iL*0PKwA!^prekDpmBwEwbbx zjYY+~h`W-9H5L==sl!2yWs70VdsJh&qOJ?D$23+TW}-7Gd0b<0QGf<n@`T1p#AxO{ zsj)I~3tU_Bw8kn$F8aQbXEYXwq4~i6qOoeRgEjZ8#(IdgXt*WMYpjo$jHXiZg2rk@ z4_fr1#%cvuCX~FSu{yzfa>>gY8zD+jHzluVtX_ne_o~Lmh*ufSuW4+ol|Bh|wib-T z?OKcPU_!e#$<g9#h>><}HRgzHDBUiuF;{eH53EpQDOUQuVA2yYlGQFD5@_%3%=9VN zBqCPYl_i;6in?!Co@DYSO0+YB(-d(tT5h}cx@QcF8g$L=D)q>~vs%3Y0WTnm-NXzz z95rppmtnpwH=~zA<LC`_aub`y)$m^8WNO1mD{cg~;Q;d3$j2b46C3rdD=hZGCkeAR zdsJKkX^BlbFD9OZfW)b~-^sSp$>2M59r45lu>&1TV(THr(h@(xCy6sP=8Es*z_xQd z#Bs;D&^EC{V^MJrv9sH$yqNe2!-K@G8ii$xGpWkC6BL#!o?Hg({9_eXfL@WN#ooZh z=F){sLgiy?kFXM(>61(@sQ}fVk(}Y)gKLZ;f2GyyIk_G>awjgOGui!t^dpi|ADelK zEmCs;$EeqD7%(4M?U_X-)nW=&Ak$<GMrt-Swv&9O0!hCTm%iu?!im>}ga3qDCy$DW zT#v?>IG*`xAN<4{`ltaHVy;DE1;3`Fwe4|KGeCZ0haIDB|5}7rqF8ARHvV-WNt7hl zL|Cl~(OSxJD{#w($dO&4wQP?r)RtF)ha)$_=|Y|iMO;}LXZQCIT6&7C2Apa+ocRow zHHdi_Rg{gVWe}0KV9`peY=XvItIcc>@hiV@+1R%8XC7yjHBe^T={UfZS(dvJ*WCcj zU^@Le;MGepSSy<;dZ2SFn?<`AoVQZWZ2dTk#h|@`&Czl=#>3QBCc+}SY?+vc##z?L z6C7CVR5Z%6<*gLvimy<MWh*om7KtdZl^TnRJXlw@N|%dSl?2+u!DL-Q8pbyh(#dNf zE@fq_#Z`#tvc<H5!oGnDWlJ;`6&KV1TS`A+7kx1Too3l_EXR>lIy#)P<Fz#<U`o4N zeTvL<j7i+`TA7cAgZe--vgK3`#^j1P>*F})%g0eqA^9Pgq)35!N|l2P;AL4J;q55T z=j~`G92Swa;2D+U;6y1m!TL;jGF+D~vAZ!J>M$U{YK|;8DO|on)n0j{9{XFWX3LkG zrv%ICs)&xyTUKS2uQIigx2&^Oew;?IvSXeiS)Fz>;;wuN9hR1lz$jnJs7(8wsVit? zS}AlcKb~T(^s{ii306hR7m8QWr<E__{wWNmrOU1I)fCGCjPneDX!(}NBk01)R<{L> zcm|Fs-^>hpe@8m7(~iUg6T7c4^G??u=F-sfK$XtSZBQd#ewx^W@G3v^97S%4zu~4{ zzU^vL_!@Y>eETB`3yVe7z|MMKVNr2xXJ9)tWlXez$IEx7!kc_!m7fXhPeT-zE1qrv zY}Yh}v1@t(D#X+WbF?s%P^1Xfly9LAGU@3|wrt%8{R(l`4#nqy9*f9v&8Bo)F+j>5 zrrh6@l1UKBtc(oz1|Tu2u6roT{8a|>|IB=<|C@X^tK>R_PnqAldH%1Ov5GU9t1tZl zQ6>z@aH(~ME0GkV3)FmzQdt>_tdU5~--*lfXnC>nxwd~0;a)z^Hudk%qTlD+G=ve* z>RQllf^Kd_oty~2Te2I*-?r82ATmBhS+`=O>;`6*{G`9i_!b!_Blg{jBV=3EqW*?9 zV9VBMjE+1Nxk5gMTa7CpLi-6>tzxL$%UZL*1o;*knI+eu8R0yXuE+>iELKgVV&QL_ zNX4S2CNf?THHs)IwPLA`J>6wjEThwCMff;Hv?A?6SWvN;0@E%*NLDmbgq40huAeT! z?YUyE7zza|<|TWVzA&I-K5fO|mW`ug1qJe1!FdiqxMG7=@1$nxot#uJ{FNd)CGASY zdc_6`O1l?rreY(Tr<I=H#j;+3<Y>h@aWfid#R+tuX^0i4P=qp|7wB>;cBGvN$5w2o zIcZyQ7pXXl{H^q9$bPW}CLt=$5MRcTekSWJ$z}(okWKo70FJ_4r>x?9adHjlb|=+d zhnTClz^E+_A&x5cXe=xyGVel-MMYIVU>9iz#l-iJT5++)3as!u5KvZeZQ5hB<r=D$ zwh`mXihVS|O3y`fWuw~-Dy|fFVVqU5Hz|J~?jRLcF*2d3a`d%SmQyb2+@)qpBOK6v zq*#jHs{N>BsSH@(ezbYi7hICtzWzuE;^5{ow21bHYb+{0mB5bBH?Npjl?m)f@<k7V zW<jd-^MI~Etwh^T6fYtU+fOo4V7213AYXR7l}F2*ED+24(3{wDS`9c@>D}PcF)*{b za<bR~B`T+Av6fhiN~)a7IVPAT)<%I%+XZSW*nkq1vyuvCpfOa=;Y1ea03LuIm2<WH zxF~^hD(C6gt`swf&DU6f=YOyx<J`(=Vh5US<)X)w04%vhh*vItQ(>+sMf0p&@{1Av zHEyDnOEqOw3_wU$F4K82AxYVoiTjlH4$X_*jf?KD2t{aa<pTO8^FiS7;XeG~Glwp` zUH~{o!8V<m$r4C?nyD1>E1zcl;(N%-rdT^!A_OFzFNg-ma098lTpW%YY2_8k>fezA z>`IMcq7H4f(gYONGSQ@~v<LC^4o$jR`z%*n2X2+uP%A7TLi2}!5cH0A=q^^kxgC1w zh_b|bR7!`Q%xCqTh%tAEUb@0~dm~jkzZ<+2^&9J0AQr_jOWZnH_($}L9or-ezl))L z$F`=hNI`edF|IX_TInMpb;LAS-)X8Ch+>_lC7CQi-`Qz;lF4%D(`iPM$<v7bPBS$Y z6;E{pHcL}tZtyIy+}Rg;b$Uizj9Y!Dza&|{nFs9IB+D#BTqpHODr0ghI_XZ&X-cf> z`!kqa2r1D{0*loU%bm>j;uzWXMHBB7(#N{syN>k;y-s@HT{hbMBjK!musPo8G~53a z4(fEetyRSWMC!A}psqh2t-jM%gNA(v_1NhQgGT*&$~n{4uU}&REzI3!lRcu-7iB=V zoAGt7Kb^T}+2d3$@Bf@$!W|cOsFN(>!5}eApaZJa2O*<Lp90$5h)*>fZi_`fz%Lq$ z=g(lY7=f?}Opk)yR#0idpvlM=2jRouaGoW|eznzb7&PS=Fbg)wQ~Q9KyaQKTo{x6v z$RE&FLh_v!9663ch)6leL#vP{K-nyL6>?k1D0<awxdHS!aw}|VDQBYB&6gu^i!G2p zrf@k%1ROd@2mMb!c`o=5ka_ScmMvs+*}{jY{~>Y)Ql?w2&VWKiN}&toG6WR1hCzF< z<zR?(WG(7k$eYnLUAYR?8<JO|MWo0hQTwSfihB%t-P^!o2DA-hSBf@}5L~|zd^qT9 zfR@1x8pBOx4-A+Aht&qRL?|=3n(h$kh}_`T2LZCo#0+4!nKEJ)Y9%<Jv2fmUynG67 z*H|=fHnBT2785*WCAd>#*&+&`1b1mHSELmHQ?KZ_4seSP>>iyL7n7NHuf|Hm#|X~g zK8=+{X27c8Cb~!r8xQdQIj{h0l>|4h2p-TFCOzQd;6aV`vC@A5(KRqH9z3e8c}(-O zwEmB4%+>mHTR5-`YyF?pSX7Jqvu25LEX6&gv0N+6jaO=e*CX_0@G#w<b}ni^c#Q{A zTIt-3g*&v=1usV!Yr$?ts#^XSyi$Qox|z?#!Ru6&ahbjy$@AM`ayob?(q#s+#xP{X zUq%Cbcakm>p;5sztVN;Z9a^a};#T^@01iTAN$_FbjJZp#;G<-jXOR>9mHmWJ2gHAT zO|r~q0G@1#h`{C~O3oLijAs5Njlt6x9-QFb8Nm+`5(i&XRGQjq=KF5|;+~3Z;&Xy; zwT<5;Z9D~@34Wj)+8F0<G~pm*M;^t~NALo5@>LTTc0XjnD+LQ?F2p`A$^OR5=cYB> z%Ld_O``jckux-QLAB)BZZWK@U`ELT?UN@CNn(ZrtBDS%}S7!l7%{z&>I$kG`1}*Ws z!tp`6LCf&|AMG$4<l25AT58bJWLSP<E1=0Q(;%@6=$^{O4IyY@`>IB=ZS6H<SguW_ z@Nt<{$Srq91ckQ80zsZl8AU7S0FK*z09*Md;>!Jb>VwwC);3MHE@%zHLYo4WE4gV; zIOu3JtZc4fho(u4d~WE&eSWHggz-sflTT{%z_ZL0jcXT`8`Suu!k}fko;upp5$(^) z=k7q<G^jS{Vn-Nx!KVyipM(3L+Ri{0>Qg(VtF7a-#LDMJLfl`dF6d?ZKf&stcapZC zagi1DF&Id>&k(mCstx)Z(g8`;73n}j8n^Nv11WbWstf82>Ck3ehne^)(fSNG`jp|= zVwB`wMzuk`t#v;<SyIy;@vH8A(DCm_K$HdJaV7y9*@UD*-hnPK<rsSlEb@P0r8d}^ zAV>4ju{JU3SA;au>jvT;5=^uGml4~+bT!JNyHxKt!_c~ZDQ*$LOj{31^*K+$EL%6= zsFpL^$cb6`uYqyNB({|Ww*NQ=T!B7~4U9bs4CdN2L7n~@EVN0ESSpygXqFW$GAeMe zbR#kf-5N4lWqafpEU~Gz4%e0T$AGQ;02I^FiN%6O6Ryjf!ZnvobCvy$7GdRY2JLVd zlO3$J{T{86xy~4g(Fxnw2~9Suo5Om8hJCJX2~IR<)ZbeG^dy7Etio%+I6FAq_Q*Zh zU{fir=4tj>fUW!&apg`_xxpr*=BZ6;o=7!Mx2c_KRX+g;2iM>$uH{y6m2C#E1(O%x zPH8aLDs0`KRM*-aCNH)?OaUDx!-xB9HES0ii2R!1Ud`oh+YDb6mwOBrw>r)N)pM|> zCb&~`y4NOSyyk^Y_u0(0+*P>l2B1bf{jB`UfL}F^;d;C6+qnA&cNkw-{!4Vt{Whhm z?(hQuGaJ|_9=AOf3Ldq|PlxUkHn)#pXpCS5U$43h^<7x-r2VAIs<ra7F_0WQh2}nE z`)X+Mm!wzx)*NX(+vJvfE~RWWnY1t1{zYsLFE-Ks89oYLYNG9wg>d{CorW8{WlIZp z152i3ykpCyxcxiw9<UC{P3WIeWM}9i<@pG-Ov!n`EO}>J4#T>kqsf+cplpsDj7P?n za#wd$x15NPY=P{GF;pA5H_TPB(=s>=`)eBx!)^e#LGl#zVuNKa;30`Z&}5b-UIUA) z(!|^O;9Z({Cl2PNi7!zmr!+AJBrQu5DSgp+N)rK8%q>lv012&16W@b*Ua8fo9u{T6 z+ioBnc^=}*l4;P#mK||#apVbLFQf!NS5AbCkW9hAGDUum7Lsb^A6|=9e(>FxAk*>4 zCcxkk9MyMwL6$QSF#88JHf!OS=O|wd&aw>}RttcF9D_#Hs-K{xL1W&|DPVi8{ZkFt zs<E2y>{o2F{a0`&4ss1z;IS5iS8P_kuSRaIoHrC@iLVB0d4{r5_gDD_t@aoy!E-hv z&sXEM0>_7CPH`V!ji6c^wANQ6wl)SGfer#>s}Q4OgCa+Fyv2^0MfK>IAnwq^{(8o7 ziNnLbRggafAQe(`gEB{3Rc^Ggw8#o0BdkTXHz>NxA5nC}1O!Xa$%qV^M3QCD!J$@K zWR>$4U?s9<5ZV<~Y7DwM9vK8(9ID|j?FqP>;~}5zisl3fhgIwUlTEd|Q=+m8to#K4 zo>FbIx8t`$Yy^FhvV9!cX9b5jx_P>I`U34ukmm;d9k~Q`V#&8KO0Z>S9|*GY4<NtC zc(%Jb$Nw|i-OwgpDqe;q;|1?OK>Gpo3J0SM?dT+}j-L8oX=8)2ue#zp#?T(sM5{*r zNB>IO2eft2F&d0Fv=frFx>g!Ytzgj92-?Bm8x5v79t{fyJM@RveX27Q`HXcHK+{Zp zq1;7C&zs0{vy6<{O|&Yy=KM;_eFnd3heck&0>`6sf)Nh=s2k9+&b3;GM_&X-ICP=5 zf1&fJ$_jflJs9iIW4a|Ra^6*0F^`r8lN>roTesNxS!LyVG%T3m&>z}>B~B~og7fV? z>K@E<Xub~3rB1cV3atEN0dR}N+TeJ{A3hU=t4#o)OFIpj9iZr#-~`8SjmN)Wy=efr zPjeT=F~O;hM~dJihnzYrnr{W0oE-qI{C9EXj*DZ14UWe?H?SRc)p|6&(Kx_b)x zEbyT)73LXrSpTi6bc^G2bwIGSsY=g8<OOH^>h9^F<xY-Ug0qbJJCf>qtcIY`VeR=> z@|LmFX%BLYi9NCpwmH<$zXkJr!P(AWWQpSSww1pHP)9V8#^7AXV|xpFI&8B3h#J7> zIg1q&7<d(cpV15(gFTMUys#;=@Ev3}274Wktb*MRHC67p!np~c$AZDd4uuu{$VPvq z^B7<&KYb|P7$b94aH*qRby;&)?R6+vxr&>g@+^wFU>{Vapr>r!sr_L^fUkA(kq;MJ z`O5$_B0|0nZZv+nDd{I&u{WD8qSh~GkFj5&qIfs_u?=MpqK?M~2OOPyyCFstJci7# zg9jXs+=E*kYNX7(&p8*M$AZC~4&`ez?{{thjPWn7JSyY6;BI5)J<ZL0z@cDeX5la_ z-9wPp1qY4xk2Gn&uPp>TYB~fA97clnO1Q5fc-F-1Uz6JUiuO4}JJ!md2ihxCguY~G zUry3mh5T}}A$Z5}$Srump%Tinx141FJr)dJbtq3;_O^2xU@M<ngmUN5DZ%T;vNxJr z_Krir%Cb)YaA(ol;BSsUgaP}0(lRT*WVmG=3n>l3$Bw@p)fjw|RL{GYae1R2JE?)& zr%o8;PBF~t58x|Q$JpQtQ|`-TIjfMHmNo=GI376%pF7k*ne|U+7_vMT48C?KTbuR0 zGZnCv&mB%5m_oC@F=l<++^ioQ3RY&l4S*Y?)(5{h`hnOIX4I#j_H3a?0JZud#Swa7 zF~W*_BY2aLLO%gV#74+wFQJ}*qxyJBK67*1#2XOt3xX8kk4Lu>qzZkPvoz1J;W<`6 zXLv&2)f@B!r4-cBDW0PH(R3qcj+K7`7@vb+8yjR9#x0VJt-=GyTo4opkD`N^ptj0; zdE!)n9t#FJg34*{<%<ge<Nk*$w})LE_(HepT+yrpED#h-?{#uV;!`3pa)Y>#cXmK= z`JK=E?gBJz2XoxW4+ou$i#jxSQO93hv>kLuqQ)A6QlV?1ta&YzH?M_?WG#4<9<&ow z+MibnxV@MKW=?Uv_FgAJ9i8GaR{r|{-heYU1YLxGZ+p;I3)4gDdgyAnHv021a1FW{ zPOH2|y6`sj=;EKX3~+bxgyOo!%6CWM8y$prW6;}h?UUqc6&{An4Va|$C^zUSXs4=# zfkGn7W5J-Wpk}%fYDEdst^B#To(9uS4Qfm!^lx4Xg9HVuO4tlw2HJuf3>ESSkCl*G z$x686Fjhk0ZOHEv93_g>eep=aTZ{KF{dS8osYsbLM(hEdmCx;d4@33W1ogrr+hCNS zHhNfaxOfOKqx@uc0Y?c6R}HCjG?w`)jmHT+OE6xTPTeEF;21$g3g#?YiAezw0l8CL zX9bG^d;>RR+rebvaljY!b2xr1STt>(6-*Ib6{V#+|EVUx>wMlUf@#XtqWB_o{#!x! zlB$SV*xQS_vzscSiW?V0!eZf3Rxm?QT~!kc#V&vzO9pcVwb3=PNL&XP9SpAA{W#YS z=9{Wm(7Y-Z3yM}wh}YwzF*spE&}f{nJm~~IVqc-WuMD3E+A&}ZwT>6s*wxLAUDMpy zwZAd;cocpHg&Tttjj<>FYAknS2G`StNB+SHf@&#aH;WAbJ(dhM2x_2>Jx!bs*b3gp z^>I`dj6KB|yQ#UcrwfW!#&V<Qf8eHB6Pzh@r@zfuXZbsNgWGnYJNiCW{vgnDf9O+# zvyEf^lyr<o9>ESl+4{z^OO%6LIq4(-WpF6WIo~*GcXKCQ(A-ITe&eJIQMg1o=@R3l zOMi9J%g6-ReZnLE;37d4m6NU(R{```GPqn&1MQ@1#C?FRAms>5L!%6wbft09-sVo) zCn#DusWXT@#8z!^gYch7LGc@#oYWFg9^54Kb7LQ?a0+N^gFA)C0>OSkK}DgCfNvMQ z3BOYV_zqPbgFmDQs8JW(D)ck(Z6f&$+=X|UI|YR+Egk~EZMe4u_Zs)#*W`Zk2<{dX zt_NfHi@mBG8Yd^2KR`UdQ4bsUA8hXaN1D6;(cieg4GNz$g)#c1asQuxb^lmog6j*y zBmdxWK^2wzpA%)s^H?%?T2KS+{^vzMz*fLr1G$Yj-2WHj{%4!J{{=zO%J547aG&wI z;1!`C8eUBrj?agX*&3W0gEx(WZ#5~H{~9t&mG*~B=6g+<h1{pSG5Ab)EFQcgC`Kv& ziFgm7$AZE8f&#SipNby=W59!gOHsS4f)9=EA2rwgGeN;h_er2V8nku6mqz!me${<7 zGE0%!h!+aFxBIS1z5H{KS)&a5(PaMAlv&7q)f<Bp*JJVEdqFWuch|ibpvQv2FM<NJ z?jiR8U~HCz>wXxvDzIHWFmT*v1A`Qof|c%NN1@lmXmMMR=IS}@h->Z!YGOKSusSP$ z3`oC541N&AT>oc24P?5>+wwJ;K(h?mXyva5?Rdn?2SH0i>o=#(ZK5sYnOh$Og|0{D zLAFbkl$~we%>X^}3G!Tu*LKF;ivU~s+-yDtjsKINz}VTkxt)bB^-y+Z9*t$Vuyae$ z&ea2#ggOe5cgUyOKtQQ66yyEFK)VD&pA6dLEiqeYrJEcFZps1L!6=V`@m$b;3mu*e zstj%C=Coa!XbZU^{*yr;*CX?wlS`eHp}pLt06p>vy1Epv4ejl225jX&h3i37=rcig zV`z`&hW2r(hcdL}7(4)h|Jb0$)w84hn+#Qx`2$=tPWDb_0Nt!dOT+sC9_ap4u^FL< zS+#0;ml~2y2G=vIK@!xtdZKKoVWaoo3Wm9QYB;QC@`syTyp`C6qUW~b@N%^4DIOzT zDygUL>s{uf7W9z%aJ35=4Z%7GWaMY!d2SryVpVXItDh8)Zt}0v`52?~SdT)2BVDTM zt9Maj-A5H~Gaw)5ZiF+P;yK#7@djO>HJ)HJUTo!egv@Dh_t;>vkvSzP(<<cY1*?L& zu1BqciS7jup(=5<+Z|aR3kK6%>a8nrjynplmCvINcml$jV1}v0nawM4u1mqH5<daJ z6A~JNV@)M4Y+i|rl9lKyCoFENxG_B;eo3<8{O@^tUusZ<>tHawEQ67>!qnr+=JmL$ zc|9JNtVchqB{&^#{1Eul+X7wfa?GbT8p>Y>&Yz~Unon>&iU`)aG(%VOdYAc4)qLV_ zs`*G{<hMlVzo4A6(YXASq|3F1o0_VbLV}ZBTB57@RQGzt+f?)B=GA;!Q#C7JoNmy? zdYHGxrDUhL(J$=@wp(4E)aDegLin-8XD9>n`yugD6%gBvc4sBEvkF%sb4_r*>rwyU zOm_p6Q2}wTTZb%<1%sV#5nvq<=ed&s<NXJ&Jo;jN@Fx=xyP5~Y`7Q;kfVdw3kH)AC zF2qh&t-<l4WI$N?Lm{|3?h#vp%UnJBzC5X(pLINFuQ0VZ*1xY4&?^l(#c$sW=w5@) z@wefbB)H0;i~W2C&(#K9<uC6G^csb7R?9!U3iv)1TRH@0L62<ZrW=eNHzxJ43VDjh zmf#N8qo%=iE-g}SI^ZrsmdAp@%`R=xZo1t)1+bORBR_Z&$hP1X<EC4iyXg*>f|Z*- z0Kk(%YJ+=>o9_M9O}o+EZ3LrJg9nXz4<*&}&uazg4=3A+e^q~=2MyZbuaZC?G3XTk zU>~558gzy~fPQ(*pmY3Z!$2Q5=mP)2a-dHbbg{p?0O*s(r;YvvEr9;n)h`y-_~*xg zKBb7co0pY;J9O@cdLJ7+Yjpl=QfI4>2cVo9yybedDtN}F^~$AhxDO!9W5M8gmv(EH zzUjUU*vjV&GY?VO61-|$`dV|BzU5M|a%peS@{pC<;E<~aX7By#(wmUE6Pb&H4~=>s zCDrp^$cFTfjp1YcA~o4$&<6kd0_1*bx`8QvSqGq>8FYr<fxXM;2A$(y%fZIq4Y~l+ zVG#Qn6dW6TZN&Z~Db_0F@h^*mpIwiN1z)-}KzZdy_a%TH3kKi1R9$=JC--x}RzA;) zxdmQn4E|}n@_lo!{OnS&^2#F6@`#!GzzON$ga|d8jdDZHW}`x(<ZM*_`6zljerp0R zq-QtMLdjv2dcco_^qd8TQ3pVmr&P)?w6P?uRmk&f)	W9=QcFM4gnOe&~LH9t#Fp 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 E3lf{3xc~qF literal 0 HcmV?d00001 diff --git a/TBBT/trace_play/sfs_2_ops.c b/TBBT/trace_play/sfs_2_ops.c new file mode 100644 index 0000000..f50f353 --- /dev/null +++ b/TBBT/trace_play/sfs_2_ops.c @@ -0,0 +1,2131 @@ +#ifndef lint +static char sfs_c_opsSid[] = "@(#)sfs_2_ops.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ---------------------- sfs_c_ops.c --------------------- + * + * RPC routines to implement the NFS protocol. + * + *.Local Routines + * int op_null(void) + * int op_getattr(void) + * int op_setattr(int) + * int op_nosys(void) + * int op_lookup(void) + * int op_readlink(void) + * int op_read(int) + * int op_write(int, int, int) + * int op_create(void) + * int op_remove(void) + * int op_rename(void) + * int op_link(void) + * int op_symlink(void) + * int op_mkdir(void) + * int op_rmdir(void) + * int op_readdir(void) + * int op_fsstat(void) + * + *.Revision_History + * 20-Apr-92 Wittle Fix i/o offsets randomization. + * 05-Jan-92 Pawlowski Added hooks in for raw data dump. + * 04-Dec-91 Keith Define string.h for SYSV/SVR4. + * 28-Nov-91 Teelucksingh ANSI C + * 01-Aug-91 Santa Wiryaman fix declaration of sfs_srandom() + * and sfs_random() + * 25-Jun-91 Santa Wiryaman op_rmdir bug fix: when reply==NFS_OK + * Cur_file_ptr->state = Nonexistent + * 17-May-90 Richard Bean Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" + +/* + * -------------------- Local NFS ops function -------------------- + */ +static int op_null(void); +static int op_getattr(void); +static int op_setattr(int); +static int op_lookup(void); +static int op_readlink(void); +static int op_read(int); +static int op_write(int, int, int); +static int op_create(void); +static int op_remove(void); +static int op_rename(void); +static int op_link(void); +static int op_symlink(void); +static int op_mkdir(void); +static int op_rmdir(void); +static int op_readdir(void); +static int op_fsstat(void); +static int op_nosys(void); +static char *nfs2_strerror(int); + +/* + * -------------------- NFS ops vector -------------------- + */ +/* + * per operation information + */ +static sfs_op_type nfsv2_Ops[] = { + +/* name mix function op call no req req req results */ +/* pcnt class targ call pcnt cnt targ */ + + { "null", 0, op_null, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "getattr", 26, op_getattr, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "setattr", 1, op_setattr, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "root", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "lookup", 36, op_lookup, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readlink", 7, op_readlink, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "read", 14, op_read, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "wrcache", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "write", 7, op_write, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "create", 1, op_create, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "remove", 1, op_remove, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rename", 0, op_rename, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "link", 0, op_link, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "symlink", 0, op_symlink, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "mkdir", 0, op_mkdir, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rmdir", 0, op_rmdir, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdir", 6, op_readdir, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsstat", 1, op_fsstat, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "access", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "commit", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsinfo", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "mknod", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "pathconf", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdirplus", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "TOTAL", 100, 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }} +}; + +sfs_op_type *Ops; + +/* + * -------------------- RPC routines for NFS protocol -------------------- + */ + +void +init_ops(void) +{ + Ops = nfsv2_Ops; + nfs_version = NFS_VERSION; +} + +/* + * The routines below attempt to do over-the-wire operations. + * Each op tries to cause one or more of a particular + * NFS operation to go over the wire. OPs return the success + * of their NFS call(s). Each OP records how many calls it + * actually made in global data. + * + * An array of file information is kept for files existing in + * the test directory. File handles, attributes, names, etc + * are stored in this array. + * + */ + +static int +op_nosys(void) +{ + /* + * This is a generic catcher for operations that either don't + * exist or were never implemented. We will be + * kind and simply mark it as a bad call. + */ + Ops[TOTAL].results.bad_calls++; + return(0); + +} /* op_nosys */ + + +static int +op_null(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[NULLCALL]; + ret = 0; + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_NULL, + xdr_void, (char *)0, + xdr_void, (char *)0, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: null_op call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_null */ + + +static int +op_getattr(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + fhandle_t fh; /* fh to do op on */ + attrstat reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[GETATTR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &fh, (char *) &Cur_file_ptr->fh2, + NFS_FHSIZE); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_GETATTR, + xdr_getattr, (char *) &fh, + xdr_getattr, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS_OK) { + Cur_file_ptr->attributes2 = reply.attrstat_u.attributes; + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: getattr call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: getattr call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_getattr */ + + +/* + * perform an RPC setattr operation. If 'truncate_size' is non-negative, + * truncate the file to that size. + */ +static int +op_setattr( + int truncate_size) +{ + sfs_op_type *op_ptr; /* per operation info */ + sattrargs args; + attrstat reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[SETATTR]; + ret = 0; + + /* set up the arguments */ + args.attributes.mode = 0666; + args.attributes.uid = (unsigned int) -1; + args.attributes.gid = (unsigned int) -1; + args.attributes.size = (unsigned int) -1; + args.attributes.atime.seconds = (unsigned int) Cur_time.esec; + args.attributes.atime.useconds = (unsigned int) Cur_time.usec; + args.attributes.mtime.seconds = (unsigned int) Cur_time.esec; + args.attributes.mtime.useconds = (unsigned int) Cur_time.usec; + (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh2, + NFS_FHSIZE); + + /* handle file truncations */ + if (truncate_size >= 0) { + if (truncate_size > Cur_file_ptr->attributes2.size) + args.attributes.size = (unsigned int) 0; + else + args.attributes.size = (unsigned int) Cur_file_ptr->attributes2.size + - truncate_size; + } + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_SETATTR, + xdr_setattr, (char *) &args, + xdr_setattr, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS_OK) { + Cur_file_ptr->attributes2 = reply.attrstat_u.attributes; + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: setattr call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: setattr call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_setattr */ + + +static int +op_lookup(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + diropargs args; + diropres reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[LOOKUP]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh2, + NFS_FHSIZE); + args.name = Cur_filename; + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_LOOKUP, + xdr_lookup, (char *) &args, + xdr_lookup, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh2, + (char *) &reply.diropres_u.diropres.file, + NFS_FHSIZE); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + Cur_file_ptr->attributes2 = reply.diropres_u.diropres.attributes; + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + /* We do lookup Nonexistent and this is not an error */ + if (reply.status != NFSERR_NOENT || + Cur_file_ptr->state != Nonexistent) { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: lookup call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: lookup call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_lookup */ + + +static int +op_readlink(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + fhandle_t fh; /* fh to do op on */ + readlinkres reply; /* the reply */ + char sym_data[NFS_MAXPATHLEN]; + int len; /* length of symlink data */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[READLINK]; + ret = 0; + + /* set up the arguments */ + /* + * Note: this fh may be bogus because SYMLINK does + * not return a fh ... only a status. So unless we have + * done a LOOKUP on this guy, the fh will probably be bad. + * If it is bad it shows up as a symlink error in the results. + */ + (void) memmove((char *) &fh, (char *) &Cur_file_ptr->fh2, + NFS_FHSIZE); + + /* Have lower layers fill in the data directly. */ + reply.readlinkres_u.data = sym_data; + len = 0; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_READLINK, + xdr_readlink, (char *) &fh, + xdr_readlink, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS_OK) { + if (DEBUG_CHILD_RPC) { + len = reply.readlinkres_u.len; + sym_data[len] = '\0'; + (void) fprintf(stderr, "%s: READLINK on %s returned %s\n", + sfs_Myname, Cur_filename, sym_data); + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readlink call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readlink call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_readlink */ + + +/* + * perform an RPC read operation of length 'xfer_size' + */ +static int +op_read( + int xfer_size) +{ + sfs_op_type *op_ptr; /* per operation info */ + int cur_cnt; + int max_cnt; /* packet ctrs */ + char buf[DEFAULT_MAX_BUFSIZE];/* data buffer */ + readargs args; + readres reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int size; + int j; + int ret; /* ret val == call success */ + + op_ptr = &Ops[READ]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh2, + NFS_FHSIZE); + + /* + * Don't allow a read of less than one block size + */ + if (xfer_size < Bytes_per_block) + xfer_size = Bytes_per_block; + + /* + * randomly choose an offset that is a multiple of the block size + * and constrained by making the transfer fit within the file + */ + if (Cur_file_ptr->attributes2.size > xfer_size) { + args.offset = Bytes_per_block * (sfs_random() % + (((Cur_file_ptr->attributes2.size - xfer_size) + / Bytes_per_block) + 1)); + } else + args.offset = 0; + + /* first read the whole buffers, then the fragment */ + for (j = 0; j < 2; j++) { + + if (j == 0) { + size = Bytes_per_block; + max_cnt = xfer_size / Bytes_per_block; + } else { + /* 1KB - (Kb_per_block -1) KB fragment */ + size = xfer_size % Bytes_per_block; + max_cnt = 1; + } + if (size == 0) + continue; + + /* check our stats to see if this would overflow */ + if (!Timed_run) { + if (op_ptr->target_calls > 0) { + if ((op_ptr->results.good_calls + max_cnt) + > op_ptr->target_calls) { + max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; + } + } + } + + args.count = size; + args.totalcount = size; /* unused */ + + /* Have lower layers fill in the data directly. */ + reply.readres_u.reply.data.data_val = buf; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "read: %d buffers\n", max_cnt); + (void) fflush(stderr); + } + + /* make the call(s) now */ + for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) { + + /* capture length for possible dump */ + Dump_length = fh_size(Cur_file_ptr); + + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_READ, + xdr_read, (char *) &args, + xdr_read, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + /* capture count and offset for possible dump */ + Dump_count = (rpc_stat == RPC_SUCCESS && reply.status == NFS_OK) + ? reply.readres_u.reply.data.data_len : 0; + Dump_offset = args.offset; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS_OK) { + Cur_file_ptr->state = Exists; + Cur_file_ptr->attributes2 = + reply.readres_u.reply.attributes; + Cur_file_ptr->size = fh_size(Cur_file_ptr); + size = reply.readres_u.reply.data.data_len; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: READ %s %d bytes\n", + sfs_Myname, Cur_filename, size); + (void) fflush(stderr); + } + args.offset += size; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: read call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: read call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + } /* for reading max_cnt packets */ + } /* for buffers and fragments */ + return(ret); + +} /* op_read */ + + +char * +init_write_buffer( + void) +{ + uint32_t *bp; + static uint32_t write_buf[DEFAULT_MAX_BUFSIZE / sizeof(uint32_t)]; + uint32_t *be = write_buf + (sizeof(write_buf) / + sizeof(uint32_t)); + + if (write_buf[0] != (uint32_t)0xdeadbeef) { + for (bp = write_buf; bp < be; bp++) + *bp = (uint32_t)0xdeadbeef; + } + return (char *)write_buf; +} + +/* + * Perform and RPC write operation of length 'xfer_size'. If 'append_flag' + * is true, then write the data to the end of the file. + */ +/* ARGSUSED2 */ +static int +op_write( + int xfer_size, + int append_flag, + int stable) +{ + sfs_op_type *op_ptr; /* per operation info */ + static char *buf = NULL; /* the data buffer */ + int size; /* size of data write */ + int cur_cnt; /* controls # NFS calls */ + int max_cnt; + writeargs args; + attrstat reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int j; + int ret; /* ret val == call success */ + + /* + * Initialize write buffer to known value + */ + if (buf == NULL) { + buf = init_write_buffer(); + } + op_ptr = &Ops[WRITE]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh2, + NFS_FHSIZE); + args.beginoffset = 0; /* unused */ + + if (append_flag == 1) { + args.offset = Cur_file_ptr->attributes2.size; + } else { + /* + * randomly choose an offset that is a multiple of the block size + * and constrained by making the transfer fit within the file + */ + if (Cur_file_ptr->attributes2.size > xfer_size) { + args.offset = Bytes_per_block * (sfs_random() % + (((Cur_file_ptr->attributes2.size - xfer_size) + / Bytes_per_block) + 1)); + } else + args.offset = 0; + } + + /* first write the whole buffers, then the fragment */ + for (j = 0; j < 2; j++) { + + if (j == 0) { + size = Bytes_per_block; + max_cnt = xfer_size / Bytes_per_block; + } else { + /* 1KB - (Kb_per_block - 1) KB fragment */ + size = xfer_size % Bytes_per_block; + max_cnt = 1; + } + if (size == 0) + continue; + + args.totalcount = size; /* unused */ + args.data.data_len = size; + args.data.data_val = buf; + + /* check our stats to see if this would overflow */ + if (!Timed_run) { + if (op_ptr->target_calls > 0) { + if ((op_ptr->results.good_calls + max_cnt) + > op_ptr->target_calls) { + max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; + } + } + } + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "write: %d buffers\n", max_cnt); + (void) fflush(stderr); + } + + /* make the call(s) now */ + for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) { + + /* capture length for possible dump */ + Dump_length = fh_size(Cur_file_ptr); + + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_WRITE, + xdr_write, (char *) &args, + xdr_write, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + /* capture count and offset for possible dump */ + Dump_count = args.data.data_len; + Dump_offset = args.offset; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS_OK) { + Cur_file_ptr->state = Exists; + Cur_file_ptr->attributes2 = reply.attrstat_u.attributes; + Cur_file_ptr->size = fh_size(Cur_file_ptr); + args.offset += size; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: WRITE %s %d bytes\n", + sfs_Myname, Cur_filename, size); + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: write call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: write call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + } /* for writing max_cnt packets */ + } /* for buffers and fragments */ + return(ret); + +} /* op_write */ + + +static int +op_create(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + createargs args; + diropres reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[CREATE]; + ret = 0; + + /* set up the arguments */ + args.attributes.mode = (0100000 | 0666); /* 666 NFREG file */ + args.attributes.uid = Cur_uid; + args.attributes.gid = Cur_gid; + args.attributes.atime.seconds = (unsigned int) Cur_time.esec; + args.attributes.atime.useconds = (unsigned int) Cur_time.usec; + args.attributes.mtime.seconds = (unsigned int) Cur_time.esec; + args.attributes.mtime.useconds = (unsigned int) Cur_time.usec; + args.attributes.size = 0; + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh2, + NFS_FHSIZE); + args.where.name = Cur_filename; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_CREATE, + xdr_create, (char *) &args, + xdr_create, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh2, + (char *) &reply.diropres_u.diropres.file, + NFS_FHSIZE); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + Cur_file_ptr->attributes2 = reply.diropres_u.diropres.attributes; + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: create call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: create call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_create */ + + +static int +op_remove(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + diropargs args; + nfsstat reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[REMOVE]; + ret = 0; + + /* set up the arguments */ + args.name = Cur_filename; + (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh2, + NFS_FHSIZE); + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_REMOVE, + xdr_remove, (char *) &args, + xdr_remove, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply == NFS_OK) { + Cur_file_ptr->state = Nonexistent; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: remove call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: remove call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_remove */ + + +static int +op_rename(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + sfs_fh_type *target_fileinfo_ptr; /* target name */ + renameargs args; + nfsstat reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[RENAME]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.from.dir, (char *) &Cur_file_ptr->dir->fh2, + NFS_FHSIZE); + (void) memmove((char *) &args.to.dir, (char *) &Cur_file_ptr->dir->fh2, + NFS_FHSIZE); + + target_fileinfo_ptr = randfh(RENAME, 0, 0, Nonexistent, + Sfs_non_io_file); + + args.from.name = Cur_file_ptr->file_name; + (void) sprintf(target_fileinfo_ptr->file_name, Filespec, + target_fileinfo_ptr->unique_num); + args.to.name = target_fileinfo_ptr->file_name; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_RENAME, + xdr_rename, (char *) &args, + xdr_rename, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply == NFS_OK) { + target_fileinfo_ptr->state = Exists; + (void) memmove((char *) &target_fileinfo_ptr->fh2, + (char *) &Cur_file_ptr->fh2, + NFS_FHSIZE); + target_fileinfo_ptr->attributes2 = Cur_file_ptr->attributes2; + target_fileinfo_ptr->size = Cur_file_ptr->size; + Cur_file_ptr->state = Nonexistent; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: rename call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: rename call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_rename */ + + +static int +op_link(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + sfs_fh_type *target_fileinfo_ptr; /* target */ + linkargs args; + nfsstat reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[LINK]; + ret = 0; + + /* set up the arguments */ + target_fileinfo_ptr = randfh(LINK, 0, 0, Exists, Sfs_non_io_file); + (void) memmove((char *) &args.from, (char *) &target_fileinfo_ptr->fh2, + NFS_FHSIZE); + (void) memmove((char *) &args.to.dir, (char *) &Cur_file_ptr->dir->fh2, + NFS_FHSIZE); + args.to.name = Cur_filename; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_LINK, + xdr_link, (char *) &args, + xdr_link, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply == NFS_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh2, + (char *) &target_fileinfo_ptr->fh2, NFS_FHSIZE); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + target_fileinfo_ptr->attributes2.nlink++; + Cur_file_ptr->attributes2 = target_fileinfo_ptr->attributes2; + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: link call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: link call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_link */ + + +static int +op_symlink(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + sfs_fh_type *target_fileinfo_ptr; /* target file */ + symlinkargs args; + nfsstat reply; /* the reply */ + char sym_data[NFS_MAXPATHLEN]; /* symlink data */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[SYMLINK]; + ret = 0; + + /* set up the arguments */ + target_fileinfo_ptr = randfh(SYMLINK, 0, 0, Exists, Sfs_non_io_file); + (void) memmove((char *) &args.from.dir, (char *) &Cur_file_ptr->dir->fh2, + NFS_FHSIZE); + args.from.name = Cur_filename; + + (void) strcpy(sym_data, "./"); + (void) strcat(sym_data, target_fileinfo_ptr->file_name); + args.attributes.size = strlen(sym_data); + args.to = sym_data; + + args.attributes.mode = (0120000 | 0777); + args.attributes.uid = Cur_uid; + args.attributes.gid = Cur_gid; + args.attributes.atime.seconds = (unsigned int) Cur_time.esec; + args.attributes.atime.useconds = (unsigned int) Cur_time.usec; + args.attributes.mtime.seconds = (unsigned int) Cur_time.esec; + args.attributes.mtime.useconds = (unsigned int) Cur_time.usec; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_SYMLINK, + xdr_symlink, (char *) &args, + xdr_symlink, (char *) &reply, + ((int)Current_test_phase < (int)Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply == NFS_OK) { + /* + * SYMLINK doesn't return a fh. If we try to + * access this symlink (eg, remove(), readlink()) + * before we do a lookup, we won't have a fh to use. + * So, we do a lookup call here. + * If it fails, we fill in what we can. + */ + Cur_file_ptr->state = Exists; + if (op_lookup() == 0) { + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + Cur_file_ptr->attributes2.type = NFLNK; + Cur_file_ptr->attributes2.mode = (0120000|0777); + Cur_file_ptr->attributes2.uid = Cur_uid; + Cur_file_ptr->attributes2.gid = Cur_gid; + Cur_file_ptr->attributes2.atime.seconds =(unsigned int)Cur_time.esec; + Cur_file_ptr->attributes2.atime.useconds=(unsigned int)Cur_time.usec; + Cur_file_ptr->attributes2.mtime = Cur_file_ptr->attributes2.atime; + } else + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: symlink call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: symlink call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_symlink */ + + +static int +op_mkdir(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + mkdirargs args; + diropres reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[MKDIR]; + ret = 0; + + /* set up the arguments */ + args.attributes.mode = (NFSMODE_DIR | 0777); + args.attributes.uid = Cur_uid; + args.attributes.gid = Cur_gid; + args.attributes.size = (unsigned int) 512; + args.attributes.atime.seconds = (unsigned int) Cur_time.esec; + args.attributes.atime.useconds = (unsigned int) Cur_time.usec; + args.attributes.mtime.seconds = (unsigned int) Cur_time.esec; + args.attributes.mtime.useconds = (unsigned int) Cur_time.usec; + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh2, + NFS_FHSIZE); + args.where.name = Cur_filename; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_MKDIR, + xdr_mkdir, (char *) &args, + xdr_mkdir, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS_OK) { + Cur_file_ptr->state = Empty_dir; + (void) memmove((char *) &Cur_file_ptr->fh2, + (char *) &reply.diropres_u.diropres.file, + NFS_FHSIZE); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + Cur_file_ptr->attributes2 = reply.diropres_u.diropres.attributes; + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: mkdir call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: mkdir call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_mkdir */ + + +static int +op_rmdir(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + diropargs args; + nfsstat reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[RMDIR]; + ret = 0; + + if (Cur_file_ptr->state != Empty_dir) { + (void) fprintf(stderr, "%s: Attempting to remove non-Empty_dir %d\n", + sfs_Myname, Cur_file_ptr->unique_num); + } + + /* set up the arguments */ + (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh2, + NFS_FHSIZE); + args.name = Cur_file_ptr->file_name; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_RMDIR, + xdr_rmdir, (char *) &args, + xdr_rmdir, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply == NFS_OK) { + Cur_file_ptr->state = Nonexistent; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: rmdir call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: rmdir call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_rmdir */ + + +static int +op_readdir(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + readdirargs args; + readdirres reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + uint_t i; + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + nfscookie cookie; + bool_t hit_eof; + /* arbitrary fixed ceiling */ + int entry_cnt = SFS_MAXDIRENTS; + /* array of entries */ + entry entry_stream[SFS_MAXDIRENTS]; + entry *entry_ptr; /* ptr to the dir entry */ + + char name[SFS_MAXNAMLEN]; + /* array of dir names */ + char name_stream[SFS_MAXDIRENTS * SFS_MAXNAMLEN]; + + + /* + * 1) need some measure of how many entries are in a directory + * currently, we assume SFS_MAXDIRENTS - it should be random + * from 0 to MAX for a large MAX we should pre-allocate a buffer for the + * returned directory names. + * 2) need some measure of how many directory entries to read + * during each readdir() call. Again, we assume SFS_MAXDIRENTS. + */ + + op_ptr = &Ops[READDIR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh2, + NFS_FHSIZE); + (void) memset((char *) args.cookie, '\0', NFS_COOKIESIZE); + args.count = DEFAULT_MAX_BUFSIZE; + + /* Have lower layers fill in the data directly. */ + reply.readdirres_u.reply.max_entries = entry_cnt; + reply.readdirres_u.reply.entries = entry_stream; + for (i = 0; i < entry_cnt; i++) { + entry_stream[i].name = &name_stream[i * SFS_MAXNAMLEN]; + } + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_READDIR, + xdr_readdir, (char *) &args, + xdr_readdir, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS_OK) { + + if (DEBUG_CHILD_RPC) { + hit_eof = reply.readdirres_u.reply.eof; + entry_cnt = reply.readdirres_u.reply.max_entries; + entry_ptr = reply.readdirres_u.reply.entries; + for (i = 0; i < entry_cnt; i++) { + entry_ptr->name[entry_ptr->name_len] ='\0'; + (void) strcpy(name, entry_ptr->name); + (void) memmove((char *) cookie, + (char *) entry_ptr->cookie, + NFS_COOKIESIZE); + (void) fprintf(stderr, "%s:READDIR (eof=%d) entry %s\n", + sfs_Myname, hit_eof, name); + entry_ptr++; + } + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readdir call NFS error %s on file %d\n", + sfs_Myname, nfs2_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: readdir call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_readdir */ + + +/* Beware - op_statfs() collides w/ some other name, use op_fsstat() */ +static int +op_fsstat(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + fhandle_t fh; + statfsres reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[FSSTAT]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &fh, (char *) &Cur_file_ptr->fh2, + NFS_FHSIZE); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC_STATFS, + xdr_statfs, (char *) &fh, + xdr_statfs, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: fsstat call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_fsstat */ + + +/* + * These are a set of reliable functions used by the initialization code. + */ + +#define LAD_RETRIABLE(stat) (((stat) == RPC_TIMEDOUT) || ((stat) == RPC_CANTDECODERES)) + +/* + * Reliably lookup a file in the current directory + * Return: + * -1 RPC error + * 1 File doesn't exist + * 0 File exists + */ +int +lad_lookup(sfs_fh_type *file_ptr, char *name) +{ + diropargs args; + diropres reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_lookup: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + (void) memmove((char *) &args.dir, (char *) &file_ptr->dir->fh2, + NFS_FHSIZE); + args.name = name; + + /* make the call */ + rpc_stat = clnt_call(NFS_client, NFSPROC_LOOKUP, + xdr_lookup, (char *) &args, + xdr_lookup, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_lookup(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + } + + if (reply.status == NFSERR_NOENT) { + return(1); + } + + if (reply.status != NFS_OK) { + (void) fprintf(stderr, "lad_lookup(%s) NFS call failed : %s\n", + name, nfs2_strerror(reply.status)); + return(-1); + } + + file_ptr->state = Exists; + (void) memmove((char *) &file_ptr->fh2, + (char *) &reply.diropres_u.diropres.file, NFS_FHSIZE); + (void) strcpy(file_ptr->file_name, name); + file_ptr->attributes2 = reply.diropres_u.diropres.attributes; + file_ptr->size = fh_size(file_ptr); + + return(0); +} + +/* + * Reliably remove a file in the current directory + */ +int +lad_remove(sfs_fh_type *file_ptr, char *name) +{ + diropargs args; + nfsstat reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_remove: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* + * This function presumes that the file name does exist + */ + if (file_ptr->attributes2.type == NFDIR) + return (lad_rmdir(file_ptr, name)); + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + args.name = name; + (void) memmove((char *) &args.dir, (char *) &file_ptr->dir->fh2, + NFS_FHSIZE); + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC_REMOVE, + xdr_remove, (char *) &args, + xdr_remove, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_remove(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (reply != NFS_OK) { + if (reply != NFSERR_NOENT || !retried) { + (void) fprintf(stderr, "lad_remove(%s) NFS call failed : %s\n", + name, nfs2_strerror(reply)); + return(-1); + } + } + + file_ptr->state = Nonexistent; + + return(0); +} + +/* + * Reliably remove a directory in the current directory + */ +int +lad_rmdir(sfs_fh_type *file_ptr, char *name) +{ + diropargs args; + nfsstat reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: lad_rmdir: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* + * This function presumes that the file name does exist and the directory + * is empty. + */ + if (file_ptr->attributes2.type != NFDIR) + return (lad_remove(file_ptr, name)); + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + args.name = name; + (void) memmove((char *) &args.dir, (char *) &file_ptr->dir->fh2, + NFS_FHSIZE); + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC_RMDIR, + xdr_remove, (char *) &args, + xdr_remove, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_rmdir(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (reply != NFS_OK) { + if (reply != NFSERR_NOENT || !retried) { + (void) fprintf(stderr, "lad_rmdir(%s) NFS call failed : %s\n", + name, nfs2_strerror(reply)); + return(-1); + } + } + + file_ptr->state = Nonexistent; + + return(0); +} + +/* + * Reliably create a symlink in the current directory + */ +int +lad_symlink(sfs_fh_type *file_ptr, char *target, char *name) +{ + symlinkargs args; + nfsstat reply; /* the reply */ + char sym_data[NFS_MAXPATHLEN]; /* symlink data */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_symlink: %lx %s -> %s\n", sfs_Myname, + (int32_t) file_ptr, name, target); + (void) fflush(stderr); + } + + /* + * This function presumes that the file name does not already exist + */ + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + (void) memmove((char *) &args.from.dir, (char *) &file_ptr->dir->fh2, + NFS_FHSIZE); + args.from.name = name; + + (void) strcpy(sym_data, "./"); + (void) strcat(sym_data, target); + args.attributes.size = strlen(sym_data); + args.to = sym_data; + + args.attributes.mode = (0120000 | 0777); + args.attributes.uid = Cur_uid; + args.attributes.gid = Cur_gid; + args.attributes.atime.seconds = (unsigned int) Cur_time.esec; + args.attributes.atime.useconds = (unsigned int) Cur_time.usec; + args.attributes.mtime.seconds = (unsigned int) Cur_time.esec; + args.attributes.mtime.useconds = (unsigned int) Cur_time.usec; + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC_SYMLINK, + xdr_symlink, (char *) &args, + xdr_symlink, (char *) &reply, + Nfs_timers[Init]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_symlink(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (reply != NFS_OK) { + if (reply != NFSERR_EXIST || !retried) { + (void) fprintf(stderr, "lad_symlink(%s, %s) NFS call failed : %s\n", + target, name, nfs2_strerror(reply)); + return(-1); + } + } + + /* + * SYMLINK doesn't return a fh. If we try to + * access this symlink (eg, remove(), readlink()) + * before we do a lookup, we won't have a fh to use. + * So, we do a lookup call here. + * If it fails, we fill in what we can. + */ + return (lad_lookup(file_ptr, name)); +} + +/* + * Reliably create a directory in the current directory + */ +int +lad_mkdir(sfs_fh_type *file_ptr, char *name) +{ + mkdirargs args; + diropres reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_mkdir: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* + * This function presumes that the file name does not already exist + */ + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + args.attributes.mode = (NFSMODE_DIR | 0777); + args.attributes.uid = Cur_uid; + args.attributes.gid = Cur_gid; + args.attributes.size = (unsigned int) 512; + args.attributes.atime.seconds = (unsigned int) Cur_time.esec; + args.attributes.atime.useconds = (unsigned int) Cur_time.usec; + args.attributes.mtime.seconds = (unsigned int) Cur_time.esec; + args.attributes.mtime.useconds = (unsigned int) Cur_time.usec; + (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh2, + NFS_FHSIZE); + args.where.name = name; + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC_MKDIR, + xdr_mkdir, (char *) &args, + xdr_mkdir, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_mkdir(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (!retried && reply.status == NFSERR_EXIST) + return(1); + + if (reply.status != NFS_OK) { + if (reply.status != NFSERR_EXIST || !retried) { + (void) fprintf(stderr, "lad_mkdir(%s) NFS call failed : %s\n", + name, nfs2_strerror(reply.status)); + return(-1); + } + /* + * If the first mkdir suceeded but the reply as dropped and + * was retransmitted, we still need to lookup the attributes + */ + if (lad_lookup(file_ptr, name)) + return (-1); + } else { + (void) memmove((char *) &file_ptr->fh2, + (char *) &reply.diropres_u.diropres.file, + NFS_FHSIZE); + (void) strcpy(file_ptr->file_name, name); + file_ptr->attributes2 = reply.diropres_u.diropres.attributes; + file_ptr->size = fh_size(file_ptr); + } + file_ptr->state = Empty_dir; + + return(0); +} + +/* + * Reliably write a file in the current directory + */ +int +lad_write(sfs_fh_type *file_ptr, int32_t offset, int32_t length) +{ + static char *buf = NULL; /* the data buffer */ + int32_t size; /* size of data write */ + int32_t cur_cnt; + writeargs args; + attrstat reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_write: %lx[%lx] %ld %ld\n", + sfs_Myname, (int32_t) file_ptr, (int32_t) file_ptr->dir, + offset, length); + (void) fflush(stderr); + } + + /* + * This function presumes that the file name does exist + * Initialize write buffer to known value + */ + if (buf == NULL) { + buf = init_write_buffer(); + } + + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &file_ptr->fh2, + NFS_FHSIZE); + args.beginoffset = 0; /* unused */ + args.offset = offset; + + size = Bytes_per_block; + for (cur_cnt = 0; cur_cnt < length; cur_cnt += size) { + if ((cur_cnt + size) > length) + size = length - cur_cnt; + + if (size == 0) + break; + + args.totalcount = size; /* unused */ + args.data.data_len = size; + args.data.data_val = buf; + + /* make the call now */ + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_WRITE, + xdr_write, (char *) &args, + xdr_write, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_write() RPC call failed : %s\n", + clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + } + if (reply.status != NFS_OK) { + (void) fprintf(stderr, "lad_write() NFS call failed : %s\n", + nfs2_strerror(reply.status)); + return(-1); + } + file_ptr->state = Exists; + file_ptr->attributes2 = reply.attrstat_u.attributes; + file_ptr->size = fh_size(file_ptr); + args.offset += size; + } + return(0); +} + +/* + * Reliably create a file in the current directory + */ +int +lad_create(sfs_fh_type *file_ptr, char *name) +{ + createargs args; + diropres reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_create: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* + * This function presumes that the file name does not already exist + */ + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + args.attributes.mode = (0100000 | 0666); /* 666 NFREG file */ + args.attributes.uid = Cur_uid; + args.attributes.gid = Cur_gid; + args.attributes.atime.seconds = (unsigned int) Cur_time.esec; + args.attributes.atime.useconds = (unsigned int) Cur_time.usec; + args.attributes.mtime.seconds = (unsigned int) Cur_time.esec; + args.attributes.mtime.useconds = (unsigned int) Cur_time.usec; + args.attributes.size = 0; + (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh2, + NFS_FHSIZE); + args.where.name = name; + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC_CREATE, + xdr_create, (char *) &args, + xdr_create, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_create(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (!retried && reply.status == NFSERR_EXIST) { + return(1); + } + + if (reply.status != NFS_OK) { + if (reply.status != NFSERR_EXIST || !retried) { + (void) fprintf(stderr, "lad_create(%s) NFS call failed : %s\n", + name, nfs2_strerror(reply.status)); + return(-1); + } + /* + * If the first create suceeded but the reply as dropped and + * was retransmitted, we still need to lookup the attributes + */ + if (lad_lookup(file_ptr, name)) + return (-1); + } else { + (void) memmove((char *) &file_ptr->fh2, + (char *) &reply.diropres_u.diropres.file, NFS_FHSIZE); + (void) strcpy(file_ptr->file_name, name); + file_ptr->attributes2 = reply.diropres_u.diropres.attributes; + file_ptr->size = fh_size(file_ptr); + } + + file_ptr->state = Exists; + /* + * Directories are created as Empty_dir, when a file is created it + * becomes an Exists. + */ + file_ptr->dir->state = Exists; + + return(0); +} + +/* + * Reliably set the size of a file in the current directory + */ +int +lad_truncate(sfs_fh_type *file_ptr, int32_t size) +{ + sattrargs args; + attrstat reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_truncate: %lx[%lx] %ld\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, size); + (void) fflush(stderr); + } + + /* + * This function presumes that the file name already exists + */ + /* CONSTCOND */ + while (1) { + /* + * set up the arguments + * Set the mode and times as well + */ + args.attributes.mode = 0666; + args.attributes.uid = (unsigned int) -1; + args.attributes.gid = (unsigned int) -1; + args.attributes.size = (unsigned int) -1; + args.attributes.atime.seconds = (unsigned int) Cur_time.esec; + args.attributes.atime.useconds = (unsigned int) Cur_time.usec; + args.attributes.mtime.seconds = (unsigned int) Cur_time.esec; + args.attributes.mtime.useconds = (unsigned int) Cur_time.usec; + (void) memmove((char *) &args.file, (char *) &file_ptr->fh2, + NFS_FHSIZE); + args.attributes.size = (unsigned int) size; + + /* make the call */ + rpc_stat = clnt_call(NFS_client, NFSPROC_SETATTR, + xdr_setattr, (char *) &args, + xdr_setattr, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, + "lad_truncate(%ld) RPC call failed : %s\n", + size, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + } + + if (reply.status != NFS_OK) { + (void) fprintf(stderr, "lad_truncate(%ld) NFS call failed : %s\n", + size, nfs2_strerror(reply.status)); + return(-1); + } + file_ptr->attributes2 = reply.attrstat_u.attributes; + file_ptr->size = fh_size(file_ptr); + + return(0); +} + +static char * +nfs2_strerror(int status) +{ + static char str[40]; + switch (status) { + case NFS_OK: + (void) strcpy(str, "no error"); + break; + case NFSERR_PERM: + (void) strcpy(str, "Not owner"); + break; + case NFSERR_NOENT: + (void) strcpy(str, "No such file or directory"); + break; + case NFSERR_IO: + (void) strcpy(str, "I/O error"); + break; + case NFSERR_NXIO: + (void) strcpy(str, "No such device or address"); + break; + case NFSERR_ACCES: + (void) strcpy(str, "Permission denied"); + break; + case NFSERR_EXIST: + (void) strcpy(str, "File exists"); + break; + case NFSERR_XDEV: + (void) strcpy(str, "Cross-device link"); + break; + case NFSERR_NODEV: + (void) strcpy(str, "No such device"); + break; + case NFSERR_NOTDIR: + (void) strcpy(str, "Not a directory"); + break; + case NFSERR_ISDIR: + (void) strcpy(str, "Is a directory"); + break; + case NFSERR_INVAL: + (void) strcpy(str, "Invalid argument"); + break; + case NFSERR_FBIG: + (void) strcpy(str, "File too large"); + break; + case NFSERR_NOSPC: + (void) strcpy(str, "No space left on device"); + break; + case NFSERR_ROFS: + (void) strcpy(str, "Read-only file system"); + break; + case NFSERR_OPNOTSUPP: + (void) strcpy(str, "Operation not supported"); + break; + case NFSERR_NAMETOOLONG: + (void) strcpy(str, "File name too long"); + break; + case NFSERR_NOTEMPTY: + (void) strcpy(str, "Directory not empty"); + break; + case NFSERR_DQUOT: + (void) strcpy(str, "Disc quota exceeded"); + break; + case NFSERR_STALE: + (void) strcpy(str, "Stale NFS file handle"); + break; + case NFSERR_REMOTE: + (void) strcpy(str, "Object is remote"); + break; + case NFSERR_WFLUSH: + (void) strcpy(str, "write cache flushed"); + break; + default: + (void) sprintf(str, "Unknown status %d", status); + break; + } + return (str); +} +/* sfs_c_ops.c */ diff --git a/TBBT/trace_play/sfs_2_vld.c b/TBBT/trace_play/sfs_2_vld.c new file mode 100644 index 0000000..45e24d9 --- /dev/null +++ b/TBBT/trace_play/sfs_2_vld.c @@ -0,0 +1,1747 @@ +#ifndef lint +static char sfs_c_vldSid[] = "@(#)sfs_2_vld.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ------------------------------ sfs_c_vld.c --------------------------- + * + * Validation suite for sfs. + * + *.Exported_routines + * void Validate_ops(int, char **) + * + *.Local_routines + * void validate_init_rpc() + * void validate_creation(void) + * void validate_attributes(void) + * void validate_read_write(void) + * void validate_rename(void) + * int compare_sattr(char *, char *, sattr *, fattr *) + * int compare_fattr(char *, char *, fattr *, fattr *) + * uint16_t sum(unsigned char *, uint_t) + * void validate_remove(void) + * void validate_cleanup(void) + * void validate_exit(void) + * void verror(int, ValMsgType, char *, ...) + * + *.Revision History + * 04-Dec-91 Keith Define string.h for SYSV/SVR4. + * 25-Jun-91 Wiryaman Created + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" + +extern struct hostent *Server_hostent; + +/* + * ----------------------- External Definitions ----------------------- + */ + +/* forward definitions for local routines */ +/* + * validate options + * BATCH - do complete pass of validation, reporting errors if any + * VERBOSE - prints step-by-step validation actions being performed + * INTERACTIVE - VERBOSE and if any errors encountered, ask to continue + * validation or not. + */ +#define VAL_BATCH 1 +#define VAL_VERBOSE 2 +#define VAL_INTERACTIVE 3 + +typedef enum { + I = 1, + W = 2, + E = 3 +} ValMsgType; + +#define NUMREGFILES 7 +#define NUMDIRS 5 +#define NUMLINKS 5 +#define NUMSYMLINKS 5 +#define NUMFILES NUMREGFILES + NUMDIRS + NUMLINKS + NUMSYMLINKS +#define NUMFRAGS 8 + +static void validate_init_rpc(void); +static void validate_exit(void); +static void validate_creation(void); +static void validate_attributes(void); +static void validate_read_write(void); +static void validate_rename(void); +static void validate_remove(void); +static void validate_cleanup(void); +static int compare_sattr(char *, char *, sattr *, fattr *); +static int compare_fattr(char *, char *, fattr *, fattr *); +static uint16_t sum(unsigned char *, uint_t); +static void verror(int, ValMsgType, char *, ...); + +static void val_op_null(void); +static void val_op_getattr(fhandle_t *, attrstat *); +static void val_op_setattr(sattrargs *, attrstat *); +static void val_op_lookup(diropargs *, diropres *); +static void val_op_readlink(fhandle_t *, readlinkres *); +static void val_op_read(readargs *, readres *); +static void val_op_write(writeargs *, attrstat *); +static void val_op_create(createargs *, diropres *); +static void val_op_remove(diropargs *, nfsstat *); +static void val_op_rename(renameargs *, nfsstat *); +static void val_op_link(linkargs *, nfsstat *); +static void val_op_symlink(symlinkargs *, nfsstat *); +static void val_op_mkdir(mkdirargs *, diropres *); +static void val_op_rmdir(diropargs *, nfsstat *); +static void val_op_readdir(readdirargs *, readdirres *); +static void val_op_statfs(fhandle_t *, statfsres *); +static void create_tmp_handles(void); +static void delete_tmp_handles(void); + +/* + * ---------------------- Static Declarations ---------------------- + */ + +int Validate; + +static int Validate_errors = 0; +static char Testdirname[SFS_MAXPATHLEN]; /* test dir component name */ + +/* + * ---------------------- SFS Validation Suite ---------------------- + */ + +void +Validate_ops( + int argc, + char * argv[]) +{ + char * valdir; + CLIENT * mount_client_ptr; + + if (argc > 1) { + verror(VAL_BATCH, E, "Can only validate one directory at a time.\n"); + exit(1); + } + + Num_io_files = NUMFILES; + Cur_uid = Real_uid; + nfs_version = NFS_VERSION; + + if (argc == 0) + valdir = "."; + else + valdir = argv++[0]; + + (void) sprintf(Testdirname, "%s/validatedir", valdir); + + do { + verror(VAL_BATCH, I, "validating sfs on \"%s\" directory ...\n", + valdir); + + init_fileinfo(); + create_tmp_handles(); + + /* + * need priv port to do following + */ + mount_client_ptr = lad_getmnt_hand(valdir); + if (mount_client_ptr == NULL) { + exit(1); + } + validate_init_rpc(); + + /* + * should be all done doing priv port stuff + */ + if (setuid(Real_uid) != 0) { + (void) fprintf(stderr,"%s: %s%s\n", + sfs_Myname, "cannot perform setuid operation.\n", + "Do `make install` as root.\n"); + } + + init_mount_point(0, valdir, mount_client_ptr); + verror(VAL_VERBOSE, I, "validating null operation ...\n"); + val_op_null(); + + validate_creation(); + validate_attributes(); + validate_read_write(); + validate_rename(); + validate_remove(); + argc--; + valdir = argv++[0]; + + /* + * Cleanup mount client handle + */ + clnt_destroy(mount_client_ptr); + + delete_tmp_handles(); + validate_cleanup(); + + } while (argc > 0); + + validate_exit(); + +} /* Validate_ops */ + + +/* + * allocate and initialize client handles + */ +static void +validate_init_rpc(void) +{ + NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent, + (uint32_t) NFS_PROGRAM, + (uint32_t) NFS_VERSION, + RPC_ANYSOCK, &Nfs_timers[0]); + + if (NFS_client == ((CLIENT *) NULL)) { + verror(VAL_BATCH, E, "portmap/nfsd server not responding\n"); + exit(1); + } + + NFS_client->cl_auth = authunix_create(lad_hostname, Real_uid, + Cur_gid, 0, NULL); +} /* validate_init_rpc */ + + +static void +validate_creation(void) +{ + int filenum; + int target_filenum; + diropargs arglp; + createargs argcr; + mkdirargs argmk; + linkargs argln; + symlinkargs argsl; + char sl_target_path[NFS_MAXPATHLEN]; + diropres reply; + readlinkres rlreply; + char sym_data[NFS_MAXPATHLEN]; + + for (filenum=0; filenum < NUMFILES ; filenum++) { + + Cur_file_ptr = &Io_files[filenum]; + sfs_gettime(&Cur_time); + + if (filenum < NUMREGFILES) { + + (void) sprintf(Cur_filename, Filespec, filenum); + + /* regular file creation */ + argcr.attributes.mode= (NFSMODE_REG | 0666); + argcr.attributes.uid = Cur_uid; + argcr.attributes.gid = Cur_gid; + argcr.attributes.atime.seconds = (unsigned int) Cur_time.esec; + argcr.attributes.atime.useconds = (unsigned int) Cur_time.usec; + argcr.attributes.mtime.seconds = (unsigned int) Cur_time.esec; + argcr.attributes.mtime.useconds = (unsigned int) Cur_time.usec; + argcr.attributes.size = 0; + (void) memmove((char *) &argcr.where.dir, (char *) &Export_dir.fh2, + NFS_FHSIZE); + argcr.where.name = Cur_filename; + + verror(VAL_VERBOSE, I, "validating create file %s ...\n", + Cur_filename); + val_op_create(&argcr, &reply); + + if (reply.status == NFS_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh2, + (char *) &reply.diropres_u.diropres.file, NFS_FHSIZE); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes2, + (char *) &reply.diropres_u.diropres.attributes, + sizeof(Cur_file_ptr->attributes2)); + (void) compare_sattr(Ops[CREATE].name, Io_files[filenum].file_name, + &argcr.attributes, &Cur_file_ptr->attributes2); + } else { + Cur_file_ptr->state = Nonexistent; + errno = (int)reply.status; + verror(VAL_BATCH, E, "create %s failed: %m\n", Cur_filename); + /* + * An error in file creation is fatal, because we use the + * created files to validate the other operations. + */ + validate_exit(); + } + + } else if (filenum < NUMREGFILES + NUMDIRS) { + + (void) sprintf(Cur_filename, Dirspec, filenum); + + /* directory creation */ + argmk.attributes.mode= (NFSMODE_DIR | 0777); + argmk.attributes.uid = Cur_uid; + argmk.attributes.gid = Cur_gid; + argmk.attributes.size = 0xFFFFFFFF; + argmk.attributes.atime.seconds = (unsigned int) Cur_time.esec; + argmk.attributes.atime.useconds = (unsigned int) Cur_time.usec; + argmk.attributes.mtime.seconds = (unsigned int) Cur_time.esec; + argmk.attributes.mtime.useconds = (unsigned int) Cur_time.usec; + (void) memmove((char *) &argmk.where.dir, (char *) &Export_dir.fh2, + NFS_FHSIZE); + argmk.where.name = Cur_filename; + + verror(VAL_VERBOSE, I, "validating mkdir %s ...\n", Cur_filename); + val_op_mkdir(&argmk, &reply); + + if (reply.status == NFS_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh2, + (char *) &reply.diropres_u.diropres.file, + NFS_FHSIZE); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes2, + (char *) &reply.diropres_u.diropres.attributes, + sizeof(Cur_file_ptr->attributes2)); + (void) compare_sattr(Ops[MKDIR].name, Io_files[filenum].file_name, + &argmk.attributes, &Cur_file_ptr->attributes2); + } else { + Cur_file_ptr->state = Nonexistent; + verror(VAL_BATCH, W, "mkdir %s failed:%m\n", Cur_filename); + } + + } else if(filenum < NUMREGFILES + NUMDIRS + NUMLINKS ) { + + (void) sprintf(Cur_filename, Filespec, filenum); + + /* hard link creation */ + target_filenum = NUMFILES-NUMSYMLINKS-1-filenum; + (void) memmove((char *) &argln.from, + (char *) &Io_files[target_filenum].fh2, NFS_FHSIZE); + (void) memmove((char *) &argln.to.dir, (char *) &Export_dir.fh2, + NFS_FHSIZE); + argln.to.name = Cur_filename; + + verror(VAL_VERBOSE, I, "validating link %s %s ...\n", + Io_files[target_filenum].file_name, Cur_filename); + val_op_link(&argln, &reply.status); + + if (reply.status == NFS_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh2, + (char *) &Io_files[target_filenum].fh2, + NFS_FHSIZE); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + Cur_file_ptr->attributes2 = Io_files[target_filenum].attributes2; + Io_files[target_filenum].attributes2.nlink++; + Cur_file_ptr->attributes2.nlink++; + } else { + Cur_file_ptr->state = Nonexistent; + verror(VAL_BATCH, W, "link %s failed: %m\n", Cur_filename); + } + + } else { + + (void) sprintf(Cur_filename, Symspec, filenum); + + /* symbolic link creation */ + target_filenum = NUMFILES-1-filenum; + (void) memmove((char *) &argsl.from.dir, (char *) &Export_dir.fh2, + NFS_FHSIZE); + argsl.from.name = Cur_filename; + (void) sprintf(sl_target_path, + "./%s", Io_files[target_filenum].file_name); + argsl.attributes.size = strlen(sl_target_path); + argsl.to = sl_target_path; + argsl.attributes.mode = (NFSMODE_LNK | 0777); + argsl.attributes.uid = Cur_uid; + argsl.attributes.gid = Cur_gid; + argsl.attributes.atime.seconds = (unsigned int)Cur_time.esec; + argsl.attributes.atime.useconds = (unsigned int)Cur_time.usec; + argsl.attributes.mtime.seconds = (unsigned int)Cur_time.esec; + argsl.attributes.mtime.useconds = (unsigned int)Cur_time.usec; + + verror(VAL_VERBOSE, I, "validating symlink %s %s ...\n", + sl_target_path, Cur_filename); + val_op_symlink(&argsl, &reply.status); + + if (reply.status == NFS_OK) { + Cur_file_ptr->state = Exists; + + /* do a lookup to get file handle and attributes */ + (void) memmove((char *) &arglp.dir, (char *) &Export_dir.fh2, + NFS_FHSIZE); + arglp.name = Cur_filename; + + val_op_lookup(&arglp, &reply); + + if (reply.status == NFS_OK) { + (void) memmove((char *) &Cur_file_ptr->fh2, + (char *) &reply.diropres_u.diropres.file, NFS_FHSIZE); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + Cur_file_ptr->attributes2 = + reply.diropres_u.diropres.attributes; + (void) compare_sattr(Ops[SYMLINK].name, + Io_files[filenum].file_name, + &argsl.attributes, + &Cur_file_ptr->attributes2); + } else { + verror(VAL_BATCH, W, "lookup %s failed: %m\n", + Cur_filename); + continue; + } + + /* validate readlink */ + rlreply.readlinkres_u.data = sym_data; + + verror(VAL_VERBOSE, I, "validating readlink %s ...\n", + Cur_filename); + val_op_readlink(&Cur_file_ptr->fh2, &rlreply); + + if (rlreply.status == NFS_OK) { + sym_data[rlreply.readlinkres_u.len] = '\0'; + if (strcmp(sl_target_path, sym_data)) { + verror(VAL_BATCH, W, + "readlink %s error, result = %s, should be %s\n", + Cur_filename, rlreply.readlinkres_u.data, + sl_target_path); + } + } else { + verror(VAL_BATCH, W, "readlink %s failed:%m\n", + Cur_filename); + } + + } else { + Cur_file_ptr->state = Nonexistent; + verror(VAL_BATCH, W, "symlink %s failed: %m\n", + Cur_filename); + } + } + } /* end for each file */ + +} /* validate_creation */ + + +static void +validate_attributes(void) +{ + int filenum; + diropargs arglp; + diropres lreply; + fhandle_t fh; + sattrargs argsa; + attrstat areply; + + /* validate fsstat */ + + /* validate lookup */ + for (filenum = 0; filenum < NUMFILES; filenum++) { + (void) memmove((char *) &arglp.dir, (char *) &Export_dir.fh2, + NFS_FHSIZE); + arglp.name = Io_files[filenum].file_name; + + verror(VAL_VERBOSE, I, "validating lookup %s ...\n", + Io_files[filenum].file_name); + val_op_lookup(&arglp, &lreply); + + if (lreply.status == NFS_OK) { + if (memcmp((char *) &(Io_files[filenum].fh2), + (char *) &(lreply.diropres_u.diropres.file), + NFS_FHSIZE)) { + verror(VAL_BATCH, W, "lookup %s: file handle mismatch\n", + Io_files[filenum].file_name); + } + (void) compare_fattr(Ops[LOOKUP].name, Io_files[filenum].file_name, + &Io_files[filenum].attributes2, + &lreply.diropres_u.diropres.attributes); + } else { + verror(VAL_BATCH, W, "lookup %s failed:%m\n", + Io_files[filenum].file_name); + } + } + + /* validate getattr */ + for (filenum = 0; filenum < NUMFILES; filenum++) { + (void) memmove((char *) &fh, (char *) &Io_files[filenum].fh2, + NFS_FHSIZE); + + verror(VAL_VERBOSE, I, "validating getattr %s ...\n", + Io_files[filenum].file_name); + val_op_getattr(&fh, &areply); + + if (areply.status == NFS_OK) { + (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name, + &Io_files[filenum].attributes2, + &areply.attrstat_u.attributes); + } else { + verror(VAL_BATCH, W, "getattr %s failed: %m\n", + Io_files[filenum].file_name); + } + } + + /*validate setattr */ + for (filenum = 0; filenum < NUMFILES; filenum++) { + sfs_gettime(&Cur_time); + if (filenum >= NUMREGFILES && filenum < NUMREGFILES + NUMDIRS) + argsa.attributes.mode= 0777; + else + argsa.attributes.mode= 0666; + argsa.attributes.uid = 0xFFFFFFFF; + argsa.attributes.gid = 0xFFFFFFFF; + argsa.attributes.size = 0xFFFFFFFF; + argsa.attributes.atime.seconds = (unsigned int)Cur_time.esec; + argsa.attributes.atime.useconds = (unsigned int)Cur_time.usec; + argsa.attributes.mtime.seconds = (unsigned int)Cur_time.esec; + argsa.attributes.mtime.useconds = (unsigned int)Cur_time.usec; + (void) memmove((char *) &argsa.file, (char *) &Io_files[filenum].fh2, + NFS_FHSIZE); + + verror(VAL_VERBOSE, I, "validating setattr %s ...\n", + Io_files[filenum].file_name); + val_op_setattr(&argsa, &areply); + + if (areply.status == NFS_OK) { + if (argsa.attributes.mode != areply.attrstat_u.attributes.mode){ + argsa.attributes.mode |= + (Io_files[filenum].attributes2.mode & NFSMODE_FMT); + argsa.attributes.mode &= Io_files[filenum].attributes2.mode; + } + Io_files[filenum].attributes2 = areply.attrstat_u.attributes; + (void) compare_sattr(Ops[SETATTR].name, Io_files[filenum].file_name, + &argsa.attributes, &areply.attrstat_u.attributes); + + val_op_getattr(&argsa.file, &areply); + + if (areply.status == NFS_OK) { + (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name, + &Io_files[filenum].attributes2, + &areply.attrstat_u.attributes); + } else { + verror(VAL_BATCH, W, "getattr %s failed: %m\n", + Io_files[filenum].file_name); + } + } else { + verror(VAL_BATCH, W, "setattr %s failed: %m\n", + Io_files[filenum].file_name); + } + } + +} /* validate_attributes */ + + +static void +validate_read_write(void) +{ + struct { + uint16_t sum; /* checksum of data */ + uint16_t len; /* length of len and data */ + char data[DEFAULT_MAX_BUFSIZE - 2 * sizeof(uint16_t)]; + } block; + writeargs argwr; + attrstat wrreply; + readargs argrd; + readres rdreply; + int maxblks; + int maxfiles; + uint_t i; + int numfiles; + int filenum; + int blocknum; + readdirargs argrdir; + readdirres rdirreply; + int entry_cnt = 9; + entry entry_stream[9]; + char name_stream[9 * SFS_MAXNAMLEN]; + + /* validate write */ + + /* get the maximum number of blocks sfs will write */ + maxblks = Io_dist_ptr->max_bufs; + maxfiles = maxblks > NUMREGFILES ? NUMREGFILES : maxblks; + + /* write maxblks - filenum + 1 blocks to each regular file */ + argwr.offset = 0; + argwr.beginoffset = 0; /* unused */ + argwr.totalcount = 0; /* unused */ + argwr.data.data_val = (char *)█ + + for (blocknum = 0; blocknum <= maxblks ; blocknum++) { + + for (i=0; i < sizeof(block.data); i++) + block.data[i] = (char)blocknum; + + for (filenum=0; filenum < maxfiles; filenum++) { + + /* Write fewer blocks to files with higher numbers. */ + if (blocknum > (maxblks - filenum)) + break; + + /* set the length field */ + if (blocknum == (maxblks - filenum)) { + block.len = ((maxfiles - filenum) * + (Bytes_per_block/Kb_per_block)) - (sizeof(block.len) + + sizeof(block.sum)); + } else { + block.len = Bytes_per_block - (sizeof(block.len) + + sizeof(block.sum)); + } + block.sum = sum((unsigned char *) &block.len, + block.len + sizeof(block.len)); + + (void) memmove((char *) &argwr.file, + (char *) &Io_files[filenum].fh2, NFS_FHSIZE); + argwr.data.data_len = block.len + + sizeof(block.len) + sizeof(block.sum); + + verror(VAL_VERBOSE, I, + "validating write %d bytes @ offset %d to %s ...\n", + argwr.data.data_len, argwr.offset, + Io_files[filenum].file_name); + + val_op_write(&argwr, &wrreply); + + if (wrreply.status == NFS_OK) { + (void) compare_fattr(Ops[WRITE].name, Io_files[filenum].file_name, + &Io_files[filenum].attributes2, + &wrreply.attrstat_u.attributes); + Io_files[filenum].attributes2 = wrreply.attrstat_u.attributes; + } else { + verror(VAL_BATCH, W, "write %s failed: %m\n", + Io_files[filenum].file_name); + } + } + argwr.offset += Bytes_per_block; + } + + /* validate read */ + + for (filenum = 0; filenum < maxfiles; filenum++) { + (void) memmove((char *) &argrd.file, (char *) &Io_files[filenum].fh2, + NFS_FHSIZE); + argrd.offset = 0; + argrd.count = 0; + rdreply.readres_u.reply.data.data_len = 0; + maxblks = Io_files[filenum].attributes2.size / Bytes_per_block; + for (blocknum = 0; blocknum <= maxblks; blocknum ++) { + + if (argrd.count != rdreply.readres_u.reply.data.data_len) { + argrd.count -= rdreply.readres_u.reply.data.data_len; + rdreply.readres_u.reply.data.data_val = (char *)&block + + rdreply.readres_u.reply.data.data_len; + blocknum--; + } else { + if (blocknum < maxblks) + argrd.count = Bytes_per_block; + else + argrd.count = (maxfiles - filenum) + * (Bytes_per_block/Kb_per_block); + rdreply.readres_u.reply.data.data_val = (char *)█ + } + argrd.totalcount = argrd.count; /* unused */ + + verror(VAL_VERBOSE, I, + "validating read %d bytes @ offset %d from %s ...\n", + argrd.count, argrd.offset, + Io_files[filenum].file_name); + + val_op_read(&argrd, &rdreply); + + if (rdreply.status == NFS_OK) { + (void) compare_fattr(Ops[READ].name, Io_files[filenum].file_name, + &Io_files[filenum].attributes2, + &rdreply.readres_u.reply.attributes); + Io_files[filenum].attributes2 = + rdreply.readres_u.reply.attributes; + argrd.offset += rdreply.readres_u.reply.data.data_len; + } else { + verror(VAL_BATCH, W, "read %s failed: %m\n", + Io_files[filenum].file_name); + } + + if (argrd.count == + (block.sum != sum((unsigned char *) &block.len, + block.len + sizeof(block.len)))) { + verror(VAL_BATCH, W, "read %s checksum mismatch\n", + Io_files[filenum].file_name); + } + } + } + + /* validate readdir */ + numfiles = 0; + + (void) memmove((char *) &argrdir.dir, (char *) &Export_dir.fh2, NFS_FHSIZE); + (void) memset((char *) argrdir.cookie, '\0', NFS_COOKIESIZE); + argrdir.count = DEFAULT_MAX_BUFSIZE; + + (void) memset((char *) &rdirreply, '\0', sizeof(rdirreply)); + rdirreply.readdirres_u.reply.max_entries = entry_cnt; + rdirreply.readdirres_u.reply.entries = entry_stream; + for (i = 0; i < entry_cnt; i++) { + entry_stream[i].name = &name_stream[i * SFS_MAXNAMLEN]; + } + + do { + verror(VAL_VERBOSE, I, "validating readdir %d entries of %s ...\n", + rdirreply.readdirres_u.reply.max_entries, Testdirname); + val_op_readdir(&argrdir, &rdirreply); + + if (rdirreply.status == NFS_OK) { + for (i = 0; i < rdirreply.readdirres_u.reply.max_entries; i++) { + numfiles++; + entry_stream[i].name[entry_stream[i].name_len] = '\0'; + if (!entry_stream[i].valid) { + verror(VAL_BATCH, W, + "readdir %s error: entry %d (%s) is marked invalid\n", + Testdirname, i, entry_stream[i].name); + } + if ((!strcmp(entry_stream[i].name, ".")) || + (!strcmp(entry_stream[i].name, "..")) ) { + numfiles--; + continue; + } + for (filenum = 0; filenum < NUMFILES; filenum++) { + if (!strcmp(entry_stream[i].name, Io_files[filenum].file_name)) { + if (entry_stream[i].fileid != + Io_files[filenum].attributes2.fileid) { + verror(VAL_BATCH, E, + "readdir %s error: file %s fileid mismatch\n", + Testdirname, entry_stream[i].name); + + verror(VAL_BATCH, W, + " fileid: got = %d, original = %d\n", + entry_stream[i].fileid, + Io_files[filenum].attributes2.fileid); + } + break; + } + } + if (filenum == NUMFILES) { + verror(VAL_BATCH, W, + "readdir %s error: file \"%s\" was not created within sfs\n", + Testdirname, entry_stream[i].name); + } + } + + if (i < entry_cnt && entry_stream[i].valid) { + verror(VAL_BATCH, W, + "readdir %s error: valid entries exceeded maximum\n", + Testdirname); + } + + } else { + verror(VAL_BATCH, W, "readdir %s failed: %m\n", Testdirname); + } + + (void) memmove((char *)argrdir.cookie, + (char *)entry_stream[rdirreply.readdirres_u.reply.max_entries-1].cookie, + NFS_COOKIESIZE); + + } while (rdirreply.readdirres_u.reply.eof == 0); + + if (numfiles != NUMFILES) { + verror(VAL_BATCH, W, + "readdir %s error: the number of files found\n\ +does not match with the number of files created within sfs\n", Testdirname); + } + +} /* validate_read_write */ + + +static void +validate_rename(void) +{ + renameargs argrn; + nfsstat rnreply; + int filenum; + char newname[SFS_MAXNAMLEN]; + int rncount = 0; + + (void) memmove((char *) &argrn.from.dir, (char *) &Export_dir.fh2, NFS_FHSIZE); + (void) memmove((char *) &argrn.to.dir, (char *) &Export_dir.fh2, NFS_FHSIZE); + + for (filenum=0; filenum < NUMFILES; filenum++) { + if (Io_files[filenum].state != Exists) + continue; + + rncount++; + (void) sprintf(newname, "n%s", Io_files[filenum].file_name); + argrn.from.name = Io_files[filenum].file_name; + argrn.to.name = newname; + + verror(VAL_VERBOSE, I, "validating rename %s %s ...\n", + argrn.from.name, argrn.to.name); + + val_op_rename(&argrn, &rnreply); + + if (rnreply == NFS_OK) { + (void) strcpy(Io_files[filenum].file_name, newname); + } else { + verror(VAL_BATCH, W, "rename %s to %s failed: %m\n", + Io_files[filenum].file_name, newname); + } + + } + + if (!rncount) { + verror(VAL_BATCH, E, "validate_rename: no files renamed\n"); + verror(VAL_BATCH, W, " due to previous operation error\n"); + } + +} /* validate_rename */ + + +static int +compare_fattr( + char * op, + char * fname, + fattr * attr1, + fattr * attr2) +{ + int ret = TRUE; + int prev_warn = FALSE; /* -1 info warning */ + int flag_error = FALSE; + + if (attr1->type != attr2->type) { + if (attr1->type == 0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " type: current = %d, previous = %d\n", + attr2->type, attr1->type); + attr1->type = attr2->type; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " type: current = %d, previous = %d\n", + attr2->type, attr1->type); + ret = FALSE; + flag_error = TRUE; + } + } + + if ((attr1->mode & NFSMODE_MASK) != (attr2->mode & NFSMODE_MASK)) { + if (attr1->mode == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " mode: current = %7o, previous = %7o\n", + attr2->mode, attr1->mode); + attr1->mode = attr2->mode; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " mode: current = %d, previous = %d\n", + attr2->mode, attr1->mode); + ret = FALSE; + flag_error = TRUE; + } + } + + if (attr1->nlink != attr2->nlink) { + if (attr1->nlink == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " nlink: current = %d, previous = %d\n", + attr2->nlink, attr1->nlink); + attr1->nlink = attr2->nlink; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " nlink: current = %d, previous = %d\n", + attr2->nlink, attr1->nlink); + ret = FALSE; + flag_error = TRUE; + } + } + + + /* + * Check for user "nobody", UID -2, which may be untranslated from + * sixteen-bit two's complement. + */ + if (attr1->uid != attr2->uid && !((attr2->uid == (unsigned int)0xFFFFFFFE || + attr2->uid == 65534) && attr1->uid ==0)) { + if (attr1->uid == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " uid: current = %d, previous = %d\n", + attr2->uid, attr1->uid); + attr1->uid = attr2->uid; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " uid: current = %d, previous = %d\n", + attr2->uid, attr1->uid); + ret = FALSE; + flag_error = TRUE; + } + } + + if (attr1->gid != attr2->gid && attr2->gid != 0) { +/* + if (attr1->gid != attr2->gid) { +*/ + if (attr1->gid == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " gid: current = %d, previous = %d\n", + attr2->gid, attr1->gid); + attr1->gid = attr2->gid; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " gid: current = %d, previous = %d\n", + attr2->gid, attr1->gid); + ret = FALSE; + flag_error = TRUE; + } + } + + if (attr1->size != attr2->size) { + if (strcmp(op, Ops[WRITE].name)) { + if (attr1->size == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " size: current = %d, previous = %d\n", + attr2->size, attr1->size); + attr1->size = attr2->size; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " size: current = %d, previous = %d\n", + attr2->size, attr1->size); + ret = FALSE; + flag_error = TRUE; + } + } + } + + if (attr1->blocksize != attr2->blocksize) { + if (attr1->blocksize == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " blocksize: current = %d, previous = %d\n", + attr2->blocksize, attr1->blocksize); + attr1->blocksize = attr2->blocksize; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " blocksize: current = %d, previous = %d\n", + attr2->blocksize, attr1->blocksize); + ret = FALSE; + flag_error = TRUE; + } + } + + + /* compare rdev only if type == NFCHR or NFBLK */ + if ((attr1->type == NFCHR || attr1->type == NFBLK) && + attr1->rdev != attr2->rdev) { + if (attr1->rdev == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " rdev: current = %d, previous = %d\n", + attr2->rdev, attr1->rdev); + attr1->rdev = attr2->rdev; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " rdev: current = %d, previous = %d\n", + attr2->rdev, attr1->rdev); + ret = FALSE; + flag_error = TRUE; + } + } + + + /* + * The NFS specification does not require that the number of blocks + * associated with a file remain constant. Certain file systems + * may pre-allocate more blocks than necessary then trim them + * back ("garbage collect") or even blow holes in files that have + * all zero blocks. + * We must check that we never get back -1. + */ + if (attr1->blocks != attr2->blocks) { + if (strcmp(op, Ops[WRITE].name)) { + if (attr1->blocks == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " blocks: current = %d, previous = %d\n", + attr2->blocks, attr1->blocks); + attr1->blocks = attr2->blocks; + ret = FALSE; + } + } + } + + + if (attr1->fsid != attr2->fsid) { + if (attr1->fsid == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " fsid: current = %d, previous = %d\n", + attr2->fsid, attr1->fsid); + attr1->fsid = attr2->fsid; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " fsid: current = %d, previous = %d\n", + attr2->fsid, attr1->fsid); + ret = FALSE; + flag_error = TRUE; + } + } + + if (attr1->fileid != attr2->fileid) { + if (attr1->fileid == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " fileid: current = %d, previous = %d\n", + attr2->fileid, attr1->fileid); + attr1->fileid = attr2->fileid; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " fileid: current = %d, previous = %d\n", + attr2->fileid, attr1->fileid); + ret = FALSE; + flag_error = TRUE; + } + } + + if (prev_warn) { + verror(VAL_BATCH, I, + "\n Warning: the previous value of a field is -1,\n"); + verror(VAL_BATCH, I, + " this resulted from an unused field returned by\n"); + verror(VAL_BATCH, I, + " the previous operation on this file/directory.\n"); + verror(VAL_BATCH, I, + " The current value is now stored for future comparison\n\n"); + } + + if (flag_error) + verror(VAL_BATCH, W,"\n"); + + return(flag_error); + +} /* ckompare_fattr */ + + +static int +compare_sattr( + char * op, + char * fname, + sattr * attr1, + fattr * attr2) +{ + int ret = TRUE; + char msg[80]; + + msg[0] = '\0'; + + if (attr1->mode != (unsigned int)0xFFFFFFFF && + (attr1->mode & NFSMODE_MASK) != (attr2->mode & NFSMODE_MASK)) { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + + } + verror(VAL_BATCH, E, " mode: returned = %7o, specified = %7o\n", + attr2->mode, attr1->mode); + ret = FALSE; + } + + /* + * Check for user "nobody", UID -2, which may be untranslated from + * sixteen-bit two's complement. + */ + if (attr1->uid != (unsigned int)0xFFFFFFFF && attr1->uid != attr2->uid && + !((attr2->uid == (unsigned int)0xFFFFFFFE || + attr2->uid == 65534) && + attr1->uid == 0)) { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + if (attr1->uid == 0) + (void) strcat(msg," (is root UID mapped to other UID?)"); + verror(VAL_BATCH, E, " uid: returned = %d, specified = %d %s\n", + attr2->uid, attr1->uid, msg); + ret = FALSE; + } + if (attr1->gid != (unsigned int)0xFFFFFFFF && + attr1->gid != attr2->gid && + attr2->gid != 0) { +/* + if (attr1->gid != 0xFFFFFFFF && attr1->gid != attr2->gid) { +*/ + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " gid: returned = %d, specified = %d\n", + attr2->gid, attr1->gid); + ret = FALSE; + } + + if (attr1->size != (unsigned int)0xFFFFFFFF && + attr1->size != attr2->size) { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " size: returned = %d, specified = %d\n", + attr2->size, attr1->size); + ret = FALSE; + } + + if (!ret) + verror(VAL_BATCH, W,"\n"); + + return(ret); + +} /* compare_sattr */ + + +/* + * Return the BSD checksum of buf[0..len-1] + */ +static uint16_t +sum( + unsigned char * buf, + uint_t len) +{ + uint16_t cksum; + + cksum = 0; + for (; len--; buf++) { + if (cksum & 1) + cksum = (cksum >> 1) + 0x8000; + else + cksum >>= 1; + cksum += (uint16_t) *buf; + cksum &= 0xFFFF; + } + return(cksum); +} /* sum */ + + +static void +validate_remove(void) +{ + diropargs args; + nfsstat reply; + diropres lreply; + int filenum; + char * op; + + (void) memmove((char *) &args.dir, (char *) &Export_dir.fh2, NFS_FHSIZE); + + for (filenum = 0; filenum < NUMFILES; filenum++) { + + if (Io_files[filenum].state != Exists) + continue; + + args.name = Io_files[filenum].file_name; + + if (Io_files[filenum].attributes2.type == NFDIR) { + op = Ops[RMDIR].name; + verror(VAL_VERBOSE, I, "validating rmdir %s ...\n", + args.name); + val_op_rmdir(&args, &reply); + } else { + op = Ops[REMOVE].name; + verror(VAL_VERBOSE, I, "validating remove %s ...\n", + args.name); + val_op_remove(&args, &reply); + } + + if (reply == NFS_OK) { + /* make sure the file is removed from the directory */ + val_op_lookup(&args, &lreply); + + if (lreply.status == NFSERR_NOENT) { + Io_files[filenum].state = Nonexistent; + } else if (lreply.status == NFS_OK) { + verror(VAL_BATCH, W, "%s %s: file not removed\n", + op, Io_files[filenum].file_name); + } else { + verror(VAL_BATCH, W, "lookup %s failed: %m\n", + Io_files[filenum].file_name); + + } + } else { + verror(VAL_BATCH, W, "%s %s failed: %m\n", op, + Io_files[filenum].file_name); + } + + } + +} /* validate_remove */ + + +static void +validate_cleanup(void) +{ + free(Io_files); + free(Non_io_files); + free(Dirs); + free(Symlinks); + clnt_destroy(NFS_client); + +} /* validate_cleanup */ + + +static void +validate_exit(void) +{ + if (!Validate_errors) { + verror(VAL_BATCH, I, "validation completed successfully.\n"); + exit(0); + } else { + verror(VAL_BATCH, I, "validation terminated with errors\n"); + exit(1); + } + +} /* validate_exit */ + + +/* PRINTFLIKE3 */ +static void +verror( + int opt, + ValMsgType msgtype, + char * fmt, + ...) +{ + va_list ap; + char buf[1024]; + char * bp = buf; + char * fp; + int repeat; + char * sp; + + va_start(ap, fmt); + + /* + * Expand the "%m" format character into the descriptive string + * for the current value of errno. Printf handles the other "%" + * formatting characters. + */ + if (Validate >= opt) { + for (fp = fmt; *fp; fp++) { + if (*fp == '%' && fp[1] == 'm') { + if ((sp = strerror(errno)) == NULL) { + (void) sprintf(bp, "unknown error %d", errno); + } else { + (void) strcpy(bp, sp); + } + bp = buf + strlen(buf); + fp++; + } else { + *bp++ = *fp; + } + } + *bp = '\0'; + (void) vprintf(buf, ap); + } + va_end(ap); + + if (msgtype != I) + Validate_errors++; + + if (msgtype == W && Validate == VAL_INTERACTIVE) { + repeat = 1; + while (repeat) { + char ans[80]; + + (void) fprintf(stderr, "continue? (y or n) "); + if (!fgets(ans,80,stdin)) { + (void) fprintf(stderr, "\n"); + continue; + } + if (ans[0] == 'n' || ans[0] == 'N') { + validate_exit(); + exit(1); + } else if (ans[0] == 'y' || ans[0] == 'Y') { + repeat = 0; + break; + } + } + } + +} /* verror */ + + +static void +val_op_null(void) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_NULL, + xdr_void, (char *)0, xdr_void, (char *)0, + Nfs_timers[Ops[NULLCALL].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "null"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_getattr(fhandle_t *args, attrstat *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_GETATTR, + xdr_getattr, (char *)args, xdr_getattr, (char *)reply, + Nfs_timers[Ops[GETATTR].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "getattr"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_setattr(sattrargs *args, attrstat *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_SETATTR, + xdr_setattr, (char *)args, xdr_setattr, (char *)reply, + Nfs_timers[Ops[SETATTR].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "setattr"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_lookup(diropargs *args, diropres *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_LOOKUP, + xdr_lookup, (char *)args, xdr_lookup, (char *)reply, + Nfs_timers[Ops[LOOKUP].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "lookup"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_readlink(fhandle_t *args, readlinkres *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_READLINK, + xdr_readlink, (char *)args, xdr_readlink, (char *)reply, + Nfs_timers[Ops[READLINK].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "readlink"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_read(readargs *args, readres *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_READ, + xdr_read, (char *)args, xdr_read, (char *)reply, + Nfs_timers[Ops[READ].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "read"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_write(writeargs *args, attrstat *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, + NFSPROC_WRITE, xdr_write, (char *)args, xdr_write, (char *)reply, + Nfs_timers[Ops[WRITE].call_class]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "write"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_create(createargs *args, diropres *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, + NFSPROC_CREATE, xdr_create, (char *)args, xdr_create, (char *)reply, + Nfs_timers[Ops[CREATE].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "create"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_remove(diropargs *args, nfsstat *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, + NFSPROC_REMOVE, xdr_remove, (char *)args, xdr_remove, (char *)reply, + Nfs_timers[Ops[REMOVE].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "remove"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_rename(renameargs *args, nfsstat *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_RENAME, + xdr_rename, (char *)args, xdr_rename, (char *)reply, + Nfs_timers[Ops[RENAME].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "rename"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_link(linkargs *args, nfsstat *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_LINK, + xdr_link, (char *)args, xdr_link, (char *)reply, + Nfs_timers[Ops[LINK].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "link"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_symlink(symlinkargs *args, nfsstat *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_SYMLINK, + xdr_symlink, (char *)args, xdr_symlink, (char *)reply, + Nfs_timers[Ops[SYMLINK].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "symlink"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_mkdir(mkdirargs *args, diropres *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_MKDIR, + xdr_mkdir, (char *)args, xdr_mkdir, (char *)reply, + Nfs_timers[Ops[MKDIR].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "mkdir"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_rmdir(diropargs *args, nfsstat *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_RMDIR, + xdr_rmdir, (char *)args, xdr_rmdir, (char *)reply, + Nfs_timers[Ops[RMDIR].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "rmdir"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_readdir(readdirargs *args, readdirres *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_READDIR, + xdr_readdir, (char *)args, xdr_readdir, (char *)reply, + Nfs_timers[Ops[READDIR].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "readdir"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_statfs(fhandle_t *args, statfsres *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC_STATFS, + xdr_statfs, (char *)args, xdr_statfs, (char *)reply, + Nfs_timers[Ops[FSSTAT].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "statfs"); + Validate_errors++; + validate_exit(); + } + } +} +static void +create_tmp_handles(void) +{ + int filenum; + for (filenum = 0; filenum < NUMFILES; filenum++) { + + if(Io_files[filenum].fh_data == (sfs_fh_data *)0) + { + Io_files[filenum].fh_data = + calloc(1,sizeof(sfs_fh_data)); + Io_files[filenum].attributes2.type = NFNON; + Io_files[filenum].attributes3.type = NF3NON; + } + } +} + +static void +delete_tmp_handles() +{ + int filenum; + for (filenum = 0; filenum < NUMFILES; filenum++) { + + if(Io_files[filenum].fh_data != (sfs_fh_data *)0) + { + free(Io_files[filenum].fh_data); + Io_files[filenum].fh_data=(sfs_fh_data *)0; + } + } +} + + + + + + + + diff --git a/TBBT/trace_play/sfs_2_xdr.c b/TBBT/trace_play/sfs_2_xdr.c new file mode 100644 index 0000000..7c84899 --- /dev/null +++ b/TBBT/trace_play/sfs_2_xdr.c @@ -0,0 +1,735 @@ +#ifndef lint +static char sfs_c_xdrSid[] = "@(#)sfs_2_xdr.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/* + * -------------------------- sfs_c_xdr.c -------------------------- + * + * XDR routines for the nfs protocol. + * + *.Exported_routines + * bool_t xdr_fhstatus(XDR *, struct fhstatus *) + * bool_t xdr_path(XDR *, char **) + * bool_t xdr_fattr(XDR *, fattr *) + * bool_t xdr_sattr(XDR *, sattr *) + * bool_t xdr_null(void) + * bool_t xdr_getattr(XDR *, char *) + * bool_t xdr_setattr(XDR *, char *) + * bool_t xdr_root(void) + * bool_t xdr_lookup(XDR *, char *) + * bool_t xdr_readlink(XDR *, char *) + * bool_t xdr_read(XDR *, char *) + * bool_t xdr_write(XDR *, char *) + * bool_t xdr_create(XDR *, char *) + * bool_t xdr_remove(XDR *, char *) + * bool_t xdr_rename(XDR *, char *) + * bool_t xdr_link(XDR *, char *) + * bool_t xdr_symlink(XDR *, char *) + * bool_t xdr_mkdir(XDR *, char *) + * bool_t xdr_rmdir(XDR *, char *) + * bool_t xdr_readdir(XDR *, char *) + * bool_t xdr_statfs(XDR *, char *) + * + *.Local_routines + * bool_t xdr_timeval(XDR *, nfstime *) + * bool_t xdr_nfsstat(XDR *, nfsstat *) + * bool_t xdr_ftype(XDR *, ftype *) + * bool_t xdr_diropargs(XDR *, diropargs *) + * bool_t xdr_diropres(XDR *, diropres *) + * bool_t xdr_attrstat(XDR *, attrstat *) + * + *.Revision_History + * 28-NOv-91 Teelucksingh ANSI C + * 25-Jun-91 Santa Wiryaman Changed return values to TRUE when + * status != NFS_OK. This way we can + * decode NFS error messages. + * 17-May-90 Richard Bean Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "sfs_c_def.h" + +/* + * ----------------------- Forward Definitions ----------------------- + */ + +static bool_t xdr_f_handle(XDR *, fhandle_t *); +static bool_t xdr_fattr(XDR *, fattr *); +static bool_t xdr_sattr(XDR *, sattr *); +static bool_t xdr_timeval(XDR *, nfstime *); +static bool_t xdr_nfsstat(XDR *, nfsstat *); +static bool_t xdr_ftype(XDR *, ftype *); +static bool_t xdr_diropargs(XDR *, diropargs *); +static bool_t xdr_diropres(XDR *, diropres *); +static bool_t xdr_attrstat(XDR *, attrstat *); + +/* + * ----------------------- SFS XDR Routines ------------------------- + */ + + +static bool_t +xdr_f_handle( + XDR * xdrs, + fhandle_t * fhandle_ptr) +{ + return(xdr_opaque(xdrs, (char *)fhandle_ptr, NFS_FHSIZE)); +} + + +static bool_t +xdr_timeval( + XDR * xdrs, + nfstime * timeval_ptr) +{ + return(xdr_int32_t(xdrs, (int32_t *) &timeval_ptr->seconds) && + xdr_int32_t(xdrs, (int32_t *) &timeval_ptr->useconds)); +} + + +static bool_t +xdr_nfsstat( + XDR * xdrs, + nfsstat * nfsstat_ptr) +{ + return(xdr_enum(xdrs, (enum_t *) nfsstat_ptr)); +} + + +static bool_t +xdr_ftype( + XDR * xdrs, + ftype * ftype_ptr) +{ + return(xdr_enum(xdrs, (enum_t *) ftype_ptr)); +} + + +bool_t +xdr_fhstatus( + XDR * xdrs, + struct fhstatus * fhsp) +{ + if (!xdr_nfsstat(xdrs, (nfsstat *) &fhsp->fhs_status)) { + return(FALSE); + } + if (fhsp->fhs_status != (int)NFS_OK) { + return(TRUE); + } + return(xdr_f_handle(xdrs, &fhsp->fhs_fh)); +} + + +bool_t +xdr_path( + XDR * xdrs, + char ** pathp) +{ + return(xdr_string(xdrs, pathp, NFS_MAXPATHLEN)); +} + + +static bool_t +xdr_fattr( + XDR * xdrs, + fattr * fattr_ptr) +{ + return(xdr_ftype(xdrs, &fattr_ptr->type) && + xdr_u_int(xdrs, &fattr_ptr->mode) && + xdr_u_int(xdrs, &fattr_ptr->nlink) && + xdr_u_int(xdrs, &fattr_ptr->uid) && + xdr_u_int(xdrs, &fattr_ptr->gid) && + xdr_u_int(xdrs, &fattr_ptr->size) && + xdr_u_int(xdrs, &fattr_ptr->blocksize) && + xdr_u_int(xdrs, &fattr_ptr->rdev) && + xdr_u_int(xdrs, &fattr_ptr->blocks) && + xdr_u_int(xdrs, &fattr_ptr->fsid) && + xdr_u_int(xdrs, &fattr_ptr->fileid) && + xdr_timeval(xdrs, &fattr_ptr->atime) && + xdr_timeval(xdrs, &fattr_ptr->mtime) && + xdr_timeval(xdrs, &fattr_ptr->ctime)); +} + + +static bool_t +xdr_sattr( + XDR * xdrs, + sattr * sattr_ptr) +{ + return(xdr_u_int(xdrs, &sattr_ptr->mode) && + xdr_u_int(xdrs, &sattr_ptr->uid) && + xdr_u_int(xdrs, &sattr_ptr->gid) && + xdr_u_int(xdrs, &sattr_ptr->size) && + xdr_timeval(xdrs, &sattr_ptr->atime) && + xdr_timeval(xdrs, &sattr_ptr->mtime)); +} + + +static bool_t +xdr_diropargs( + XDR * xdrs, + diropargs * dir_args_ptr) +{ + return(xdr_f_handle(xdrs, &dir_args_ptr->dir) && + xdr_path(xdrs, &dir_args_ptr->name)); +} + + +static bool_t +xdr_diropres( + XDR * xdrs, + diropres * dir_res_ptr) +{ + if (!xdr_nfsstat(xdrs, &dir_res_ptr->status)) { + return(FALSE); + } + + if (dir_res_ptr->status == NFS_OK) { + return(xdr_f_handle(xdrs, &dir_res_ptr->diropres_u.diropres.file) && + xdr_fattr(xdrs, &dir_res_ptr->diropres_u.diropres.attributes)); + } + return(TRUE); +} + + +static bool_t +xdr_attrstat( + XDR * xdrs, + attrstat * attrstat_ptr) +{ + if (!xdr_nfsstat(xdrs, &attrstat_ptr->status)) { + return(FALSE); + } + + if (attrstat_ptr->status == NFS_OK) { + return(xdr_fattr(xdrs, &attrstat_ptr->attrstat_u.attributes)); + } + return(TRUE); +} + + +bool_t +xdr_getattr( + XDR * xdrs, + char * params_ptr) +{ + fhandle_t * fhandle_ptr; + attrstat * attrstat_ptr; + + switch (xdrs->x_op) { + case XDR_ENCODE: + fhandle_ptr = (fhandle_t *) params_ptr; + return(xdr_f_handle(xdrs, fhandle_ptr)); + + case XDR_DECODE: + /* LINTED pointer cast */ + attrstat_ptr = (attrstat *) params_ptr; + return(xdr_attrstat(xdrs, attrstat_ptr)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +bool_t +xdr_setattr( + XDR * xdrs, + char * params_ptr) +{ + sattrargs * sattrargs_ptr; + attrstat * attrstat_ptr; + + switch (xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + sattrargs_ptr = (sattrargs *) params_ptr; + return(xdr_f_handle(xdrs, &sattrargs_ptr->file) && + xdr_sattr(xdrs, &sattrargs_ptr->attributes)); + + case XDR_DECODE: + /* LINTED pointer cast */ + attrstat_ptr = (attrstat *) params_ptr; + return(xdr_attrstat(xdrs, attrstat_ptr)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +bool_t +xdr_lookup( + XDR * xdrs, + char * params_ptr) +{ + diropargs * diropargs_ptr; + diropres * diropres_ptr; + + + switch(xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + diropargs_ptr = (diropargs *) params_ptr; + return(xdr_f_handle(xdrs, &diropargs_ptr->dir) && + xdr_path(xdrs, &diropargs_ptr->name)); + + case XDR_DECODE: + /* LINTED pointer cast */ + diropres_ptr = (diropres *) params_ptr; + return(xdr_diropres(xdrs, diropres_ptr)); + + default: + return(FALSE); + } /* switch on operation */ +} + +bool_t +xdr_readlink( + XDR * xdrs, + char * params_ptr) +{ + fhandle_t * fhandle_ptr; + readlinkres * readlinkres_ptr; + + switch(xdrs->x_op) { + case XDR_ENCODE: + fhandle_ptr = (fhandle_t *) params_ptr; + return(xdr_f_handle(xdrs, fhandle_ptr)); + + case XDR_DECODE: + /* LINTED pointer cast */ + readlinkres_ptr = (readlinkres *) params_ptr; + if (!xdr_nfsstat(xdrs, &readlinkres_ptr->status)) { + return(FALSE); + } + if (readlinkres_ptr->status != NFS_OK) { + return(TRUE); + } + return(xdr_bytes(xdrs, &readlinkres_ptr->readlinkres_u.data, + (unsigned int *) + &readlinkres_ptr->readlinkres_u.len, + (unsigned int) NFS_MAXPATHLEN)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +bool_t +xdr_read( + XDR * xdrs, + char * params_ptr) +{ + readargs * readargs_ptr; + readres * readres_ptr; + + + switch (xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + readargs_ptr = (readargs *) params_ptr; + return(xdr_f_handle(xdrs, &readargs_ptr->file) && + xdr_u_int(xdrs, &readargs_ptr->offset) && + xdr_u_int(xdrs, &readargs_ptr->count) && + xdr_u_int(xdrs, &readargs_ptr->totalcount)); + + case XDR_DECODE: + /* LINTED pointer cast */ + readres_ptr = (readres *) params_ptr; + if (!xdr_nfsstat(xdrs, &readres_ptr->status)) { + return(FALSE); + } + if (readres_ptr->status != NFS_OK) { + return(TRUE); + } + return(xdr_fattr(xdrs, &readres_ptr->readres_u.reply.attributes) && + xdr_bytes(xdrs, &readres_ptr->readres_u.reply.data.data_val, + &readres_ptr->readres_u.reply.data.data_len, + (unsigned int) NFS_MAXDATA)); + + default: + return(FALSE); + } /* switch on operation */ +} + +bool_t +xdr_write( + XDR * xdrs, + char * params_ptr) +{ + writeargs * writeargs_ptr; + attrstat * attrstat_ptr; + + + switch (xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + writeargs_ptr = (writeargs *) params_ptr; + return(xdr_f_handle(xdrs, &writeargs_ptr->file) && + xdr_u_int(xdrs, &writeargs_ptr->beginoffset) && + xdr_u_int(xdrs, &writeargs_ptr->offset) && + xdr_u_int(xdrs, &writeargs_ptr->totalcount) && + xdr_bytes(xdrs, &writeargs_ptr->data.data_val, + &writeargs_ptr->data.data_len, + (unsigned int) NFS_MAXDATA)); + + case XDR_DECODE: + /* LINTED pointer cast */ + attrstat_ptr = (attrstat *) params_ptr; + return(xdr_attrstat(xdrs, attrstat_ptr)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +bool_t +xdr_create( + XDR * xdrs, + char * params_ptr) +{ + createargs * createargs_ptr; + diropres * diropres_ptr; + + + switch (xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + createargs_ptr = (createargs *) params_ptr; + return(xdr_diropargs(xdrs, &createargs_ptr->where) && + xdr_sattr(xdrs, &createargs_ptr->attributes)); + + case XDR_DECODE: + /* LINTED pointer cast */ + diropres_ptr = (diropres *) params_ptr; + return(xdr_diropres(xdrs, diropres_ptr)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +bool_t +xdr_remove( + XDR * xdrs, + char * params_ptr) +{ + diropargs * diropargs_ptr; + nfsstat * nfsstat_ptr; + + + switch (xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + diropargs_ptr = (diropargs *) params_ptr; + return(xdr_diropargs (xdrs, diropargs_ptr)); + + case XDR_DECODE: + /* LINTED pointer cast */ + nfsstat_ptr = (nfsstat *) params_ptr; + return(xdr_nfsstat(xdrs, nfsstat_ptr)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +bool_t +xdr_rename( + XDR * xdrs, + char * params_ptr) +{ + renameargs * renameargs_ptr; + nfsstat * nfsstat_ptr; + + + switch (xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + renameargs_ptr = (renameargs *) params_ptr; + return(xdr_diropargs(xdrs, &renameargs_ptr->from) && + xdr_diropargs(xdrs, &renameargs_ptr->to)); + + case XDR_DECODE: + /* LINTED pointer cast */ + nfsstat_ptr = (nfsstat *) params_ptr; + return(xdr_nfsstat(xdrs, nfsstat_ptr)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +bool_t +xdr_link( + XDR * xdrs, + char * params_ptr) +{ + linkargs * linkargs_ptr; + nfsstat * nfsstat_ptr; + + + switch (xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + linkargs_ptr = (linkargs *) params_ptr; + return(xdr_f_handle(xdrs, &linkargs_ptr->from) && + xdr_diropargs(xdrs, &linkargs_ptr->to)); + + case XDR_DECODE: + /* LINTED pointer cast */ + nfsstat_ptr = (nfsstat *) params_ptr; + return(xdr_nfsstat(xdrs, nfsstat_ptr)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +bool_t +xdr_symlink( + XDR * xdrs, + char * params_ptr) +{ + symlinkargs * symlinkargs_ptr; + nfsstat * nfsstat_ptr; + + + switch (xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + symlinkargs_ptr = (symlinkargs *) params_ptr; + return(xdr_diropargs(xdrs, &symlinkargs_ptr->from) && + xdr_path(xdrs, &symlinkargs_ptr->to) && + xdr_sattr(xdrs, &symlinkargs_ptr->attributes)); + + case XDR_DECODE: + /* LINTED pointer cast */ + nfsstat_ptr = (nfsstat *) params_ptr; + return(xdr_nfsstat(xdrs, nfsstat_ptr)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +bool_t +xdr_mkdir( + XDR * xdrs, + char * params_ptr) +{ + mkdirargs * mkdirargs_ptr; + diropres * diropres_ptr; + + + switch (xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + mkdirargs_ptr = (mkdirargs *) params_ptr; + return(xdr_diropargs(xdrs, &mkdirargs_ptr->where) && + xdr_sattr(xdrs, &mkdirargs_ptr->attributes)); + + case XDR_DECODE: + /* LINTED pointer cast */ + diropres_ptr = (diropres *) params_ptr; + return(xdr_diropres(xdrs, diropres_ptr)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +bool_t +xdr_rmdir( + XDR * xdrs, + char * params_ptr) +{ + diropargs * diropargs_ptr; + nfsstat * nfsstat_ptr; + + + switch (xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + diropargs_ptr = (diropargs *) params_ptr; + return(xdr_diropargs(xdrs, diropargs_ptr)); + + case XDR_DECODE: + /* LINTED pointer cast */ + nfsstat_ptr = (nfsstat *) params_ptr; + return(xdr_nfsstat(xdrs, nfsstat_ptr)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +bool_t +xdr_readdir( + XDR * xdrs, + char * params_ptr) +{ + readdirargs * readdirargs_ptr; + readdirres * readdirres_ptr; + entry * entry_ptr; + int n; /* entry ctr */ + + + switch (xdrs->x_op) { + case XDR_ENCODE: + /* LINTED pointer cast */ + readdirargs_ptr = (readdirargs *) params_ptr; + return(xdr_f_handle(xdrs, &readdirargs_ptr->dir) && + xdr_opaque(xdrs, (char *) readdirargs_ptr->cookie, + NFS_COOKIESIZE) && + xdr_u_int(xdrs, &readdirargs_ptr->count)); + + case XDR_DECODE: + /* LINTED pointer cast */ + readdirres_ptr = (readdirres *) params_ptr; + if (!xdr_nfsstat(xdrs, &readdirres_ptr->status)) { + return(FALSE); + } + if (readdirres_ptr->status != NFS_OK) { + return(TRUE); + } + + /* + * go thru the stream of entries until hit an invalid one + * or have gotten all the user asked for. + * + * max_entries is read to obtain a maximum. it is written + * to return how many entries were decoded. + */ + entry_ptr = readdirres_ptr->readdirres_u.reply.entries; + + n = 0; + while (n < readdirres_ptr->readdirres_u.reply.max_entries) { + if (!xdr_bool(xdrs, &entry_ptr->valid)) { + return(FALSE); + } + + if (!entry_ptr->valid) { + break; + } + + if (!xdr_u_int(xdrs, &entry_ptr->fileid)) { + return(FALSE); + } + + if (!xdr_uint16_t(xdrs, &entry_ptr->name_len)) { + return(FALSE); + } + + if (!xdr_opaque(xdrs, entry_ptr->name, entry_ptr->name_len)) { + return(FALSE); + } + + if (!xdr_opaque(xdrs, entry_ptr->cookie, NFS_COOKIESIZE)) { + return(FALSE); + } + + n++; + entry_ptr++; + } /* while extracting entries */ + + /* If we are at the user's data buffer limit, stop right now. */ + if (n == readdirres_ptr->readdirres_u.reply.max_entries) { + return(TRUE); + } + + /* Return how many entries were gotten for the dirlist */ + readdirres_ptr->readdirres_u.reply.max_entries = n; + + /* check the EOF flag for the dirlist */ + if(!xdr_bool(xdrs, &readdirres_ptr->readdirres_u.reply.eof)) { + return(FALSE); + } + + return(TRUE); + + default: + return(FALSE); + } /* switch on operation */ +} + +bool_t +xdr_statfs( + XDR * xdrs, + char * params_ptr) +{ + fhandle_t * fhandle_ptr; + statfsres * statfsres_ptr; + + + switch (xdrs->x_op) { + case XDR_ENCODE: + fhandle_ptr = (fhandle_t *) params_ptr; + return(xdr_f_handle(xdrs, fhandle_ptr)); + + case XDR_DECODE: + /* LINTED pointer cast */ + statfsres_ptr = (statfsres *) params_ptr; + if (!xdr_nfsstat(xdrs, &statfsres_ptr->status)) { + return(FALSE); + } + if (statfsres_ptr->status != NFS_OK) { + return(TRUE); + } + return(xdr_u_int(xdrs, &statfsres_ptr->statfsres_u.reply.tsize) && + xdr_u_int(xdrs, &statfsres_ptr->statfsres_u.reply.bsize) && + xdr_u_int(xdrs, &statfsres_ptr->statfsres_u.reply.blocks) && + xdr_u_int(xdrs, &statfsres_ptr->statfsres_u.reply.bfree) && + xdr_u_int(xdrs, &statfsres_ptr->statfsres_u.reply.bavail)); + + default: + return(FALSE); + } /* switch on operation */ +} + + +/* sfs_c_xdr.c */ diff --git a/TBBT/trace_play/sfs_3_ops.c b/TBBT/trace_play/sfs_3_ops.c new file mode 100644 index 0000000..19aef00 --- /dev/null +++ b/TBBT/trace_play/sfs_3_ops.c @@ -0,0 +1,2746 @@ +#ifndef lint +static char sfs_3_opsSid[] = "@(#)sfs_3_ops.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ---------------------- sfs_3_ops.c --------------------- + * + * RPC routines to implement the NFS protocol. + * + *.Local Routines + * int op_null(void) + * int op_getattr(void) + * int op_setattr(int) + * int op_lookup(void) + * int op_access(void) + * int op_readlink(void) + * int op_read(int) + * int op_write(int, int, stable_how) + * int op_create(void) + * int op_mkdir(void); + * int op_symlink(void); + * int op_mknod(void); + * int op_remove(void); + * int op_rmdir(void); + * int op_rename(void); + * int op_link(void); + * int op_readdir(void); + * int op_readdirplus(void); + * int op_fsstat(void); + * int op_fsinfo(void); + * int op_pathconf(void); + * int op_commit(void); + * + *.Revision_History + * 30-Jun-94 ChakChung Ng Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" + +/* + * -------------------- Local NFS ops function -------------------- + */ +static int op_null(void); +static int op_getattr(void); +static int op_setattr(int); +static int op_lookup(void); +static int op_access(void); +static int op_readlink(void); +static int op_read(int); +static int op_write(int, int, stable_how); +static int op_create(void); +static int op_mkdir(void); +static int op_symlink(void); +static int op_mknod(void); +static int op_remove(void); +static int op_rmdir(void); +static int op_rename(void); +static int op_link(void); +static int op_readdir(void); +static int op_readdirplus(void); +static int op_fsstat(void); +static int op_fsinfo(void); +static int op_pathconf(void); +static int op_commit(void); +static int op_nosys(void); +static char *nfs3_strerror(int); + + +/* + * -------------------- NFS ops vector -------------------- + */ +/* + * per operation information + */ +static sfs_op_type nfsv3_Ops[] = { + +/* name mix function op call no req req req results */ +/* pcnt class targ call pcnt cnt targ */ + + { "null", 0, op_null, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "getattr", 11, op_getattr, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "setattr", 1, op_setattr, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "root", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "lookup", 27, op_lookup, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readlink", 7, op_readlink, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "read", 18, op_read, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "wrcache", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "write", 9, op_write, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "create", 1, op_create, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "remove", 1, op_remove, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rename", 0, op_rename, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "link", 0, op_link, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "symlink", 0, op_symlink, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "mkdir", 0, op_mkdir, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rmdir", 0, op_rmdir, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdir", 2, op_readdir, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsstat", 1, op_fsstat, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "access", 7, op_access, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "commit", 5, op_commit, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsinfo", 1, op_fsinfo, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "mknod", 0, op_mknod, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "pathconf", 0, op_pathconf, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdirplus", 9, op_readdirplus, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "TOTAL", 100, 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }} +}; + +sfs_op_type *Ops; + +/* + * -------------------- RPC routines for NFS protocol -------------------- + */ + +void +init_ops(void) +{ + Ops = nfsv3_Ops; + nfs_version = NFS_V3; +} + +/* + * The routines below attempt to do over-the-wire operations. + * Each op tries to cause one or more of a particular + * NFS operation to go over the wire. OPs return the success + * of their NFS call(s). Each OP records how many calls it + * actually made in global data. + * + * An array of file information is kept for files existing in + * the test directory. File handles, attributes, names, etc + * are stored in this array. + * + */ + +/* + * Generic catch all for operations not covered by this protocol. + */ +static int +op_nosys(int i) +{ + RFS_ASSERT (dep_tab[i].flag == TO_BE_SENT); + dep_tab[i].flag = DONE; + Ops[TOTAL].results.bad_calls++; + if (i==min_index) + adjust_min_index (); + return(0); +} + +static struct biod_req * +get_biod_reqp (int dep_tab_index) +{ + static int index = 0; + int i; + + for (i=0; i<max_biod_reqs; i++, index = (index+1)%max_biod_reqs) { + if (biod_reqp[index].in_use == FALSE) { + biod_reqp[index].in_use = TRUE; + dep_tab[dep_tab_index].biod_index = index; + biod_req[index].dep_tab_index = dep_tab_index; + return (biod_reqp+index); + } + } +} + +static int +op_null(int i) +{ + sfs_op_type *op_ptr; /* per operation info */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + struct biod_req * reqp; + struct ladtime call_timeout; + + if (dep_tab[i].flag == SENT) + goto RECEIVE_REPLY; + + op_ptr = &Ops[NULLCALL]; + reqp = get_biod_reqp(i); + + call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec; + call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; + + ret = 0; + + /* make the call */ + sfs_gettime(&reqp->start); + reqp->xid = boid_clnt_call(NFS_client, NFS3PROC_NULL, xdr_void, (char *)0); + if (reqp->xid != 0) { + reqp->timeout = reqp->start; + ADDTIME (reqp->timeout, call_timeout); + num_out_reqs++; + dep_tab[i].flag = SENT; + } else + RFS_ASSERT (0); + + return 0; + +RECEIVE_REPLY: + rpc_stat = clnt_call(NFS_client, NFSPROC3_NULL, + xdr_void, (char *)0, xdr_void, (char *)0, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: null_op call RPC error %d\n", + sfs_Myname, rpc_stat); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_null */ + + +static int +op_getattr(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + GETATTR3args args; /* fh to do op on */ + GETATTR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[GETATTR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_GETATTR, + xdr_GETATTR3args, (char *) &args, + xdr_GETATTR3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: getattr call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: getattr call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_getattr */ + + +/* + * perform an RPC setattr operation. If 'truncate_size' is non-negative, + * truncate the file to that size. + */ +static int +op_setattr( + int truncate_size) +{ + sfs_op_type *op_ptr; /* per operation info */ + SETATTR3args args; + SETATTR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[SETATTR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + args.new_attributes.mode.set_it = TRUE; + args.new_attributes.mode.mode = (uint32_t) 0666; + args.new_attributes.uid.set_it = FALSE; + args.new_attributes.uid.uid = (uint32_t) -1; + args.new_attributes.gid.set_it = FALSE; + args.new_attributes.gid.gid = (uint32_t) -1; + args.new_attributes.size.set_it = FALSE; + args.new_attributes.size.size._p._u = (uint32_t) ~0; + args.new_attributes.size.size._p._l = (uint32_t) -1; + args.new_attributes.atime.set_it = TRUE; + args.new_attributes.atime.atime.seconds = Cur_time.esec; + args.new_attributes.atime.atime.nseconds = Cur_time.usec * 1000; + args.new_attributes.mtime.set_it = TRUE; + args.new_attributes.mtime.mtime.seconds = Cur_time.esec; + args.new_attributes.mtime.mtime.nseconds = Cur_time.usec * 1000; + args.guard.check = FALSE; + + /* handle file truncations */ + if (truncate_size >= 0) { + args.new_attributes.size.set_it = TRUE; + args.new_attributes.size.size._p._u = (uint32_t) 0; + if (truncate_size > Cur_file_ptr->attributes3.size._p._l) + args.new_attributes.size.size._p._l = (uint32_t) 0; + else + args.new_attributes.size.size._p._l = + (uint32_t) Cur_file_ptr->attributes3.size._p._l - + truncate_size; + } + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_SETATTR, + xdr_SETATTR3args, (char *) &args, + xdr_SETATTR3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_wcc.after.attr, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: setattr call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: setattr call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_setattr */ + + +static int +op_lookup(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + LOOKUP3args args; + LOOKUP3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[LOOKUP]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.what.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.what.name = Cur_filename; + (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP, + xdr_LOOKUP3args, (char *) &args, + xdr_LOOKUP3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &reply.resok.object, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + /* We do lookup Nonexistent and this is not an error */ + if (reply.status != NFS3ERR_NOENT || + Cur_file_ptr->state != Nonexistent) { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: lookup call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: lookup call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_lookup */ + + +static int +op_access(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + ACCESS3args args; + ACCESS3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[ACCESS]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.access = ACCESS3_MODIFY; + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_ACCESS, + xdr_ACCESS3args, (char *) &args, + xdr_ACCESS3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: access call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: access call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_access */ + + +static int +op_readlink(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + READLINK3args args; /* the args */ + READLINK3res reply; /* the reply */ + char sym_data[NFS_MAXPATHLEN]; + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[READLINK]; + ret = 0; + + /* set up the arguments */ + /* + * Note: this symlink may be bogus because SYMLINK does + * not return a symlink ... only a status. So unless we have + * done a LOOKUP on this guy, the symlink will probably be bad. + * If it is bad it shows up as a symlink error in the results. + */ + (void) memmove((char *) &args.symlink, + (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + /* Have lower layers fill in the data directly. */ + reply.resok.data = sym_data; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_READLINK, + xdr_READLINK3args, (char *) &args, + xdr_READLINK3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: READLINK on %s returned %s\n", + sfs_Myname, Cur_filename, sym_data); + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readlink call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readlink call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_readlink */ + + +/* + * perform an RPC read operation of length 'xfer_size' + */ +static int +op_read( + int xfer_size) +{ + sfs_op_type *op_ptr; /* per operation info */ + int cur_cnt; + int max_cnt; /* packet ctrs */ + char buf[DEFAULT_MAX_BUFSIZE];/* data buffer */ + READ3args args; + READ3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int size; + int j; + int ret; /* ret val == call success */ + + op_ptr = &Ops[READ]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.file, + (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + /* + * Don't allow a read of less than one block size + */ + if (xfer_size < Bytes_per_block) + xfer_size = Bytes_per_block; + + /* + * randomly choose an offset that is a multiple of the block size + * and constrained by making the transfer fit within the file + */ + args.offset._p._u = 0; + if (Cur_file_ptr->attributes3.size._p._l > xfer_size) + args.offset._p._l = Bytes_per_block * (sfs_random() % + (((Cur_file_ptr->attributes3.size._p._l - xfer_size) + / Bytes_per_block) + 1)); + else + args.offset._p._l = 0; + + /* Have lower layers fill in the data directly. */ + reply.resok.data.data_len = 0; + reply.resok.data.data_val = buf; + + /* first read the whole buffers, then the fragment */ + for (j = 0; j < 2; j++) { + + if (j == 0) { + size = Bytes_per_block; + max_cnt = xfer_size / Bytes_per_block; + } else { + /* 1KB - (Kb_per_block -1) KB fragment */ + size = xfer_size % Bytes_per_block; + max_cnt = 1; + } + if (size == 0) + continue; + + /* check our stats to see if this would overflow */ + if (!Timed_run) { + if (op_ptr->target_calls > 0) { + if ((op_ptr->results.good_calls + max_cnt) + > op_ptr->target_calls) { + max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; + } + } + } + + args.count = size; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "read: %d buffers\n", max_cnt); + (void) fflush(stderr); + } + + /* make the call(s) now */ + for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) { + + /* capture length for possible dump */ + Dump_length = fh_size(Cur_file_ptr); + + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_READ, + xdr_READ3args, (char *) &args, + xdr_READ3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + /* capture count and offset for possible dump */ + Dump_count = (rpc_stat == RPC_SUCCESS && reply.status == NFS3_OK) + ? reply.resok.data.data_len : 0; + Dump_offset = args.offset._p._l; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.file_attributes.attr, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + size = reply.resok.data.data_len; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: READ %s %d bytes\n", + sfs_Myname, Cur_filename, size); + (void) fflush(stderr); + } + args.offset._p._l += size; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: read call NFS error %s on file %d\n", + sfs_Myname, + nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: read call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + } /* for reading max_cnt packets */ + } /* for buffers and fragments */ + return(ret); + +} /* op_read */ + +char * +init_write_buffer( + void) +{ + uint32_t *bp; + static uint32_t write_buf[DEFAULT_MAX_BUFSIZE / sizeof(uint32_t)]; + uint32_t *be = write_buf + (sizeof(write_buf) / + sizeof(uint32_t)); + + if (write_buf[0] != (uint32_t)0xdeadbeef) { + for (bp = write_buf; bp < be; bp++) + *bp = (uint32_t)0xdeadbeef; + } + return (char *)write_buf; +} + + +/* + * Perform and RPC write operation of length 'xfer_size'. If 'append_flag' + * is true, then write the data to the end of the file. + * + * If the stab_flag is set to UNSTABLE we issue the requests and then + * issue a op_commit to sync the data. + */ +static int +op_write( + int xfer_size, + int append_flag, + stable_how stab_flag) +{ + sfs_op_type *op_ptr; /* per operation info */ + static char *buf = NULL; /* the data buffer */ + unsigned int size; /* size of data write */ + int cur_cnt; /* controls # NFS calls */ + int max_cnt; + WRITE3args args; + WRITE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int j; + int ret; /* ret val == call success */ + + /* + * For now we treat DATA_SYNC to be the same as FILE_SYNC. + */ + if (stab_flag == DATA_SYNC) + stab_flag = FILE_SYNC; + + /* + * Initialize write buffer to known value + */ + if (buf == NULL) { + buf = init_write_buffer(); + } + + op_ptr = &Ops[WRITE]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + args.offset._p._u = 0; + if (append_flag == 1) { + args.offset._p._l = Cur_file_ptr->attributes3.size._p._l; + } else { + /* + * randomly choose an offset that is a multiple of the block size + * and constrained by making the transfer fit within the file + */ + if (Cur_file_ptr->attributes3.size._p._l > xfer_size) { + args.offset._p._l = Bytes_per_block * (sfs_random() % + (((Cur_file_ptr->attributes3.size._p._l - xfer_size) + / Bytes_per_block) + 1)); + } else + args.offset._p._l = 0; + } + + /* stab_flag has to be set in op() in sfs_3_chd.c */ + args.stable = stab_flag; + + /* first write the whole buffers, then the fragment */ + for (j = 0; j < 2; j++) { + + if (j == 0) { + size = Bytes_per_block; + max_cnt = xfer_size / Bytes_per_block; + } else { + /* 1KB - (Kb_per_block - 1) KB fragment */ + size = xfer_size % Bytes_per_block; + max_cnt = 1; + } + if (size == 0) + continue; + + args.count = size; + args.data.data_len = size; + args.data.data_val = buf; + + /* check our stats to see if this would overflow */ + if (!Timed_run) { + if (op_ptr->target_calls > 0) { + if ((op_ptr->results.good_calls + max_cnt) + > op_ptr->target_calls) { + max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; + } + } + } + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "write: %d buffers\n", max_cnt); + (void) fflush(stderr); + } + + /* make the call(s) now */ + for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) { + + if (DEBUG_CHILD_RPC) { +(void) fprintf(stderr, "%s: WRITE %s offset %u count %lu stable %d\n", +sfs_Myname, Cur_filename, args.offset._p._l, args.count, args.stable); + (void) fflush(stderr); + } + + /* capture length for possible dump */ + Dump_length = fh_size(Cur_file_ptr); + + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_WRITE, + xdr_WRITE3args, (char *) &args, + xdr_WRITE3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + /* capture count and offset for possible dump */ + Dump_count = args.data.data_len; + Dump_offset = args.offset._p._l; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.file_wcc.after.attr, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + args.offset._p._l += size; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: WRITE %s %d bytes\n", + sfs_Myname, Cur_filename, size); + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: write call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: write call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + } /* for writing max_cnt packets */ + } /* for buffers and fragments */ + + /* + * If we have not gotten an error and we were asked for an async write + * send a commit operation. + */ + if (ret && stab_flag != FILE_SYNC) + ret += op_commit(); + + return(ret); + +} /* op_write */ + + +static int +op_create(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + CREATE3args args; + CREATE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[CREATE]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = Cur_filename; + args.how.mode = UNCHECKED; + args.how.createhow3_u.obj_attributes.mode.set_it = TRUE; + args.how.createhow3_u.obj_attributes.mode.mode = (NFSMODE_REG | 0666); + args.how.createhow3_u.obj_attributes.uid.set_it = TRUE; + args.how.createhow3_u.obj_attributes.uid.uid = Cur_uid; + args.how.createhow3_u.obj_attributes.gid.set_it = TRUE; + args.how.createhow3_u.obj_attributes.gid.gid = Cur_gid; + args.how.createhow3_u.obj_attributes.atime.set_it = TRUE; + args.how.createhow3_u.obj_attributes.atime.atime.seconds = Cur_time.esec; + args.how.createhow3_u.obj_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.how.createhow3_u.obj_attributes.mtime.set_it = TRUE; + args.how.createhow3_u.obj_attributes.mtime.mtime.seconds = Cur_time.esec; + args.how.createhow3_u.obj_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + args.how.createhow3_u.obj_attributes.size.set_it = TRUE; + args.how.createhow3_u.obj_attributes.size.size._p._u = (uint32_t) 0; + args.how.createhow3_u.obj_attributes.size.size._p._l = (uint32_t) 0; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_CREATE, + xdr_CREATE3args, (char *) &args, + xdr_CREATE3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &reply.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof(Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: create call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: create call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_create */ + + +static int +op_mkdir(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + MKDIR3args args; + MKDIR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[MKDIR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = Cur_filename; + args.attributes.mode.set_it = TRUE; + args.attributes.mode.mode = (NFSMODE_DIR | 0777); + args.attributes.uid.set_it = TRUE; + args.attributes.uid.uid = Cur_uid; + args.attributes.gid.set_it = TRUE; + args.attributes.gid.gid = Cur_gid; + args.attributes.size.set_it = TRUE; + args.attributes.size.size._p._u = 0; + args.attributes.size.size._p._l = 512; + args.attributes.atime.set_it = TRUE; + args.attributes.atime.atime.seconds = Cur_time.esec; + args.attributes.atime.atime.nseconds = Cur_time.usec * 1000; + args.attributes.mtime.set_it = TRUE; + args.attributes.mtime.mtime.seconds = Cur_time.esec; + args.attributes.mtime.mtime.nseconds = Cur_time.usec * 1000; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_MKDIR, + xdr_MKDIR3args, (char *) &args, + xdr_MKDIR3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Empty_dir; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &reply.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof(Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: mkdir call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: mkdir call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_mkdir */ + + +static int +op_symlink(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + sfs_fh_type *target_fileinfo_ptr; /* target file */ + SYMLINK3args args; + SYMLINK3res reply; /* the reply */ + char sym_data[NFS_MAXPATHLEN]; /* symlink data */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[SYMLINK]; + ret = 0; + + /* set up the arguments */ + target_fileinfo_ptr = randfh(SYMLINK, 0, 0, Exists, Sfs_non_io_file); + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = Cur_filename; + + (void) strcpy(sym_data, "./"); + (void) strcat(sym_data, target_fileinfo_ptr->file_name); + args.symlink.symlink_attributes.size.set_it = TRUE; + args.symlink.symlink_attributes.size.size._p._u = (uint32_t) 0; + args.symlink.symlink_attributes.size.size._p._l = strlen(sym_data); + args.symlink.symlink_data = sym_data; + + args.symlink.symlink_attributes.mode.set_it = TRUE; + args.symlink.symlink_attributes.mode.mode = (NFSMODE_LNK | 0777); + args.symlink.symlink_attributes.uid.set_it = TRUE; + args.symlink.symlink_attributes.uid.uid = Cur_uid; + args.symlink.symlink_attributes.gid.set_it = TRUE; + args.symlink.symlink_attributes.gid.gid = Cur_gid; + args.symlink.symlink_attributes.atime.set_it = TRUE; + args.symlink.symlink_attributes.atime.atime.seconds = Cur_time.esec; + args.symlink.symlink_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.symlink.symlink_attributes.mtime.set_it = TRUE; + args.symlink.symlink_attributes.mtime.mtime.seconds = Cur_time.esec; + args.symlink.symlink_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_SYMLINK, + xdr_SYMLINK3args, (char *) &args, + xdr_SYMLINK3res, (char *) &reply, + ((int)Current_test_phase < (int)Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + /* + * SYMLINK doesn't return a fh. If we try to access this symlink + * (eg, remove(), readlink()) before we do a lookup, we won't have + * a fh to use. So, we do a lookup call here. If it fails, we fill + * in what we can. + */ + Cur_file_ptr->state = Exists; + if (op_lookup() == 0) { + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + Cur_file_ptr->attributes3.type = NF3LNK; + Cur_file_ptr->attributes3.mode = (NFSMODE_LNK|0777); + Cur_file_ptr->attributes3.uid = Cur_uid; + Cur_file_ptr->attributes3.gid = Cur_gid; + Cur_file_ptr->attributes3.atime.seconds = Cur_time.esec; + Cur_file_ptr->attributes3.atime.nseconds = + Cur_time.usec * 1000; + Cur_file_ptr->attributes3.mtime = + Cur_file_ptr->attributes3.atime; + } else + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: symlink call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: symlink call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_symlink */ + + +static int +op_mknod(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + MKNOD3args args; + MKNOD3res reply; + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[MKNOD]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = Cur_filename; + args.what.type = NF3FIFO; + args.what.mknoddata3_u.pipe_attributes.mode.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.mode.mode = (NFSMODE_FIFO | 0777); + args.what.mknoddata3_u.pipe_attributes.uid.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.uid.uid = Cur_uid; + args.what.mknoddata3_u.pipe_attributes.gid.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.gid.gid = Cur_gid; + args.what.mknoddata3_u.pipe_attributes.size.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.size.size._p._u = (uint32_t) 0; + args.what.mknoddata3_u.pipe_attributes.size.size._p._l = + (uint32_t) 512; + args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.atime.atime.seconds = + Cur_time.esec; + args.what.mknoddata3_u.pipe_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.mtime.mtime.seconds = + Cur_time.esec; + args.what.mknoddata3_u.pipe_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_MKNOD, + xdr_MKNOD3args, (char *) &args, + xdr_MKNOD3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &reply.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof(Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: mknod call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: mknod call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_mknod */ + + +static int +op_remove(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + REMOVE3args args; + REMOVE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[REMOVE]; + ret = 0; + + /* set up the arguments */ + args.object.name = Cur_filename; + (void) memmove((char *) &args.object.dir,(char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_REMOVE, + xdr_REMOVE3args, (char *) &args, + xdr_REMOVE3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Nonexistent; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: remove call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: remove call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_remove */ + + +static int +op_rmdir(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + RMDIR3args args; + RMDIR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[RMDIR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.object.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.object.name = Cur_file_ptr->file_name; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_RMDIR, + xdr_RMDIR3args, (char *) &args, + xdr_RMDIR3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Nonexistent; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: rmdir call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: rmdir call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_rmdir */ + + +static int +op_rename(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + sfs_fh_type *target_fileinfo_ptr; /* target name */ + RENAME3args args; + RENAME3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[RENAME]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.from.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + (void) memmove((char *) &args.to.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + + target_fileinfo_ptr = randfh(RENAME, 0, 0, Nonexistent, + Sfs_non_io_file); + + args.from.name = Cur_file_ptr->file_name; + (void) sprintf(target_fileinfo_ptr->file_name, Filespec, + target_fileinfo_ptr->unique_num); + args.to.name = target_fileinfo_ptr->file_name; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME, + xdr_RENAME3args, (char *) &args, + xdr_RENAME3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + target_fileinfo_ptr->state = Exists; + (void) memmove((char *) &target_fileinfo_ptr->fh3, + (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + target_fileinfo_ptr->attributes3 = Cur_file_ptr->attributes3; + target_fileinfo_ptr->size = fh_size(Cur_file_ptr); + Cur_file_ptr->state = Nonexistent; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: rename call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: rename call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_rename */ + + +static int +op_link(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + sfs_fh_type *target_fileinfo_ptr; /* target */ + LINK3args args; + LINK3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[LINK]; + ret = 0; + + /* set up the arguments */ + target_fileinfo_ptr = randfh(LINK, 0, 0, Exists, Sfs_non_io_file); + (void) memmove((char *) &args.file, (char *) &target_fileinfo_ptr->fh3, + sizeof (nfs_fh3)); + (void) memmove((char *) &args.link.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.link.name = Cur_filename; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_LINK, + xdr_LINK3args, (char *) &args, + xdr_LINK3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &target_fileinfo_ptr->fh3, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + target_fileinfo_ptr->attributes3.nlink++; + Cur_file_ptr->attributes3 = target_fileinfo_ptr->attributes3; + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: link call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: link call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_link */ + + +static int +op_readdir(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + READDIR3args args; + READDIR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int i; + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + bool_t hit_eof; + /* array of entries */ + entry3 entry_stream[SFS_MAXDIRENTS]; + entry3 *entries; /* ptr to the dir entry */ + + op_ptr = &Ops[READDIR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.cookie._p._l = args.cookie._p._u = (uint32_t) 0; + (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE); + args.count = DEFAULT_MAX_BUFSIZE; + + /* Have lower layers fill in the data directly. */ + (void) memset((char *) &reply, '\0', sizeof (reply)); + (void) memset((char *) entry_stream, '\0', + sizeof (entry3) * SFS_MAXDIRENTS); + reply.resok.count = SFS_MAXDIRENTS; + reply.resok.reply.entries = entry_stream; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_READDIR, + xdr_READDIR3args, (char *) &args, + xdr_READDIR3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + + if (DEBUG_CHILD_RPC) { + hit_eof = reply.resok.reply.eof; + entries = reply.resok.reply.entries; + for (i = 0; i < reply.resok.count; i++) { + (void) fprintf(stderr, "%s:READDIR (eof=%d) entry %s\n", + sfs_Myname, hit_eof, entries[i].name); + } + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readdir call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readdir call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_readdir */ + + +static int +op_readdirplus(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + READDIRPLUS3args args; + READDIRPLUS3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int i; + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + bool_t hit_eof; + /* array of entries */ + entryplus3 entry_stream[SFS_MAXDIRENTS]; + entryplus3 *entries; + + op_ptr = &Ops[READDIRPLUS]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.cookie._p._l = args.cookie._p._u = (uint32_t) 0; + (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE); + (void) memset((char *) entry_stream, '\0', + sizeof (entryplus3) * SFS_MAXDIRENTS); + args.dircount = DEFAULT_MAX_BUFSIZE; + args.maxcount = DEFAULT_MAX_BUFSIZE; + + /* Have lower layers fill in the data directly. */ + reply.resok.count = SFS_MAXDIRENTS; + reply.resok.reply.entries = entry_stream; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_READDIRPLUS, + xdr_READDIRPLUS3args, (char *) &args, + xdr_READDIRPLUS3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + + if (DEBUG_CHILD_RPC) { + hit_eof = reply.resok.reply.eof; + entries = reply.resok.reply.entries; + for (i = 0; i < reply.resok.count; i++) { + (void) fprintf(stderr, "%s:READDIR (eof=%d) entry %s\n", + sfs_Myname, hit_eof, entries[i].name); + } + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readdir call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readdir call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_readdirplus */ + + +static int +op_fsstat(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + FSSTAT3args args; + FSSTAT3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[FSSTAT]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.fsroot, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_FSSTAT, + xdr_FSSTAT3args, (char *) &args, + xdr_FSSTAT3args, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: fsstat call RPC error %d\n", + sfs_Myname, rpc_stat); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_fsstat */ + + +static int +op_fsinfo(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + FSINFO3args args; + FSINFO3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[FSINFO]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.fsroot, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_FSINFO, + xdr_FSINFO3args, (char *) &args, + xdr_FSINFO3args, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: fsinfo call RPC error %d\n", + sfs_Myname, rpc_stat); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_fsinfo */ + + +static int +op_pathconf(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + PATHCONF3args args; + PATHCONF3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[PATHCONF]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_PATHCONF, + xdr_PATHCONF3args, (char *) &args, + xdr_PATHCONF3args, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: pathconf call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_pathconf */ + + +static int +op_commit(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + int32_t size; /* size of data write */ + COMMIT3args args; + COMMIT3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[COMMIT]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + args.offset._p._u = args.offset._p._l = (uint32_t) 0; + args.count = Cur_file_ptr->attributes3.size._p._l; + size = args.count; + + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_COMMIT, + xdr_COMMIT3args, (char *) &args, + xdr_COMMIT3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.file_wcc.after.attr, + sizeof(Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: WRITE %s %ld bytes\n", + sfs_Myname, Cur_filename, size); + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: write call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: write call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + + return(ret); + +} /* op_commit */ + + +#define LAD_RETRIABLE(stat) (((stat) == RPC_TIMEDOUT) || ((stat) == RPC_CANTDECODERES)) + +/* + * Reliably lookup a file in the current directory + * Return: + * -1 RPC error + * 1 File doesn't exist + * 0 File exists + */ +int +lad_lookup(sfs_fh_type *file_ptr, char *name) +{ + LOOKUP3args args; + LOOKUP3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_lookup: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + (void) memmove((char *) &args.what.dir, (char *) &file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.what.name = name; + (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3)); + + /* make the call */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP, + xdr_LOOKUP3args, (char *) &args, + xdr_LOOKUP3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_lookup(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + } + + if (reply.status == NFS3ERR_NOENT) { + return(1); + } + + if (reply.status != NFS3_OK) { + (void) fprintf(stderr, "lad_lookup(%s) NFS call failed : %s\n", + name, nfs3_strerror(reply.status)); + return(-1); + } + + file_ptr->state = Exists; + (void) memmove((char *) &file_ptr->fh3, + (char *) &reply.resok.object, + sizeof (nfs_fh3)); + (void) strcpy(file_ptr->file_name, name); + (void) memmove((char *) &file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof (file_ptr->attributes3)); + file_ptr->size = fh_size(file_ptr); + return(0); +} + +/* + * Reliably remove a file in the current directory + */ +int +lad_remove(sfs_fh_type *file_ptr, char *name) +{ + REMOVE3args args; + REMOVE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + /* + * This function presumes that the file name does exist + */ + if (file_ptr->attributes3.type == NF3DIR) + return (lad_rmdir(file_ptr, name)); + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_remove: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + args.object.name = name; + (void) memmove((char *) &args.object.dir, (char *) &file_ptr->dir->fh3, + sizeof(nfs_fh3)); + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_REMOVE, + xdr_REMOVE3args, (char *) &args, + xdr_REMOVE3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_remove(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (reply.status != NFS3_OK) { + if (reply.status != NFS3ERR_NOENT || !retried) { + (void) fprintf(stderr, "lad_remove(%s) NFS call failed : %s\n", + name, nfs3_strerror(reply.status)); + return(-1); + } + } + + file_ptr->state = Nonexistent; + + return(0); +} + +/* + * Reliably remove a directory in the current directory + */ +int +lad_rmdir(sfs_fh_type *file_ptr, char *name) +{ + RMDIR3args args; + RMDIR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + /* + * This function presumes that the file name does exist and is empty + */ + if (file_ptr->attributes3.type != NF3DIR) + return (lad_remove(file_ptr, name)); + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_rmdir: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + args.object.name = name; + (void) memmove((char *) &args.object.dir, (char *) &file_ptr->dir->fh3, + sizeof (nfs_fh3)); + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_RMDIR, + xdr_RMDIR3args, (char *) &args, + xdr_RMDIR3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_rmdir(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (reply.status != NFS3_OK) { + if (reply.status != NFS3ERR_NOENT || !retried) { + (void) fprintf(stderr, "lad_rmdir(%s) NFS call failed : %s\n", + name, nfs3_strerror(reply.status)); + return(-1); + } + } + + file_ptr->state = Nonexistent; + + return(0); +} + +/* + * Reliably create a symlink in the current directory + */ +int +lad_symlink(sfs_fh_type *file_ptr, char *target, char *name) +{ + SYMLINK3args args; + SYMLINK3res reply; /* the reply */ + char sym_data[NFS_MAXPATHLEN]; /* symlink data */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + /* + * This function presumes that the file name does not already exist + */ + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_symlink: %lx[%lx] %s -> %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name, target); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = name; + + (void) strcpy(sym_data, "./"); + (void) strcat(sym_data, target); + args.symlink.symlink_attributes.size.set_it = TRUE; + args.symlink.symlink_attributes.size.size._p._u = (uint32_t) 0; + args.symlink.symlink_attributes.size.size._p._l = strlen(sym_data); + args.symlink.symlink_data = sym_data; + + args.symlink.symlink_attributes.mode.set_it = TRUE; + args.symlink.symlink_attributes.mode.mode = (NFSMODE_LNK | 0777); + args.symlink.symlink_attributes.uid.set_it = TRUE; + args.symlink.symlink_attributes.uid.uid = Cur_uid; + args.symlink.symlink_attributes.gid.set_it = TRUE; + args.symlink.symlink_attributes.gid.gid = Cur_gid; + args.symlink.symlink_attributes.atime.set_it = TRUE; + args.symlink.symlink_attributes.atime.atime.seconds = Cur_time.esec; + args.symlink.symlink_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.symlink.symlink_attributes.mtime.set_it = TRUE; + args.symlink.symlink_attributes.mtime.mtime.seconds = Cur_time.esec; + args.symlink.symlink_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_SYMLINK, + xdr_SYMLINK3args, (char *) &args, + xdr_SYMLINK3res, (char *) &reply, + Nfs_timers[Init]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_symlink(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (reply.status != NFS3_OK) { + if (reply.status != NFS3ERR_EXIST || !retried) { + (void) fprintf(stderr, "lad_symlink(%s, %s) NFS call failed : %s\n", + target, name, nfs3_strerror(reply.status)); + return(-1); + } + } + + /* + * SYMLINK may not return a fh. If we try to + * access this symlink (eg, remove(), readlink()) + * before we do a lookup, we won't have a fh to use. + * So, we do a lookup call here. + * If it fails, we fill in what we can. + */ + return (lad_lookup(file_ptr, name)); +} + +/* + * Reliably create a directory in the current directory + */ +int +lad_mkdir(sfs_fh_type *file_ptr, char *name) +{ + MKDIR3args args; + MKDIR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + /* + * This function presumes that the file name does not already exist + */ + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_mkdir: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = name; + args.attributes.mode.set_it = TRUE; + args.attributes.mode.mode = (NFSMODE_DIR | 0777); + args.attributes.uid.set_it = TRUE; + args.attributes.uid.uid = Cur_uid; + args.attributes.gid.set_it = TRUE; + args.attributes.gid.gid = Cur_gid; + args.attributes.size.set_it = TRUE; + args.attributes.size.size._p._u = 0; + args.attributes.size.size._p._l = 512; + args.attributes.atime.set_it = TRUE; + args.attributes.atime.atime.seconds = Cur_time.esec; + args.attributes.atime.atime.nseconds = Cur_time.usec * 1000; + args.attributes.mtime.set_it = TRUE; + args.attributes.mtime.mtime.seconds = Cur_time.esec; + args.attributes.mtime.mtime.nseconds = Cur_time.usec * 1000; + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_MKDIR, + xdr_MKDIR3args, (char *) &args, + xdr_MKDIR3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_mkdir(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (!retried && reply.status == NFS3ERR_EXIST) + return(1); + + if (reply.status != NFS3_OK) { + if (reply.status != NFS3ERR_EXIST || !retried) { + (void) fprintf(stderr, "lad_mkdir(%s) NFS call failed : %s\n", + name, nfs3_strerror(reply.status)); + return(-1); + } + /* + * If the first mkdir suceeded but the reply as dropped and + * was retransmitted, we still need to lookup the attributes + */ + if (lad_lookup(file_ptr, name)) + return (-1); + } else { + (void) memmove((char *) &file_ptr->fh3, + (char *) &reply.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(file_ptr->file_name, name); + (void) memmove((char *) &file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof(file_ptr->attributes3)); + file_ptr->size = fh_size(file_ptr); + } + file_ptr->state = Empty_dir; + + return(0); +} + +/* + * Reliably commit a file + */ +static int +lad_commit(sfs_fh_type *file_ptr) +{ + COMMIT3args args; + COMMIT3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_commit: %lx[%lx]\n", + sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir); + (void) fflush(stderr); + } + + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &file_ptr->fh3, + sizeof (nfs_fh3)); + args.offset._p._u = args.offset._p._l = (uint32_t) 0; + args.count = file_ptr->attributes3.size._p._l; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_COMMIT, + xdr_COMMIT3args, (char *) &args, + xdr_COMMIT3res, (char *) &reply, + Nfs_timers[Init]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_commit() RPC call failed : %s\n", + clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + } + + return(0); +} + +/* + * Reliably write a file in the current directory + */ +int +lad_write(sfs_fh_type *file_ptr, int32_t offset, int32_t length) +{ + static char *buf = NULL; /* the data buffer */ + int32_t size; /* size of data write */ + int32_t cur_cnt; + WRITE3args args; + WRITE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int async = 1; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_write: %lx[%lx] %ld %ld\n", + sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, offset, length); + (void) fflush(stderr); + } + + /* + * This function presumes that the file name does exist + * Initialize write buffer to known value + */ + if (buf == NULL) { + buf = init_write_buffer(); + } + + /* + * If a short file write don't bother with the commit, just write sync. + */ + if (length <= Bytes_per_block) + async = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &file_ptr->fh3, + sizeof (nfs_fh3)); + args.offset._p._u = 0; + args.offset._p._l = offset; + if (async) + args.stable = UNSTABLE; + else + args.stable = FILE_SYNC; + + size = Bytes_per_block; + for (cur_cnt = 0; cur_cnt < length; cur_cnt += size) { + if ((cur_cnt + size) > length) + size = length - cur_cnt; + + if (size == 0) + break; + + args.count = size; + args.data.data_len = size; + args.data.data_val = buf; + + /* make the call now */ + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_WRITE, + xdr_WRITE3args, (char *) &args, + xdr_WRITE3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_write() RPC call failed : %s\n", + clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + } + if (reply.status != NFS3_OK) { + (void) fprintf(stderr, "lad_write() NFS call failed : %s\n", + nfs3_strerror(reply.status)); + return(-1); + } + file_ptr->state = Exists; + (void) memmove((char *) &file_ptr->attributes3, + (char *) &reply.resok.file_wcc.after.attr, + sizeof (file_ptr->attributes3)); + file_ptr->size = fh_size(file_ptr); + + args.offset._p._l += size; + } + + if (async) + (void) lad_commit(file_ptr); + return(0); +} + +/* + * Reliably create a file in the current directory + */ +int +lad_create(sfs_fh_type *file_ptr, char *name) +{ + CREATE3args args; + CREATE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + /* + * This function presumes that the file name does not already exist + */ + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_create: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = name; + args.how.mode = UNCHECKED; + args.how.createhow3_u.obj_attributes.mode.set_it = TRUE; + args.how.createhow3_u.obj_attributes.mode.mode = (NFSMODE_REG | 0666); + args.how.createhow3_u.obj_attributes.uid.set_it = TRUE; + args.how.createhow3_u.obj_attributes.uid.uid = Cur_uid; + args.how.createhow3_u.obj_attributes.gid.set_it = TRUE; + args.how.createhow3_u.obj_attributes.gid.gid = Cur_gid; + args.how.createhow3_u.obj_attributes.atime.set_it = TRUE; + args.how.createhow3_u.obj_attributes.atime.atime.seconds = + Cur_time.esec; + args.how.createhow3_u.obj_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.how.createhow3_u.obj_attributes.mtime.set_it = TRUE; + args.how.createhow3_u.obj_attributes.mtime.mtime.seconds = + Cur_time.esec; + args.how.createhow3_u.obj_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + args.how.createhow3_u.obj_attributes.size.set_it = TRUE; + args.how.createhow3_u.obj_attributes.size.size._p._u = + (uint32_t) 0; + args.how.createhow3_u.obj_attributes.size.size._p._l = + (uint32_t) 0; + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_CREATE, + xdr_CREATE3args, (char *) &args, + xdr_CREATE3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_create(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (!retried && reply.status == NFS3ERR_EXIST) { + return(1); + } + + if (reply.status != NFS3_OK) { + if (reply.status != NFS3ERR_EXIST || !retried) { + (void) fprintf(stderr, "lad_create(%s) NFS call failed : %s\n", + name, nfs3_strerror(reply.status)); + return(-1); + } + /* + * If the first create suceeded but the reply as dropped and + * was retransmitted, we still need to lookup the attributes + */ + if (lad_lookup(file_ptr, name)) + return (-1); + } else { + (void) memmove((char *) &file_ptr->fh3, + (char *) &reply.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(file_ptr->file_name, name); + (void) memmove((char *) &file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof(file_ptr->attributes3)); + file_ptr->size = fh_size(file_ptr); + } + + file_ptr->state = Exists; + /* + * Directories are created as Empty_dir, when a file is created it + * becomes an Exists. + */ + file_ptr->dir->state = Exists; + + return(0); +} + +/* + * Reliably set the size of a file in the current directory + */ +int +lad_truncate(sfs_fh_type *file_ptr, int32_t size) +{ + SETATTR3args args; + SETATTR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + + /* + * This function presumes that the file name already exists + */ + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_truncate: %lx[%lx] %ld\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, size); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* + * set up the arguments + * Set the mode and times as well + */ + (void) memmove((char *) &args.object, (char *) &file_ptr->fh3, + sizeof (nfs_fh3)); + args.new_attributes.mode.set_it = TRUE; + args.new_attributes.mode.mode = (uint32_t) 0666; + args.new_attributes.uid.set_it = FALSE; + args.new_attributes.uid.uid = (uint32_t) -1; + args.new_attributes.gid.set_it = FALSE; + args.new_attributes.gid.gid = (uint32_t) -1; + args.new_attributes.size.set_it = TRUE; + args.new_attributes.size.size._p._u = 0; + args.new_attributes.size.size._p._l = size; + args.new_attributes.atime.set_it = TRUE; + args.new_attributes.atime.atime.seconds = Cur_time.esec; + args.new_attributes.atime.atime.nseconds = Cur_time.usec * 1000; + args.new_attributes.mtime.set_it = TRUE; + args.new_attributes.mtime.mtime.seconds = Cur_time.esec; + args.new_attributes.mtime.mtime.nseconds = Cur_time.usec * 1000; + args.guard.check = FALSE; + + /* make the call */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_SETATTR, + xdr_SETATTR3args, (char *) &args, + xdr_SETATTR3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void)fprintf(stderr, + "lad_truncate(%ld) RPC call failed : %s\n", + size, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + } + + if (reply.status != NFS3_OK) { + (void) fprintf(stderr, "lad_truncate(%ld) NFS call failed : %s\n", + size, nfs3_strerror(reply.status)); + return(-1); + } + (void) memmove(&file_ptr->attributes3, + &reply.resok.obj_wcc.after.attr, + sizeof (file_ptr->attributes3)); + file_ptr->size = fh_size(file_ptr); + + return(0); +} + +static char * +nfs3_strerror(int status) +{ + static char str[40]; + switch (status) { + case NFS3_OK: + (void) strcpy(str, "no error"); + break; + case NFS3ERR_PERM: + (void) strcpy(str, "Not owner"); + break; + case NFS3ERR_NOENT: + (void) strcpy(str, "No such file or directory"); + break; + case NFS3ERR_IO: + (void) strcpy(str, "I/O error"); + break; + case NFS3ERR_NXIO: + (void) strcpy(str, "No such device or address"); + break; + case NFS3ERR_ACCES: + (void) strcpy(str, "Permission denied"); + break; + case NFS3ERR_EXIST: + (void) strcpy(str, "File exists"); + break; + case NFS3ERR_XDEV: + (void) strcpy(str, "Cross-device link"); + break; + case NFS3ERR_NODEV: + (void) strcpy(str, "No such device"); + break; + case NFS3ERR_NOTDIR: + (void) strcpy(str, "Not a directory"); + break; + case NFS3ERR_ISDIR: + (void) strcpy(str, "Is a directory"); + break; + case NFS3ERR_INVAL: + (void) strcpy(str, "Invalid argument"); + break; + case NFS3ERR_FBIG: + (void) strcpy(str, "File too large"); + break; + case NFS3ERR_NOSPC: + (void) strcpy(str, "No space left on device"); + break; + case NFS3ERR_ROFS: + (void) strcpy(str, "Read-only file system"); + break; + case NFS3ERR_MLINK: + (void) strcpy(str, "Too many links"); + break; + case NFS3ERR_NAMETOOLONG: + (void) strcpy(str, "File name too long"); + break; + case NFS3ERR_NOTEMPTY: + (void) strcpy(str, "Directory not empty"); + break; + case NFS3ERR_DQUOT: + (void) strcpy(str, "Disc quota exceeded"); + break; + case NFS3ERR_STALE: + (void) strcpy(str, "Stale NFS file handle"); + break; + case NFS3ERR_REMOTE: + (void) strcpy(str, "Object is remote"); + break; + case NFS3ERR_BADHANDLE: + (void) strcpy(str, "Bad file handle"); + break; + case NFS3ERR_NOT_SYNC: + (void) strcpy(str, "Not sync write"); + break; + case NFS3ERR_BAD_COOKIE: + (void) strcpy(str, "Bad cookie"); + break; + case NFS3ERR_NOTSUPP: + (void) strcpy(str, "Operation not supported"); + break; + case NFS3ERR_TOOSMALL: + (void) strcpy(str, "Value too small"); + break; + case NFS3ERR_SERVERFAULT: + (void) strcpy(str, "Server fault"); + break; + case NFS3ERR_BADTYPE: + (void) strcpy(str, "Bad type"); + break; + case NFS3ERR_JUKEBOX: + (void) strcpy(str, "Jukebox"); + break; + case NFS3ERR_RFS_TIMEOUT: + (void) strcpy(str, "RFS timeout"); + default: + (void) sprintf(str, "Unknown status %d", status); + break; + } + return (str); +} + +/* sfs_3_ops.c */ diff --git a/TBBT/trace_play/sfs_3_ops.c.org b/TBBT/trace_play/sfs_3_ops.c.org new file mode 100644 index 0000000..57cbb57 --- /dev/null +++ b/TBBT/trace_play/sfs_3_ops.c.org @@ -0,0 +1,2701 @@ +#ifndef lint +static char sfs_3_opsSid[] = "@(#)sfs_3_ops.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ---------------------- sfs_3_ops.c --------------------- + * + * RPC routines to implement the NFS protocol. + * + *.Local Routines + * int op_null(void) + * int op_getattr(void) + * int op_setattr(int) + * int op_lookup(void) + * int op_access(void) + * int op_readlink(void) + * int op_read(int) + * int op_write(int, int, stable_how) + * int op_create(void) + * int op_mkdir(void); + * int op_symlink(void); + * int op_mknod(void); + * int op_remove(void); + * int op_rmdir(void); + * int op_rename(void); + * int op_link(void); + * int op_readdir(void); + * int op_readdirplus(void); + * int op_fsstat(void); + * int op_fsinfo(void); + * int op_pathconf(void); + * int op_commit(void); + * + *.Revision_History + * 30-Jun-94 ChakChung Ng Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" + +/* + * -------------------- Local NFS ops function -------------------- + */ +static int op_null(void); +static int op_getattr(void); +static int op_setattr(int); +static int op_lookup(void); +static int op_access(void); +static int op_readlink(void); +static int op_read(int); +static int op_write(int, int, stable_how); +static int op_create(void); +static int op_mkdir(void); +static int op_symlink(void); +static int op_mknod(void); +static int op_remove(void); +static int op_rmdir(void); +static int op_rename(void); +static int op_link(void); +static int op_readdir(void); +static int op_readdirplus(void); +static int op_fsstat(void); +static int op_fsinfo(void); +static int op_pathconf(void); +static int op_commit(void); +static int op_nosys(void); +static char *nfs3_strerror(int); + + +/* + * -------------------- NFS ops vector -------------------- + */ +/* + * per operation information + */ +static sfs_op_type nfsv3_Ops[] = { + +/* name mix function op call no req req req results */ +/* pcnt class targ call pcnt cnt targ */ + + { "null", 0, op_null, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "getattr", 11, op_getattr, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "setattr", 1, op_setattr, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "root", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "lookup", 27, op_lookup, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readlink", 7, op_readlink, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "read", 18, op_read, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "wrcache", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "write", 9, op_write, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "create", 1, op_create, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "remove", 1, op_remove, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rename", 0, op_rename, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "link", 0, op_link, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "symlink", 0, op_symlink, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "mkdir", 0, op_mkdir, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "rmdir", 0, op_rmdir, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdir", 2, op_readdir, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsstat", 1, op_fsstat, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "access", 7, op_access, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "commit", 5, op_commit, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "fsinfo", 1, op_fsinfo, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "mknod", 0, op_mknod, Write, 0, 0, 0.0, 0, 0, { 0, }}, + { "pathconf", 0, op_pathconf, Lookup, 0, 0, 0.0, 0, 0, { 0, }}, + { "readdirplus", 9, op_readdirplus, Read, 0, 0, 0.0, 0, 0, { 0, }}, + { "TOTAL", 100, 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }} +}; + +sfs_op_type *Ops; + +/* + * -------------------- RPC routines for NFS protocol -------------------- + */ + +void +init_ops(void) +{ + Ops = nfsv3_Ops; + nfs_version = NFS_V3; +} + +/* + * The routines below attempt to do over-the-wire operations. + * Each op tries to cause one or more of a particular + * NFS operation to go over the wire. OPs return the success + * of their NFS call(s). Each OP records how many calls it + * actually made in global data. + * + * An array of file information is kept for files existing in + * the test directory. File handles, attributes, names, etc + * are stored in this array. + * + */ + +/* + * Generic catch all for operations not covered by this protocol. + */ +static int +op_nosys(void) +{ + Ops[TOTAL].results.bad_calls++; + return(0); +} + +static int +op_null(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[NULLCALL]; + ret = 0; + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_NULL, + xdr_void, (char *)0, xdr_void, (char *)0, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: null_op call RPC error %d\n", + sfs_Myname, rpc_stat); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_null */ + + +static int +op_getattr(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + GETATTR3args args; /* fh to do op on */ + GETATTR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[GETATTR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_GETATTR, + xdr_GETATTR3args, (char *) &args, + xdr_GETATTR3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: getattr call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: getattr call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_getattr */ + + +/* + * perform an RPC setattr operation. If 'truncate_size' is non-negative, + * truncate the file to that size. + */ +static int +op_setattr( + int truncate_size) +{ + sfs_op_type *op_ptr; /* per operation info */ + SETATTR3args args; + SETATTR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[SETATTR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + args.new_attributes.mode.set_it = TRUE; + args.new_attributes.mode.mode = (uint32_t) 0666; + args.new_attributes.uid.set_it = FALSE; + args.new_attributes.uid.uid = (uint32_t) -1; + args.new_attributes.gid.set_it = FALSE; + args.new_attributes.gid.gid = (uint32_t) -1; + args.new_attributes.size.set_it = FALSE; + args.new_attributes.size.size._p._u = (uint32_t) ~0; + args.new_attributes.size.size._p._l = (uint32_t) -1; + args.new_attributes.atime.set_it = TRUE; + args.new_attributes.atime.atime.seconds = Cur_time.esec; + args.new_attributes.atime.atime.nseconds = Cur_time.usec * 1000; + args.new_attributes.mtime.set_it = TRUE; + args.new_attributes.mtime.mtime.seconds = Cur_time.esec; + args.new_attributes.mtime.mtime.nseconds = Cur_time.usec * 1000; + args.guard.check = FALSE; + + /* handle file truncations */ + if (truncate_size >= 0) { + args.new_attributes.size.set_it = TRUE; + args.new_attributes.size.size._p._u = (uint32_t) 0; + if (truncate_size > Cur_file_ptr->attributes3.size._p._l) + args.new_attributes.size.size._p._l = (uint32_t) 0; + else + args.new_attributes.size.size._p._l = + (uint32_t) Cur_file_ptr->attributes3.size._p._l - + truncate_size; + } + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_SETATTR, + xdr_SETATTR3args, (char *) &args, + xdr_SETATTR3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_wcc.after.attr, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: setattr call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: setattr call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_setattr */ + + +static int +op_lookup(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + LOOKUP3args args; + LOOKUP3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[LOOKUP]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.what.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.what.name = Cur_filename; + (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP, + xdr_LOOKUP3args, (char *) &args, + xdr_LOOKUP3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &reply.resok.object, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + /* We do lookup Nonexistent and this is not an error */ + if (reply.status != NFS3ERR_NOENT || + Cur_file_ptr->state != Nonexistent) { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: lookup call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: lookup call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_lookup */ + + +static int +op_access(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + ACCESS3args args; + ACCESS3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[ACCESS]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.access = ACCESS3_MODIFY; + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_ACCESS, + xdr_ACCESS3args, (char *) &args, + xdr_ACCESS3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: access call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: access call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_access */ + + +static int +op_readlink(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + READLINK3args args; /* the args */ + READLINK3res reply; /* the reply */ + char sym_data[NFS_MAXPATHLEN]; + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[READLINK]; + ret = 0; + + /* set up the arguments */ + /* + * Note: this symlink may be bogus because SYMLINK does + * not return a symlink ... only a status. So unless we have + * done a LOOKUP on this guy, the symlink will probably be bad. + * If it is bad it shows up as a symlink error in the results. + */ + (void) memmove((char *) &args.symlink, + (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + /* Have lower layers fill in the data directly. */ + reply.resok.data = sym_data; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_READLINK, + xdr_READLINK3args, (char *) &args, + xdr_READLINK3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: READLINK on %s returned %s\n", + sfs_Myname, Cur_filename, sym_data); + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readlink call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readlink call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_readlink */ + + +/* + * perform an RPC read operation of length 'xfer_size' + */ +static int +op_read( + int xfer_size) +{ + sfs_op_type *op_ptr; /* per operation info */ + int cur_cnt; + int max_cnt; /* packet ctrs */ + char buf[DEFAULT_MAX_BUFSIZE];/* data buffer */ + READ3args args; + READ3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int size; + int j; + int ret; /* ret val == call success */ + + op_ptr = &Ops[READ]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.file, + (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + /* + * Don't allow a read of less than one block size + */ + if (xfer_size < Bytes_per_block) + xfer_size = Bytes_per_block; + + /* + * randomly choose an offset that is a multiple of the block size + * and constrained by making the transfer fit within the file + */ + args.offset._p._u = 0; + if (Cur_file_ptr->attributes3.size._p._l > xfer_size) + args.offset._p._l = Bytes_per_block * (sfs_random() % + (((Cur_file_ptr->attributes3.size._p._l - xfer_size) + / Bytes_per_block) + 1)); + else + args.offset._p._l = 0; + + /* Have lower layers fill in the data directly. */ + reply.resok.data.data_len = 0; + reply.resok.data.data_val = buf; + + /* first read the whole buffers, then the fragment */ + for (j = 0; j < 2; j++) { + + if (j == 0) { + size = Bytes_per_block; + max_cnt = xfer_size / Bytes_per_block; + } else { + /* 1KB - (Kb_per_block -1) KB fragment */ + size = xfer_size % Bytes_per_block; + max_cnt = 1; + } + if (size == 0) + continue; + + /* check our stats to see if this would overflow */ + if (!Timed_run) { + if (op_ptr->target_calls > 0) { + if ((op_ptr->results.good_calls + max_cnt) + > op_ptr->target_calls) { + max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; + } + } + } + + args.count = size; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "read: %d buffers\n", max_cnt); + (void) fflush(stderr); + } + + /* make the call(s) now */ + for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) { + + /* capture length for possible dump */ + Dump_length = fh_size(Cur_file_ptr); + + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_READ, + xdr_READ3args, (char *) &args, + xdr_READ3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + /* capture count and offset for possible dump */ + Dump_count = (rpc_stat == RPC_SUCCESS && reply.status == NFS3_OK) + ? reply.resok.data.data_len : 0; + Dump_offset = args.offset._p._l; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.file_attributes.attr, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + size = reply.resok.data.data_len; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: READ %s %d bytes\n", + sfs_Myname, Cur_filename, size); + (void) fflush(stderr); + } + args.offset._p._l += size; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: read call NFS error %s on file %d\n", + sfs_Myname, + nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: read call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + } /* for reading max_cnt packets */ + } /* for buffers and fragments */ + return(ret); + +} /* op_read */ + +char * +init_write_buffer( + void) +{ + uint32_t *bp; + static uint32_t write_buf[DEFAULT_MAX_BUFSIZE / sizeof(uint32_t)]; + uint32_t *be = write_buf + (sizeof(write_buf) / + sizeof(uint32_t)); + + if (write_buf[0] != (uint32_t)0xdeadbeef) { + for (bp = write_buf; bp < be; bp++) + *bp = (uint32_t)0xdeadbeef; + } + return (char *)write_buf; +} + + +/* + * Perform and RPC write operation of length 'xfer_size'. If 'append_flag' + * is true, then write the data to the end of the file. + * + * If the stab_flag is set to UNSTABLE we issue the requests and then + * issue a op_commit to sync the data. + */ +static int +op_write( + int xfer_size, + int append_flag, + stable_how stab_flag) +{ + sfs_op_type *op_ptr; /* per operation info */ + static char *buf = NULL; /* the data buffer */ + unsigned int size; /* size of data write */ + int cur_cnt; /* controls # NFS calls */ + int max_cnt; + WRITE3args args; + WRITE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int j; + int ret; /* ret val == call success */ + + /* + * For now we treat DATA_SYNC to be the same as FILE_SYNC. + */ + if (stab_flag == DATA_SYNC) + stab_flag = FILE_SYNC; + + /* + * Initialize write buffer to known value + */ + if (buf == NULL) { + buf = init_write_buffer(); + } + + op_ptr = &Ops[WRITE]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + args.offset._p._u = 0; + if (append_flag == 1) { + args.offset._p._l = Cur_file_ptr->attributes3.size._p._l; + } else { + /* + * randomly choose an offset that is a multiple of the block size + * and constrained by making the transfer fit within the file + */ + if (Cur_file_ptr->attributes3.size._p._l > xfer_size) { + args.offset._p._l = Bytes_per_block * (sfs_random() % + (((Cur_file_ptr->attributes3.size._p._l - xfer_size) + / Bytes_per_block) + 1)); + } else + args.offset._p._l = 0; + } + + /* stab_flag has to be set in op() in sfs_3_chd.c */ + args.stable = stab_flag; + + /* first write the whole buffers, then the fragment */ + for (j = 0; j < 2; j++) { + + if (j == 0) { + size = Bytes_per_block; + max_cnt = xfer_size / Bytes_per_block; + } else { + /* 1KB - (Kb_per_block - 1) KB fragment */ + size = xfer_size % Bytes_per_block; + max_cnt = 1; + } + if (size == 0) + continue; + + args.count = size; + args.data.data_len = size; + args.data.data_val = buf; + + /* check our stats to see if this would overflow */ + if (!Timed_run) { + if (op_ptr->target_calls > 0) { + if ((op_ptr->results.good_calls + max_cnt) + > op_ptr->target_calls) { + max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; + } + } + } + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "write: %d buffers\n", max_cnt); + (void) fflush(stderr); + } + + /* make the call(s) now */ + for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) { + + if (DEBUG_CHILD_RPC) { +(void) fprintf(stderr, "%s: WRITE %s offset %u count %lu stable %d\n", +sfs_Myname, Cur_filename, args.offset._p._l, args.count, args.stable); + (void) fflush(stderr); + } + + /* capture length for possible dump */ + Dump_length = fh_size(Cur_file_ptr); + + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_WRITE, + xdr_WRITE3args, (char *) &args, + xdr_WRITE3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + /* capture count and offset for possible dump */ + Dump_count = args.data.data_len; + Dump_offset = args.offset._p._l; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.file_wcc.after.attr, + sizeof (Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + args.offset._p._l += size; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: WRITE %s %d bytes\n", + sfs_Myname, Cur_filename, size); + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: write call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: write call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + } /* for writing max_cnt packets */ + } /* for buffers and fragments */ + + /* + * If we have not gotten an error and we were asked for an async write + * send a commit operation. + */ + if (ret && stab_flag != FILE_SYNC) + ret += op_commit(); + + return(ret); + +} /* op_write */ + + +static int +op_create(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + CREATE3args args; + CREATE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[CREATE]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = Cur_filename; + args.how.mode = UNCHECKED; + args.how.createhow3_u.obj_attributes.mode.set_it = TRUE; + args.how.createhow3_u.obj_attributes.mode.mode = (NFSMODE_REG | 0666); + args.how.createhow3_u.obj_attributes.uid.set_it = TRUE; + args.how.createhow3_u.obj_attributes.uid.uid = Cur_uid; + args.how.createhow3_u.obj_attributes.gid.set_it = TRUE; + args.how.createhow3_u.obj_attributes.gid.gid = Cur_gid; + args.how.createhow3_u.obj_attributes.atime.set_it = TRUE; + args.how.createhow3_u.obj_attributes.atime.atime.seconds = Cur_time.esec; + args.how.createhow3_u.obj_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.how.createhow3_u.obj_attributes.mtime.set_it = TRUE; + args.how.createhow3_u.obj_attributes.mtime.mtime.seconds = Cur_time.esec; + args.how.createhow3_u.obj_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + args.how.createhow3_u.obj_attributes.size.set_it = TRUE; + args.how.createhow3_u.obj_attributes.size.size._p._u = (uint32_t) 0; + args.how.createhow3_u.obj_attributes.size.size._p._l = (uint32_t) 0; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_CREATE, + xdr_CREATE3args, (char *) &args, + xdr_CREATE3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &reply.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof(Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: create call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: create call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_create */ + + +static int +op_mkdir(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + MKDIR3args args; + MKDIR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[MKDIR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = Cur_filename; + args.attributes.mode.set_it = TRUE; + args.attributes.mode.mode = (NFSMODE_DIR | 0777); + args.attributes.uid.set_it = TRUE; + args.attributes.uid.uid = Cur_uid; + args.attributes.gid.set_it = TRUE; + args.attributes.gid.gid = Cur_gid; + args.attributes.size.set_it = TRUE; + args.attributes.size.size._p._u = 0; + args.attributes.size.size._p._l = 512; + args.attributes.atime.set_it = TRUE; + args.attributes.atime.atime.seconds = Cur_time.esec; + args.attributes.atime.atime.nseconds = Cur_time.usec * 1000; + args.attributes.mtime.set_it = TRUE; + args.attributes.mtime.mtime.seconds = Cur_time.esec; + args.attributes.mtime.mtime.nseconds = Cur_time.usec * 1000; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_MKDIR, + xdr_MKDIR3args, (char *) &args, + xdr_MKDIR3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Empty_dir; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &reply.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof(Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: mkdir call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: mkdir call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_mkdir */ + + +static int +op_symlink(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + sfs_fh_type *target_fileinfo_ptr; /* target file */ + SYMLINK3args args; + SYMLINK3res reply; /* the reply */ + char sym_data[NFS_MAXPATHLEN]; /* symlink data */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[SYMLINK]; + ret = 0; + + /* set up the arguments */ + target_fileinfo_ptr = randfh(SYMLINK, 0, 0, Exists, Sfs_non_io_file); + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = Cur_filename; + + (void) strcpy(sym_data, "./"); + (void) strcat(sym_data, target_fileinfo_ptr->file_name); + args.symlink.symlink_attributes.size.set_it = TRUE; + args.symlink.symlink_attributes.size.size._p._u = (uint32_t) 0; + args.symlink.symlink_attributes.size.size._p._l = strlen(sym_data); + args.symlink.symlink_data = sym_data; + + args.symlink.symlink_attributes.mode.set_it = TRUE; + args.symlink.symlink_attributes.mode.mode = (NFSMODE_LNK | 0777); + args.symlink.symlink_attributes.uid.set_it = TRUE; + args.symlink.symlink_attributes.uid.uid = Cur_uid; + args.symlink.symlink_attributes.gid.set_it = TRUE; + args.symlink.symlink_attributes.gid.gid = Cur_gid; + args.symlink.symlink_attributes.atime.set_it = TRUE; + args.symlink.symlink_attributes.atime.atime.seconds = Cur_time.esec; + args.symlink.symlink_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.symlink.symlink_attributes.mtime.set_it = TRUE; + args.symlink.symlink_attributes.mtime.mtime.seconds = Cur_time.esec; + args.symlink.symlink_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_SYMLINK, + xdr_SYMLINK3args, (char *) &args, + xdr_SYMLINK3res, (char *) &reply, + ((int)Current_test_phase < (int)Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + /* + * SYMLINK doesn't return a fh. If we try to access this symlink + * (eg, remove(), readlink()) before we do a lookup, we won't have + * a fh to use. So, we do a lookup call here. If it fails, we fill + * in what we can. + */ + Cur_file_ptr->state = Exists; + if (op_lookup() == 0) { + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + Cur_file_ptr->attributes3.type = NF3LNK; + Cur_file_ptr->attributes3.mode = (NFSMODE_LNK|0777); + Cur_file_ptr->attributes3.uid = Cur_uid; + Cur_file_ptr->attributes3.gid = Cur_gid; + Cur_file_ptr->attributes3.atime.seconds = Cur_time.esec; + Cur_file_ptr->attributes3.atime.nseconds = + Cur_time.usec * 1000; + Cur_file_ptr->attributes3.mtime = + Cur_file_ptr->attributes3.atime; + } else + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: symlink call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: symlink call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_symlink */ + + +static int +op_mknod(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + MKNOD3args args; + MKNOD3res reply; + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[MKNOD]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = Cur_filename; + args.what.type = NF3FIFO; + args.what.mknoddata3_u.pipe_attributes.mode.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.mode.mode = (NFSMODE_FIFO | 0777); + args.what.mknoddata3_u.pipe_attributes.uid.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.uid.uid = Cur_uid; + args.what.mknoddata3_u.pipe_attributes.gid.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.gid.gid = Cur_gid; + args.what.mknoddata3_u.pipe_attributes.size.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.size.size._p._u = (uint32_t) 0; + args.what.mknoddata3_u.pipe_attributes.size.size._p._l = + (uint32_t) 512; + args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.atime.atime.seconds = + Cur_time.esec; + args.what.mknoddata3_u.pipe_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE; + args.what.mknoddata3_u.pipe_attributes.mtime.mtime.seconds = + Cur_time.esec; + args.what.mknoddata3_u.pipe_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_MKNOD, + xdr_MKNOD3args, (char *) &args, + xdr_MKNOD3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &reply.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof(Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: mknod call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: mknod call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_mknod */ + + +static int +op_remove(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + REMOVE3args args; + REMOVE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[REMOVE]; + ret = 0; + + /* set up the arguments */ + args.object.name = Cur_filename; + (void) memmove((char *) &args.object.dir,(char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_REMOVE, + xdr_REMOVE3args, (char *) &args, + xdr_REMOVE3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Nonexistent; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: remove call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: remove call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_remove */ + + +static int +op_rmdir(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + RMDIR3args args; + RMDIR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[RMDIR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.object.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.object.name = Cur_file_ptr->file_name; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_RMDIR, + xdr_RMDIR3args, (char *) &args, + xdr_RMDIR3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Nonexistent; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: rmdir call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: rmdir call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_rmdir */ + + +static int +op_rename(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + sfs_fh_type *target_fileinfo_ptr; /* target name */ + RENAME3args args; + RENAME3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[RENAME]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.from.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + (void) memmove((char *) &args.to.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + + target_fileinfo_ptr = randfh(RENAME, 0, 0, Nonexistent, + Sfs_non_io_file); + + args.from.name = Cur_file_ptr->file_name; + (void) sprintf(target_fileinfo_ptr->file_name, Filespec, + target_fileinfo_ptr->unique_num); + args.to.name = target_fileinfo_ptr->file_name; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME, + xdr_RENAME3args, (char *) &args, + xdr_RENAME3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + target_fileinfo_ptr->state = Exists; + (void) memmove((char *) &target_fileinfo_ptr->fh3, + (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + target_fileinfo_ptr->attributes3 = Cur_file_ptr->attributes3; + target_fileinfo_ptr->size = fh_size(Cur_file_ptr); + Cur_file_ptr->state = Nonexistent; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: rename call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: rename call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_rename */ + + +static int +op_link(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + sfs_fh_type *target_fileinfo_ptr; /* target */ + LINK3args args; + LINK3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[LINK]; + ret = 0; + + /* set up the arguments */ + target_fileinfo_ptr = randfh(LINK, 0, 0, Exists, Sfs_non_io_file); + (void) memmove((char *) &args.file, (char *) &target_fileinfo_ptr->fh3, + sizeof (nfs_fh3)); + (void) memmove((char *) &args.link.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.link.name = Cur_filename; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_LINK, + xdr_LINK3args, (char *) &args, + xdr_LINK3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &target_fileinfo_ptr->fh3, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + target_fileinfo_ptr->attributes3.nlink++; + Cur_file_ptr->attributes3 = target_fileinfo_ptr->attributes3; + Cur_file_ptr->size = fh_size(Cur_file_ptr); + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: link call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: link call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_link */ + + +static int +op_readdir(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + READDIR3args args; + READDIR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int i; + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + bool_t hit_eof; + /* array of entries */ + entry3 entry_stream[SFS_MAXDIRENTS]; + entry3 *entries; /* ptr to the dir entry */ + + op_ptr = &Ops[READDIR]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.cookie._p._l = args.cookie._p._u = (uint32_t) 0; + (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE); + args.count = DEFAULT_MAX_BUFSIZE; + + /* Have lower layers fill in the data directly. */ + (void) memset((char *) &reply, '\0', sizeof (reply)); + (void) memset((char *) entry_stream, '\0', + sizeof (entry3) * SFS_MAXDIRENTS); + reply.resok.count = SFS_MAXDIRENTS; + reply.resok.reply.entries = entry_stream; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_READDIR, + xdr_READDIR3args, (char *) &args, + xdr_READDIR3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + + if (DEBUG_CHILD_RPC) { + hit_eof = reply.resok.reply.eof; + entries = reply.resok.reply.entries; + for (i = 0; i < reply.resok.count; i++) { + (void) fprintf(stderr, "%s:READDIR (eof=%d) entry %s\n", + sfs_Myname, hit_eof, entries[i].name); + } + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readdir call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readdir call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_readdir */ + + +static int +op_readdirplus(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + READDIRPLUS3args args; + READDIRPLUS3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int i; + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + bool_t hit_eof; + /* array of entries */ + entryplus3 entry_stream[SFS_MAXDIRENTS]; + entryplus3 *entries; + + op_ptr = &Ops[READDIRPLUS]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.cookie._p._l = args.cookie._p._u = (uint32_t) 0; + (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE); + (void) memset((char *) entry_stream, '\0', + sizeof (entryplus3) * SFS_MAXDIRENTS); + args.dircount = DEFAULT_MAX_BUFSIZE; + args.maxcount = DEFAULT_MAX_BUFSIZE; + + /* Have lower layers fill in the data directly. */ + reply.resok.count = SFS_MAXDIRENTS; + reply.resok.reply.entries = entry_stream; + + /* make the call now */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_READDIRPLUS, + xdr_READDIRPLUS3args, (char *) &args, + xdr_READDIRPLUS3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + + if (DEBUG_CHILD_RPC) { + hit_eof = reply.resok.reply.eof; + entries = reply.resok.reply.entries; + for (i = 0; i < reply.resok.count; i++) { + (void) fprintf(stderr, "%s:READDIR (eof=%d) entry %s\n", + sfs_Myname, hit_eof, entries[i].name); + } + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readdir call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: readdir call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_readdirplus */ + + +static int +op_fsstat(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + FSSTAT3args args; + FSSTAT3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[FSSTAT]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.fsroot, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_FSSTAT, + xdr_FSSTAT3args, (char *) &args, + xdr_FSSTAT3args, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: fsstat call RPC error %d\n", + sfs_Myname, rpc_stat); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_fsstat */ + + +static int +op_fsinfo(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + FSINFO3args args; + FSINFO3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[FSINFO]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.fsroot, (char *) &Cur_file_ptr->dir->fh3, + sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_FSINFO, + xdr_FSINFO3args, (char *) &args, + xdr_FSINFO3args, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: fsinfo call RPC error %d\n", + sfs_Myname, rpc_stat); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_fsinfo */ + + +static int +op_pathconf(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + PATHCONF3args args; + PATHCONF3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[PATHCONF]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_PATHCONF, + xdr_PATHCONF3args, (char *) &args, + xdr_PATHCONF3args, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: pathconf call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + return(ret); + +} /* op_pathconf */ + + +static int +op_commit(void) +{ + sfs_op_type *op_ptr; /* per operation info */ + int32_t size; /* size of data write */ + COMMIT3args args; + COMMIT3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + int ret; /* ret val == call success */ + + op_ptr = &Ops[COMMIT]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + args.offset._p._u = args.offset._p._l = (uint32_t) 0; + args.count = Cur_file_ptr->attributes3.size._p._l; + size = args.count; + + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_COMMIT, + xdr_COMMIT3args, (char *) &args, + xdr_COMMIT3res, (char *) &reply, + (Current_test_phase < Warmup_phase) + ? Nfs_timers[Init] + : Nfs_timers[op_ptr->call_class]); + sfs_gettime(&stop); + Cur_time = stop; + + if (rpc_stat == RPC_SUCCESS) { + if (reply.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &reply.resok.file_wcc.after.attr, + sizeof(Cur_file_ptr->attributes3)); + Cur_file_ptr->size = fh_size(Cur_file_ptr); + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: WRITE %s %ld bytes\n", + sfs_Myname, Cur_filename, size); + (void) fflush(stderr); + } + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, + "%s: write call NFS error %s on file %d\n", + sfs_Myname, nfs3_strerror(reply.status), + Cur_file_ptr->unique_num); + } + } + sfs_elapsedtime(op_ptr, &start, &stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + } else { + if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: write call RPC error %d on file %d\n", + sfs_Myname, rpc_stat, Cur_file_ptr->unique_num); + } + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + + return(ret); + +} /* op_commit */ + + +#define LAD_RETRIABLE(stat) (((stat) == RPC_TIMEDOUT) || ((stat) == RPC_CANTDECODERES)) + +/* + * Reliably lookup a file in the current directory + * Return: + * -1 RPC error + * 1 File doesn't exist + * 0 File exists + */ +int +lad_lookup(sfs_fh_type *file_ptr, char *name) +{ + LOOKUP3args args; + LOOKUP3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_lookup: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + (void) memmove((char *) &args.what.dir, (char *) &file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.what.name = name; + (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3)); + + /* make the call */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP, + xdr_LOOKUP3args, (char *) &args, + xdr_LOOKUP3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_lookup(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + } + + if (reply.status == NFS3ERR_NOENT) { + return(1); + } + + if (reply.status != NFS3_OK) { + (void) fprintf(stderr, "lad_lookup(%s) NFS call failed : %s\n", + name, nfs3_strerror(reply.status)); + return(-1); + } + + file_ptr->state = Exists; + (void) memmove((char *) &file_ptr->fh3, + (char *) &reply.resok.object, + sizeof (nfs_fh3)); + (void) strcpy(file_ptr->file_name, name); + (void) memmove((char *) &file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof (file_ptr->attributes3)); + file_ptr->size = fh_size(file_ptr); + return(0); +} + +/* + * Reliably remove a file in the current directory + */ +int +lad_remove(sfs_fh_type *file_ptr, char *name) +{ + REMOVE3args args; + REMOVE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + /* + * This function presumes that the file name does exist + */ + if (file_ptr->attributes3.type == NF3DIR) + return (lad_rmdir(file_ptr, name)); + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_remove: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + args.object.name = name; + (void) memmove((char *) &args.object.dir, (char *) &file_ptr->dir->fh3, + sizeof(nfs_fh3)); + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_REMOVE, + xdr_REMOVE3args, (char *) &args, + xdr_REMOVE3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_remove(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (reply.status != NFS3_OK) { + if (reply.status != NFS3ERR_NOENT || !retried) { + (void) fprintf(stderr, "lad_remove(%s) NFS call failed : %s\n", + name, nfs3_strerror(reply.status)); + return(-1); + } + } + + file_ptr->state = Nonexistent; + + return(0); +} + +/* + * Reliably remove a directory in the current directory + */ +int +lad_rmdir(sfs_fh_type *file_ptr, char *name) +{ + RMDIR3args args; + RMDIR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + /* + * This function presumes that the file name does exist and is empty + */ + if (file_ptr->attributes3.type != NF3DIR) + return (lad_remove(file_ptr, name)); + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_rmdir: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + args.object.name = name; + (void) memmove((char *) &args.object.dir, (char *) &file_ptr->dir->fh3, + sizeof (nfs_fh3)); + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_RMDIR, + xdr_RMDIR3args, (char *) &args, + xdr_RMDIR3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_rmdir(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (reply.status != NFS3_OK) { + if (reply.status != NFS3ERR_NOENT || !retried) { + (void) fprintf(stderr, "lad_rmdir(%s) NFS call failed : %s\n", + name, nfs3_strerror(reply.status)); + return(-1); + } + } + + file_ptr->state = Nonexistent; + + return(0); +} + +/* + * Reliably create a symlink in the current directory + */ +int +lad_symlink(sfs_fh_type *file_ptr, char *target, char *name) +{ + SYMLINK3args args; + SYMLINK3res reply; /* the reply */ + char sym_data[NFS_MAXPATHLEN]; /* symlink data */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + /* + * This function presumes that the file name does not already exist + */ + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_symlink: %lx[%lx] %s -> %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name, target); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = name; + + (void) strcpy(sym_data, "./"); + (void) strcat(sym_data, target); + args.symlink.symlink_attributes.size.set_it = TRUE; + args.symlink.symlink_attributes.size.size._p._u = (uint32_t) 0; + args.symlink.symlink_attributes.size.size._p._l = strlen(sym_data); + args.symlink.symlink_data = sym_data; + + args.symlink.symlink_attributes.mode.set_it = TRUE; + args.symlink.symlink_attributes.mode.mode = (NFSMODE_LNK | 0777); + args.symlink.symlink_attributes.uid.set_it = TRUE; + args.symlink.symlink_attributes.uid.uid = Cur_uid; + args.symlink.symlink_attributes.gid.set_it = TRUE; + args.symlink.symlink_attributes.gid.gid = Cur_gid; + args.symlink.symlink_attributes.atime.set_it = TRUE; + args.symlink.symlink_attributes.atime.atime.seconds = Cur_time.esec; + args.symlink.symlink_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.symlink.symlink_attributes.mtime.set_it = TRUE; + args.symlink.symlink_attributes.mtime.mtime.seconds = Cur_time.esec; + args.symlink.symlink_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_SYMLINK, + xdr_SYMLINK3args, (char *) &args, + xdr_SYMLINK3res, (char *) &reply, + Nfs_timers[Init]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_symlink(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (reply.status != NFS3_OK) { + if (reply.status != NFS3ERR_EXIST || !retried) { + (void) fprintf(stderr, "lad_symlink(%s, %s) NFS call failed : %s\n", + target, name, nfs3_strerror(reply.status)); + return(-1); + } + } + + /* + * SYMLINK may not return a fh. If we try to + * access this symlink (eg, remove(), readlink()) + * before we do a lookup, we won't have a fh to use. + * So, we do a lookup call here. + * If it fails, we fill in what we can. + */ + return (lad_lookup(file_ptr, name)); +} + +/* + * Reliably create a directory in the current directory + */ +int +lad_mkdir(sfs_fh_type *file_ptr, char *name) +{ + MKDIR3args args; + MKDIR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + /* + * This function presumes that the file name does not already exist + */ + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_mkdir: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = name; + args.attributes.mode.set_it = TRUE; + args.attributes.mode.mode = (NFSMODE_DIR | 0777); + args.attributes.uid.set_it = TRUE; + args.attributes.uid.uid = Cur_uid; + args.attributes.gid.set_it = TRUE; + args.attributes.gid.gid = Cur_gid; + args.attributes.size.set_it = TRUE; + args.attributes.size.size._p._u = 0; + args.attributes.size.size._p._l = 512; + args.attributes.atime.set_it = TRUE; + args.attributes.atime.atime.seconds = Cur_time.esec; + args.attributes.atime.atime.nseconds = Cur_time.usec * 1000; + args.attributes.mtime.set_it = TRUE; + args.attributes.mtime.mtime.seconds = Cur_time.esec; + args.attributes.mtime.mtime.nseconds = Cur_time.usec * 1000; + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_MKDIR, + xdr_MKDIR3args, (char *) &args, + xdr_MKDIR3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_mkdir(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (!retried && reply.status == NFS3ERR_EXIST) + return(1); + + if (reply.status != NFS3_OK) { + if (reply.status != NFS3ERR_EXIST || !retried) { + (void) fprintf(stderr, "lad_mkdir(%s) NFS call failed : %s\n", + name, nfs3_strerror(reply.status)); + return(-1); + } + /* + * If the first mkdir suceeded but the reply as dropped and + * was retransmitted, we still need to lookup the attributes + */ + if (lad_lookup(file_ptr, name)) + return (-1); + } else { + (void) memmove((char *) &file_ptr->fh3, + (char *) &reply.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(file_ptr->file_name, name); + (void) memmove((char *) &file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof(file_ptr->attributes3)); + file_ptr->size = fh_size(file_ptr); + } + file_ptr->state = Empty_dir; + + return(0); +} + +/* + * Reliably commit a file + */ +static int +lad_commit(sfs_fh_type *file_ptr) +{ + COMMIT3args args; + COMMIT3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_commit: %lx[%lx]\n", + sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir); + (void) fflush(stderr); + } + + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &file_ptr->fh3, + sizeof (nfs_fh3)); + args.offset._p._u = args.offset._p._l = (uint32_t) 0; + args.count = file_ptr->attributes3.size._p._l; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_COMMIT, + xdr_COMMIT3args, (char *) &args, + xdr_COMMIT3res, (char *) &reply, + Nfs_timers[Init]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_commit() RPC call failed : %s\n", + clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + } + + return(0); +} + +/* + * Reliably write a file in the current directory + */ +int +lad_write(sfs_fh_type *file_ptr, int32_t offset, int32_t length) +{ + static char *buf = NULL; /* the data buffer */ + int32_t size; /* size of data write */ + int32_t cur_cnt; + WRITE3args args; + WRITE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int async = 1; + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_write: %lx[%lx] %ld %ld\n", + sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, offset, length); + (void) fflush(stderr); + } + + /* + * This function presumes that the file name does exist + * Initialize write buffer to known value + */ + if (buf == NULL) { + buf = init_write_buffer(); + } + + /* + * If a short file write don't bother with the commit, just write sync. + */ + if (length <= Bytes_per_block) + async = 0; + + /* set up the arguments */ + (void) memmove((char *) &args.file, (char *) &file_ptr->fh3, + sizeof (nfs_fh3)); + args.offset._p._u = 0; + args.offset._p._l = offset; + if (async) + args.stable = UNSTABLE; + else + args.stable = FILE_SYNC; + + size = Bytes_per_block; + for (cur_cnt = 0; cur_cnt < length; cur_cnt += size) { + if ((cur_cnt + size) > length) + size = length - cur_cnt; + + if (size == 0) + break; + + args.count = size; + args.data.data_len = size; + args.data.data_val = buf; + + /* make the call now */ + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_WRITE, + xdr_WRITE3args, (char *) &args, + xdr_WRITE3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_write() RPC call failed : %s\n", + clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + } + if (reply.status != NFS3_OK) { + (void) fprintf(stderr, "lad_write() NFS call failed : %s\n", + nfs3_strerror(reply.status)); + return(-1); + } + file_ptr->state = Exists; + (void) memmove((char *) &file_ptr->attributes3, + (char *) &reply.resok.file_wcc.after.attr, + sizeof (file_ptr->attributes3)); + file_ptr->size = fh_size(file_ptr); + + args.offset._p._l += size; + } + + if (async) + (void) lad_commit(file_ptr); + return(0); +} + +/* + * Reliably create a file in the current directory + */ +int +lad_create(sfs_fh_type *file_ptr, char *name) +{ + CREATE3args args; + CREATE3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + int retried = 0; + + /* + * This function presumes that the file name does not already exist + */ + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_create: %lx[%lx] %s\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, name); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* set up the arguments */ + (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3, + sizeof (nfs_fh3)); + args.where.name = name; + args.how.mode = UNCHECKED; + args.how.createhow3_u.obj_attributes.mode.set_it = TRUE; + args.how.createhow3_u.obj_attributes.mode.mode = (NFSMODE_REG | 0666); + args.how.createhow3_u.obj_attributes.uid.set_it = TRUE; + args.how.createhow3_u.obj_attributes.uid.uid = Cur_uid; + args.how.createhow3_u.obj_attributes.gid.set_it = TRUE; + args.how.createhow3_u.obj_attributes.gid.gid = Cur_gid; + args.how.createhow3_u.obj_attributes.atime.set_it = TRUE; + args.how.createhow3_u.obj_attributes.atime.atime.seconds = + Cur_time.esec; + args.how.createhow3_u.obj_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + args.how.createhow3_u.obj_attributes.mtime.set_it = TRUE; + args.how.createhow3_u.obj_attributes.mtime.mtime.seconds = + Cur_time.esec; + args.how.createhow3_u.obj_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + args.how.createhow3_u.obj_attributes.size.set_it = TRUE; + args.how.createhow3_u.obj_attributes.size.size._p._u = + (uint32_t) 0; + args.how.createhow3_u.obj_attributes.size.size._p._l = + (uint32_t) 0; + + /* make the call now */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_CREATE, + xdr_CREATE3args, (char *) &args, + xdr_CREATE3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void) fprintf(stderr, "lad_create(%s) RPC call failed : %s\n", + name, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + retried++; + } + + if (!retried && reply.status == NFS3ERR_EXIST) { + return(1); + } + + if (reply.status != NFS3_OK) { + if (reply.status != NFS3ERR_EXIST || !retried) { + (void) fprintf(stderr, "lad_create(%s) NFS call failed : %s\n", + name, nfs3_strerror(reply.status)); + return(-1); + } + /* + * If the first create suceeded but the reply as dropped and + * was retransmitted, we still need to lookup the attributes + */ + if (lad_lookup(file_ptr, name)) + return (-1); + } else { + (void) memmove((char *) &file_ptr->fh3, + (char *) &reply.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(file_ptr->file_name, name); + (void) memmove((char *) &file_ptr->attributes3, + (char *) &reply.resok.obj_attributes.attr, + sizeof(file_ptr->attributes3)); + file_ptr->size = fh_size(file_ptr); + } + + file_ptr->state = Exists; + /* + * Directories are created as Empty_dir, when a file is created it + * becomes an Exists. + */ + file_ptr->dir->state = Exists; + + return(0); +} + +/* + * Reliably set the size of a file in the current directory + */ +int +lad_truncate(sfs_fh_type *file_ptr, int32_t size) +{ + SETATTR3args args; + SETATTR3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + + /* + * This function presumes that the file name already exists + */ + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s:lad_truncate: %lx[%lx] %ld\n", sfs_Myname, + (int32_t) file_ptr, (int32_t) file_ptr->dir, size); + (void) fflush(stderr); + } + + /* CONSTCOND */ + while (1) { + /* + * set up the arguments + * Set the mode and times as well + */ + (void) memmove((char *) &args.object, (char *) &file_ptr->fh3, + sizeof (nfs_fh3)); + args.new_attributes.mode.set_it = TRUE; + args.new_attributes.mode.mode = (uint32_t) 0666; + args.new_attributes.uid.set_it = FALSE; + args.new_attributes.uid.uid = (uint32_t) -1; + args.new_attributes.gid.set_it = FALSE; + args.new_attributes.gid.gid = (uint32_t) -1; + args.new_attributes.size.set_it = TRUE; + args.new_attributes.size.size._p._u = 0; + args.new_attributes.size.size._p._l = size; + args.new_attributes.atime.set_it = TRUE; + args.new_attributes.atime.atime.seconds = Cur_time.esec; + args.new_attributes.atime.atime.nseconds = Cur_time.usec * 1000; + args.new_attributes.mtime.set_it = TRUE; + args.new_attributes.mtime.mtime.seconds = Cur_time.esec; + args.new_attributes.mtime.mtime.nseconds = Cur_time.usec * 1000; + args.guard.check = FALSE; + + /* make the call */ + rpc_stat = clnt_call(NFS_client, NFSPROC3_SETATTR, + xdr_SETATTR3args, (char *) &args, + xdr_SETATTR3res, (char *) &reply, + Nfs_timers[Init]); + + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + (void)fprintf(stderr, + "lad_truncate(%ld) RPC call failed : %s\n", + size, clnt_sperrno(rpc_stat)); + } + if (!LAD_RETRIABLE(rpc_stat)) { + return(-1); + } + } + + if (reply.status != NFS3_OK) { + (void) fprintf(stderr, "lad_truncate(%ld) NFS call failed : %s\n", + size, nfs3_strerror(reply.status)); + return(-1); + } + (void) memmove(&file_ptr->attributes3, + &reply.resok.obj_wcc.after.attr, + sizeof (file_ptr->attributes3)); + file_ptr->size = fh_size(file_ptr); + + return(0); +} + +static char * +nfs3_strerror(int status) +{ + static char str[40]; + switch (status) { + case NFS3_OK: + (void) strcpy(str, "no error"); + break; + case NFS3ERR_PERM: + (void) strcpy(str, "Not owner"); + break; + case NFS3ERR_NOENT: + (void) strcpy(str, "No such file or directory"); + break; + case NFS3ERR_IO: + (void) strcpy(str, "I/O error"); + break; + case NFS3ERR_NXIO: + (void) strcpy(str, "No such device or address"); + break; + case NFS3ERR_ACCES: + (void) strcpy(str, "Permission denied"); + break; + case NFS3ERR_EXIST: + (void) strcpy(str, "File exists"); + break; + case NFS3ERR_XDEV: + (void) strcpy(str, "Cross-device link"); + break; + case NFS3ERR_NODEV: + (void) strcpy(str, "No such device"); + break; + case NFS3ERR_NOTDIR: + (void) strcpy(str, "Not a directory"); + break; + case NFS3ERR_ISDIR: + (void) strcpy(str, "Is a directory"); + break; + case NFS3ERR_INVAL: + (void) strcpy(str, "Invalid argument"); + break; + case NFS3ERR_FBIG: + (void) strcpy(str, "File too large"); + break; + case NFS3ERR_NOSPC: + (void) strcpy(str, "No space left on device"); + break; + case NFS3ERR_ROFS: + (void) strcpy(str, "Read-only file system"); + break; + case NFS3ERR_MLINK: + (void) strcpy(str, "Too many links"); + break; + case NFS3ERR_NAMETOOLONG: + (void) strcpy(str, "File name too long"); + break; + case NFS3ERR_NOTEMPTY: + (void) strcpy(str, "Directory not empty"); + break; + case NFS3ERR_DQUOT: + (void) strcpy(str, "Disc quota exceeded"); + break; + case NFS3ERR_STALE: + (void) strcpy(str, "Stale NFS file handle"); + break; + case NFS3ERR_REMOTE: + (void) strcpy(str, "Object is remote"); + break; + case NFS3ERR_BADHANDLE: + (void) strcpy(str, "Bad file handle"); + break; + case NFS3ERR_NOT_SYNC: + (void) strcpy(str, "Not sync write"); + break; + case NFS3ERR_BAD_COOKIE: + (void) strcpy(str, "Bad cookie"); + break; + case NFS3ERR_NOTSUPP: + (void) strcpy(str, "Operation not supported"); + break; + case NFS3ERR_TOOSMALL: + (void) strcpy(str, "Value too small"); + break; + case NFS3ERR_SERVERFAULT: + (void) strcpy(str, "Server fault"); + break; + case NFS3ERR_BADTYPE: + (void) strcpy(str, "Bad type"); + break; + case NFS3ERR_JUKEBOX: + (void) strcpy(str, "Jukebox"); + break; + default: + (void) sprintf(str, "Unknown status %d", status); + break; + } + return (str); +} + +/* sfs_3_ops.c */ diff --git a/TBBT/trace_play/sfs_3_vld.c b/TBBT/trace_play/sfs_3_vld.c new file mode 100644 index 0000000..8ede891 --- /dev/null +++ b/TBBT/trace_play/sfs_3_vld.c @@ -0,0 +1,2126 @@ +#ifndef lint +static char sfs_3_vldSid[] = "@(#)sfs_3_vld.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ------------------------------ sfs_3_vld.c --------------------------- + * + * Validation suite for sfs. + * + *.Exported_routines + * void Validate_ops(int, char **) + * + *.Local_routines + * void validate_init_rpc() + * void validate_creation(void) + * void validate_attributes(void) + * void validate_read_write(void) + * void validate_rename(void) + * int compare_sattr(char *, char *, sattr3 *, fattr3 *) + * int compare_fattr(char *, char *, fattr3 *, fattr3 *) + * uint16_t sum(unsigned char *, int) + * void validate_remove(void) + * void validate_cleanup(void) + * void validate_exit(void) + * void verror(int, ValMsgType, char *, ...) + * + *.Revision History + * 11-Jul-94 ChakChung Ng Created + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> + +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "sfs_c_def.h" + +extern struct hostent *Server_hostent; + +/* + * ----------------------- External Definitions ----------------------- + */ + +/* forward definitions for local routines */ +/* + * validate options + * BATCH - do complete pass of validation, reporting errors if any + * VERBOSE - prints step-by-step validation actions being performed + * INTERACTIVE - VERBOSE and if any errors encountered, ask to continue + * validation or not. + */ +#define VAL_BATCH 1 +#define VAL_VERBOSE 2 +#define VAL_INTERACTIVE 3 + +typedef enum { + I = 1, + W = 2, + E = 3 +} ValMsgType; + +#define NUMREGFILES 21 +#define NUMDIRS 10 +#define NUMLINKS 10 +#define NUMSYMLINKS 10 +#define NUMFILES NUMREGFILES + NUMDIRS + NUMLINKS + NUMSYMLINKS +#define NUMFRAGS 8 + +static void val_op_null(void); +static void val_op_getattr(GETATTR3args *, GETATTR3res *); +static void val_op_setattr(SETATTR3args *, SETATTR3res *); +static void val_op_lookup(LOOKUP3args *, LOOKUP3res *); +static void val_op_access(ACCESS3args *, ACCESS3res *); +static void val_op_readlink(READLINK3args *, READLINK3res *); +static void val_op_read(READ3args *, READ3res *); +static void val_op_write(WRITE3args *, WRITE3res *); +static void val_op_create(CREATE3args *, CREATE3res *); +static void val_op_mkdir(MKDIR3args *, MKDIR3res *); +static void val_op_symlink(SYMLINK3args *, SYMLINK3res *); +static void val_op_mknod(MKNOD3args *, MKNOD3res *); +static void val_op_remove(REMOVE3args *, REMOVE3res *); +static void val_op_rmdir(RMDIR3args *, RMDIR3res *); +static void val_op_rename(RENAME3args *, RENAME3res *); +static void val_op_link(LINK3args *, LINK3res *); +static void val_op_readdir(READDIR3args *, READDIR3res *); +static void val_op_readdirplus(READDIRPLUS3args *, READDIRPLUS3res *); +static void val_op_fsstat(FSSTAT3args *, FSSTAT3res *); +static void val_op_fsinfo(FSINFO3args *, FSINFO3res *); +static void val_op_pathconf(PATHCONF3args *, PATHCONF3res *); +static void val_op_commit(COMMIT3args *, COMMIT3res *); + +static void validate_init_rpc(void); +static void validate_exit(void); +static void validate_creation(void); +static void validate_attributes(void); +static void validate_read_write(void); +static void validate_rename(void); +static void validate_remove(void); +static void validate_cleanup(void); +static int compare_sattr(char *, char *, sattr3 *, fattr3 *); +static int compare_fattr(char *, char *, fattr3 *, fattr3 *); +static uint16_t sum(unsigned char *, int); +static void verror(int, ValMsgType, char *, ...); +static void create_3tmp_handles(void); +static void delete_3tmp_handles(void); + +/* + * ---------------------- Static Declarations ---------------------- + */ + +int Validate; + +static int Validate_errors = 0; +static char Testdirname[SFS_MAXPATHLEN]; /* test dir component name */ +/* + * packed structure to keep track of file status + */ +struct sfs_fileinfo { + int file_found:1, /* file has been found */ + file_is_dup:1, /* file has a duplicate */ + pad:30; /* pad the rest */ +}; + +typedef struct sfs_fileinfo sfs_fileinfo; +/* + * This vector is used for readdirplus validation currently, but could be + * extended to keep track of other interesting pieces of information. + */ +static sfs_fileinfo check_files[NUMFILES]; + +/* + * ---------------------- SFS Validation Suite ---------------------- + */ + +/* + * XXXXX Must make sure that we validate that all servers return back + * XXXXX All optional values + */ +void +Validate_ops( + int argc, + char * argv[]) +{ + char * valdir; + CLIENT * mount_client_ptr; + int i; + + if (argc > 1) { + verror(VAL_BATCH, E, "Can only validate one directory at a time.\n"); + exit(1); + } + + Num_io_files = NUMFILES; + Cur_uid = Real_uid; + nfs_version = NFS_V3; + + if (argc == 0) + valdir = "."; + else + valdir = argv++[0]; + + (void) sprintf(Testdirname, "%s/validatedir", valdir); + + do { + verror(VAL_BATCH, I, "validating sfs on \"%s\" directory ...\n", + valdir); + + init_fileinfo(); + create_3tmp_handles(); + + /* + * need priv port to do following + */ + mount_client_ptr = lad_getmnt_hand(valdir); + if (mount_client_ptr == NULL) { + exit(1); + } + validate_init_rpc(); + + /* + * should be all done doing priv port stuff + */ + if (setuid(Real_uid) != 0) { + (void) fprintf(stderr,"%s: %s%s\n", + sfs_Myname, "cannot perform setuid operation.\n", + "Do `make install` as root.\n"); + } + + init_mount_point(0, valdir, mount_client_ptr); + + /* + * initialize the check_file array + */ + (void) memset((void *) check_files, '\0', sizeof(check_files)); + + verror(VAL_VERBOSE, I, "validating null operation ...\n"); + val_op_null(); + + validate_creation(); + validate_attributes(); + validate_read_write(); + validate_rename(); + validate_remove(); + argc--; + valdir = argv++[0]; + + /* + * Cleanup mount client handle + */ + clnt_destroy(mount_client_ptr); + + delete_3tmp_handles(); + validate_cleanup(); + } while (argc > 0); + + validate_exit(); + +} /* Validate_ops */ + + +/* + * allocate and initialize client handles + */ +static void +validate_init_rpc(void) +{ + NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent, + (uint32_t) NFS_PROGRAM, + (uint32_t) NFS_V3, + RPC_ANYSOCK, &Nfs_timers[0]); + + if (NFS_client == ((CLIENT *) NULL)) { + verror(VAL_BATCH, E, + "portmap/nfsd server not responding\n"); + exit(1); + } + + NFS_client->cl_auth = authunix_create(lad_hostname, Real_uid, + Cur_gid, 0, NULL); +} /* validate_init_rpc */ + + +static void +validate_creation(void) +{ + int filenum; + int target_filenum; + CREATE3args argcr; + CREATE3res repcr; + MKDIR3args argmk; + MKDIR3res repmk; + LINK3args argln; + LINK3res repln; + SYMLINK3args argsl; + SYMLINK3res repsl; + LOOKUP3args arglp; + LOOKUP3res replp; + READLINK3args argrl; + READLINK3res reprl; + char sl_target_path[NFS_MAXPATHLEN]; + char sym_data[NFS_MAXPATHLEN]; + + for (filenum=0; filenum < NUMFILES ; filenum++) { + + Cur_file_ptr = &Io_files[filenum]; + sfs_gettime(&Cur_time); + + if (filenum < NUMREGFILES) { + + (void) sprintf(Cur_filename, Filespec, filenum); + + /* regular file creation */ + (void) memmove((char *) &argcr.where.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + argcr.where.name = Cur_filename; + argcr.how.mode = UNCHECKED; + argcr.how.createhow3_u.obj_attributes.mode.set_it = TRUE; + argcr.how.createhow3_u.obj_attributes.mode.mode = + (NFSMODE_REG | 0666); + argcr.how.createhow3_u.obj_attributes.uid.set_it = TRUE; + argcr.how.createhow3_u.obj_attributes.uid.uid = Cur_uid; + argcr.how.createhow3_u.obj_attributes.gid.set_it = TRUE; + argcr.how.createhow3_u.obj_attributes.gid.gid = Cur_gid; + argcr.how.createhow3_u.obj_attributes.atime.set_it = TRUE; + argcr.how.createhow3_u.obj_attributes.atime.atime.seconds = + Cur_time.esec; + argcr.how.createhow3_u.obj_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + argcr.how.createhow3_u.obj_attributes.mtime.set_it = TRUE; + argcr.how.createhow3_u.obj_attributes.mtime.mtime.seconds = + Cur_time.esec; + argcr.how.createhow3_u.obj_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + argcr.how.createhow3_u.obj_attributes.size.set_it = TRUE; + argcr.how.createhow3_u.obj_attributes.size.size._p._u = + (uint32_t) 0; + argcr.how.createhow3_u.obj_attributes.size.size._p._l = + (uint32_t) 0; + + (void) memset((char *) &repcr.resok.obj.handle, '\0', + sizeof (nfs_fh3)); + verror(VAL_VERBOSE, I, "validating create file %s ...\n", + Cur_filename); + val_op_create(&argcr, &repcr); + + if (repcr.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &repcr.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &repcr.resok.obj_attributes.attr, + sizeof(Cur_file_ptr->attributes3)); + (void) compare_sattr(Ops[CREATE].name, Io_files[filenum].file_name, + &argcr.how.createhow3_u.obj_attributes, + &Cur_file_ptr->attributes3); + } else { + Cur_file_ptr->state = Nonexistent; + errno = (int)repcr.status; + verror(VAL_BATCH, E, "create %s failed: %m\n", + Cur_filename); + /* + * An error in file creation is fatal, because we use the + * created files to validate the other operations. + */ + validate_exit(); + } + + } else if (filenum < NUMREGFILES + NUMDIRS) { + + (void) sprintf(Cur_filename, Dirspec, filenum - NUMREGFILES); + + /* directory creation */ + (void) memmove((char *) &argmk.where.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + argmk.where.name = Cur_filename; + argmk.attributes.mode.set_it = TRUE; + argmk.attributes.mode.mode = (NFSMODE_DIR | 0777); + argmk.attributes.uid.set_it = TRUE; + argmk.attributes.uid.uid = Cur_uid; + argmk.attributes.gid.set_it = TRUE; + argmk.attributes.gid.gid = Cur_gid; + argmk.attributes.size.set_it = FALSE; + argmk.attributes.size.size._p._u = (uint32_t) 0; + argmk.attributes.size.size._p._l = (uint32_t) 0; + argmk.attributes.atime.set_it = TRUE; + argmk.attributes.atime.atime.seconds = Cur_time.esec; + argmk.attributes.atime.atime.nseconds = Cur_time.usec * 1000; + argmk.attributes.mtime.set_it = TRUE; + argmk.attributes.mtime.mtime.seconds = Cur_time.esec; + argmk.attributes.mtime.mtime.nseconds = Cur_time.usec * 1000; + + (void) memset((char *) &repmk.resok.obj.handle, '\0', sizeof (nfs_fh3)); + verror(VAL_VERBOSE, I, "validating mkdir %s ...\n", Cur_filename); + val_op_mkdir(&argmk, &repmk); + + if (repmk.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &repmk.resok.obj.handle, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &repmk.resok.obj_attributes.attr, + sizeof(Cur_file_ptr->attributes3)); + (void) compare_sattr(Ops[MKDIR].name, Io_files[filenum].file_name, + &argmk.attributes, &Cur_file_ptr->attributes3); + } else { + Cur_file_ptr->state = Nonexistent; + verror(VAL_BATCH, W, "mkdir %s failed:%m\n", Cur_filename); + } + + } else if (filenum < NUMREGFILES + NUMDIRS + NUMLINKS ) { + + (void) sprintf(Cur_filename, Filespec, filenum - NUMDIRS); + + /* hard link creation */ + target_filenum = NUMFILES-NUMSYMLINKS-1-filenum; + (void) memmove((char *) &argln.file, + (char *) &Io_files[target_filenum].fh3, + sizeof (nfs_fh3)); + (void) memmove((char *) &argln.link.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + argln.link.name = Cur_filename; + + verror(VAL_VERBOSE, I, "validating link %s %s ...\n", + Io_files[target_filenum].file_name, Cur_filename); + val_op_link(&argln, &repln); + + if (repln.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &Io_files[target_filenum].fh3, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + Cur_file_ptr->attributes3 = Io_files[target_filenum].attributes3; + Io_files[target_filenum].attributes3.nlink++; + Cur_file_ptr->attributes3.nlink++; + } else { + Cur_file_ptr->state = Nonexistent; + verror(VAL_BATCH, W, "link %s failed: %m\n", Cur_filename); + exit(1); + } + + } else { + + (void) sprintf(Cur_filename, Symspec, filenum - + (NUMREGFILES + NUMDIRS + NUMLINKS)); + + /* symbolic link creation */ + target_filenum = NUMFILES-1-filenum; + (void) memmove((char *) &argsl.where.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + argsl.where.name = Cur_filename; + (void) sprintf(sl_target_path, + "./%s", Io_files[target_filenum].file_name); + argsl.symlink.symlink_attributes.size.set_it = TRUE; + argsl.symlink.symlink_attributes.size.size._p._u = + (uint32_t) 0; + argsl.symlink.symlink_attributes.size.size._p._l = + (uint32_t) strlen(sl_target_path); + argsl.symlink.symlink_data = sl_target_path; + argsl.symlink.symlink_attributes.mode.set_it = TRUE; + argsl.symlink.symlink_attributes.mode.mode = (NFSMODE_LNK | 0777); + argsl.symlink.symlink_attributes.uid.set_it = TRUE; + argsl.symlink.symlink_attributes.uid.uid = Cur_uid; + argsl.symlink.symlink_attributes.gid.set_it = TRUE; + argsl.symlink.symlink_attributes.gid.gid = Cur_gid; + argsl.symlink.symlink_attributes.atime.set_it = TRUE; + argsl.symlink.symlink_attributes.atime.atime.seconds = + Cur_time.esec; + argsl.symlink.symlink_attributes.atime.atime.nseconds = + Cur_time.usec * 1000; + argsl.symlink.symlink_attributes.mtime.set_it = TRUE; + argsl.symlink.symlink_attributes.mtime.mtime.seconds = + Cur_time.esec; + argsl.symlink.symlink_attributes.mtime.mtime.nseconds = + Cur_time.usec * 1000; + + verror(VAL_VERBOSE, I, "validating symlink %s %s ...\n", + sl_target_path, Cur_filename); + val_op_symlink(&argsl, &repsl); + + if (repsl.status == NFS3_OK) { + Cur_file_ptr->state = Exists; + + /* do a lookup to get file handle and attributes */ + (void) memmove((char *) &arglp.what.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + arglp.what.name = Cur_filename; + (void) memset((char *) &replp.resok.object, '\0', sizeof (nfs_fh3)); + + val_op_lookup(&arglp, &replp); + + if (replp.status == NFS3_OK) { + (void) memmove((char *) &Cur_file_ptr->fh3, + (char *) &replp.resok.object, + sizeof (nfs_fh3)); + (void) strcpy(Cur_file_ptr->file_name, Cur_filename); + (void) memmove((char *) &Cur_file_ptr->attributes3, + (char *) &replp.resok.obj_attributes.attr, + sizeof (Cur_file_ptr->attributes3)); + (void) compare_sattr(Ops[SYMLINK].name, + Io_files[filenum].file_name, + &argsl.symlink.symlink_attributes, + &Cur_file_ptr->attributes3); + } else { + verror(VAL_BATCH, W, "lookup %s failed: %m\n", + Cur_filename); + continue; + } + + /* validate readlink */ + (void) memmove((char *) &argrl.symlink, + (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + reprl.resok.data = sym_data; + + verror(VAL_VERBOSE, I, "validating readlink %s ...\n", + Cur_filename); + val_op_readlink(&argrl, &reprl); + + if (reprl.status == NFS3_OK) { + if (strcmp(sl_target_path, sym_data)) { + verror(VAL_BATCH, W, + "readlink %s error, result = %s, should be %s\n", + Cur_filename, reprl.resok.data, + sl_target_path); + } + } else { + verror(VAL_BATCH, W, "readlink %s failed:%m\n", + Cur_filename); + } + + } else { + Cur_file_ptr->state = Nonexistent; + verror(VAL_BATCH, W, "symlink %s failed: %m\n", Cur_filename); + } + } + } /* end for each file */ + +} /* validate_creation */ + + +static void +validate_attributes(void) +{ + int filenum; + LOOKUP3args arglp; + LOOKUP3res replp; + GETATTR3args argga; + GETATTR3res repga; + SETATTR3args argsa; + SETATTR3res repsa; + + /* validate fsstat */ + + /* validate lookup */ + for (filenum = 0; filenum < NUMFILES; filenum++) { + (void) memmove((char *) &arglp.what.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + arglp.what.name = Io_files[filenum].file_name; + (void) memset((char *) &replp.resok.object, '\0', sizeof (nfs_fh3)); + + verror(VAL_VERBOSE, I, "validating lookup %s ...\n", + Io_files[filenum].file_name); + val_op_lookup(&arglp, &replp); + + if (replp.status == NFS3_OK) { + if (memcmp((char *) &(Io_files[filenum].fh3), + (char *) &(replp.resok.object), sizeof (nfs_fh3))) { + verror(VAL_BATCH, W, "lookup %s: file handle mismatch\n", + Io_files[filenum].file_name); + exit(1); + } + (void) compare_fattr(Ops[LOOKUP].name, Io_files[filenum].file_name, + &Io_files[filenum].attributes3, + &replp.resok.obj_attributes.attr); + } else { + verror(VAL_BATCH, W, "lookup %s failed:%m\n", + Io_files[filenum].file_name); + } + } + + /* validate getattr */ + for (filenum = 0; filenum < NUMFILES; filenum++) { + (void) memmove((char *) &argga.object, + (char *) &Io_files[filenum].fh3, + sizeof (nfs_fh3)); + + verror(VAL_VERBOSE, I, "validating getattr %s ...\n", + Io_files[filenum].file_name); + val_op_getattr(&argga, &repga); + + if (repga.status == NFS3_OK) { + (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name, + &Io_files[filenum].attributes3, + &repga.resok.obj_attributes); + } else { + verror(VAL_BATCH, W, "getattr %s failed: %m\n", + Io_files[filenum].file_name); + } + } + + /*validate setattr */ + for (filenum = 0; filenum < NUMFILES; filenum++) { + sfs_gettime(&Cur_time); + (void) memmove((char *) &argsa.object, + (char *) &Io_files[filenum].fh3, + sizeof (nfs_fh3)); + argsa.new_attributes.mode.set_it = TRUE; + if (filenum >= NUMREGFILES && filenum < NUMREGFILES + NUMDIRS) + argsa.new_attributes.mode.mode = 0777; + else + argsa.new_attributes.mode.mode = 0666; + argsa.new_attributes.uid.set_it = FALSE; + argsa.new_attributes.uid.uid = 0; + argsa.new_attributes.gid.set_it = FALSE; + argsa.new_attributes.gid.gid = 0; + argsa.new_attributes.size.set_it = FALSE; + argsa.new_attributes.size.size._p._u = 0; + argsa.new_attributes.size.size._p._l = 0; + argsa.new_attributes.atime.set_it = TRUE; + argsa.new_attributes.atime.atime.seconds = Cur_time.esec; + argsa.new_attributes.atime.atime.nseconds = Cur_time.usec * 1000; + argsa.new_attributes.mtime.set_it = TRUE; + argsa.new_attributes.mtime.mtime.seconds = Cur_time.esec; + argsa.new_attributes.mtime.mtime.nseconds = Cur_time.usec * 1000; + argsa.guard.check = FALSE; + + verror(VAL_VERBOSE, I, "validating setattr %s ...\n", + Io_files[filenum].file_name); + val_op_setattr(&argsa, &repsa); + + if (repsa.status == NFS3_OK) { + (void) memmove((char *) &Io_files[filenum].attributes3, + (char *) &repsa.resok.obj_wcc.after.attr, + sizeof (Io_files[filenum].attributes3)); + (void) compare_sattr(Ops[SETATTR].name, Io_files[filenum].file_name, + &argsa.new_attributes, &repsa.resok.obj_wcc.after.attr); + + (void) memmove((char *) &argga.object, + (char *) &Io_files[filenum].fh3, + sizeof (nfs_fh3)); + + val_op_getattr(&argga, &repga); + + if (repga.status == NFS3_OK) { + (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name, + &Io_files[filenum].attributes3, + &repga.resok.obj_attributes); + } else { + verror(VAL_BATCH, W, "getattr %s failed: %m\n", + Io_files[filenum].file_name); + } + } else { + verror(VAL_BATCH, W, "setattr %s failed: %m\n", + Io_files[filenum].file_name); + exit(1); + } + } + +} /* validate_attributes */ + +#define WAITFOREOF 1 +/* + * need to check if readdirplus returns a reasonable amount of data. + */ +static void +val_rddirplus_retsize(uint32_t dircount, uint32_t maxcount, + READDIRPLUS3resok *rp) +{ + entryplus3 *esp; + static int eof_wait = 0; + int i; + int size = 0; + int tsize = 0; + int msize = 0; + double mpcnt = 0; + + esp = rp->reply.entries; + + for (i = 0; i < rp->count; i++) { + size += sizeof(esp[i].fileid); + size += strlen(esp[i].name) * sizeof(char); + size += sizeof(esp[i].cookie); + tsize += sizeof(esp[i].name_attributes); + tsize += sizeof(esp[i].name_handle.handle_follows); + tsize += esp[i].name_handle.handle.fh3_length * sizeof(char); + } + + msize = size + tsize; + mpcnt = (double) msize / (double) maxcount * 100; + + if (rp->reply.eof) { + verror(VAL_VERBOSE, I, "readdirplus on %s returned EOF.\n" + "\treceived %d bytes of directory information and %d bytes including\n" + "\tpost op attributes and filehandle.\n", + Testdirname, size, msize); + } else if (mpcnt < 80) { + eof_wait++; + if (eof_wait > WAITFOREOF) { + verror(VAL_BATCH, E, + "readdirplus on %s did not return a reasonable amount of data.\n" + "\treceived %d bytes. should receive close to %d bytes.\n", + Testdirname, msize, maxcount); + } else { + verror(VAL_VERBOSE, I, "readdirplus on %s did not return EOF.\n" + "\treceived %d bytes of directory information and %d bytes including\n" + "\tpost op attributes and filehandle.\n", + Testdirname, size, msize); + } + } else { + verror(VAL_VERBOSE, I, "readdirplus on %s did not return EOF.\n" + "\treceived %d bytes of directory information and %d bytes including\n" + "\tpost op attributes and filehandle.\n", + Testdirname, size, msize); + } +} + +static void +validate_read_write(void) +{ + struct { + uint16_t sum; /* checksum of data */ + uint16_t len; /* length of len and data */ + char data[DEFAULT_MAX_BUFSIZE - 2 * sizeof(uint16_t)]; + } block; + WRITE3args argwr; + WRITE3res repwr; + READ3args argrd; + READ3res reprd; + READDIR3args argdr; + READDIR3res repdr; + READDIRPLUS3args argdrp; + READDIRPLUS3res repdrp; + int maxblks; + int maxfiles; + int i; + int numfiles; + int filenum; + int blocknum; + entry3 entry_stream[SFS_MAXDIRENTS]; + entryplus3 entryplus_stream[SFS_MAXDIRENTS]; + + /* validate write */ + + /* get the maximum number of blocks sfs will write */ + maxblks = Io_dist_ptr->max_bufs; + maxfiles = maxblks > NUMREGFILES ? NUMREGFILES : maxblks; + + /* write maxblks - filenum + 1 blocks to each regular file */ + argwr.offset._p._u = argwr.offset._p._l = (uint32_t) 0; + argwr.stable = FILE_SYNC; + argwr.data.data_val = (char *)█ + + for (blocknum = 0; blocknum <= maxblks ; blocknum++) { + + for (i=0; i < sizeof(block.data); i++) + block.data[i] = (char)blocknum; + + for (filenum=0; filenum < maxfiles; filenum++) { + /* Write fewer blocks to files with higher numbers. */ + if (blocknum > (maxblks - filenum)) + break; + + /* set the length field */ + if (blocknum == (maxblks - filenum)) { + block.len = ((maxfiles - filenum) * + (Bytes_per_block/Kb_per_block)) - (sizeof(block.len) + + sizeof(block.sum)); + /* + * XXX - write kludge. + * + * Writes must be less than 8K in + * size or else the checksum will incur a buffer overflow + */ + block.len = (block.len % DEFAULT_MAX_BUFSIZE) - + (sizeof(block.len) + sizeof(block.sum)); + } else { + block.len = Bytes_per_block - (sizeof(block.len) + + sizeof(block.sum)); + } + block.sum = sum((unsigned char *) &block.len, + (int)(block.len + sizeof(block.len))); + + (void) memmove((char *) &argwr.file, + (char *) &Io_files[filenum].fh3, + sizeof (nfs_fh3)); + argwr.data.data_len = block.len + + sizeof(block.len) + sizeof(block.sum); + argwr.count = argwr.data.data_len; + + verror(VAL_VERBOSE, I, + "validating write %d bytes @ offset %lu to %s ...\n", + argwr.data.data_len, argwr.offset._p._l, + Io_files[filenum].file_name); + + val_op_write(&argwr, &repwr); + + if (repwr.status == NFS3_OK) { + (void) compare_fattr(Ops[WRITE].name, Io_files[filenum].file_name, + &Io_files[filenum].attributes3, + &repwr.resok.file_wcc.after.attr); + Io_files[filenum].attributes3 = repwr.resok.file_wcc.after.attr; + /* + * XXX-bookeeping kludge. + * + * Need to update hardlink attributes, so readdirplus vali- + * dation doesn't barf. This is necessary because setattr was + * validated on all the test files and the attributes in + * Io_files[] were updated accordingly. Since the write + * op has been validated on just the regular files, we have to + * make sure that the corresponding indexes in Io_files[] that + * point to the hard links reflect the current file attributes. + */ + if (filenum < NUMLINKS) { + Io_files[NUMREGFILES+NUMDIRS+NUMLINKS-1-filenum].attributes3 = Io_files[filenum].attributes3; + } + + } else { + verror(VAL_BATCH, W, "write %s failed: %m\n", + Io_files[filenum].file_name); + } + } + argwr.offset._p._l += Bytes_per_block; + } + + /* validate read */ + + for (filenum = 0; filenum < maxfiles; filenum++) { + (void) memmove((char *) &argrd.file, + (char *) &Io_files[filenum].fh3, + sizeof (nfs_fh3)); + argrd.offset._p._u = argrd.offset._p._l = (uint32_t) 0; + argrd.count = 0; + reprd.resok.data.data_len = 0; + maxblks = Io_files[filenum].attributes3.size._p._l / Bytes_per_block; + for (blocknum = 0; blocknum <= maxblks; blocknum ++) { + + if (argrd.count != reprd.resok.data.data_len) { + argrd.count -= reprd.resok.data.data_len; + reprd.resok.data.data_val = (char *)&block + + reprd.resok.data.data_len; + blocknum--; + } else { + if (blocknum < maxblks) + argrd.count = Bytes_per_block; + else + argrd.count = (maxfiles - filenum) + * (Bytes_per_block/Kb_per_block); + reprd.resok.data.data_val = (char *)█ + } + + verror(VAL_VERBOSE, I, + "validating read %lu bytes @ offset %lu from %s ...\n", + argrd.count, argrd.offset._p._l, + Io_files[filenum].file_name); + + val_op_read(&argrd, &reprd); + + if (reprd.status == NFS3_OK) { + (void) compare_fattr(Ops[READ].name, Io_files[filenum].file_name, + &Io_files[filenum].attributes3, + &reprd.resok.file_attributes.attr); + Io_files[filenum].attributes3 = reprd.resok.file_attributes.attr; + argrd.offset._p._l += reprd.resok.data.data_len; + } else { + verror(VAL_BATCH, W, "read %s failed: %m\n", + Io_files[filenum].file_name); + } + + if ((argrd.count == + (block.sum != sum((unsigned char *) &block.len, + (int)(block.len + sizeof(block.len)))))) { + verror(VAL_BATCH, W, "read %s checksum mismatch\n", + Io_files[filenum].file_name); + } + } + } + + /* validate readdir */ + (void) memmove((char *) &argdr.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + argdr.cookie._p._l = argdr.cookie._p._u = (uint32_t) 0; + (void) memset((char *) argdr.cookieverf, '\0', NFS3_COOKIEVERFSIZE); + argdr.count = DEFAULT_MAX_BUFSIZE; + + do { + (void) memset((char *) entry_stream, '\0', sizeof (entry3) * SFS_MAXDIRENTS); + (void) memset((char *) &repdr, '\0', sizeof(repdr)); + repdr.resok.count = SFS_MAXDIRENTS; + repdr.resok.reply.entries = entry_stream; + verror(VAL_VERBOSE, I, "validating readdir on %s ...\n", Testdirname); + val_op_readdir(&argdr, &repdr); + + + if (repdr.status == NFS3_OK) { + for (i = 0; i < repdr.resok.count; i++) { + for (filenum = 0; filenum < NUMFILES; filenum++) { + if (!strcmp(entry_stream[i].name, Io_files[filenum].file_name)) { if (entry_stream[i].fileid._p._l != + Io_files[filenum].attributes3.fileid._p._l) { + verror(VAL_BATCH, E, + "readdir %s error: file %s fileid mismatch\n", + Testdirname, entry_stream[i].name); + verror(VAL_BATCH, W, + " fileid: got = %lu, original = %lu\n", + entry_stream[i].fileid._p._l, + Io_files[filenum].attributes3.fileid._p._l); + } + /* + * mark file as either found or dup + */ + if (check_files[filenum].file_found && + !check_files[filenum].file_is_dup) + check_files[filenum].file_is_dup = 1; + else + check_files[filenum].file_found = 1; + break; + } + } + } + } else { + verror(VAL_BATCH, W, "readdir %s failed: %m\n", Testdirname); + } + + argdr.cookie = entry_stream[repdr.resok.count-1].cookie; + + } while (repdr.resok.reply.eof == 0); + + /* + * check if any known files have not been found + */ + for (i = 0; i < NUMFILES; i++) { + if (!check_files[i].file_found) + verror(VAL_BATCH, E, + "readdir %s error: file %s not found\n", + Testdirname, Io_files[i].file_name); + else { + if (check_files[i].file_is_dup) + verror(VAL_BATCH, E, + "readdir %s error: file %s returned more than once\n", + Testdirname, Io_files[i].file_name); + } + } + + /* validate readdirplus */ + (void) memset((void *) check_files, '\0', sizeof(check_files)); + + argdrp.cookie._p._l = argdrp.cookie._p._u = (uint32_t) 0; + (void) memmove((char *) &argdrp.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + (void) memset((char *) argdrp.cookieverf, '\0', NFS3_COOKIEVERFSIZE); + /* + * We validate readdirplus with dircount and maxcount both set at 8K. + * This is not the most efficient way, but this is how readdirplus is + * called in SFS. With the numbers set as such, maxcount becomes the + * bottleneck and we will not get 8K worth of directory info. What we + * should get is 8K worth which includes directory info plus post_op_ + * attributes and filehandle. + */ + argdrp.dircount = DEFAULT_MAX_BUFSIZE; + argdrp.maxcount = DEFAULT_MAX_BUFSIZE; + do { + (void) memset((char *) entryplus_stream, '\0', + sizeof (entryplus_stream)); + + (void) memset((char *) &repdrp, '\0', sizeof(repdrp)); + repdrp.resok.count = SFS_MAXDIRENTS; + repdrp.resok.reply.entries = entryplus_stream; + + verror(VAL_VERBOSE, I, "validating readdirplus on %s ...\n", + Testdirname); + val_op_readdirplus(&argdrp, &repdrp); + + if (repdrp.status == NFS3_OK) { + verror(VAL_VERBOSE, I, "readdirplus found %d entries in %s...\n", + repdrp.resok.count, Testdirname); + val_rddirplus_retsize(argdrp.dircount, argdrp.maxcount, + &repdrp.resok); + for (i = 0; i < repdrp.resok.count; i++) { + for (filenum = 0; filenum < NUMFILES; filenum++) { + if (!strcmp(entryplus_stream[i].name, + Io_files[filenum].file_name)) { + if (entryplus_stream[i].fileid._p._l != + Io_files[filenum].attributes3.fileid._p._l) { + verror(VAL_BATCH, E, + "readdirplus %s error: file %s fileid mismatch\n", + Testdirname, entryplus_stream[i].name); + verror(VAL_BATCH, W, + " fileid: got = %lu, original = %lu\n", + entryplus_stream[i].fileid._p._l, + Io_files[filenum].attributes3.fileid._p._l); + } + + /* + * mark file as either found or dup + */ + if (check_files[filenum].file_found && + !check_files[filenum].file_is_dup) + check_files[filenum].file_is_dup = 1; + else + check_files[filenum].file_found = 1; + + /* + * check to make sure post op attributes and + * file handle are returned. + */ + if (!entryplus_stream[i].name_attributes.attributes) + verror(VAL_BATCH, E, + "readdirplus %s warning: did not receive post op attributes for file %s\n\n", + Testdirname, entryplus_stream[i].name); + else + (void) compare_fattr(Ops[READDIRPLUS].name, + Io_files[filenum].file_name, + &Io_files[filenum].attributes3, &entryplus_stream[i].name_attributes.attr); + + if (!entryplus_stream[i].name_handle.handle_follows) + verror(VAL_BATCH, E, + "readdirplus %s warning: did not receive file handle for file %s\n\n", + Testdirname, entryplus_stream[i].name); + else + if (memcmp((void *) &Io_files[filenum].fh3.fh3_u.data, + (void *) &entryplus_stream[i].name_handle.handle.fh3_u.data, Io_files[filenum].fh3.fh3_length) != 0) + verror(VAL_BATCH, E, + "readdirplus %s error: file %s, filehandles do not match\n\n", + Testdirname, entryplus_stream[i].name); + break; + } + } + } + } else { + verror(VAL_BATCH, W, "readdirplus %s failed: %m\n", Testdirname); + } + argdrp.cookie = entryplus_stream[repdrp.resok.count-1].cookie; + + } while (repdrp.resok.reply.eof == 0); + /* + * check if any known files have not been found + */ + for (i = 0; i < NUMFILES; i++) { + if (!check_files[i].file_found) + verror(VAL_BATCH, E, + "readdirplus %s error: file %s not found\n", + Testdirname, Io_files[i].file_name); + else { + if (check_files[i].file_is_dup) + verror(VAL_BATCH, E, + "readdirplus %s error: file %s returned more than once\n", + Testdirname, Io_files[i].file_name); + } + } +} /* validate_read_write */ + + +static void +validate_rename(void) +{ + RENAME3args argrn; + RENAME3res reprn; + int filenum; + char newname[SFS_MAXNAMLEN]; + int rncount = 0; + + (void) memmove((char *) &argrn.from.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + (void) memmove((char *) &argrn.to.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + + for (filenum=0; filenum < NUMFILES; filenum++) { + if (Io_files[filenum].state != Exists) + continue; + + rncount++; + (void) sprintf(newname, "n%s", Io_files[filenum].file_name); + argrn.from.name = Io_files[filenum].file_name; + argrn.to.name = newname; + + verror(VAL_VERBOSE, I, "validating rename %s %s ...\n", + argrn.from.name, argrn.to.name); + + val_op_rename(&argrn, &reprn); + + if (reprn.status == NFS3_OK) { + (void) strcpy(Io_files[filenum].file_name, newname); + } else { + verror(VAL_BATCH, W, "rename %s to %s failed: %m\n", + Io_files[filenum].file_name, newname); + } + + } + + if (!rncount) { + verror(VAL_BATCH, E, "validate_rename: no files renamed\n"); + verror(VAL_BATCH, W, " due to previous operation error\n"); + } + +} /* validate_rename */ + + +static int +compare_fattr( + char * op, + char * fname, + fattr3 * attr1, + fattr3 * attr2) +{ + int ret = TRUE; + int prev_warn = FALSE; /* -1 info warning */ + int flag_error = FALSE; + + if (attr1->type != attr2->type) { + if (attr1->type == 0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " type: current = %d, previous = %d\n", + attr2->type, attr1->type); + attr1->type = attr2->type; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " type: current = %d, previous = %d\n", + attr2->type, attr1->type); + ret = FALSE; + flag_error = TRUE; + } + } + + if ((attr1->mode & NFSMODE_MASK) != (attr2->mode & NFSMODE_MASK)) { + if (attr1->mode == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " mode: current = %7lo, previous = %7lo\n", + attr2->mode, attr1->mode); + attr1->mode = attr2->mode; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " mode: current = %7lo, previous = %7lo\n", + attr2->mode, attr1->mode); + ret = FALSE; + flag_error = TRUE; + } + } + + if (attr1->nlink != attr2->nlink) { + if (attr1->nlink == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, + " nlink: current = %lu, previous = %lu\n", + attr2->nlink, attr1->nlink); + ret = FALSE; + attr1->nlink = attr2->nlink; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, + " nlink: current = %lu, previous = %lu\n", + attr2->nlink, attr1->nlink); + ret = FALSE; + flag_error = TRUE; + } + } + + + /* + * Check for user "nobody", UID -2, which may be untranslated from + * sixteen-bit two's complement. + */ + if (attr1->uid != attr2->uid && !((attr2->uid == (unsigned int)0xFFFFFFFE || + attr2->uid == 65534) && attr1->uid ==0)) { + if (attr1->uid == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, + " uid: current = %lu, previous = %lu\n", + attr2->uid, attr1->uid); + attr1->uid = attr2->uid; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, + " uid: current = %lu, previous = %lu\n", + attr2->uid, attr1->uid); + ret = FALSE; + flag_error = TRUE; + } + } + + if (attr1->gid != attr2->gid && attr2->gid != 0) { +/* + if (attr1->gid != attr2->gid) { +*/ + if (attr1->gid == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, + " gid: current = %lu, previous = %lu\n", + attr2->gid, attr1->gid); + attr1->gid = attr2->gid; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, + " gid: current = %lu, previous = %lu\n", + attr2->gid, attr1->gid); + ret = FALSE; + flag_error = TRUE; + } + } + + if (attr1->size._p._l != attr2->size._p._l) { + if (strcmp(op, Ops[WRITE].name)) { + if (attr1->size._p._l == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, + " size: current = %lu, previous = %lu\n", + attr2->size._p._l, attr1->size._p._l); + attr1->size._p._l = attr2->size._p._l; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, + " size: current = %lu, previous = %lu\n", + attr2->size._p._l, attr1->size._p._l); + ret = FALSE; + flag_error = TRUE; + } + } + } + + /* compare rdev only if type == NFCHR or NFBLK */ + if ((attr1->type == NF3CHR || attr1->type == NF3BLK) && + (attr1->rdev.specdata1 != attr2->rdev.specdata1 || + attr1->rdev.specdata2 != attr2->rdev.specdata2)) { + if (attr1->rdev.specdata1 == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, + " rdev: current = %lu %lu, previous = %lu %lu\n", + attr2->rdev.specdata1, attr2->rdev.specdata2, + attr1->rdev.specdata1, attr1->rdev.specdata2); + attr1->rdev.specdata1 = attr2->rdev.specdata1; + attr1->rdev.specdata2 = attr2->rdev.specdata2; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, + " rdev: current = %lu %lu, previous = %lu %lu\n", + attr2->rdev.specdata1, attr2->rdev.specdata2, + attr1->rdev.specdata1, attr1->rdev.specdata2); + ret = FALSE; + flag_error = TRUE; + } + } + + if (attr1->fsid._p._l != attr2->fsid._p._l) { + if (attr1->fsid._p._l == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, " fsid: current = %lu, previous = %lu\n", + attr2->fsid._p._l, attr1->fsid._p._l); + attr1->fsid._p._l = attr2->fsid._p._l; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " fsid: current = %lu, previous = %lu\n", + attr2->fsid._p._l, attr1->fsid._p._l); + ret = FALSE; + flag_error = TRUE; + } + } + + if (attr1->fileid._p._l != attr2->fileid._p._l) { + if (attr1->fileid._p._l == (unsigned int)0xFFFFFFFF) { + prev_warn = TRUE; + if (ret) { + verror(VAL_BATCH, I, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, I, + " fileid: current = %lu, previous = %lu\n", + attr2->fileid._p._l, attr1->fileid._p._l); + attr1->fileid._p._l = attr2->fileid._p._l; + ret = FALSE; + } + else { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, + " fileid: current = %lu, previous = %lu\n", + attr2->fileid._p._l, attr1->fileid._p._l); + ret = FALSE; + flag_error = TRUE; + } + } + + if (prev_warn) { + verror(VAL_BATCH, I, + "\n Warning: the previous value of a field is -1,\n"); + verror(VAL_BATCH, I, + " this resulted from an unused field returned by\n"); + verror(VAL_BATCH, I, + " the previous operation on this file/directory.\n"); + verror(VAL_BATCH, I, + " The current value is now stored for future comparison\n\n"); + } + + if (flag_error) + verror(VAL_BATCH, W,"\n"); + + return(flag_error); + +} /* ckompare_fattr */ + + +uint32_t sattr_types[8] = { + 0000000, /* NF3NON */ + 0100000, /* NF3REG */ + 0040000, /* NF3DIR */ + 0060000, /* NF3BLK */ + 0020000, /* NF3CHR */ + 0120000, /* NF3LNK */ + 0140000, /* NF3SOCK */ + 0010000 }; /* NF3FIFO */ +static int +compare_sattr( + char * op, + char * fname, + sattr3 * attr1, + fattr3 * attr2) +{ + int ret = TRUE; + char msg[80]; + + msg[0] = '\0'; + +#ifdef notyet + if (attr1->mode.mode.set_it == TRUE && + (((attr1->mode.mode & NFSTYPE_FMT) != sattr_types[attr2->type]) && + ((attr1->mode.mode & NFSTYPE_FMT) != sattr_types[0]) || + ((attr1->mode.mode & NFSMODE_FMT) != (attr2->mode & NFSMODE_FMT)))) { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + + } + verror(VAL_BATCH, E, " mode: returned = %7o, specified = %7o\n", + attr1->mode.mode, attr2->mode); + ret = FALSE; + } +#endif + + /* + * Check for user "nobody", UID -2, which may be untranslated from + * sixteen-bit two's complement. + */ + if (attr1->uid.set_it == TRUE && attr1->uid.uid != attr2->uid && + !((attr2->uid == (unsigned int)0xFFFFFFFE || + attr2->uid == 65534) && + attr1->uid.uid == 0)) { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + if (attr1->uid.uid == 0) + (void) strcat(msg," (is root UID mapped to other UID?)"); + verror(VAL_BATCH, E, " uid: returned = %lu, specified = %lu %s\n", + attr2->uid, attr1->uid.uid, msg); + ret = FALSE; + } + + if (attr1->gid.set_it == TRUE && attr1->gid.gid != attr2->gid && + attr2->gid != 0) { +/* + if (attr1->gid.set_it == TRUE && attr1->gid != attr2->gid) { +*/ + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " gid: returned = %lu, specified = %lu\n", + attr2->gid, attr1->gid.gid); + ret = FALSE; + } + + if (attr1->size.set_it == TRUE && + attr1->size.size._p._l != attr2->size._p._l) { + if (ret) { + verror(VAL_BATCH, E, "%s: %s attribute mismatch\n", + fname, op); + } + verror(VAL_BATCH, E, " size: returned = %lu, specified = %lu\n", + attr2->size._p._l, attr1->size.size._p._l); + ret = FALSE; + } + + if (!ret) + verror(VAL_BATCH, W,"\n"); + + return(ret); + +} /* compare_sattr */ + + +/* + * Return the BSD checksum of buf[0..len-1] + */ +static uint16_t +sum( + unsigned char * buf, + int len) +{ + uint16_t cksum; + + cksum = 0; + for (; len--; buf++) { + if (cksum & 01) + cksum = (cksum >> 1) + 0x8000; + else + cksum >>= 1; + cksum += (uint16_t) *buf; + cksum &= 0xFFFF; + } + return(cksum); + +} /* sum */ + + +static void +validate_remove(void) +{ + REMOVE3args argrm; + REMOVE3res reprm; + RMDIR3args argrd; + RMDIR3res reprd; + LOOKUP3args arglp; + LOOKUP3res replp; + nfsstat3 reply; + int filenum; + char * op; + + for (filenum = 0; filenum < NUMFILES; filenum++) { + + if (Io_files[filenum].state != Exists) + continue; + + if (Io_files[filenum].attributes3.type == NF3DIR) { + op = Ops[RMDIR].name; + verror(VAL_VERBOSE, I, "validating rmdir %s ...\n", + Io_files[filenum].file_name); + + (void) memmove((char *) &argrd.object.dir, + (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + argrd.object.name = Io_files[filenum].file_name; + val_op_rmdir(&argrd, &reprd); + reply = reprd.status; + } else { + op = Ops[REMOVE].name; + verror(VAL_VERBOSE, I, "validating remove %s ...\n", + Io_files[filenum].file_name); + + (void) memmove((char *) &argrm.object.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + argrm.object.name = Io_files[filenum].file_name; + val_op_remove(&argrm, &reprm); + reply = reprm.status; + } + + if (reply == NFS3_OK) { + /* make sure the file is removed from the directory */ + (void) memmove((char *) &arglp.what.dir, (char *) &Export_dir.fh3, + sizeof (nfs_fh3)); + arglp.what.name = Io_files[filenum].file_name; + val_op_lookup(&arglp, &replp); + + if (replp.status == NFS3ERR_NOENT) { + Io_files[filenum].state = Nonexistent; + } else if (replp.status == NFS3_OK) { + verror(VAL_BATCH, W, "%s %s: file not removed\n", + op, Io_files[filenum].file_name); + } else { + verror(VAL_BATCH, W, "lookup %s failed: %m\n", + Io_files[filenum].file_name); + + } + } else { + verror(VAL_BATCH, W, "%s %s failed: %m\n", op, + Io_files[filenum].file_name); + } + + } + +} /* validate_remove */ + + +static void +validate_cleanup(void) +{ + free(Io_files); + free(Non_io_files); + free(Dirs); + free(Symlinks); + clnt_destroy(NFS_client); +} /* validate_cleanup */ + + +static void +validate_exit(void) +{ + if (!Validate_errors) { + verror(VAL_BATCH, I, "validation completed successfully.\n"); + exit(0); + } else { + verror(VAL_BATCH, I, "validation terminated with errors\n"); + exit(1); + } + +} /* validate_exit */ + + +/* PRINTFLIKE3 */ +static void +verror( + int opt, + ValMsgType msgtype, + char * fmt, + ...) +{ + va_list ap; + char buf[1024]; + char * bp = buf; + char * fp; + char * sp; + int repeat; + + va_start(ap, fmt); + + /* + * Expand the "%m" format character into the descriptive string + * for the current value of errno. Printf handles the other "%" + * formatting characters. + */ + if (Validate >= opt) { + for (fp = fmt; *fp; fp++) { + if (*fp == '%' && fp[1] == 'm') { + if ((sp = strerror(errno)) == NULL) { + (void) sprintf(bp, "unknown error %d", errno); + } else { + (void) strcpy(bp, sp); + } + bp = buf + strlen(buf); + fp++; + } else { + *bp++ = *fp; + } + } + *bp = '\0'; + (void) vfprintf(stderr, buf, ap); + } + va_end(ap); + + if (msgtype != I) + Validate_errors++; + + if (msgtype == W && Validate == VAL_INTERACTIVE) { + repeat = 1; + while (repeat) { + char ans[80]; + + (void) fprintf(stderr, "continue? (y or n) "); + if (!fgets(ans,80,stdin)) { + (void) fprintf(stderr, "\n"); + continue; + } + if (ans[0] == 'n' || ans[0] == 'N') { + validate_exit(); + exit(1); + } else if (ans[0] == 'y' || ans[0] == 'Y') { + repeat = 0; + break; + } + } + } + +} /* verror */ + + +static void +val_op_null(void) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_NULL, + xdr_void, (char *)0, xdr_void, (char *)0, + Nfs_timers[Ops[NULLCALL].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "null"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_getattr(GETATTR3args *args, GETATTR3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_GETATTR, + xdr_GETATTR3args, (char *)args, xdr_GETATTR3res, (char *)reply, + Nfs_timers[Ops[GETATTR].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "getattr"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_setattr(SETATTR3args *args, SETATTR3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_SETATTR, + xdr_SETATTR3args, (char *)args, xdr_SETATTR3res, (char *)reply, + Nfs_timers[Ops[SETATTR].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "setattr"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_lookup(LOOKUP3args *args, LOOKUP3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP, + xdr_LOOKUP3args, (char *)args, xdr_LOOKUP3res, (char *)reply, + Nfs_timers[Ops[LOOKUP].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "lookup"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_access(ACCESS3args *args, ACCESS3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_ACCESS, + xdr_ACCESS3args, (char *)args, xdr_ACCESS3res, (char *)reply, + Nfs_timers[Ops[ACCESS].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "access"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_readlink(READLINK3args *args, READLINK3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_READLINK, + xdr_READLINK3args, (char *)args, xdr_READLINK3res, (char *)reply, + Nfs_timers[Ops[READLINK].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "readlink"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_read(READ3args *args, READ3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_READ, + xdr_READ3args, (char *)args, xdr_READ3res, (char *)reply, + Nfs_timers[Ops[READ].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "read"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_write(WRITE3args *args, WRITE3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_WRITE, + xdr_WRITE3args, (char *)args, xdr_WRITE3res, (char *)reply, + Nfs_timers[Ops[WRITE].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "write"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_create(CREATE3args *args, CREATE3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_CREATE, + xdr_CREATE3args, (char *)args, xdr_CREATE3res, (char *)reply, + Nfs_timers[Ops[CREATE].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "create"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_mkdir(MKDIR3args *args, MKDIR3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_MKDIR, + xdr_MKDIR3args, (char *)args, xdr_MKDIR3res, (char *)reply, + Nfs_timers[Ops[MKDIR].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "mkdir"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_symlink(SYMLINK3args *args, SYMLINK3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_SYMLINK, + xdr_SYMLINK3args, (char *)args, xdr_SYMLINK3res, (char *)reply, + Nfs_timers[Ops[SYMLINK].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "symlink"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_mknod(MKNOD3args *args, MKNOD3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_MKNOD, + xdr_MKNOD3args, (char *)args, xdr_MKNOD3res, (char *)reply, + Nfs_timers[Ops[MKNOD].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "mknod"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_remove(REMOVE3args *args, REMOVE3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_REMOVE, + xdr_REMOVE3args, (char *)args, xdr_REMOVE3res, (char *)reply, + Nfs_timers[Ops[REMOVE].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "remove"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_rmdir(RMDIR3args *args, RMDIR3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_RMDIR, + xdr_RMDIR3args, (char *)args, xdr_RMDIR3res, (char *)reply, + Nfs_timers[Ops[RMDIR].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "rmdir"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_rename(RENAME3args *args, RENAME3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME, + xdr_RENAME3args, (char *)args, xdr_RENAME3res, (char *)reply, + Nfs_timers[Ops[RENAME].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "rename"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_link(LINK3args *args, LINK3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_LINK, + xdr_LINK3args, (char *)args, xdr_LINK3res, (char *)reply, + Nfs_timers[Ops[LINK].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, + "link"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_readdir(READDIR3args *args, READDIR3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_READDIR, + xdr_READDIR3args, (char *)args, xdr_READDIR3res, (char *)reply, + Nfs_timers[Ops[READDIR].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "readdir"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, + NFSPROC3_READDIRPLUS, xdr_READDIRPLUS3args, (char *)args, + xdr_READDIRPLUS3res, (char *)reply, + Nfs_timers[Ops[READDIRPLUS].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, + "readdirplus"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_fsstat(FSSTAT3args *args, FSSTAT3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_FSSTAT, + xdr_FSSTAT3args, (char *)args, xdr_FSSTAT3res, (char *)reply, + Nfs_timers[Ops[FSSTAT].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "fsstat"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_fsinfo(FSINFO3args *args, FSINFO3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_FSINFO, + xdr_FSINFO3args, (char *)args, xdr_FSINFO3res, (char *)reply, + Nfs_timers[Ops[FSINFO].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "fsinfo"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_pathconf(PATHCONF3args *args, PATHCONF3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_PATHCONF, + xdr_PATHCONF3args, (char *)args, xdr_PATHCONF3res, (char *)reply, + Nfs_timers[Ops[PATHCONF].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "pathconf"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +val_op_commit(COMMIT3args *args, COMMIT3res *reply) +{ + int rpc_stat; + + /* CONSTCOND */ + while (1) { + rpc_stat = clnt_call(NFS_client, NFSPROC3_COMMIT, + xdr_COMMIT3args, (char *)args, xdr_COMMIT3res, (char *)reply, + Nfs_timers[Ops[COMMIT].call_class]); + if (rpc_stat == RPC_SUCCESS) + break; + if (rpc_stat != RPC_TIMEDOUT) { + clnt_perror(NFS_client, "commit"); + Validate_errors++; + validate_exit(); + } + } +} + +static void +create_3tmp_handles(void) +{ + int filenum; + for (filenum = 0; filenum < NUMFILES; filenum++) { + + if(Io_files[filenum].fh_data == (sfs_fh_data *)0) + { + Io_files[filenum].fh_data = + calloc(1,sizeof(sfs_fh_data)); + Io_files[filenum].attributes2.type = NFNON; + Io_files[filenum].attributes3.type = NF3NON; + } + } +} + +static void +delete_3tmp_handles(void) +{ + int filenum; + for (filenum = 0; filenum < NUMFILES; filenum++) { + + if(Io_files[filenum].fh_data != (sfs_fh_data *)0) + { + free(Io_files[filenum].fh_data); + Io_files[filenum].fh_data= (sfs_fh_data *)0; + } + } +} +/* sfs_3_vld.c */ diff --git a/TBBT/trace_play/sfs_3_xdr.c b/TBBT/trace_play/sfs_3_xdr.c new file mode 100644 index 0000000..01a69fb --- /dev/null +++ b/TBBT/trace_play/sfs_3_xdr.c @@ -0,0 +1,1720 @@ +#ifndef lint +static char sfs_3_xdrSid[] = "@(#)sfs_3_xdr.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/* + * -------------------------- sfs_3_xdr.c -------------------------- + * + * XDR routines for the nfs protocol. + * + *.Exported_routines + * xdr_GETATTR3args(XDR *, GETATTR3args *) + * xdr_GETATTR3res(XDR *, GETATTR3res *) + * xdr_SETATTR3args(XDR *, SETATTR3args *) + * xdr_SETATTR3res(XDR *, SETATTR3res *) + * xdr_LOOKUP3args(XDR *, LOOKUP3args *) + * xdr_LOOKUP3res(XDR *, LOOKUP3res *) + * xdr_ACCESS3args(XDR *, ACCESS3args *) + * xdr_ACCESS3res(XDR *, ACCESS3res *) + * xdr_READLINK3args(XDR *, READLINK3args *) + * xdr_READLINK3res(XDR *, READLINK3res *) + * xdr_READ3args(XDR *, READ3args *) + * xdr_READ3res(XDR *, READ3res *) + * xdr_WRITE3args(XDR *, WRITE3args *) + * xdr_WRITE3res(XDR *, WRITE3res *) + * xdr_CREATE3args(XDR *, CREATE3args *) + * xdr_CREATE3res(XDR *, CREATE3res *) + * xdr_MKDIR3args(XDR *, MKDIR3args *) + * xdr_MKDIR3res(XDR *, MKDIR3res *) + * xdr_SYMLINK3args(XDR *, SYMLINK3args *) + * xdr_SYMLINK3res(XDR *, SYMLINK3res *) + * xdr_MKNOD3args(XDR *, MKNOD3args *) + * xdr_MKNOD3res(XDR *, MKNOD3res *) + * xdr_REMOVE3args(XDR *, REMOVE3args *) + * xdr_REMOVE3res(XDR *, REMOVE3res *) + * xdr_RMDIR3args(XDR *, RMDIR3args *) + * xdr_RMDIR3res(XDR *, RMDIR3res *) + * xdr_RENAME3args(XDR *, RENAME3args *) + * xdr_RENAME3res(XDR *, RENAME3res *) + * xdr_LINK3args(XDR *, LINK3args *) + * xdr_LINK3res(XDR *, LINK3res *) + * xdr_READDIR3args(XDR *, READDIR3args *) + * xdr_READDIR3res(XDR *, READDIR3res *) + * xdr_READDIRPLUS3args(XDR *, READDIRPLUS3args *) + * xdr_READDIRPLUS3res(XDR *, READDIRPLUS3res *) + * xdr_FSSTAT3args(XDR *, FSSTAT3args *) + * xdr_FSSTAT3res(XDR *, FSSTAT3res *) + * xdr_FSINFO3args(XDR *, FSINFO3args *) + * xdr_FSINFO3res(XDR *, FSINFO3res *) + * xdr_PATHCONF3args(XDR *, PATHCONF3args *) + * xdr_PATHCONF3res(XDR *, PATHCONF3res *) + * xdr_COMMIT3args(XDR *, COMMIT3args *) + * xdr_COMMIT3res(XDR *, COMMIT3res *) + * xdr_mntres3(XDR *, mountres3 *) + * + *.Local_routines + * xdr_string3(XDR *, char **, unsigned int) + * xdr_filename3(XDR *, filename3 *) + * xdr_nfspath3(XDR *, nfspath3 *) + * xdr_nfs_uint64_t(XDR *, nfs_uint64_t *) + * xdr_cookieverf3(XDR *, cookieverf3) + * xdr_createverf3(XDR *, createverf3) + * xdr_writeverf3(XDR *, writeverf3) + * xdr_nfs_fh3(XDR *, nfs_fh3 *) + * xdr_diropargs3(XDR *, diropargs3 *) + * xdr_nfstime3(XDR *, nfstime3 *) + * xdr_specdata3(XDR *, specdata3 *) + * xdr_nfsstat3(XDR *, nfsstat3 *) + * xdr_ftype3(XDR *, ftype3 *) + * xdr_fattr3(XDR *, fattr3 *) + * xdr_post_op_attr(XDR *, post_op_attr *) + * xdr_wcc_attr(XDR *, wcc_attr *) + * xdr_pre_op_attr(XDR *, pre_op_attr *) + * xdr_wcc_data(XDR *, wcc_data *) + * xdr_post_op_fh3(XDR *, post_op_fh3 *) + * xdr_time_how(XDR *, time_how *) + * xdr_set_mode3(XDR *, set_mode3 *) + * xdr_set_uid3(XDR *, set_uid3 *) + * xdr_set_gid3(XDR *, set_gid3 *) + * xdr_set_size3(XDR *, set_size3 *) + * xdr_set_atime(XDR *, set_atime *) + * xdr_set_mtime(XDR *, set_mtime *) + * xdr_sattr3(XDR *, sattr3 *) + * xdr_GETATTR3resok(XDR *, GETATTR3resok *) + * xdr_sattrguard3(XDR *, sattrguard3 *) + * xdr_SETATTR3resok(XDR *, SETATTR3resok *) + * xdr_SETATTR3resfail(XDR *, SETATTR3resfail *) + * xdr_LOOKUP3resok(XDR *, LOOKUP3resok *) + * xdr_LOOKUP3resfail(XDR *, LOOKUP3resfail *) + * xdr_ACCESS3resok(XDR *, ACCESS3resok *) + * xdr_ACCESS3resfail(XDR *, ACCESS3resfail *) + * xdr_READLINK3resok(XDR *, READLINK3resok *) + * xdr_READLINK3resfail(XDR *, READLINK3resfail *) + * xdr_READ3resok(XDR *, READ3resok *) + * xdr_READ3resfail(XDR *, READ3resfail *) + * xdr_stable_how(XDR *, stable_how *) + * xdr_WRITE3resok(XDR *, WRITE3resok *) + * xdr_WRITE3resfail(XDR *, WRITE3resfail *) + * xdr_createmode3(XDR *, createmode3 *) + * xdr_createhow3(XDR *, createhow3 *) + * xdr_CREATE3resok(XDR *, CREATE3resok *) + * xdr_CREATE3resfail(XDR *, CREATE3resfail *) + * xdr_MKDIR3resok(XDR *, MKDIR3resok *) + * xdr_MKDIR3resfail(XDR *, MKDIR3resfail *) + * xdr_symlinkdata3(XDR *, symlinkdata3 *) + * xdr_SYMLINK3resok(XDR *, SYMLINK3resok *) + * xdr_SYMLINK3resfail(XDR *, SYMLINK3resfail *) + * xdr_devicedata3(XDR *, devicedata3 *) + * xdr_mknoddata3(XDR *, mknoddata3 *) + * xdr_MKNOD3resok(XDR *, MKNOD3resok *) + * xdr_MKNOD3resfail(XDR *, MKNOD3resfail *) + * xdr_REMOVE3resok(XDR *, REMOVE3resok *) + * xdr_REMOVE3resfail(XDR *, REMOVE3resfail *) + * xdr_RMDIR3resok(XDR *, RMDIR3resok *) + * xdr_RMDIR3resfail(XDR *, RMDIR3resfail *) + * xdr_RENAME3resok(XDR *, RENAME3resok *) + * xdr_RENAME3resfail(XDR *, RENAME3resfail *) + * xdr_LINK3resok(XDR *, LINK3resok *) + * xdr_LINK3resfail(XDR *, LINK3resfail *) + * xdr_getdirlist(XDR *, READDIR3resok *) + * xdr_READDIR3resok(XDR *, READDIR3resok *) + * xdr_READDIR3resfail(XDR *, READDIR3resfail *) + * xdr_getdirpluslist(XDR *, READDIRPLUS3resok *) + * xdr_READDIRPLUS3resok(XDR *, READDIRPLUS3resok *) + * xdr_READDIRPLUS3resfail(XDR *, READDIRPLUS3resfail *) + * xdr_FSSTAT3resok(XDR *, FSSTAT3resok *) + * xdr_FSSTAT3resfail(XDR *, FSSTAT3resfail *) + * xdr_FSINFO3resok(XDR *, FSINFO3resok *) + * xdr_FSINFO3resfail(XDR *, FSINFO3resfail *) + * xdr_PATHCONF3resok(XDR *, PATHCONF3resok *) + * xdr_PATHCONF3resfail(XDR *, PATHCONF3resfail *) + * xdr_COMMIT3resok(XDR *, COMMIT3resok *) + * xdr_COMMIT3resfail(XDR *, COMMIT3resfail *) + * + *.Revision_History + * 28-Jun-94 ChakChung Ng Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "sfs_c_def.h" + +/* + * ----------------------- SFS XDR Routines ------------------------- + */ + +/* + * xdr_string3 deals with "C strings" - arrays of bytes that are terminated by + * a NULL character. The parameter cpp references a pointer to storage. If the + * pointer is null, then necessary storage is allocated. The last parameter is + * the max allowed length of the string as allowed by the system. The NFS + * Version 3 protocol does not place limits on strings, but the implementation + * needs to place a reasonable limit to avoid problems. + */ + +static bool_t +xdr_string3( + XDR * xdrs, + char ** cpp, + unsigned int maxsize) +{ + char *sp; + unsigned int size, nodesize; + + /* + * first deal with the length since xdr strings are counted-strings + */ + sp = *cpp; + switch (xdrs->x_op) { + case XDR_FREE: + if (sp == NULL || sp == nfs3nametoolong) + return(TRUE); /* already free */ + /* FALLTHROUGH */ + case XDR_ENCODE: + size = strlen(sp); + break; + } + + if (!xdr_u_int(xdrs, &size)) + return(FALSE); + + /* + * now deal with the actual bytes + */ + switch (xdrs->x_op) { + case XDR_DECODE: + if (size > maxsize) { + int xskp = (((((int)size) + 3) / 4) * 4); + + *cpp = nfs3nametoolong; + if ((xdrs->x_handy -= xskp) < 0) + return(FALSE); + xdrs->x_private += xskp; + return(TRUE); + } + nodesize = size + 1; + if (nodesize == 0) + return(TRUE); + if (sp == NULL) { + sp = (char *)malloc(nodesize); + *cpp = sp; + if (sp == NULL) + return(FALSE); + } + sp[size] = 0; /* 0 through size-1 are original string */ + /* FALLTHROUGH */ + case XDR_ENCODE: + return(xdr_opaque(xdrs, sp, size)); + case XDR_FREE: + nodesize = size + 1; + mem_free((caddr_t)sp, nodesize); + *cpp = NULL; + return(TRUE); + } + + return(FALSE); +} + +static bool_t +xdr_filename3( + XDR * xdrs, + filename3 * objp) +{ + + return(xdr_string3(xdrs, objp, NFS_MAXNAMLEN)); +} + +static bool_t +xdr_nfspath3( + XDR * xdrs, + nfspath3 * objp) +{ + + return(xdr_string3(xdrs, objp, NFS_MAXPATHLEN)); +} + +static bool_t +xdr_nfs_uint64_t( + XDR *xdrs, + nfs_uint64_t * objp) +{ + return(xdr_int(xdrs, (int *)&objp->_p._u) && + xdr_int(xdrs, (int *)&objp->_p._l)); +} + +static bool_t +xdr_cookieverf3( + XDR * xdrs, + cookieverf3 objp) +{ + return(xdr_opaque(xdrs, objp, NFS3_COOKIEVERFSIZE)); +} + +static bool_t +xdr_createverf3( + XDR * xdrs, + createverf3 objp) +{ + return(xdr_opaque(xdrs, objp, NFS3_CREATEVERFSIZE)); +} + +static bool_t +xdr_writeverf3( + XDR * xdrs, + writeverf3 objp) +{ + return(xdr_opaque(xdrs, objp, NFS3_WRITEVERFSIZE)); +} + +static bool_t +xdr_nfs_fh3( + XDR * xdrs, + nfs_fh3 * objp) +{ + if (!xdr_u_int(xdrs, &objp->fh3_length)) + return(FALSE); + + if (objp->fh3_length > NFS3_FHSIZE) + return(FALSE); + if (xdrs->x_op == XDR_DECODE || xdrs->x_op == XDR_ENCODE) + return(xdr_opaque(xdrs, objp->fh3_u.data, objp->fh3_length)); + + if (xdrs->x_op == XDR_FREE) + return(TRUE); + + return(FALSE); +} + +static bool_t +xdr_diropargs3( + XDR * xdrs, + diropargs3 * objp) +{ + if (xdr_nfs_fh3(xdrs, &objp->dir)) + return(xdr_filename3(xdrs, &objp->name)); + return(FALSE); +} + +static bool_t +xdr_nfstime3( + XDR * xdrs, + nfstime3 * objp) +{ + if (xdr_uint32_t(xdrs, &objp->seconds)) + return(xdr_uint32_t(xdrs, &objp->nseconds)); + return(FALSE); +} + +static bool_t +xdr_specdata3( + XDR * xdrs, + specdata3 * objp) +{ + if (xdr_uint32_t(xdrs, &objp->specdata1)) + return(xdr_uint32_t(xdrs, &objp->specdata2)); + return(FALSE); +} + +static bool_t +xdr_nfsstat3( + XDR * xdrs, + nfsstat3 * objp) +{ + return(xdr_enum(xdrs, (enum_t *)objp)); +} + +static bool_t +xdr_ftype3( + XDR * xdrs, + ftype3 * objp) +{ + return(xdr_enum(xdrs, (enum_t *)objp)); +} + +static bool_t +xdr_fattr3( + XDR * xdrs, + fattr3 * objp) +{ + if (!xdr_ftype3(xdrs, &objp->type)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->mode)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->nlink)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->uid)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->gid)) + return(FALSE); + if (!xdr_nfs_uint64_t(xdrs, &objp->size)) + return(FALSE); + if (!xdr_nfs_uint64_t(xdrs, &objp->used)) + return(FALSE); + if (!xdr_specdata3(xdrs, &objp->rdev)) + return(FALSE); + if (!xdr_nfs_uint64_t(xdrs, &objp->fsid)) + return(FALSE); + if (!xdr_nfs_uint64_t(xdrs, &objp->fileid)) + return(FALSE); + if (!xdr_nfstime3(xdrs, &objp->atime)) + return(FALSE); + if (!xdr_nfstime3(xdrs, &objp->mtime)) + return(FALSE); + if (!xdr_nfstime3(xdrs, &objp->ctime)) + return(FALSE); + return(TRUE); +} + +static bool_t +xdr_post_op_attr( + XDR * xdrs, + post_op_attr * objp) +{ + if (!xdr_bool(xdrs, &objp->attributes)) + return(FALSE); + switch (objp->attributes) { + case TRUE: return(xdr_fattr3(xdrs, &objp->attr)); + case FALSE: return(TRUE); + default: return(FALSE); + } +} + +static bool_t +xdr_wcc_attr( + XDR * xdrs, + wcc_attr * objp) +{ + if (xdr_nfs_uint64_t(xdrs, &objp->size) && + xdr_nfstime3(xdrs, &objp->mtime)) + return(xdr_nfstime3(xdrs, &objp->ctime)); + return(FALSE); +} + +static bool_t +xdr_pre_op_attr( + XDR * xdrs, + pre_op_attr * objp) +{ + if (!xdr_bool(xdrs, &objp->attributes)) + return(FALSE); + switch (objp->attributes) { + case TRUE: return(xdr_wcc_attr(xdrs, &objp->attr)); + case FALSE: return(TRUE); + default: return(FALSE); + } +} + +static bool_t +xdr_wcc_data( + XDR * xdrs, + wcc_data * objp) +{ + if (xdr_pre_op_attr(xdrs, &objp->before)) + return(xdr_post_op_attr(xdrs, &objp->after)); + return(FALSE); +} + +static bool_t +xdr_post_op_fh3( + XDR * xdrs, + post_op_fh3 * objp) +{ + if (!xdr_bool(xdrs, &objp->handle_follows)) + return(FALSE); + switch (objp->handle_follows) { + case TRUE: return(xdr_nfs_fh3(xdrs, &objp->handle)); + case FALSE: return(TRUE); + default: return(FALSE); + } +} + +static bool_t +xdr_time_how( + XDR * xdrs, + time_how * objp) +{ + return(xdr_enum(xdrs, (enum_t *)objp)); +} + +static bool_t +xdr_set_mode3( + XDR * xdrs, + set_mode3 * objp) +{ + if (!xdr_bool(xdrs, &objp->set_it)) + return(FALSE); + if (objp->set_it == TRUE) + return(xdr_uint32_t(xdrs, &objp->mode)); + return(TRUE); +} + +static bool_t +xdr_set_uid3( + XDR * xdrs, + set_uid3 * objp) +{ + if (!xdr_bool(xdrs, &objp->set_it)) + return(FALSE); + if (objp->set_it == TRUE) + return(xdr_uint32_t(xdrs, &objp->uid)); + return(TRUE); +} + +static bool_t +xdr_set_gid3( + XDR * xdrs, + set_gid3 * objp) +{ + if (!xdr_bool(xdrs, &objp->set_it)) + return(FALSE); + if (objp->set_it == TRUE) + return(xdr_uint32_t(xdrs, &objp->gid)); + return(TRUE); +} + +static bool_t +xdr_set_size3( + XDR * xdrs, + set_size3 * objp) +{ + if (!xdr_bool(xdrs, &objp->set_it)) + return(FALSE); + if (objp->set_it == TRUE) + return(xdr_nfs_uint64_t(xdrs, &objp->size)); + return(TRUE); +} + +static bool_t +xdr_set_atime( + XDR * xdrs, + set_atime * objp) +{ + if (!xdr_time_how(xdrs, &objp->set_it)) + return(FALSE); + if (objp->set_it == SET_TO_CLIENT_TIME) + return(xdr_nfstime3(xdrs, &objp->atime)); + return(TRUE); +} + +static bool_t +xdr_set_mtime( + XDR * xdrs, + set_mtime * objp) +{ + if (!xdr_time_how(xdrs, &objp->set_it)) + return(FALSE); + if (objp->set_it == SET_TO_CLIENT_TIME) + return(xdr_nfstime3(xdrs, &objp->mtime)); + return(TRUE); +} + +static bool_t +xdr_sattr3( + XDR * xdrs, + sattr3 * objp) +{ + if (xdr_set_mode3(xdrs, &objp->mode) && + xdr_set_uid3(xdrs, &objp->uid) && + xdr_set_gid3(xdrs, &objp->gid) && + xdr_set_size3(xdrs, &objp->size) && + xdr_set_atime(xdrs, &objp->atime)) + return(xdr_set_mtime(xdrs, &objp->mtime)); + return(FALSE); +} + + +bool_t +xdr_GETATTR3args( + XDR * xdrs, + GETATTR3args * objp) +{ + return(xdr_nfs_fh3(xdrs, &objp->object)); +} + +static bool_t +xdr_GETATTR3resok( + XDR * xdrs, + GETATTR3resok * objp) +{ + return(xdr_fattr3(xdrs, &objp->obj_attributes)); +} + +bool_t +xdr_GETATTR3res( + XDR * xdrs, + GETATTR3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_GETATTR3resok(xdrs, &objp->resok)); + return(TRUE); +} + + +static bool_t +xdr_sattrguard3( + XDR * xdrs, + sattrguard3 * objp) +{ + if (!xdr_bool(xdrs, &objp->check)) + return(FALSE); + switch (objp->check) { + case TRUE: return(xdr_nfstime3(xdrs, &objp->obj_ctime)); + case FALSE: return(TRUE); + default: return(FALSE); + } +} + +bool_t +xdr_SETATTR3args( + XDR * xdrs, + SETATTR3args * objp) +{ + if (xdr_nfs_fh3(xdrs, &objp->object) && + xdr_sattr3(xdrs, &objp->new_attributes)) + return(xdr_sattrguard3(xdrs, &objp->guard)); + return(FALSE); +} + +static bool_t +xdr_SETATTR3resok( + XDR * xdrs, + SETATTR3resok * objp) +{ + return(xdr_wcc_data(xdrs, &objp->obj_wcc)); +} + +static bool_t +xdr_SETATTR3resfail( + XDR * xdrs, + SETATTR3resfail * objp) +{ + return(xdr_wcc_data(xdrs, &objp->obj_wcc)); +} + +bool_t +xdr_SETATTR3res( + XDR * xdrs, + SETATTR3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_SETATTR3resok(xdrs, &objp->resok)); + return(xdr_SETATTR3resfail(xdrs, &objp->resfail)); +} + + +bool_t +xdr_LOOKUP3args( + XDR * xdrs, + LOOKUP3args * objp) +{ + return(xdr_diropargs3(xdrs, &objp->what)); +} + +static bool_t +xdr_LOOKUP3resok( + XDR * xdrs, + LOOKUP3resok * objp) +{ + if (xdr_nfs_fh3(xdrs, &objp->object) && + xdr_post_op_attr(xdrs, &objp->obj_attributes)) + return(xdr_post_op_attr(xdrs, &objp->dir_attributes)); + return(FALSE); +} + +static bool_t +xdr_LOOKUP3resfail( + XDR * xdrs, + LOOKUP3resfail * objp) +{ + return(xdr_post_op_attr(xdrs, &objp->dir_attributes)); +} + +bool_t +xdr_LOOKUP3res( + XDR * xdrs, + LOOKUP3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_LOOKUP3resok(xdrs, &objp->resok)); + return(xdr_LOOKUP3resfail(xdrs, &objp->resfail)); +} + + +bool_t +xdr_ACCESS3args( + XDR * xdrs, + ACCESS3args * objp) +{ + if (xdr_nfs_fh3(xdrs, &objp->object)) + return(xdr_uint32_t(xdrs, &objp->access)); + return(FALSE); +} + +static bool_t +xdr_ACCESS3resok( + XDR * xdrs, + ACCESS3resok * objp) +{ + if (xdr_post_op_attr(xdrs, &objp->obj_attributes)) + return(xdr_uint32_t(xdrs, &objp->access)); + return(FALSE); +} + +static bool_t +xdr_ACCESS3resfail( + XDR * xdrs, + ACCESS3resfail * objp) +{ + return(xdr_post_op_attr(xdrs, &objp->obj_attributes)); +} + +bool_t +xdr_ACCESS3res( + XDR * xdrs, + ACCESS3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_ACCESS3resok(xdrs, &objp->resok)); + return(xdr_ACCESS3resfail(xdrs, &objp->resfail)); +} + + +bool_t +xdr_READLINK3args( + XDR * xdrs, + READLINK3args * objp) +{ + return(xdr_nfs_fh3(xdrs, &objp->symlink)); +} + +static bool_t +xdr_READLINK3resok( + XDR * xdrs, + READLINK3resok * objp) +{ + if (xdr_post_op_attr(xdrs, &objp->symlink_attributes)) + return(xdr_nfspath3(xdrs, &objp->data)); + return(FALSE); +} + +static bool_t +xdr_READLINK3resfail( + XDR * xdrs, + READLINK3resfail * objp) +{ + return(xdr_post_op_attr(xdrs, &objp->symlink_attributes)); +} + +bool_t +xdr_READLINK3res( + XDR * xdrs, + READLINK3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_READLINK3resok(xdrs, &objp->resok)); + return(xdr_READLINK3resfail(xdrs, &objp->resfail)); +} + + +bool_t +xdr_READ3args( + XDR * xdrs, + READ3args * objp) +{ + + if (xdr_nfs_fh3(xdrs, &objp->file) && + xdr_nfs_uint64_t(xdrs, &objp->offset)) + return(xdr_uint32_t(xdrs, &objp->count)); + return(FALSE); +} + +static bool_t +xdr_READ3resok( + XDR * xdrs, + READ3resok * objp) +{ + if (xdr_post_op_attr(xdrs, &objp->file_attributes) && + xdr_uint32_t(xdrs, &objp->count) && + xdr_bool(xdrs, &objp->eof)) + return(xdr_bytes(xdrs, (char **)&objp->data.data_val, + (unsigned int *)&objp->data.data_len, + ~0)); + return(FALSE); +} + +static bool_t +xdr_READ3resfail( + XDR * xdrs, + READ3resfail * objp) +{ + return(xdr_post_op_attr(xdrs, &objp->file_attributes)); +} + +bool_t +xdr_READ3res( + XDR * xdrs, + READ3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_READ3resok(xdrs, &objp->resok)); + return(xdr_READ3resfail(xdrs, &objp->resfail)); +} + + +static bool_t +xdr_stable_how( + XDR * xdrs, + stable_how * objp) +{ + return(xdr_enum(xdrs, (enum_t *)objp)); +} + +bool_t +xdr_WRITE3args( + XDR * xdrs, + WRITE3args * objp) +{ + if (xdr_nfs_fh3(xdrs, &objp->file) && + xdr_nfs_uint64_t(xdrs, &objp->offset) && + xdr_uint32_t(xdrs, &objp->count) && + xdr_stable_how(xdrs, &objp->stable)) + return(xdr_bytes(xdrs, (char **)&objp->data.data_val, + (unsigned int *)&objp->data.data_len, + ~0)); + return(FALSE); +} + +static bool_t +xdr_WRITE3resok( + XDR * xdrs, + WRITE3resok * objp) +{ + if (xdr_wcc_data(xdrs, &objp->file_wcc) && + xdr_uint32_t(xdrs, &objp->count) && + xdr_stable_how(xdrs, &objp->committed)) + return(xdr_writeverf3(xdrs, objp->verf)); + return(FALSE); +} + +static bool_t +xdr_WRITE3resfail( + XDR * xdrs, + WRITE3resfail * objp) +{ + return(xdr_wcc_data(xdrs, &objp->file_wcc)); +} + +bool_t +xdr_WRITE3res( + XDR * xdrs, + WRITE3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_WRITE3resok(xdrs, &objp->resok)); + return(xdr_WRITE3resfail(xdrs, &objp->resfail)); +} + + +static bool_t +xdr_createmode3( + XDR * xdrs, + createmode3 * objp) +{ + return(xdr_enum(xdrs, (enum_t *)objp)); +} + +static bool_t +xdr_createhow3( + XDR * xdrs, + createhow3 * objp) +{ + if (!xdr_createmode3(xdrs, &objp->mode)) + return(FALSE); + switch (objp->mode) { + case UNCHECKED: + case GUARDED: + return(xdr_sattr3(xdrs, &objp->createhow3_u.obj_attributes)); + case EXCLUSIVE: + return(xdr_createverf3(xdrs, objp->createhow3_u.verf)); + default: + return(FALSE); + } +} + +bool_t +xdr_CREATE3args( + XDR * xdrs, + CREATE3args * objp) +{ + if (xdr_diropargs3(xdrs, &objp->where)) + return(xdr_createhow3(xdrs, &objp->how)); + return(FALSE); +} + +static bool_t +xdr_CREATE3resok( + XDR * xdrs, + CREATE3resok * objp) +{ + if (xdr_post_op_fh3(xdrs, &objp->obj) && + xdr_post_op_attr(xdrs, &objp->obj_attributes)) + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); + return(FALSE); +} + +static bool_t +xdr_CREATE3resfail( + XDR * xdrs, + CREATE3resfail * objp) +{ + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); +} + +bool_t +xdr_CREATE3res( + XDR * xdrs, + CREATE3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_CREATE3resok(xdrs, &objp->resok)); + return(xdr_CREATE3resfail(xdrs, &objp->resfail)); +} + + +bool_t +xdr_MKDIR3args( + XDR * xdrs, + MKDIR3args * objp) +{ + if (xdr_diropargs3(xdrs, &objp->where)) + return(xdr_sattr3(xdrs, &objp->attributes)); + return(FALSE); +} + +static bool_t +xdr_MKDIR3resok( + XDR * xdrs, + MKDIR3resok * objp) +{ + if (xdr_post_op_fh3(xdrs, &objp->obj) && + xdr_post_op_attr(xdrs, &objp->obj_attributes)) + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); + return(FALSE); +} + +static bool_t +xdr_MKDIR3resfail( + XDR * xdrs, + MKDIR3resfail * objp) +{ + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); +} + +bool_t +xdr_MKDIR3res( + XDR * xdrs, + MKDIR3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_MKDIR3resok(xdrs, &objp->resok)); + return(xdr_MKDIR3resfail(xdrs, &objp->resfail)); +} + + +static bool_t +xdr_symlinkdata3( + XDR * xdrs, + symlinkdata3 * objp) +{ + if (xdr_sattr3(xdrs, &objp->symlink_attributes)) + return(xdr_nfspath3(xdrs, &objp->symlink_data)); + return(FALSE); +} + +bool_t +xdr_SYMLINK3args( + XDR * xdrs, + SYMLINK3args * objp) +{ + if (xdr_diropargs3(xdrs, &objp->where)) + return(xdr_symlinkdata3(xdrs, &objp->symlink)); + return(FALSE); +} + +static bool_t +xdr_SYMLINK3resok( + XDR * xdrs, + SYMLINK3resok * objp) +{ + if (xdr_post_op_fh3(xdrs, &objp->obj) && + xdr_post_op_attr(xdrs, &objp->obj_attributes)) + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); + return(FALSE); +} + +static bool_t +xdr_SYMLINK3resfail( + XDR * xdrs, + SYMLINK3resfail * objp) +{ + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); +} + +bool_t +xdr_SYMLINK3res( + XDR * xdrs, + SYMLINK3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_SYMLINK3resok(xdrs, &objp->resok)); + return(xdr_SYMLINK3resfail(xdrs, &objp->resfail)); +} + + +static bool_t +xdr_devicedata3( + XDR * xdrs, + devicedata3 * objp) +{ + if (xdr_sattr3(xdrs, &objp->dev_attributes)) + return(xdr_specdata3(xdrs, &objp->spec)); + return(FALSE); +} + +static bool_t +xdr_mknoddata3( + XDR * xdrs, + mknoddata3 * objp) +{ + if (!xdr_ftype3(xdrs, &objp->type)) + return(FALSE); + switch (objp->type) { + case NF3CHR: + case NF3BLK: + if (!xdr_devicedata3(xdrs, &objp->mknoddata3_u.device)) + return(FALSE); + break; + case NF3SOCK: + case NF3FIFO: + if (!xdr_sattr3(xdrs, &objp->mknoddata3_u.pipe_attributes)) + return(FALSE); + break; + } + return(TRUE); +} + +bool_t +xdr_MKNOD3args( + XDR * xdrs, + MKNOD3args * objp) +{ + if (xdr_diropargs3(xdrs, &objp->where)) + return(xdr_mknoddata3(xdrs, &objp->what)); + return(FALSE); +} + +static bool_t +xdr_MKNOD3resok( + XDR * xdrs, + MKNOD3resok * objp) +{ + if (xdr_post_op_fh3(xdrs, &objp->obj) && + xdr_post_op_attr(xdrs, &objp->obj_attributes)) + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); + return(FALSE); +} + +static bool_t +xdr_MKNOD3resfail( + XDR * xdrs, + MKNOD3resfail * objp) +{ + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); +} + +bool_t +xdr_MKNOD3res( + XDR * xdrs, + MKNOD3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_MKNOD3resok(xdrs, &objp->resok)); + return(xdr_MKNOD3resfail(xdrs, &objp->resfail)); +} + + +bool_t +xdr_REMOVE3args( + XDR * xdrs, + REMOVE3args * objp) +{ + return(xdr_diropargs3(xdrs, &objp->object)); +} + +static bool_t +xdr_REMOVE3resok( + XDR * xdrs, + REMOVE3resok * objp) +{ + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); +} + +static bool_t +xdr_REMOVE3resfail( + XDR * xdrs, + REMOVE3resfail * objp) +{ + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); +} + +bool_t +xdr_REMOVE3res( + XDR * xdrs, + REMOVE3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_REMOVE3resok(xdrs, &objp->resok)); + return(xdr_REMOVE3resfail(xdrs, &objp->resfail)); +} + + +bool_t +xdr_RMDIR3args( + XDR * xdrs, + RMDIR3args * objp) +{ + return(xdr_diropargs3(xdrs, &objp->object)); +} + +static bool_t +xdr_RMDIR3resok( + XDR * xdrs, + RMDIR3resok * objp) +{ + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); +} + +static bool_t +xdr_RMDIR3resfail( + XDR * xdrs, + RMDIR3resfail * objp) +{ + return(xdr_wcc_data(xdrs, &objp->dir_wcc)); +} + +bool_t +xdr_RMDIR3res( + XDR * xdrs, + RMDIR3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_RMDIR3resok(xdrs, &objp->resok)); + return(xdr_RMDIR3resfail(xdrs, &objp->resfail)); +} + + +bool_t +xdr_RENAME3args( + XDR * xdrs, + RENAME3args * objp) +{ + if (xdr_diropargs3(xdrs, &objp->from)) + return(xdr_diropargs3(xdrs, &objp->to)); + return(FALSE); +} + +static bool_t +xdr_RENAME3resok( + XDR * xdrs, + RENAME3resok * objp) +{ + if (xdr_wcc_data(xdrs, &objp->fromdir_wcc)) + return(xdr_wcc_data(xdrs, &objp->todir_wcc)); + return(FALSE); +} + +static bool_t +xdr_RENAME3resfail( + XDR * xdrs, + RENAME3resfail * objp) +{ + if (xdr_wcc_data(xdrs, &objp->fromdir_wcc)) + return(xdr_wcc_data(xdrs, &objp->todir_wcc)); + return(FALSE); +} + +bool_t +xdr_RENAME3res( + XDR * xdrs, + RENAME3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_RENAME3resok(xdrs, &objp->resok)); + return(xdr_RENAME3resfail(xdrs, &objp->resfail)); +} + + +bool_t +xdr_LINK3args( + XDR * xdrs, + LINK3args * objp) +{ + if (xdr_nfs_fh3(xdrs, &objp->file)) + return(xdr_diropargs3(xdrs, &objp->link)); + return(FALSE); +} + +static bool_t +xdr_LINK3resok( + XDR * xdrs, + LINK3resok * objp) +{ + if (xdr_post_op_attr(xdrs, &objp->file_attributes)) + return(xdr_wcc_data(xdrs, &objp->linkdir_wcc)); + return(FALSE); +} + +static bool_t +xdr_LINK3resfail( + XDR * xdrs, + LINK3resfail * objp) +{ + if (xdr_post_op_attr(xdrs, &objp->file_attributes)) + return(xdr_wcc_data(xdrs, &objp->linkdir_wcc)); + return(FALSE); +} + +bool_t +xdr_LINK3res( + XDR * xdrs, + LINK3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_LINK3resok(xdrs, &objp->resok)); + return(xdr_LINK3resfail(xdrs, &objp->resfail)); +} + + +bool_t +xdr_READDIR3args( + XDR * xdrs, + READDIR3args * objp) +{ + if (xdr_nfs_fh3(xdrs, &objp->dir) && + xdr_nfs_uint64_t(xdrs, &objp->cookie) && + xdr_cookieverf3(xdrs, objp->cookieverf)) + return(xdr_uint32_t(xdrs, &objp->count)); + return(FALSE); +} + +#define roundtoint(x) (((x) + sizeof (int) - 1) & ~(sizeof (int) - 1)) + +/* + * DECODE ONLY + */ +static bool_t +xdr_getdirlist( + XDR * xdrs, + READDIR3resok * objp) +{ + register int i; + bool_t valid; + unsigned int namlen; + char name[SFS_MAXNAMLEN]; + nfs_uint64_t fileid, cookie; + entry3 *dp; + + i = 0; + dp = objp->reply.entries; + for (;;) { + if (!xdr_bool(xdrs, &valid)) + return(FALSE); + if (!valid) + break; + if (!xdr_nfs_uint64_t(xdrs, &fileid) || + !xdr_u_int(xdrs, &namlen)) + return(FALSE); + if (namlen >= SFS_MAXNAMLEN) + namlen = SFS_MAXNAMLEN - 1; + if (!xdr_opaque(xdrs, name, namlen) || + !xdr_nfs_uint64_t(xdrs, &cookie)) + return(FALSE); + name[namlen] = '\0'; + if (i < SFS_MAXDIRENTS) { + dp[i].fileid = fileid; + (void)memmove(dp[i].name, name, (namlen+1)); + dp[i].cookie = cookie; + i++; + } + } + objp->count = i; + if (!xdr_bool(xdrs, &objp->reply.eof)) + return(FALSE); + return(TRUE); +} + +static bool_t +xdr_READDIR3resok( + XDR * xdrs, + READDIR3resok * objp) +{ + if (!xdr_post_op_attr(xdrs, &objp->dir_attributes)) + return(FALSE); + if (!xdr_cookieverf3(xdrs, objp->cookieverf)) + return(FALSE); + if (xdrs->x_op == XDR_DECODE) + return(xdr_getdirlist(xdrs, objp)); + return(TRUE); +} + +static bool_t +xdr_READDIR3resfail( + XDR * xdrs, + READDIR3resfail * objp) +{ + return(xdr_post_op_attr(xdrs, &objp->dir_attributes)); +} + +bool_t +xdr_READDIR3res( + XDR * xdrs, + READDIR3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_READDIR3resok(xdrs, &objp->resok)); + return(xdr_READDIR3resfail(xdrs, &objp->resfail)); +} + +bool_t +xdr_READDIRPLUS3args( + XDR * xdrs, + READDIRPLUS3args * objp) +{ + if (xdr_nfs_fh3(xdrs, &objp->dir) && + xdr_nfs_uint64_t(xdrs, &objp->cookie) && + xdr_cookieverf3(xdrs, objp->cookieverf) && + xdr_uint32_t(xdrs, &objp->dircount)) + return(xdr_uint32_t(xdrs, &objp->maxcount)); + return(FALSE); +} + +/* + * copy post_op_attr from s2 to s1 + */ +static void +copy_post_op_attr(post_op_attr *s1, post_op_attr *s2) +{ + s1->attributes = s2->attributes; + (void) memmove((void *) &s1->attr, (void *) &s2->attr, + sizeof (fattr3)); +} + +/* + * copy post_op_fh3 from s2 to s1 + */ +static void +copy_post_op_fh3(post_op_fh3 *s1, post_op_fh3 *s2) +{ + s1->handle_follows = s2->handle_follows; + (void) memmove((void *) &s1->handle, (void *) &s2->handle, + sizeof (nfs_fh3)); +} + +/* + * DECODE ONLY + */ +static bool_t +xdr_getdirpluslist( + XDR * xdrs, + READDIRPLUS3resok * objp) +{ + register int i; + bool_t valid; + unsigned int namlen; + char name[SFS_MAXNAMLEN]; + nfs_uint64_t fileid, cookie; + entryplus3 *dp; + post_op_attr at; + post_op_fh3 fh; + + i = 0; + dp = objp->reply.entries; + for (;;) { + if (!xdr_bool(xdrs, &valid)) + return(FALSE); + if (!valid) + break; + if (!xdr_nfs_uint64_t(xdrs, &fileid) || + !xdr_u_int(xdrs, &namlen)) + return(FALSE); + if (namlen >= SFS_MAXNAMLEN) + namlen = SFS_MAXNAMLEN - 1; + if (!xdr_opaque(xdrs, name, namlen) || + !xdr_nfs_uint64_t(xdrs, &cookie)) + return(FALSE); + name[namlen] = '\0'; + if (!xdr_post_op_attr(xdrs, &at)) + return(FALSE); + if (!xdr_post_op_fh3(xdrs, &fh)) + return(FALSE); + if (i < SFS_MAXDIRENTS) { + dp[i].fileid = fileid; + (void)memmove(dp[i].name, name, (namlen+1)); + dp[i].cookie = cookie; + copy_post_op_attr(&dp[i].name_attributes, &at); + copy_post_op_fh3(&dp[i].name_handle, &fh); + i++; + } + } + + objp->count = i; + if (!xdr_bool(xdrs, &objp->reply.eof)) + return(FALSE); + return(TRUE); +} + +static bool_t +xdr_READDIRPLUS3resok( + XDR * xdrs, + READDIRPLUS3resok * objp) +{ + + if (!xdr_post_op_attr(xdrs, &objp->dir_attributes)) + return(FALSE); + if (!xdr_cookieverf3(xdrs, objp->cookieverf)) + return(FALSE); + if (xdrs->x_op == XDR_DECODE) + return(xdr_getdirpluslist(xdrs, objp)); + return(TRUE); +} + +static bool_t +xdr_READDIRPLUS3resfail( + XDR * xdrs, + READDIRPLUS3resfail * objp) +{ + return(xdr_post_op_attr(xdrs, &objp->dir_attributes)); +} + +bool_t +xdr_READDIRPLUS3res( + XDR * xdrs, + READDIRPLUS3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_READDIRPLUS3resok(xdrs, &objp->resok)); + return(xdr_READDIRPLUS3resfail(xdrs, &objp->resfail)); +} + +bool_t +xdr_FSSTAT3args( + XDR * xdrs, + FSSTAT3args * objp) +{ + return(xdr_nfs_fh3(xdrs, &objp->fsroot)); +} + +static bool_t +xdr_FSSTAT3resok( + XDR * xdrs, + FSSTAT3resok * objp) +{ + if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) + return(FALSE); + if (!xdr_nfs_uint64_t(xdrs, &objp->tbytes)) + return(FALSE); + if (!xdr_nfs_uint64_t(xdrs, &objp->fbytes)) + return(FALSE); + if (!xdr_nfs_uint64_t(xdrs, &objp->abytes)) + return(FALSE); + if (!xdr_nfs_uint64_t(xdrs, &objp->tfiles)) + return(FALSE); + if (!xdr_nfs_uint64_t(xdrs, &objp->ffiles)) + return(FALSE); + if (!xdr_nfs_uint64_t(xdrs, &objp->afiles)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->invarsec)) + return(FALSE); + return(TRUE); +} + +static bool_t +xdr_FSSTAT3resfail( + XDR * xdrs, + FSSTAT3resfail * objp) +{ + return(xdr_post_op_attr(xdrs, &objp->obj_attributes)); +} + +bool_t +xdr_FSSTAT3res( + XDR * xdrs, + FSSTAT3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_FSSTAT3resok(xdrs, &objp->resok)); + return(xdr_FSSTAT3resfail(xdrs, &objp->resfail)); +} + +bool_t +xdr_FSINFO3args( + XDR * xdrs, + FSINFO3args * objp) +{ + return(xdr_nfs_fh3(xdrs, &objp->fsroot)); +} + +static bool_t +xdr_FSINFO3resok( + XDR * xdrs, + FSINFO3resok * objp) +{ + if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->rtmax)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->rtpref)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->rtmult)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->wtmax)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->wtpref)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->wtmult)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->dtpref)) + return(FALSE); + if (!xdr_nfs_uint64_t(xdrs, &objp->maxfilesize)) + return(FALSE); + if (!xdr_nfstime3(xdrs, &objp->time_delta)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->properties)) + return(FALSE); + return(TRUE); +} + +static bool_t +xdr_FSINFO3resfail( + XDR * xdrs, + FSINFO3resfail * objp) +{ + return(xdr_post_op_attr(xdrs, &objp->obj_attributes)); +} + +bool_t +xdr_FSINFO3res( + XDR * xdrs, + FSINFO3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_FSINFO3resok(xdrs, &objp->resok)); + return(xdr_FSINFO3resfail(xdrs, &objp->resfail)); +} + +bool_t +xdr_PATHCONF3args( + XDR * xdrs, + PATHCONF3args * objp) +{ + return(xdr_nfs_fh3(xdrs, &objp->object)); +} + +static bool_t +xdr_PATHCONF3resok( + XDR * xdrs, + PATHCONF3resok * objp) +{ + if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->link_max)) + return(FALSE); + if (!xdr_uint32_t(xdrs, &objp->name_max)) + return(FALSE); + if (!xdr_bool(xdrs, &objp->no_trunc)) + return(FALSE); + if (!xdr_bool(xdrs, &objp->chown_restricted)) + return(FALSE); + if (!xdr_bool(xdrs, &objp->case_insensitive)) + return(FALSE); + if (!xdr_bool(xdrs, &objp->case_preserving)) + return(FALSE); + return(TRUE); +} + +static bool_t +xdr_PATHCONF3resfail( + XDR * xdrs, + PATHCONF3resfail * objp) +{ + return(xdr_post_op_attr(xdrs, &objp->obj_attributes)); +} + +bool_t +xdr_PATHCONF3res( + XDR * xdrs, + PATHCONF3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_PATHCONF3resok(xdrs, &objp->resok)); + return(xdr_PATHCONF3resfail(xdrs, &objp->resfail)); +} + +bool_t +xdr_COMMIT3args( + XDR * xdrs, + COMMIT3args * objp) +{ + if (xdr_nfs_fh3(xdrs, &objp->file) && + xdr_nfs_uint64_t(xdrs, &objp->offset)) + return(xdr_uint32_t(xdrs, &objp->count)); + return(FALSE); +} + +static bool_t +xdr_COMMIT3resok( + XDR * xdrs, + COMMIT3resok * objp) +{ + if (xdr_wcc_data(xdrs, &objp->file_wcc)) + return(xdr_writeverf3(xdrs, objp->verf)); + return(FALSE); +} + +static bool_t +xdr_COMMIT3resfail( + XDR * xdrs, + COMMIT3resfail * objp) +{ + return(xdr_wcc_data(xdrs, &objp->file_wcc)); +} + +bool_t +xdr_COMMIT3res( + XDR * xdrs, + COMMIT3res * objp) +{ + if (!xdr_nfsstat3(xdrs, &objp->status)) + return(FALSE); + if (objp->status == NFS3_OK) + return(xdr_COMMIT3resok(xdrs, &objp->resok)); + return(xdr_COMMIT3resfail(xdrs, &objp->resfail)); +} + + +bool_t +xdr_dirpath( + XDR * xdrs, + dirpath * objp) +{ + return(xdr_string(xdrs, objp, MNTPATHLEN)); +} + +static bool_t +xdr_fhandle3( + XDR * xdrs, + fhandle3 * objp) +{ + return(xdr_bytes(xdrs, (char **) &objp->fhandle3_val, + (unsigned int *) &objp->fhandle3_len, NFS3_FHSIZE)); +} + +static bool_t +xdr_mntres3_ok( + XDR * xdrs, + mntres3_ok * objp) +{ + if (xdr_fhandle3(xdrs, &objp->fhandle)) { + return(xdr_array(xdrs, + (void **) &objp->auth_flavors.auth_flavors_val, + (unsigned int *) &objp->auth_flavors.auth_flavors_len, + ~0, sizeof (int), (xdrproc_t) xdr_int)); + } + return(FALSE); +} + +bool_t +xdr_mntres3( + XDR * xdrs, + mountres3 * objp) +{ + if (!xdr_enum(xdrs, (enum_t *) &objp->fhs_status)) + return(FALSE); + if (objp->fhs_status == MNT_OK) + return(xdr_mntres3_ok(xdrs, &objp->mntres3_u.mntinfo)); + return(TRUE); +} +/* sfs_3_xdr.c */ diff --git a/TBBT/trace_play/sfs_c_bio.c b/TBBT/trace_play/sfs_c_bio.c new file mode 100644 index 0000000..ed35c42 --- /dev/null +++ b/TBBT/trace_play/sfs_c_bio.c @@ -0,0 +1,1023 @@ +#ifndef lint +static char sfs_c_bioSid[] = "@(#)sfs_c_bio.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/* + * ---------------------- sfs_c_bio.c --------------------- + * + * Routines that attempt to simulate biod behavior + * + * The routines contained here model biod behavior. Simply call + * biod_init() to replace regular calls to op_read() and op_write() + * with calls to op_biod_read() and op_biod_write(). The variables + * max_out_writes and max_out_reads control the maximum number of + * outstanding writes and reads respectively. + * + *.Exported Routines + * int biod_init(int, int); + * void biod_turn_on(void); + * void op_biod_write(int, int, int); + * void op_biod_read(int); + * + *.Local Routines + * uint32_t biod_clnt_call(CLIENT *, uint32_t, + * xdrproc_t, void *); + * struct biod_req *biod_get_reply(CLIENT *, xdrproc_t, + * void *, struct timeval *); + * int biod_poll_wait(CLIENT *, uint32_t); + * + *.Revision_History + * 03-May-94 Robinson + * History now kept in SCCS + * 03-Mar-92 0.1.0 Corbin + * Added biod behavior + */ + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" +#include "rfs_c_def.h" + +/* + * Information associated with outstanding read/write requests + */ +#ifndef RFS +struct biod_req { + uint32_t xid; /* RPC transmission ID */ + bool_t in_use; /* Indicates if the entry is in use */ + int dep_tab_index; /* corresponding index in dep_tab */ + unsigned int count; /* Count saved for Dump routines */ + unsigned int offset; /* Offset saved for Dump routines */ + struct ladtime start; /* Time RPC call was made */ + struct ladtime stop; /* Time RPC reply was received */ + struct ladtime timeout; /* Time RPC call will time out */ +}; +#endif + +/* + * ---------------------- Static Declarations ---------------------- + */ + +static int max_out_writes; +static int max_out_reads; +int max_biod_reqs = 0; +struct biod_req *biod_reqp; + +/* forward definitions for local functions */ +extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *); +static struct biod_req *biod_get_reply(CLIENT *, xdrproc_t, + void *, struct timeval *); +extern int biod_poll_wait(CLIENT *, uint32_t); + +static int op_biod_write(int, int, int); +static int op_biod_read(int); + +/* + * ---------------------- BIOD Support Routines ---------------------- + */ + +/* + * biod_init() + * + * This function is called during the initialization phase. It performs + * the following tasks: + * - Allocate memory to hold outstanding biod request information + * + * Returns 0 for OK, -1 for failure + */ +int +biod_init( + int out_writes, + int out_reads) +{ + // RFS max_out_writes = MAXIMUM(1, out_writes); + // RFS max_out_reads = MAXIMUM(1, out_reads); + // RFS max_biod_reqs = MAXIMUM(out_writes, out_reads); + max_biod_reqs = MAX_OUTSTANDING_REQ; // RFS + + biod_reqp = (struct biod_req *) calloc(max_biod_reqs, + sizeof (struct biod_req)); + if (biod_reqp == (struct biod_req *)0) { + (void) fprintf(stderr, "%s: biod_init calloc failed.\n", sfs_Myname); + (void) fflush(stderr); + return (-1); + } + + return (0); +} /* biod_init */ + +#ifndef RFS + +/* + * - Change the operation functions for reads and writes to use the + * biod routines. This step should be done last to allow callers + * to still run with the old op functions if the biod initialization + * fails. + */ +void +biod_turn_on(void) +{ + Ops[WRITE].funct = op_biod_write; + Ops[READ].funct = op_biod_read; +} + +#endif + +/* + * biod_term() + * + * This function is called during the termination phase to free any resources + * allocated by the biod_init() routine. It performs the following tasks: + * - Frees memory associated with outstanding biod request information + * - Frees the biod client handle + */ +void +biod_term(void) +{ + if (max_biod_reqs) { + free(biod_reqp); + } +} /* biod_term */ + +#ifndef RFS +/* + * Perform and RPC biod style write operation of length 'xfer_size'. + * If 'append_flag' is true, then write the data to the end of the file. + */ +static int +op_biod_write( + int xfer_size, + int append_flag, + int stab_flag) +{ + sfs_op_type *op_ptr; /* per operation info */ + static char *buf = NULL; /* the data buffer */ + unsigned int size; /* size of data write */ + int max_cnt; + attrstat reply2; /* the reply */ + writeargs args2; + WRITE3res reply3; /* the reply */ + WRITE3args args3; + struct ladtime curr_time; + struct ladtime tmp_time; + struct ladtime call_timeout; + struct biod_req *reqp; + int ret; /* ret val == call success */ + int num_out_reqs; /* # of outstanding writes */ + int i; + int error; + int32_t offset; + static int calls = 0; + + calls++; + + if (nfs_version != NFS_VERSION && nfs_version != NFS_V3) + return (0); + + /* + * Initialize write buffer to known value + */ + if (buf == NULL) { + buf = init_write_buffer(); + } + + + /* + * For now we treat DATA_SYNC to be the same as FILE_SYNC. + * If it is not a V3 op then it must always be stable + */ + if (stab_flag == DATA_SYNC || nfs_version != NFS_V3) + stab_flag = FILE_SYNC; + + op_ptr = &Ops[WRITE]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2, + NFS_FHSIZE); + (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + args2.beginoffset = 0; /* unused */ + + if (append_flag == 1) { + args2.offset = Cur_file_ptr->attributes2.size; + args3.offset = Cur_file_ptr->attributes3.size; + } else { + if (fh_size(Cur_file_ptr) > xfer_size) { + offset = Bytes_per_block * (sfs_random() % + (((fh_size(Cur_file_ptr) - xfer_size) + / Bytes_per_block) + 1)); + args2.offset = offset; + args3.offset._p._u = 0; + args3.offset._p._l = offset; + } else { + args2.offset = 0; + args3.offset._p._u = args3.offset._p._l = 0; + } + } + + size = Bytes_per_block; + args2.totalcount = size; /* unused */ + args2.data.data_len = size; + args2.data.data_val = buf; + args3.data.data_len = size; + args3.data.data_val = buf; + args3.count = size; + args3.stable = stab_flag; + + /* Calculate the number of NFS writes required */ + max_cnt = xfer_size / Bytes_per_block; + if ((xfer_size % Bytes_per_block) != 0) { + max_cnt++; + } + + /* check our stats to see if this would overflow */ + if (!Timed_run) { + if (op_ptr->target_calls > 0 && + (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) { + max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; + } + } + + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "write: %d buffers xfer_size %d\n", + max_cnt, xfer_size); + (void) fflush(stderr); + } + + /* Mark all request slots as not in use */ + for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) { + reqp->in_use = FALSE; + } + + if (Current_test_phase < Warmup_phase) { + call_timeout.sec = Nfs_timers[Init].tv_sec; + call_timeout.usec = Nfs_timers[Init].tv_usec; + } else { + call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec; + call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; + } + + /* capture length for possible dump */ + Dump_length = fh_size(Cur_file_ptr); + + /* make the call(s) now */ + num_out_reqs = 0; + while (xfer_size > 0 || num_out_reqs > 0) { + /* + * Send out calls async until either the maximum number of outstanding + * requests has been reached or there are no more requests to make. + */ + while (num_out_reqs < max_out_writes && xfer_size > 0) { + + /* find an empty write request slot */ + for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) { + if (reqp->in_use == FALSE) { + break; + } + } + + if (xfer_size < size) { + size = xfer_size; + args2.data.data_len = xfer_size; + args2.totalcount = xfer_size; /* unused */ + args3.data.data_len = xfer_size; + args3.count = xfer_size; + } + xfer_size -= size; + + sfs_gettime(&reqp->start); + if (nfs_version == NFS_V3) { + reqp->xid = biod_clnt_call(NFS_client, + (uint32_t)NFSPROC3_WRITE, + xdr_WRITE3args, (char *) &args3); + if (reqp->xid != 0) { + /* capture count and offset for possible dump */ + reqp->count = args3.data.data_len; + reqp->offset = args3.offset._p._l; + reqp->timeout = reqp->start; + ADDTIME(reqp->timeout, call_timeout); + reqp->in_use = TRUE; + num_out_reqs++; + } + } + if (nfs_version == NFS_VERSION) { + reqp->xid = biod_clnt_call(NFS_client, + (uint32_t)NFSPROC_WRITE, + xdr_write, (char *) &args2); + if (reqp->xid != 0) { + /* capture count and offset for possible dump */ + reqp->count = args2.data.data_len; + reqp->offset = args2.offset; + reqp->timeout = reqp->start; + ADDTIME(reqp->timeout, call_timeout); + reqp->in_use = TRUE; + num_out_reqs++; + } + } + if (DEBUG_CHILD_BIOD) { + (void) fprintf (stderr, +"[%d]:Biod write started xid %x start (%d.%06d) timeo (%d.%06d)\n", + calls, reqp->xid, + reqp->start.sec, reqp->start.usec, + reqp->timeout.sec, reqp->timeout.usec); + } + + args2.offset += size; + args3.offset._p._l += size; + if (biod_poll_wait(NFS_client, 0) > 0) { + break; + } + } /* while can make an async call */ + + /* + * Process replies while there is data on the socket buffer. + * Just do polls on the select, no sleeping occurs in this loop. + */ + do { + error = biod_poll_wait(NFS_client, 0); + switch (error) { + case -1: + if (errno == EINTR) { + error = 1; + continue; + } + if (DEBUG_CHILD_BIOD) { + (void) fprintf(stderr, "%s:[%d]: biod_poll_wait error\n", + sfs_Myname, calls); + (void) fflush(stderr); + } + break; + + case 0: + break; + + + default: + if (nfs_version == NFS_VERSION) + reqp = biod_get_reply(NFS_client, xdr_write, + (char *) &reply2, + &Nfs_timers[op_ptr->call_class]); + if (nfs_version == NFS_V3) + reqp = biod_get_reply(NFS_client, xdr_WRITE3res, + (char *) &reply3, + &Nfs_timers[op_ptr->call_class]); + + /* + * If biod_get_reply returns NULL then we got an RPC + * level error, probably a dropped fragment or the + * remains of a previous partial request. + */ + if (reqp == (struct biod_req *)NULL) { + error = 0; + break; + } + + /* + * We have a valid response, check if procedure completed + * correctly. + */ + if ((nfs_version == NFS_VERSION && + reply2.status == NFS_OK) || + (nfs_version == NFS_V3 && reply3.status == NFS3_OK)) { + Cur_file_ptr->state = Exists; + /* + * In updating attributes we may get replies out + * of order. We blindly update the attributes + * which may cause old attributes to be stored. + * XXX We should check for old attributes. + */ + if (nfs_version == NFS_VERSION) + Cur_file_ptr->attributes2 = + reply2.attrstat_u.attributes; + if (nfs_version == NFS_V3) + Cur_file_ptr->attributes3 = + reply3.res_u.ok.file_wcc.after.attr; + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, + "%s: WRITE %s %d bytes offset %d \n", + sfs_Myname, Cur_filename, + reqp->count, reqp->offset); + (void) fflush(stderr); + } + + /* capture count and offset for possible dump */ + Dump_count = reqp->count; + Dump_offset = reqp->offset; + sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + reqp->in_use = FALSE; + num_out_reqs--; + if (DEBUG_CHILD_BIOD) { + (void) fprintf (stderr, +"[%d]:Biod write succeded xid %x start (%d.%06d) timeo (%d.%06d) stop (%d.%06d)\n", + calls, reqp->xid, + reqp->start.sec, reqp->start.usec, + reqp->timeout.sec, reqp->timeout.usec, + reqp->stop.sec, reqp->stop.usec); + } + } else { + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + reqp->in_use = FALSE; + num_out_reqs--; + if (DEBUG_CHILD_BIOD) { + (void) fprintf (stderr, +"[%d]:Biod write failed xid %x start (%d.%06d) timeo (%d.%06d)\n", + calls, reqp->xid, + reqp->start.sec, reqp->start.usec, + reqp->timeout.sec, reqp->timeout.usec); + + (void) fprintf(stderr, + "[%d]:BIOD WRITE FAILED: xid %x", + calls, reqp->xid); + + if (nfs_version == NFS_VERSION) + (void) fprintf(stderr, " status %d", + reply2.status); + if (nfs_version == NFS_V3) + (void) fprintf(stderr, " status %d", + reply3.status); + (void) fprintf(stderr, "\n"); + } + } + break; + } + } while (error > 0 && num_out_reqs > 0); + + /* Scan for replies that have timed out */ + if (num_out_reqs > 0) { + sfs_gettime(&curr_time); + for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) { + if (reqp->in_use == FALSE) { + continue; + } + if (reqp->timeout.sec < curr_time.sec || + (reqp->timeout.sec == curr_time.sec && + reqp->timeout.usec < curr_time.usec)) { + + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + reqp->in_use = FALSE; + num_out_reqs--; + if (DEBUG_CHILD_BIOD) { + (void) fprintf (stderr, +"[%d]:Biod write timed out %x start (%d.%06d) timeo (%d.%06d) now (%d.%06d)\n", + calls, reqp->xid, + reqp->start.sec, reqp->start.usec, + reqp->timeout.sec, reqp->timeout.usec, + curr_time.sec, curr_time.usec); + if (biod_poll_wait(NFS_client, 0) > 0) { + (void) fprintf(stderr, + "[%d]:BIOD WRITE TIMEOUT - data on input queue!\n", calls); + } + } + } + } + } + + /* + * We go to sleep waiting for a reply if all the requests have + * been sent and there are outstanding requests, or we cannot + * send any more requests. + */ + if ((xfer_size <= 0 && num_out_reqs > 0) || + num_out_reqs == max_out_writes) { + /* + * Find the next outstanding request that will timeout + * and take a time differential to use for the poll timeout. + * If the differential is less than zero, then we go to the + * top of the loop. Note that we are not picky on errors + * returned by select, after the sleep we return to the top + * of the loop so extensive error/status checking is not + * needed. + */ + tmp_time.sec = 0; + tmp_time.usec = 0; + for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) { + if (reqp->in_use == FALSE) { + continue; + } + if (tmp_time.sec == 0 || + (reqp->timeout.sec < tmp_time.sec || + (reqp->timeout.sec == tmp_time.sec && + reqp->timeout.usec < tmp_time.usec))) { + + tmp_time = reqp->timeout; + } + } + if (tmp_time.sec == 0 && tmp_time.usec == 0) + continue; + sfs_gettime(&curr_time); + SUBTIME(tmp_time, curr_time); + (void) biod_poll_wait(NFS_client, + tmp_time.sec * 1000000 + tmp_time.usec); + } + } /* while not done */ + + + /* + * If we have not gotten an error and we were asked for an async write + * send a commit operation. + */ + if (ret && stab_flag != FILE_SYNC) + ret += (*Ops[COMMIT].funct)(); + + return (ret); + +} /* op_biod_write */ + + +/* + * perform an RPC read operation of length 'xfer_size' + */ +static int +op_biod_read( + int xfer_size) +{ + sfs_op_type *op_ptr; /* per operation info */ + int max_cnt; /* packet ctrs */ + char buf[DEFAULT_MAX_BUFSIZE];/* data buffer */ + readargs args2; + readres reply2; /* the reply */ + READ3args args3; + READ3res reply3; /* the reply */ + int size; + struct ladtime curr_time; + struct ladtime call_timeout; + struct ladtime tmp_time; + struct biod_req *reqp; + int ret; /* ret val == call success */ + int num_out_reqs; /* # of outstanding writes */ + int i; + int error; + int32_t offset; + static int calls = 0; + + calls++; + + if (nfs_version != NFS_VERSION && nfs_version != NFS_V3) + return (0); + + op_ptr = &Ops[READ]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2, + NFS_FHSIZE); + (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + /* + * Don't allow a read of less than one block size + */ + if (xfer_size < Bytes_per_block) + xfer_size = Bytes_per_block; + + + /* Calculate the number of NFS reads required */ + max_cnt = xfer_size / Bytes_per_block; + if ((xfer_size % Bytes_per_block) != 0) { + max_cnt++; + } + + /* check our stats to see if this would overflow */ + if (!Timed_run) { + if (op_ptr->target_calls > 0 && + (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) { + max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; + } + } + + args2.offset = 0; + args3.offset._p._l = args3.offset._p._u = 0; + + /* + * randomly choose an offset that is a multiple of the block size + * and constrained by making the transfer fit within the file + */ + if (fh_size(Cur_file_ptr) > xfer_size) { + offset = Bytes_per_block * (sfs_random() % + (((fh_size(Cur_file_ptr) - xfer_size) + / Bytes_per_block) + 1)); + args2.offset = offset; + args3.offset._p._u = 0; + args3.offset._p._l = offset; + } + + size = Bytes_per_block; + args2.count = size; + args3.count = size; + args2.totalcount = size; /* unused */ + + /* Have lower layers fill in the data directly. */ + reply2.readres_u.reply.data.data_val = buf; + reply3.res_u.ok.data.data_val = buf; + + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "read: %d buffers xfer_size %d\n", + max_cnt, xfer_size); + (void) fflush(stderr); + } + + /* Mark all request slots as not in use */ + for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) { + reqp->in_use = FALSE; + } + + if (Current_test_phase < Warmup_phase) { + call_timeout.sec = Nfs_timers[Init].tv_sec; + call_timeout.usec = Nfs_timers[Init].tv_usec; + } else { + call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec; + call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; + } + + /* capture length for possible dump */ + Dump_length = fh_size(Cur_file_ptr); + + /* make the call(s) now */ + num_out_reqs = 0; + while (xfer_size > 0 || num_out_reqs > 0) { + /* + * Send out calls async until either the maximum number of outstanding + * requests has been reached or there are no more requests to make. + */ + while (num_out_reqs < max_out_reads && xfer_size > 0) { + + /* find an empty read request slot */ + for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) { + if (reqp->in_use == FALSE) { + break; + } + } + + if (xfer_size < size) { + size = xfer_size; + args2.count = xfer_size; + args3.count = xfer_size; + args2.totalcount = xfer_size; /* unused */ + } + xfer_size -= size; + + sfs_gettime(&reqp->start); + if (nfs_version == NFS_VERSION) { + reqp->xid = biod_clnt_call(NFS_client, + (uint32_t)NFSPROC_READ, + xdr_read, (char *) &args2); + if (reqp->xid != 0) { + /* capture count and offset for possible dump */ + reqp->count = args2.count; + reqp->offset = args2.offset; + reqp->timeout = reqp->start; + ADDTIME(reqp->timeout, call_timeout); + reqp->in_use = TRUE; + num_out_reqs++; + } + } else if (nfs_version == NFS_V3) { + reqp->xid = biod_clnt_call(NFS_client, + (uint32_t)NFSPROC3_READ, + xdr_READ3args, (char *) &args3); + if (reqp->xid != 0) { + /* capture count and offset for possible dump */ + reqp->count = args3.count; + reqp->offset = args3.offset._p._l; + reqp->timeout = reqp->start; + ADDTIME(reqp->timeout, call_timeout); + reqp->in_use = TRUE; + num_out_reqs++; + } + } + + args2.offset += size; + args3.offset._p._l += size; + if (biod_poll_wait(NFS_client, 0) > 0) { + break; + } + } /* while can make an async call */ + + /* + * Process replies while there is data on the socket buffer. + * Just do polls on the select, no sleeping occurs in this loop. + */ + do { + error = biod_poll_wait(NFS_client, 0); + switch (error) { + case -1: + if (errno == EINTR) { + error = 1; + continue; + } + if (DEBUG_CHILD_BIOD) { + (void) fprintf(stderr, + "%s:[%d]: biod_poll_wait error\n", + sfs_Myname, calls); + (void) fflush(stderr); + } + break; + + case 0: + break; + + + default: + if (nfs_version == NFS_VERSION) + reqp = biod_get_reply(NFS_client, xdr_read, + (char *) &reply2, + &Nfs_timers[op_ptr->call_class]); + if (nfs_version == NFS_V3) + reqp = biod_get_reply(NFS_client, xdr_READ3res, + (char *) &reply3, + &Nfs_timers[op_ptr->call_class]); + + /* + * If biod_get_reply returns NULL then we got an RPC + * level error, probably a dropped fragment or the + * remains of a previous partial request. + */ + if (reqp == (struct biod_req *)NULL) { + error = 0; + break; + } + + /* + * We have a valid response, check if procedure completed + * correctly. + */ + if ((nfs_version == NFS_VERSION && + reply2.status == NFS_OK) || + (nfs_version == NFS_V3 && + reply3.status == NFS3_OK)) { + Cur_file_ptr->state = Exists; + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: READ %s %d bytes offset %d\n", + sfs_Myname, Cur_filename, reqp->count, reqp->offset); + (void) fflush(stderr); + } + /* + * In updating attributes we may get replies out + * of order. We blindly update the attributes + * which may cause old attributes to be stored. + * XXX We should check for old attributes. + */ + if (nfs_version == NFS_VERSION) { + Cur_file_ptr->attributes2 = + reply2.readres_u.reply.attributes; + /* capture count and offset for possible dump */ + Dump_count = reply2.readres_u.reply.data.data_len; + } + if (nfs_version == NFS_V3) { + Cur_file_ptr->attributes3 = + reply3.res_u.ok.file_attributes.attr; + /* capture count and offset for possible dump */ + Dump_count = reply3.res_u.ok.data.data_len; + } + + Dump_offset = reqp->offset; + sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + reqp->in_use = FALSE; + num_out_reqs--; + } else { + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + reqp->in_use = FALSE; + num_out_reqs--; + + if (DEBUG_CHILD_BIOD) { + (void) fprintf(stderr, + "[%d]:BIOD READ FAILED: xid %x", + calls, reqp->xid); + + if (nfs_version == NFS_VERSION) + (void) fprintf(stderr, " status %d", + reply2.status); + if (nfs_version == NFS_V3) + (void) fprintf(stderr, " status %d", + reply3.status); + (void) fprintf(stderr, "\n"); + } + } + break; + } /* switch */ + } while (error > 0 && num_out_reqs > 0); + + /* Scan for replies that have timed out */ + if (num_out_reqs > 0) { + sfs_gettime(&curr_time); + for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) { + if (reqp->in_use == FALSE) { + continue; + } + if (reqp->timeout.sec < curr_time.sec || + (reqp->timeout.sec == curr_time.sec && + reqp->timeout.usec < curr_time.usec)) { + + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + reqp->in_use = FALSE; + num_out_reqs--; + if (DEBUG_CHILD_BIOD) { + (void) fprintf (stderr, +"[%d]:Biod read timed out %x (%d.%06d) now (%d.%06d)\n", + calls, reqp->xid, + reqp->timeout.sec, reqp->timeout.usec, + curr_time.sec, curr_time.usec); + if (biod_poll_wait(NFS_client, 0) > 0) { + (void) fprintf(stderr, + "[%d]:BIOD READ TIMEOUT - data on input queue!\n", calls); + } + } + } + } + } + + /* + * We go to sleep waiting for a reply if all the requests have + * been sent and there are outstanding requests, or we cannot + * send any more requests. + */ + if ((xfer_size <= 0 && num_out_reqs > 0) || + num_out_reqs == max_out_reads) { + /* + * Find the next outstanding request that will timeout + * and take a time differential to use for the poll timeout. + * If the differential is less than zero, then we go to the + * top of the loop. Note that we are not picky on errors + * returned by select, after the sleep we return to the top + * of the loop so extensive error/status checking is not + * needed. + */ + tmp_time.sec = 0; + tmp_time.usec = 0; + for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) { + if (reqp->in_use == FALSE) { + continue; + } + if (tmp_time.sec == 0 || + (reqp->timeout.sec < tmp_time.sec || + (reqp->timeout.sec == tmp_time.sec && + reqp->timeout.usec < tmp_time.usec))) { + + tmp_time = reqp->timeout; + } + } + if (tmp_time.sec == 0 && tmp_time.usec == 0) + continue; + sfs_gettime(&curr_time); + SUBTIME(tmp_time, curr_time); + (void) biod_poll_wait(NFS_client, + tmp_time.sec * 1000000 + tmp_time.usec); + } + } /* while not done */ + + return(ret); + +} /* op_biod_read */ + +#endif + +/* + * ---------------------- Async RPC Support Routines ---------------------- + */ + +/* + * biod_clnt_call() + * + * Returns XID indicating success, 0 indicating failure. + */ +uint32_t +biod_clnt_call( + CLIENT *clnt_handlep, + uint32_t proc, + xdrproc_t xargs, + void *argsp) +{ + struct timeval timeout; + uint32_t xid; + + /* + * Set timeouts to be zero to force message passing semantics. + */ + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + if ((clnt_call(clnt_handlep, proc, xargs, argsp, NULL, + &xid, timeout)) != RPC_TIMEDOUT) { + clnt_perror(clnt_handlep, "biod_clnt_call failed"); + return (0); + } + + return (xid); +} /* biod_clnt_call */ + + +/* + * biod_get_reply() + * + * Returns pointer to the biod_req struct entry that a reply was received + * for. Returns NULL if an error was detected. + * NOTES: + * 1) This routine should only be called when it is known that there is + * data waiting on the socket. + */ +static struct biod_req * +biod_get_reply( + CLIENT *clnt_handlep, + xdrproc_t xresults, + void *resultsp, + struct timeval *tv) +{ + uint32_t xid; + int i; + int cnt = 0; + bool_t res; + uint32_t xids[MAX_BIODS]; + + /* + * Load list of valid outstanding xids + */ + for (i = 0; i < max_biod_reqs; i++) { + if (biod_reqp[i].in_use == TRUE) + xids[cnt++] = biod_reqp[i].xid; + } + + if (cnt == 0) + return (NULL); + + if ((res = clnt_getreply(clnt_handlep, xresults, + resultsp, cnt, xids, &xid, tv)) != RPC_SUCCESS) { + if (DEBUG_CHILD_BIOD) { + if (res == RPC_CANTDECODERES) { + (void) fprintf(stderr, "No xid matched, found %x\n", + xid); + } + } + return (NULL); + } + + /* + * Scan to find XID matched in the outstanding request queue. + */ + for (i = 0; i < max_biod_reqs; i++) { + if (biod_reqp[i].in_use == TRUE && biod_reqp[i].xid == xid) { + sfs_gettime(&(biod_reqp[i].stop)); + return (&biod_reqp[i]); + } + } + + return ((struct biod_req *)0); +} /* biod_get_reply */ + +/* + * biod_poll_wait() + * + * Returns -1 on error, 0 for no data available, > 0 to indicate data available + */ +int +biod_poll_wait( + CLIENT *clnt_handlep, + uint32_t usecs) +{ + return (clnt_poll(clnt_handlep, usecs)); +} /* biod_poll_wait */ + diff --git a/TBBT/trace_play/sfs_c_bio.org b/TBBT/trace_play/sfs_c_bio.org new file mode 100644 index 0000000..88c54f5 --- /dev/null +++ b/TBBT/trace_play/sfs_c_bio.org @@ -0,0 +1,1012 @@ +#ifndef lint +static char sfs_c_bioSid[] = "@(#)sfs_c_bio.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/* + * ---------------------- sfs_c_bio.c --------------------- + * + * Routines that attempt to simulate biod behavior + * + * The routines contained here model biod behavior. Simply call + * biod_init() to replace regular calls to op_read() and op_write() + * with calls to op_biod_read() and op_biod_write(). The variables + * max_out_writes and max_out_reads control the maximum number of + * outstanding writes and reads respectively. + * + *.Exported Routines + * int biod_init(int, int); + * void biod_turn_on(void); + * void op_biod_write(int, int, int); + * void op_biod_read(int); + * + *.Local Routines + * uint32_t biod_clnt_call(CLIENT *, uint32_t, + * xdrproc_t, void *); + * struct biod_req *biod_get_reply(CLIENT *, xdrproc_t, + * void *, struct timeval *); + * int biod_poll_wait(CLIENT *, uint32_t); + * + *.Revision_History + * 03-May-94 Robinson + * History now kept in SCCS + * 03-Mar-92 0.1.0 Corbin + * Added biod behavior + */ + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" + +/* + * Information associated with outstanding read/write requests + */ +struct biod_req { + uint32_t xid; /* RPC transmission ID */ + bool_t in_use; /* Indicates if the entry is in use */ + unsigned int count; /* Count saved for Dump routines */ + unsigned int offset; /* Offset saved for Dump routines */ + struct ladtime start; /* Time RPC call was made */ + struct ladtime stop; /* Time RPC reply was received */ + struct ladtime timeout; /* Time RPC call will time out */ +}; + +/* + * ---------------------- Static Declarations ---------------------- + */ + +static int max_out_writes; +static int max_out_reads; +static int max_biod_reqs = 0; +static struct biod_req *biod_reqp; + +/* forward definitions for local functions */ +static uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *); +static struct biod_req *biod_get_reply(CLIENT *, xdrproc_t, + void *, struct timeval *); +static int biod_poll_wait(CLIENT *, uint32_t); + +static int op_biod_write(int, int, int); +static int op_biod_read(int); + +/* + * ---------------------- BIOD Support Routines ---------------------- + */ + +/* + * biod_init() + * + * This function is called during the initialization phase. It performs + * the following tasks: + * - Allocate memory to hold outstanding biod request information + * + * Returns 0 for OK, -1 for failure + */ +int +biod_init( + int out_writes, + int out_reads) +{ + max_out_writes = MAXIMUM(1, out_writes); + max_out_reads = MAXIMUM(1, out_reads); + max_biod_reqs = MAXIMUM(out_writes, out_reads); + + biod_reqp = (struct biod_req *) calloc(max_biod_reqs, + sizeof (struct biod_req)); + if (biod_reqp == (struct biod_req *)0) { + (void) fprintf(stderr, "%s: biod_init calloc failed.\n", sfs_Myname); + (void) fflush(stderr); + return (-1); + } + + return (0); +} /* biod_init */ + + +/* + * - Change the operation functions for reads and writes to use the + * biod routines. This step should be done last to allow callers + * to still run with the old op functions if the biod initialization + * fails. + */ +void +biod_turn_on(void) +{ + Ops[WRITE].funct = op_biod_write; + Ops[READ].funct = op_biod_read; +} + +/* + * biod_term() + * + * This function is called during the termination phase to free any resources + * allocated by the biod_init() routine. It performs the following tasks: + * - Frees memory associated with outstanding biod request information + * - Frees the biod client handle + */ +void +biod_term(void) +{ + if (max_biod_reqs) { + free(biod_reqp); + } +} /* biod_term */ + +/* + * Perform and RPC biod style write operation of length 'xfer_size'. + * If 'append_flag' is true, then write the data to the end of the file. + */ +static int +op_biod_write( + int xfer_size, + int append_flag, + int stab_flag) +{ + sfs_op_type *op_ptr; /* per operation info */ + static char *buf = NULL; /* the data buffer */ + unsigned int size; /* size of data write */ + int max_cnt; + attrstat reply2; /* the reply */ + writeargs args2; + WRITE3res reply3; /* the reply */ + WRITE3args args3; + struct ladtime curr_time; + struct ladtime tmp_time; + struct ladtime call_timeout; + struct biod_req *reqp; + int ret; /* ret val == call success */ + int num_out_reqs; /* # of outstanding writes */ + int i; + int error; + int32_t offset; + static int calls = 0; + + calls++; + + if (nfs_version != NFS_VERSION && nfs_version != NFS_V3) + return (0); + + /* + * Initialize write buffer to known value + */ + if (buf == NULL) { + buf = init_write_buffer(); + } + + + /* + * For now we treat DATA_SYNC to be the same as FILE_SYNC. + * If it is not a V3 op then it must always be stable + */ + if (stab_flag == DATA_SYNC || nfs_version != NFS_V3) + stab_flag = FILE_SYNC; + + op_ptr = &Ops[WRITE]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2, + NFS_FHSIZE); + (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + args2.beginoffset = 0; /* unused */ + + if (append_flag == 1) { + args2.offset = Cur_file_ptr->attributes2.size; + args3.offset = Cur_file_ptr->attributes3.size; + } else { + if (fh_size(Cur_file_ptr) > xfer_size) { + offset = Bytes_per_block * (sfs_random() % + (((fh_size(Cur_file_ptr) - xfer_size) + / Bytes_per_block) + 1)); + args2.offset = offset; + args3.offset._p._u = 0; + args3.offset._p._l = offset; + } else { + args2.offset = 0; + args3.offset._p._u = args3.offset._p._l = 0; + } + } + + size = Bytes_per_block; + args2.totalcount = size; /* unused */ + args2.data.data_len = size; + args2.data.data_val = buf; + args3.data.data_len = size; + args3.data.data_val = buf; + args3.count = size; + args3.stable = stab_flag; + + /* Calculate the number of NFS writes required */ + max_cnt = xfer_size / Bytes_per_block; + if ((xfer_size % Bytes_per_block) != 0) { + max_cnt++; + } + + /* check our stats to see if this would overflow */ + if (!Timed_run) { + if (op_ptr->target_calls > 0 && + (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) { + max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; + } + } + + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "write: %d buffers xfer_size %d\n", + max_cnt, xfer_size); + (void) fflush(stderr); + } + + /* Mark all request slots as not in use */ + for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) { + reqp->in_use = FALSE; + } + + if (Current_test_phase < Warmup_phase) { + call_timeout.sec = Nfs_timers[Init].tv_sec; + call_timeout.usec = Nfs_timers[Init].tv_usec; + } else { + call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec; + call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; + } + + /* capture length for possible dump */ + Dump_length = fh_size(Cur_file_ptr); + + /* make the call(s) now */ + num_out_reqs = 0; + while (xfer_size > 0 || num_out_reqs > 0) { + /* + * Send out calls async until either the maximum number of outstanding + * requests has been reached or there are no more requests to make. + */ + while (num_out_reqs < max_out_writes && xfer_size > 0) { + + /* find an empty write request slot */ + for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) { + if (reqp->in_use == FALSE) { + break; + } + } + + if (xfer_size < size) { + size = xfer_size; + args2.data.data_len = xfer_size; + args2.totalcount = xfer_size; /* unused */ + args3.data.data_len = xfer_size; + args3.count = xfer_size; + } + xfer_size -= size; + + sfs_gettime(&reqp->start); + if (nfs_version == NFS_V3) { + reqp->xid = biod_clnt_call(NFS_client, + (uint32_t)NFSPROC3_WRITE, + xdr_WRITE3args, (char *) &args3); + if (reqp->xid != 0) { + /* capture count and offset for possible dump */ + reqp->count = args3.data.data_len; + reqp->offset = args3.offset._p._l; + reqp->timeout = reqp->start; + ADDTIME(reqp->timeout, call_timeout); + reqp->in_use = TRUE; + num_out_reqs++; + } + } + if (nfs_version == NFS_VERSION) { + reqp->xid = biod_clnt_call(NFS_client, + (uint32_t)NFSPROC_WRITE, + xdr_write, (char *) &args2); + if (reqp->xid != 0) { + /* capture count and offset for possible dump */ + reqp->count = args2.data.data_len; + reqp->offset = args2.offset; + reqp->timeout = reqp->start; + ADDTIME(reqp->timeout, call_timeout); + reqp->in_use = TRUE; + num_out_reqs++; + } + } + if (DEBUG_CHILD_BIOD) { + (void) fprintf (stderr, +"[%d]:Biod write started xid %x start (%d.%06d) timeo (%d.%06d)\n", + calls, reqp->xid, + reqp->start.sec, reqp->start.usec, + reqp->timeout.sec, reqp->timeout.usec); + } + + args2.offset += size; + args3.offset._p._l += size; + if (biod_poll_wait(NFS_client, 0) > 0) { + break; + } + } /* while can make an async call */ + + /* + * Process replies while there is data on the socket buffer. + * Just do polls on the select, no sleeping occurs in this loop. + */ + do { + error = biod_poll_wait(NFS_client, 0); + switch (error) { + case -1: + if (errno == EINTR) { + error = 1; + continue; + } + if (DEBUG_CHILD_BIOD) { + (void) fprintf(stderr, "%s:[%d]: biod_poll_wait error\n", + sfs_Myname, calls); + (void) fflush(stderr); + } + break; + + case 0: + break; + + + default: + if (nfs_version == NFS_VERSION) + reqp = biod_get_reply(NFS_client, xdr_write, + (char *) &reply2, + &Nfs_timers[op_ptr->call_class]); + if (nfs_version == NFS_V3) + reqp = biod_get_reply(NFS_client, xdr_WRITE3res, + (char *) &reply3, + &Nfs_timers[op_ptr->call_class]); + + /* + * If biod_get_reply returns NULL then we got an RPC + * level error, probably a dropped fragment or the + * remains of a previous partial request. + */ + if (reqp == (struct biod_req *)NULL) { + error = 0; + break; + } + + /* + * We have a valid response, check if procedure completed + * correctly. + */ + if ((nfs_version == NFS_VERSION && + reply2.status == NFS_OK) || + (nfs_version == NFS_V3 && reply3.status == NFS3_OK)) { + Cur_file_ptr->state = Exists; + /* + * In updating attributes we may get replies out + * of order. We blindly update the attributes + * which may cause old attributes to be stored. + * XXX We should check for old attributes. + */ + if (nfs_version == NFS_VERSION) + Cur_file_ptr->attributes2 = + reply2.attrstat_u.attributes; + if (nfs_version == NFS_V3) + Cur_file_ptr->attributes3 = + reply3.res_u.ok.file_wcc.after.attr; + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, + "%s: WRITE %s %d bytes offset %d \n", + sfs_Myname, Cur_filename, + reqp->count, reqp->offset); + (void) fflush(stderr); + } + + /* capture count and offset for possible dump */ + Dump_count = reqp->count; + Dump_offset = reqp->offset; + sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + reqp->in_use = FALSE; + num_out_reqs--; + if (DEBUG_CHILD_BIOD) { + (void) fprintf (stderr, +"[%d]:Biod write succeded xid %x start (%d.%06d) timeo (%d.%06d) stop (%d.%06d)\n", + calls, reqp->xid, + reqp->start.sec, reqp->start.usec, + reqp->timeout.sec, reqp->timeout.usec, + reqp->stop.sec, reqp->stop.usec); + } + } else { + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + reqp->in_use = FALSE; + num_out_reqs--; + if (DEBUG_CHILD_BIOD) { + (void) fprintf (stderr, +"[%d]:Biod write failed xid %x start (%d.%06d) timeo (%d.%06d)\n", + calls, reqp->xid, + reqp->start.sec, reqp->start.usec, + reqp->timeout.sec, reqp->timeout.usec); + + (void) fprintf(stderr, + "[%d]:BIOD WRITE FAILED: xid %x", + calls, reqp->xid); + + if (nfs_version == NFS_VERSION) + (void) fprintf(stderr, " status %d", + reply2.status); + if (nfs_version == NFS_V3) + (void) fprintf(stderr, " status %d", + reply3.status); + (void) fprintf(stderr, "\n"); + } + } + break; + } + } while (error > 0 && num_out_reqs > 0); + + /* Scan for replies that have timed out */ + if (num_out_reqs > 0) { + sfs_gettime(&curr_time); + for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) { + if (reqp->in_use == FALSE) { + continue; + } + if (reqp->timeout.sec < curr_time.sec || + (reqp->timeout.sec == curr_time.sec && + reqp->timeout.usec < curr_time.usec)) { + + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + reqp->in_use = FALSE; + num_out_reqs--; + if (DEBUG_CHILD_BIOD) { + (void) fprintf (stderr, +"[%d]:Biod write timed out %x start (%d.%06d) timeo (%d.%06d) now (%d.%06d)\n", + calls, reqp->xid, + reqp->start.sec, reqp->start.usec, + reqp->timeout.sec, reqp->timeout.usec, + curr_time.sec, curr_time.usec); + if (biod_poll_wait(NFS_client, 0) > 0) { + (void) fprintf(stderr, + "[%d]:BIOD WRITE TIMEOUT - data on input queue!\n", calls); + } + } + } + } + } + + /* + * We go to sleep waiting for a reply if all the requests have + * been sent and there are outstanding requests, or we cannot + * send any more requests. + */ + if ((xfer_size <= 0 && num_out_reqs > 0) || + num_out_reqs == max_out_writes) { + /* + * Find the next outstanding request that will timeout + * and take a time differential to use for the poll timeout. + * If the differential is less than zero, then we go to the + * top of the loop. Note that we are not picky on errors + * returned by select, after the sleep we return to the top + * of the loop so extensive error/status checking is not + * needed. + */ + tmp_time.sec = 0; + tmp_time.usec = 0; + for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) { + if (reqp->in_use == FALSE) { + continue; + } + if (tmp_time.sec == 0 || + (reqp->timeout.sec < tmp_time.sec || + (reqp->timeout.sec == tmp_time.sec && + reqp->timeout.usec < tmp_time.usec))) { + + tmp_time = reqp->timeout; + } + } + if (tmp_time.sec == 0 && tmp_time.usec == 0) + continue; + sfs_gettime(&curr_time); + SUBTIME(tmp_time, curr_time); + (void) biod_poll_wait(NFS_client, + tmp_time.sec * 1000000 + tmp_time.usec); + } + } /* while not done */ + + + /* + * If we have not gotten an error and we were asked for an async write + * send a commit operation. + */ + if (ret && stab_flag != FILE_SYNC) + ret += (*Ops[COMMIT].funct)(); + + return (ret); + +} /* op_biod_write */ + + +/* + * perform an RPC read operation of length 'xfer_size' + */ +static int +op_biod_read( + int xfer_size) +{ + sfs_op_type *op_ptr; /* per operation info */ + int max_cnt; /* packet ctrs */ + char buf[DEFAULT_MAX_BUFSIZE];/* data buffer */ + readargs args2; + readres reply2; /* the reply */ + READ3args args3; + READ3res reply3; /* the reply */ + int size; + struct ladtime curr_time; + struct ladtime call_timeout; + struct ladtime tmp_time; + struct biod_req *reqp; + int ret; /* ret val == call success */ + int num_out_reqs; /* # of outstanding writes */ + int i; + int error; + int32_t offset; + static int calls = 0; + + calls++; + + if (nfs_version != NFS_VERSION && nfs_version != NFS_V3) + return (0); + + op_ptr = &Ops[READ]; + ret = 0; + + /* set up the arguments */ + (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2, + NFS_FHSIZE); + (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3, + sizeof (nfs_fh3)); + + /* + * Don't allow a read of less than one block size + */ + if (xfer_size < Bytes_per_block) + xfer_size = Bytes_per_block; + + + /* Calculate the number of NFS reads required */ + max_cnt = xfer_size / Bytes_per_block; + if ((xfer_size % Bytes_per_block) != 0) { + max_cnt++; + } + + /* check our stats to see if this would overflow */ + if (!Timed_run) { + if (op_ptr->target_calls > 0 && + (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) { + max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; + } + } + + args2.offset = 0; + args3.offset._p._l = args3.offset._p._u = 0; + + /* + * randomly choose an offset that is a multiple of the block size + * and constrained by making the transfer fit within the file + */ + if (fh_size(Cur_file_ptr) > xfer_size) { + offset = Bytes_per_block * (sfs_random() % + (((fh_size(Cur_file_ptr) - xfer_size) + / Bytes_per_block) + 1)); + args2.offset = offset; + args3.offset._p._u = 0; + args3.offset._p._l = offset; + } + + size = Bytes_per_block; + args2.count = size; + args3.count = size; + args2.totalcount = size; /* unused */ + + /* Have lower layers fill in the data directly. */ + reply2.readres_u.reply.data.data_val = buf; + reply3.res_u.ok.data.data_val = buf; + + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "read: %d buffers xfer_size %d\n", + max_cnt, xfer_size); + (void) fflush(stderr); + } + + /* Mark all request slots as not in use */ + for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) { + reqp->in_use = FALSE; + } + + if (Current_test_phase < Warmup_phase) { + call_timeout.sec = Nfs_timers[Init].tv_sec; + call_timeout.usec = Nfs_timers[Init].tv_usec; + } else { + call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec; + call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; + } + + /* capture length for possible dump */ + Dump_length = fh_size(Cur_file_ptr); + + /* make the call(s) now */ + num_out_reqs = 0; + while (xfer_size > 0 || num_out_reqs > 0) { + /* + * Send out calls async until either the maximum number of outstanding + * requests has been reached or there are no more requests to make. + */ + while (num_out_reqs < max_out_reads && xfer_size > 0) { + + /* find an empty read request slot */ + for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) { + if (reqp->in_use == FALSE) { + break; + } + } + + if (xfer_size < size) { + size = xfer_size; + args2.count = xfer_size; + args3.count = xfer_size; + args2.totalcount = xfer_size; /* unused */ + } + xfer_size -= size; + + sfs_gettime(&reqp->start); + if (nfs_version == NFS_VERSION) { + reqp->xid = biod_clnt_call(NFS_client, + (uint32_t)NFSPROC_READ, + xdr_read, (char *) &args2); + if (reqp->xid != 0) { + /* capture count and offset for possible dump */ + reqp->count = args2.count; + reqp->offset = args2.offset; + reqp->timeout = reqp->start; + ADDTIME(reqp->timeout, call_timeout); + reqp->in_use = TRUE; + num_out_reqs++; + } + } else if (nfs_version == NFS_V3) { + reqp->xid = biod_clnt_call(NFS_client, + (uint32_t)NFSPROC3_READ, + xdr_READ3args, (char *) &args3); + if (reqp->xid != 0) { + /* capture count and offset for possible dump */ + reqp->count = args3.count; + reqp->offset = args3.offset._p._l; + reqp->timeout = reqp->start; + ADDTIME(reqp->timeout, call_timeout); + reqp->in_use = TRUE; + num_out_reqs++; + } + } + + args2.offset += size; + args3.offset._p._l += size; + if (biod_poll_wait(NFS_client, 0) > 0) { + break; + } + } /* while can make an async call */ + + /* + * Process replies while there is data on the socket buffer. + * Just do polls on the select, no sleeping occurs in this loop. + */ + do { + error = biod_poll_wait(NFS_client, 0); + switch (error) { + case -1: + if (errno == EINTR) { + error = 1; + continue; + } + if (DEBUG_CHILD_BIOD) { + (void) fprintf(stderr, + "%s:[%d]: biod_poll_wait error\n", + sfs_Myname, calls); + (void) fflush(stderr); + } + break; + + case 0: + break; + + + default: + if (nfs_version == NFS_VERSION) + reqp = biod_get_reply(NFS_client, xdr_read, + (char *) &reply2, + &Nfs_timers[op_ptr->call_class]); + if (nfs_version == NFS_V3) + reqp = biod_get_reply(NFS_client, xdr_READ3res, + (char *) &reply3, + &Nfs_timers[op_ptr->call_class]); + + /* + * If biod_get_reply returns NULL then we got an RPC + * level error, probably a dropped fragment or the + * remains of a previous partial request. + */ + if (reqp == (struct biod_req *)NULL) { + error = 0; + break; + } + + /* + * We have a valid response, check if procedure completed + * correctly. + */ + if ((nfs_version == NFS_VERSION && + reply2.status == NFS_OK) || + (nfs_version == NFS_V3 && + reply3.status == NFS3_OK)) { + Cur_file_ptr->state = Exists; + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "%s: READ %s %d bytes offset %d\n", + sfs_Myname, Cur_filename, reqp->count, reqp->offset); + (void) fflush(stderr); + } + /* + * In updating attributes we may get replies out + * of order. We blindly update the attributes + * which may cause old attributes to be stored. + * XXX We should check for old attributes. + */ + if (nfs_version == NFS_VERSION) { + Cur_file_ptr->attributes2 = + reply2.readres_u.reply.attributes; + /* capture count and offset for possible dump */ + Dump_count = reply2.readres_u.reply.data.data_len; + } + if (nfs_version == NFS_V3) { + Cur_file_ptr->attributes3 = + reply3.res_u.ok.file_attributes.attr; + /* capture count and offset for possible dump */ + Dump_count = reply3.res_u.ok.data.data_len; + } + + Dump_offset = reqp->offset; + sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop); + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + ret++; + reqp->in_use = FALSE; + num_out_reqs--; + } else { + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + reqp->in_use = FALSE; + num_out_reqs--; + + if (DEBUG_CHILD_BIOD) { + (void) fprintf(stderr, + "[%d]:BIOD READ FAILED: xid %x", + calls, reqp->xid); + + if (nfs_version == NFS_VERSION) + (void) fprintf(stderr, " status %d", + reply2.status); + if (nfs_version == NFS_V3) + (void) fprintf(stderr, " status %d", + reply3.status); + (void) fprintf(stderr, "\n"); + } + } + break; + } /* switch */ + } while (error > 0 && num_out_reqs > 0); + + /* Scan for replies that have timed out */ + if (num_out_reqs > 0) { + sfs_gettime(&curr_time); + for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) { + if (reqp->in_use == FALSE) { + continue; + } + if (reqp->timeout.sec < curr_time.sec || + (reqp->timeout.sec == curr_time.sec && + reqp->timeout.usec < curr_time.usec)) { + + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + reqp->in_use = FALSE; + num_out_reqs--; + if (DEBUG_CHILD_BIOD) { + (void) fprintf (stderr, +"[%d]:Biod read timed out %x (%d.%06d) now (%d.%06d)\n", + calls, reqp->xid, + reqp->timeout.sec, reqp->timeout.usec, + curr_time.sec, curr_time.usec); + if (biod_poll_wait(NFS_client, 0) > 0) { + (void) fprintf(stderr, + "[%d]:BIOD READ TIMEOUT - data on input queue!\n", calls); + } + } + } + } + } + + /* + * We go to sleep waiting for a reply if all the requests have + * been sent and there are outstanding requests, or we cannot + * send any more requests. + */ + if ((xfer_size <= 0 && num_out_reqs > 0) || + num_out_reqs == max_out_reads) { + /* + * Find the next outstanding request that will timeout + * and take a time differential to use for the poll timeout. + * If the differential is less than zero, then we go to the + * top of the loop. Note that we are not picky on errors + * returned by select, after the sleep we return to the top + * of the loop so extensive error/status checking is not + * needed. + */ + tmp_time.sec = 0; + tmp_time.usec = 0; + for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) { + if (reqp->in_use == FALSE) { + continue; + } + if (tmp_time.sec == 0 || + (reqp->timeout.sec < tmp_time.sec || + (reqp->timeout.sec == tmp_time.sec && + reqp->timeout.usec < tmp_time.usec))) { + + tmp_time = reqp->timeout; + } + } + if (tmp_time.sec == 0 && tmp_time.usec == 0) + continue; + sfs_gettime(&curr_time); + SUBTIME(tmp_time, curr_time); + (void) biod_poll_wait(NFS_client, + tmp_time.sec * 1000000 + tmp_time.usec); + } + } /* while not done */ + + return(ret); + +} /* op_biod_read */ + +/* + * ---------------------- Async RPC Support Routines ---------------------- + */ + +/* + * biod_clnt_call() + * + * Returns XID indicating success, 0 indicating failure. + */ +static uint32_t +biod_clnt_call( + CLIENT *clnt_handlep, + uint32_t proc, + xdrproc_t xargs, + void *argsp) +{ + struct timeval timeout; + uint32_t xid; + + /* + * Set timeouts to be zero to force message passing semantics. + */ + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + if ((clnt_call(clnt_handlep, proc, xargs, argsp, NULL, + &xid, timeout)) != RPC_TIMEDOUT) { + clnt_perror(clnt_handlep, "biod_clnt_call failed"); + return (0); + } + + return (xid); +} /* biod_clnt_call */ + + +/* + * biod_get_reply() + * + * Returns pointer to the biod_req struct entry that a reply was received + * for. Returns NULL if an error was detected. + * NOTES: + * 1) This routine should only be called when it is known that there is + * data waiting on the socket. + */ +static struct biod_req * +biod_get_reply( + CLIENT *clnt_handlep, + xdrproc_t xresults, + void *resultsp, + struct timeval *tv) +{ + uint32_t xid; + int i; + int cnt = 0; + bool_t res; + uint32_t xids[MAX_BIODS]; + + /* + * Load list of valid outstanding xids + */ + for (i = 0; i < max_biod_reqs; i++) { + if (biod_reqp[i].in_use == TRUE) + xids[cnt++] = biod_reqp[i].xid; + } + + if (cnt == 0) + return (NULL); + + if ((res = clnt_getreply(clnt_handlep, xresults, + resultsp, cnt, xids, &xid, tv)) != RPC_SUCCESS) { + if (DEBUG_CHILD_BIOD) { + if (res == RPC_CANTDECODERES) { + (void) fprintf(stderr, "No xid matched, found %x\n", + xid); + } + } + return (NULL); + } + + /* + * Scan to find XID matched in the outstanding request queue. + */ + for (i = 0; i < max_biod_reqs; i++) { + if (biod_reqp[i].in_use == TRUE && biod_reqp[i].xid == xid) { + sfs_gettime(&(biod_reqp[i].stop)); + return (&biod_reqp[i]); + } + } + + return ((struct biod_req *)0); +} /* biod_get_reply */ + + +/* + * biod_poll_wait() + * + * Returns -1 on error, 0 for no data available, > 0 to indicate data available + */ +static int +biod_poll_wait( + CLIENT *clnt_handlep, + uint32_t usecs) +{ + return (clnt_poll(clnt_handlep, usecs)); +} /* biod_poll_wait */ diff --git a/TBBT/trace_play/sfs_c_chd.2thread.c b/TBBT/trace_play/sfs_c_chd.2thread.c new file mode 100644 index 0000000..48ab54e --- /dev/null +++ b/TBBT/trace_play/sfs_c_chd.2thread.c @@ -0,0 +1,3509 @@ +#ifndef lint +static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * -------------------------- sfs_c_chd.c ------------------------- + * + * The sfs child. Routines to initialize child parameters, + * initialize test directories, and generate load. + * + *.Exported_Routines + * void child(int, float, int, char *); + * void init_fileinfo(void); + * void init_counters(void); + * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type, + * sfs_file_type); + * int check_access(struct *stat) + * int check_fh_access(); + * + *.Local_Routines + * void check_call_rate(void); + * void init_targets(void); + * void init_dirlayout(void); + * void init_rpc(void); + * void init_testdir(void); + * int do_op(void); + * int op(int); + * + *.Revision_History + * 21-Aug-92 Wittle randfh() uses working set files array. + * init_fileinfo() sets up working set. + * 02-Jul-92 Teelucksingh Target file size now based on peak load + * instead of BTDT. + * 04-Jan-92 Pawlowski Added raw data dump hooks. + * 16-Dec-91 Wittle Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" +#include "rfs_c_def.h" +#include "generic_hash.h" +#include "nfsd_nfsfh_cust.h" + +extern struct hostent *Server_hostent; + +#define PROB_SCALE 1000L +#define _M_MODULUS 2147483647L /* (2**31)-1 */ + +#define _GROUP_DIVISOR 500 +#define _FILES_PER_GROUP 4 +#define _MIN_GROUPS 12 +#define _WORKING_SET_AT_25_OPS_PER_SEC 975 + + +/* + * ----------------------- External Definitions ----------------------- + */ +extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *); +extern enum clnt_stat proc_header(CLIENT *cl, xdrproc_t xdr_results, void *results_ptr); +extern int biod_poll_wait(CLIENT *, uint32_t); +extern enum clnt_stat get_areply_udp (CLIENT * cl, uint32_t *xid, struct timeval *timeout); +extern char * parse_name (char * t, char * buf); + +/* forward definitions for local functions */ +static int init_rpc(void); + +/* RFS: forward definitions for local functions */ +void init_ops(void); +static void init_signal(); +extern void init_file_system (void); +extern void init_dep_tab (void); +static int read_trace(void); +static void read_fh_map(); +static void init_play (char * mount_point); +static void trace_play(void); +void print_result(void); +static int get_nextop(void); +static int check_timeout(void); +static struct biod_req * get_biod_req(int dep_tab_index); +static int lookup_biod_req (int xid); +static void init_time_offset(void); +void adjust_play_window (int flag, int * poll_timeout); +static int poll_and_get_reply (int usecs); +static char * nfs3_strerror(int status); +static void check_clock(void); +static double time_so_far1(void); +static double get_resolution(void); +static void usage(void); +void init_dep_tab_entry (int dep_index); +extern inline fh_map_t * lookup_fh (char * trace_fh ); +static inline void finish_request (int biod_index, int dep_index, int status); +static inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line); +static inline char * find_lead_trace_fh(int proc, char * line); +static inline char * find_reply_trace_fh (char * line); + +/* + * ------------- Per Child Load Generation Rate Variables ----------- + */ +static uint_t Calls_this_period; /* calls made during the current run period */ +static uint_t Calls_this_test; /* calls made during the test so far */ +static uint_t Reqs_this_period; /* reqs made during the current run period */ +static uint_t Reqs_this_test; /* reqs made during the test so far */ +static uint_t Sleep_msec_this_test; /* msec slept during the test so far */ +static uint_t Sleep_msec_this_period; +static uint_t Previous_chkpnt_msec; /* beginning time of current run period */ +static int Target_sleep_mspc; /* targeted sleep time per call */ + +static char io_buf[BUFSIZ]; /* io buffer for print out messages */ + +char * sfs_Myname; +int Log_fd; /* log fd */ +char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */ +int Validate = 0; /* fake variable */ +int Child_num = 0; /* fake: child index */ +int Tcp = 0; /* We implement UDP first */ +int Client_num = 1; /* fake: number of client */ +uid_t Real_uid; +gid_t Cur_gid; +uid_t Cur_uid; +/* + * ------------------------- SFS Child ------------------------- + */ + +static int nfs2proc_to_rfsproc[18] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17}; +static int nfs3proc_to_rfsproc[NFS3_PROCEDURE_COUNT] = {0, 1, 2, 4, 18, 5, 6, 8, 9, 14, + 13, 21, 10, 15, 11, 12, 16, 23, 17, 20, + 22, 19}; +void print_usage(int pos, int argc, char ** argv) +{ + int i; + printf("sfs3 hostname:mount_dir trace_file|stdin fh_map_file play_scale warmup_time(in seconds) \n"); + printf("sfs3 -pair_trace trace_file\n"); + printf("sfs3 -pair_write trace_file\n"); + printf("sfs3 -help\n"); + printf ("pos %d argc %d", pos, argc); + for (i=0; i<argc; i++) { + printf(" %s", argv[i]); + } + printf ("\n"); + exit; +} + +void print_cyclic_buffers () +{ + CYCLIC_PRINT(memory_trace_index); + CYCLIC_PRINT(dep_tab_index); + CYCLIC_PRINT(dep_window_index); +} + +void add_to_dep_tab(int i) +{ + char * trace_fh; + fh_map_t * fh_map_entry; + dep_tab_t * ent; + + trace_fh = strstr (memory_trace[i].line, "fh"); + if (!trace_fh) + printf ("memory_trace[%d].line %s\n", i, memory_trace[i].line); + RFS_ASSERT (trace_fh); + trace_fh += 3; + fh_map_entry = lookup_fh (trace_fh); + if (fh_map_entry && (fh_map_entry->flag==FH_MAP_FLAG_DISCARD) ) { + req_num_with_discard_fh ++; + return; + } + if (fh_map_entry) + req_num_with_init_fh ++; + else + req_num_with_new_fh ++; + + RFS_ASSERT (!CYCLIC_FULL(dep_tab_index)); + ent = &(dep_tab[dep_tab_index.head]); + + ent->disk_index = memory_trace[i].disk_index; + ent->memory_index = i; +#ifdef REDUCE_MEMORY_TRACE_SIZE + ent->trace_status = memory_trace[i].trace_status; + ent->reply_trace_fh = memory_trace[i].reply_trace_fh; +#endif + ent->line = memory_trace[i].line; + init_dep_tab_entry(dep_tab_index.head); + + if (rfs_debug && (i%100000)==0) + printf ("dep_tab[%d].disk_index %d = memory_trace[%d].disk_index %d\n", dep_tab_index.head, ent->disk_index, i, memory_trace[i].disk_index); + CYCLIC_MOVE_HEAD(memory_trace_index); + CYCLIC_MOVE_HEAD(dep_tab_index); +} + +void init_profile_variables () +{ + init_profile ("total_profile", &total_profile); + init_profile ("execute_next_request_profile", &execute_next_request_profile); + init_profile ("valid_get_nextop_profile", &valid_get_nextop_profile); + init_profile ("invalid_get_nextop_profile",&invalid_get_nextop_profile); + init_profile ("prepare_argument_profile", &prepare_argument_profile); + init_profile ("biod_clnt_call_profile", &biod_clnt_call_profile); + init_profile ("receive_next_reply_profile", &receive_next_reply_profile); + init_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile); + init_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile); + init_profile ("decode_reply_profile", &decode_reply_profile); + init_profile ("check_reply_profile", &check_reply_profile); + init_profile ("add_create_object_profile", &add_create_object_profile); + init_profile ("check_timeout_profile", &check_timeout_profile); + init_profile ("adjust_play_window_profile",&adjust_play_window_profile); + init_profile ("fgets_profile",&fgets_profile); + init_profile ("read_line_profile",&read_line_profile); + init_profile ("read_trace_profile",&read_trace_profile); +} + +static char trace_file[256]="anon-lair62-011130-1200.txt"; +int print_memory_usage() +{ + printf("size of fh_map_t %d size of fh_map %d\n", sizeof(fh_map_t), sizeof(fh_map)); + printf("sizeof dep_tab_t %d sizeof dep_tab %d\n", sizeof(dep_tab_t), sizeof(dep_tab)); + printf("size of memory_trace_ent_t %d sizeof memory_trace %d\n", sizeof(memory_trace_ent_t), sizeof(memory_trace)); + printf("size of CREATE3args %d\n", sizeof( CREATE3args)); + printf("size of MKDIR3args %d\n", sizeof( MKDIR3args)); + printf("size of READ3args %d\n", sizeof( READ3args)); + printf("size of WRITE3args %d\n", sizeof( WRITE3args)); + printf("size of RENAME3args %d\n", sizeof( RENAME3args)); + printf("size of GETATTR3args %d\n", sizeof( GETATTR3args)); + printf("size of SETATTR3args %d\n", sizeof( SETATTR3args)); + printf("size of LINK3args %d\n", sizeof( LINK3args)); + printf("size of SYMLINK3args %d\n", sizeof( SYMLINK3args)); + printf("size of MKNOD3args %d\n", sizeof( MKNOD3args)); + printf("size of RMDIR3args %d\n", sizeof( RMDIR3args)); + printf("size of REMOVE3args %d\n", sizeof( REMOVE3args)); + printf("size of LOOKUP3args %d\n", sizeof( LOOKUP3args)); + printf("size of READDIR3args %d\n", sizeof( READDIR3args)); + printf("size of READDIRPLUS3args %d\n", sizeof( READDIRPLUS3args)); + printf("size of FSSTAT3args %d\n", sizeof( FSSTAT3args)); + printf("size of FSINFO3args %d\n", sizeof( FSINFO3args)); + printf("size of COMMIT3args %d\n", sizeof( COMMIT3args)); + printf("size of ACCESS3args %d\n", sizeof( ACCESS3args)); + printf("size of READLINK3args %d\n", sizeof( READLINK3args)); + + +} + +int io_thread () +{ +/* number of seconds the I/O thread pauses after each time trying to read the requests */ +#define IO_THREAD_PAUSE_TIME 1 + + int i; + int j = 0; + + disk_io_status = read_trace (); + while (disk_io_status == TRACE_BUF_FULL) { + + usleep (10000); + if ((j++%200)==0) { + printf("&&&&&&&&&& io thread, sleep %d seconds\n", j/10); + } + + disk_io_status = read_trace (); + //printf ("io_thread, after read_trace, disk_index %d\n", disk_index); + +#ifdef SEQUEN_READ + for (i=0; i<SEQUEN_READ_NUM; i++) { + add_to_dep_tab(CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size)); + } +#endif + //printf ("***************** IO THREAD SLEEP 1 s\n"); + //print_cyclic_buffers(); + //pthread_yield(); + } + RFS_ASSERT (disk_io_status == TRACE_FILE_END); +} + +int execute_thread() +{ + int i; + struct timeval playstart_time, playend_time; + + sleep (10); /* first let io_thread to run for a while */ + init_time_offset(); + printf ("trace_play\n"); + + gettimeofday(&playstart_time, NULL); + + trace_play (); + + gettimeofday(&playend_time, NULL); + + { + int usec, sec; + sec = playend_time.tv_sec - playstart_time.tv_sec; + usec = sec * 1000000 + (playend_time.tv_usec - playstart_time.tv_usec); + sec = usec / 1000000; + usec = usec % 1000000; + printf("Total play time: %d sec %d usec\n", sec, usec); + } + + print_result(); +} + +int init_thread () +{ + pthread_attr_t attr; + int arg; + int ret = 0; + pthread_t io_thread_var; + pthread_t execute_thread_var; + + ret = pthread_attr_init (&attr); + if (ret!=0) { + perror("pthread_attr_init attr"); + return ret; + } + ret = pthread_create (&(io_thread_var), &attr, &io_thread, (void *)&arg ); + if (ret!=0) { + perror("io_pthread_attr_init"); + return ret; + } + +/* + ret = pthread_create (&(execute_thread_var), &attr, &execute_thread, (void *)&arg ); + if (ret!=0) { + perror("io_pthread_attr_init"); + return ret; + } +*/ +} + +void init_buffers() +{ + CYCLIC_INIT("memory_trace_index",memory_trace_index,MAX_MEMORY_TRACE_LINES); + CYCLIC_INIT("dep_tab_index ",dep_tab_index,DEP_TAB_SIZE); + CYCLIC_INIT("dep_window_index ",dep_window_index,DEP_TAB_SIZE); +} + +int main(int argc, char ** argv) +{ + extern char * optarg; + int i; + struct timeval in={1000000, 100}; + + init_profile_variables(); + if ((argc==1) || (argc==2 && !strcmp(argv[1],"-help"))) { + print_usage(0, argc, argv); + exit(0); + } + if (!strcmp(argv[1], "-pair_write")) { + if (argc==3) + strcpy(trace_file, argv[2]); + else + strcpy(trace_file, "stdin"); + pair_write(trace_file); + exit(0); + } + if (!strcmp(argv[1], "-pair_trace")) { + if (argc==3) + strcpy(trace_file, argv[2]); + else + strcpy(trace_file, "stdin"); + pair_trace(trace_file); + exit(0); + } + if (!strcmp(argv[1], "-check_aging")) { + if (argc!=3) { + print_usage(3, argc, argv); + exit(0); + } + strcpy(trace_file, argv[2]); + check_aging (trace_file); + exit(0); + } + if (!strcmp(argv[1], "-check_statistics")) { + if (argc!=3) { + print_usage(1, argc, argv); + exit(0); + } + strcpy(trace_file, argv[2]); + memset(fh_htable, 0, sizeof (fh_htable)); + check_statistics (trace_file); + exit(0); + } + + if (argc!=6) { + print_usage(2, argc, argv); + exit(0); + } + PLAY_SCALE = atoi (argv[4]); + RFS_ASSERT (PLAY_SCALE >=1 && PLAY_SCALE <=10000); + + WARMUP_TIME = atoi (argv[5]); + RFS_ASSERT (WARMUP_TIME >=0 && WARMUP_TIME <=1000); + + print_memory_usage(); + check_clock(); + getmyhostname(lad_hostname, HOSTNAME_LEN); + + init_ops(); + /* + * Get the uid and gid information. + */ + Real_uid = getuid(); + Cur_gid = getgid(); + //Real_uid = 513; + //Cur_gid = 513; + + Nfs_timers = Nfs_udp_timers; + + init_file_system (); + init_signal(); + init_play (argv[1]); + //init_play ("capella:/p5/RFSFS"); + init_fh_map(); + read_fh_map (argv[3]); + //read_fh_map ("fh-path-map-play"); + strcpy(trace_file, argv[2]); + +/* If ordered by TIMESTAMP, + * memory_trace_index.tail <= dep_tab_index.tail < dep_window_max <= + * dep_tab_index.head <= memory_trace_index.head + */ + + init_buffers(); + //init_thread(); + pthread_yield(); + execute_thread(); +} + +void init_ops (void) +{ + Ops = nfsv3_Ops; + nfs_version = NFS_V3; +} + +/* Set up the signal handlers for all signals */ +void init_signal() +{ +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + struct sigaction sig_act, old_sig_act; + + /* use XOPEN signal handling */ + + sig_act.sa_handler = generic_catcher; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + + /* signals handlers for signals used by sfs */ + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGINT"); + exit(135); + } + + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) { + perror("sigaction failed: SIGTERM"); + exit(137); + } +#else + /* signals handlers for signals used by sfs */ + (void) signal(SIGINT, sfs_cleanup); + // RFS (void) signal(SIGALRM, sfs_alarm); + (void) signal(SIGTERM, sfs_cleanup); +#endif +} + +void +init_play ( + char * mount_point) /* Mount point for remote FS */ +{ + char namebuf[NFS_MAXNAMLEN] = "trace_play"; /* unique name for this program */ + CLIENT * mount_client_ptr; /* Mount client handle */ + + if (!rfs_debug); + (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ); + + sfs_Myname = namebuf; + + /* + * May require root priv to perform bindresvport operation + */ + mount_client_ptr = lad_getmnt_hand(mount_point); + if (mount_client_ptr == NULL) { + exit(145); + } + + /* + * should be all done doing priv port stuff + */ + + if (init_rpc() == -1) { + (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname); + (void) generic_kill(0, SIGINT); + exit(146); + } + + + /* + * finish all priv bindresvport calls + * reset uid + */ + if (setuid(Real_uid) != (uid_t)0) { + (void) fprintf(stderr,"%s: %s%s", sfs_Myname, + "cannot perform setuid operation.\n", + "Do `make install` as root.\n"); + } + + init_mount_point(0, mount_point, mount_client_ptr); + + + /* + * Cleanup client handle for mount point + */ + clnt_destroy(mount_client_ptr); + + init_counters(); +} + +#ifdef REDUCE_MEMORY_TRACE_SIZE +inline char * read_line (int disk_index) +{ + static FILE * fp=NULL; + static int start=0; + static int start_disk_index=0; + int i; + static int finish_flag = 0; + +#define READ_LINE_BUF_SIZE (MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR+2) +#define SAFE_BYTES 1000 +#define READ_LINE_LENGTH (MAX_TRACE_LINE_LENGTH+SAFE_BYTES) + + static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH]; + start_profile (&read_line_profile); + + if (fp==NULL) { + if (strcmp(trace_file, "stdin")) { + fp = fopen(trace_file, "r"); + if (!fp) { + printf("can not open files %s\n", fp); + perror("open"); + } + } else { + fp = stdin; + } + RFS_ASSERT (fp!=NULL); + for (i=0; i<READ_LINE_BUF_SIZE; i++) { + start_profile(&fgets_profile); + if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) { + RFS_ASSERT (0); + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]); + } + } + + RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE) + if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) { + if (finish_flag) { + return NULL; + } + start_profile(&fgets_profile); + if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) { + end_profile(&fgets_profile); + finish_flag = 1; + return NULL; + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]); + start = (start+1) % READ_LINE_BUF_SIZE; + start_disk_index ++; + } + RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE) + i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE; + +/* + if (!(strlen(line_buf[i])>80)) { + printf ("start %d start_disk_index %d disk_index %d strlen %d line_buf[%d] %s\n", start, start_disk_index, disk_index, strlen(line_buf[i]), i, line_buf[i]); + RFS_ASSERT (strlen(line_buf[i])>80); + } + if (!((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH))) + printf ("disk_index %d strlen %d line_buf[%d] %s\n", disk_index, strlen(line_buf[i]), i, line_buf[i]); + RFS_ASSERT ((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH)); +*/ + + end_profile (&read_line_profile); + return (line_buf[i]); +} + +#define OPS_FLAG_INC 0 +#define OPS_FLAG_PRINT 1 +int read_write_fh_statistics (int flag, char * buf, int timestamp) +{ + static FILE * fp = NULL; + char * p; + char c; + if (!fp) { + fp = fopen ("read_write_fh.output", "w"); + RFS_ASSERT (fp); + } + if (flag == OPS_FLAG_INC) { + p = strstr (buf, "fh"); + RFS_ASSERT (p); + c = p[40+3]; + p[40+3]=0; + fprintf(fp, "%s\n", p+3+24); + p[40+3]=c; + }; + if (flag == OPS_FLAG_PRINT) { + int disk_index = (int) buf; + fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + } +} + +int write_statistics(int flag, char * buf, char * reply_buf, int trace_status) +{ + static FILE * fp = NULL; + int pre_size, size, count; + char * p = reply_buf; + if (!fp) { + fp = fopen ("write.output", "w"); + RFS_ASSERT (fp); + } + if (flag == OPS_FLAG_INC) { + p = strstr (p, "pre-size"); + RFS_ASSERT (p); + sscanf (p, "pre-size %x", &pre_size); + p += strlen("pre-size"); + p = strstr (p, "size"); + RFS_ASSERT (p); + sscanf (p, "size %x", &size); + p = strstr (p, "count"); + if (!p) + fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + RFS_ASSERT (p); + sscanf (p, "count %x", &count); + fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + } + if (flag == OPS_FLAG_PRINT) { + int disk_index = (int) buf; + int timestamp = (int) reply_buf; + fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + } +} + +void ops_statistics (int ops_flag, int proc, int timestamp) +{ + static FILE * fp = NULL; + static struct { + int count; + int count_K; + int update_flag; + char name[32]; + } ops[NOPS]={{0, 0, 0, "NULL"},{0, 0, 0, "GETATTR"},{0, 0, 1, "SETATTR"},{0, 0, 0, "ROOT"}, + {0, 0, 0, "LOOKUP"},{0, 0, 0, "READLINK"},{0, 0, 0, "READ"},{0, 0, 1, "WRCACHE"}, + {0, 0, 1, "WRITE"}, {0, 0, 1, "CREATE"},{0, 0, 1, "REMOVE"},{0, 0, 1, "RENAME"}, + {0, 0, 1, "LINK"}, {0, 0, 1, "SYMLINK"},{0, 0, 1, "MKDIR"},{0, 0, 1, "RMDIR"}, + {0, 0, 0, "READDIR"},{0, 0, 0, "FSSTAT"},{0, 0, 0, "ACCESS"},{0, 0, 0, "COMMIT"}, + {0, 0, 0, "FSINFO"},{0, 0, 1, "MKNOD"}, {0, 0, 0, "PATHCONF"}, {0, 0, 0, "READDIRPLUS"}}; + + if (ops_flag == OPS_FLAG_INC) { + RFS_ASSERT (proc>=0 && proc<NOPS); + ops[proc].count ++; + if (ops[proc].count == 1000) { + ops[proc].count_K ++; + ops[proc].count = 0; + } + } + if (ops_flag == OPS_FLAG_PRINT) { + int i; + int update_K=0, update=0, total_K=0, total=0; + float f1, f2; + int disk_index = proc; + + if (!fp) { + fp = fopen ("mix.output", "w"); + RFS_ASSERT (fp); + } + for (i=0; i<NOPS; i++) { + total_K += ops[i].count_K; + total += ops[i].count; + if (ops[i].update_flag) { + update_K += ops[i].count_K; + update += ops[i].count; + } + } + update_K += update/1000; + update = update%1000; + total_K += total/1000; + total = total%1000; + + f1 = total_K; + f1 = f1*1000+total; + for (i=0; i<NOPS; i++) { + f2 = ops[i].count_K; + f2 = f2*1000+ops[i].count; + fprintf (fp, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1); + fprintf (stderr, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1); + } + f2 = update_K; + f2 = f2*1000+update; + fprintf (fp, " total %8d,%03d\n", total_K, total); + fprintf (stderr, " total %8d,%03d\n", total_K, total); + fprintf (fp, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1); + fprintf (stderr, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1); + fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + fprintf(stderr, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + } +} + + +void truncate_statistics (int flag, int proc, char * buf, char * reply_buf) +{ +#define TRACE_FH_SIZE2 16 +#define BLOCK_SIZE 4096 + static int no_size_num = 0; + static int have_size_num = 0; + static int equal_size_num = 0; + static int first_size_num = 0; + static int truncate_num = 0; + static int truncate_size = 0; + static int truncate_KB = 0; + static int truncate_block_num = 0; + static int padding_num = 0; + static int padding_KB = 0; + static int padding_size = 0; + static FILE * fp = NULL; + char * p; + char * trace_fh; + int pre_size, size, count; + struct generic_entry * ent; + + if (flag == OPS_FLAG_PRINT) { + int disk_index = proc; + int timestamp = (int)buf; + if (!fp) { + fp = fopen ("truncate.output", "w"); + RFS_ASSERT (fp); + } + truncate_KB += truncate_size/1000; + truncate_size %= 1000; + padding_KB += padding_size/1000; + padding_size %= 1000; + fprintf(fp, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB); + fprintf(stderr, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB); + return; + } + + p = strstr (&buf[TRACE_MSGID_POS], "fh"); + RFS_ASSERT (p); + p+=3; + trace_fh = p; + + if (proc==SETATTR) { + p = strstr (buf, " size "); + } else { + RFS_ASSERT (proc==WRITE); + p = strstr (reply_buf, " ftype 1 "); + RFS_ASSERT (p); + p = strstr (p, " size "); + RFS_ASSERT (p); + if (strstr(p, " ftype 1 ")) { + fprintf (fp, "#### %s%s\n", buf, reply_buf); + fprintf (stderr, "#### %s%s\n", buf, reply_buf); + } + RFS_ASSERT (!strstr(p, " ftype 1 ")); + } + if (!p) { + no_size_num ++; + return; + } + have_size_num ++; + + sscanf (p, " size %x", &size); + if (size <0 || size > 2000000000) { + fprintf (fp, "#### size too big %x %s %s\n", size, buf, reply_buf); + fprintf (stderr, "#### size too big %x %s %s\n", size, buf, reply_buf); + } + + RFS_ASSERT (size >=0 && size <2000000000); + ent = generic_lookup (trace_fh+24, TRACE_FH_SIZE2, 0, fh_htable, FH_HTABLE_SIZE); + if (ent) { + if (ent->key3 != size) { + if (proc==SETATTR) { + //printf ("%s\n", buf); + //printf ("size change fh %s pre-size %x size %x\n", trace_fh, ent->key3, size); + if (ent->key3 > size) { + truncate_num ++; + truncate_size += ent->key3 - size; + truncate_block_num += (ent->key3+BLOCK_SIZE-1)/BLOCK_SIZE; + if (size!=0) { + //fprintf (stderr, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf); + //fprintf (fp, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf); + truncate_block_num -= (size + BLOCK_SIZE-1)/BLOCK_SIZE; + } + if (truncate_size > 1000000000) { + truncate_KB += truncate_size/1000; + truncate_size %= 1000; + } + } else { + padding_num ++; + //printf ("%s\n", buf); + //printf ("padding fh %s pre-size %x size %x\n", trace_fh, ent->key3, size); + padding_size += size - ent->key3; + if (padding_size > 1000000000) { + padding_KB += padding_size/1000; + padding_size %= 1000; + } + } + } + ent->key3 = size; + }else + equal_size_num++; + } else { + generic_insert(trace_fh+24, TRACE_FH_SIZE2, size, fh_htable, FH_HTABLE_SIZE); + first_size_num ++; + } +}; + +int get_timestamp (char * buf) +{ + char str[128]; + int ret; + strncpy(str, buf, 100); + RFS_ASSERT (str[10]=='.'); + str[10]=0; + ret = atoi(str); + RFS_ASSERT (ret >1000000000 && ret <2000000000); + return ret; +} + +int check_aging (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + int trace_status; + int debug = 0; + int nfs3proc, msgid, proc; + + while ((buf=read_line(++disk_index))!=NULL) { + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + + proc = nfs3proc_to_rfsproc[nfs3proc]; + ops_statistics (OPS_FLAG_INC, proc, -1); + + switch (proc) { + int off, count, size; + char * t; + case CREATE: printf("%s\n", "create"); break; + case MKDIR: printf("%s\n", "mkdir"); break; + case REMOVE: printf("%s\n", "remove"); break; + case RMDIR: printf("%s\n", "rmdir"); break; + case WRITE: + t = buf; + t = strstr (t, "off"); + RFS_ASSERT (t); + t+=4; + sscanf (t, "%x", &off); + RFS_ASSERT (off>=0 && off<0x7FFFFFFF) + t = strstr (t, "count"); + RFS_ASSERT (t); + t+=6; + sscanf (t, "%x", &count); + RFS_ASSERT (count <= 32768); + printf("%s off %x count %x\n", "write", off, count); + //printf("%s count %x\n", "write", count); + break; + case SETATTR: + t = strstr (buf, " size "); + if (t) { + sscanf (t, " size %x", &size); + printf ("%s size %x\n", "setattr", size); + } + } + if ((disk_index%10000)==0) { + fprintf(stderr, "%d disk trace passed\n", disk_index); + } + }; + + fprintf(stderr, "%d disk trace parsed\n", disk_index); + ops_statistics (OPS_FLAG_PRINT, disk_index, -1); +} + + +int check_statistics (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + char * p; + int trace_status; + int debug = 0; + int nfs3proc, msgid, proc; + static int last_timestamp_sec = -1; + int timestamp_sec; + int memory_trace_size = 0; + + while ((buf=read_line(++disk_index))!=NULL) { + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + timestamp_sec = get_timestamp(buf); + + proc = nfs3proc_to_rfsproc[nfs3proc]; + ops_statistics (OPS_FLAG_INC, proc, -1); + + if (proc!= WRITE && proc!=SETATTR && proc!=READ) { + continue; + } + RFS_ASSERT (buf[strlen(buf)-1]=='\n'); + buf [strlen(buf)-1] = 0; + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + if (debug) + printf("read line disk_index %d %s\n", disk_index, buf); + + trace_status = NFS3ERR_RFS_MISS; + for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE; i++) { + reply_buf = read_line(i); + if (debug) + printf("searching for reply: read line %s\n", reply_buf); + if (!reply_buf) + break; + if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&reply_buf[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) { + trace_status = find_reply_status(reply_buf); + if (trace_status == NFS3_OK) { + if (proc==READ || proc==WRITE) + read_write_fh_statistics (OPS_FLAG_INC, buf, 0); + if (proc == WRITE) + write_statistics (OPS_FLAG_INC, buf, reply_buf, trace_status); + if (proc==WRITE || proc==SETATTR) + truncate_statistics (OPS_FLAG_INC, proc, buf, reply_buf); + } + }; + } + } + //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS) + if (trace_status == NFS3ERR_RFS_MISS) { + //printf ("%s no reply\n", buf); + missing_reply_num ++; + } + +#if 0 /* commented out by G. Jason Peng */ + if * + if ((missing_reply_num > memory_trace_size/10) && (missing_reply_num > 100)) { + printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size); + exit (0); + } +#endif + + memory_trace_size ++; + + if (last_timestamp_sec == -1) { + last_timestamp_sec = timestamp_sec; + } else if (timestamp_sec - last_timestamp_sec >=3600) { + ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec); + truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL); + read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec); + write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1); + last_timestamp_sec = timestamp_sec; + } +/* + if ((memory_trace_size%10000)==0) { + fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num); + ops_statistics (OPS_FLAG_PRINT, -1); + truncate_statistics (OPS_FLAG_PRINT, -1, NULL, NULL); + } +*/ + }; + + fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num); + ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec); + truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL); + read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec); + write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1); +} + + +/* This routine output all the requests, together with their replies */ +int pair_trace (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + char * p; + int trace_status; + int debug = 0; + int nfs3proc, msgid; + int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + int memory_trace_size = 0; + FILE * outputfp; + + outputfp = fopen ("pair.output", "w"); + RFS_ASSERT (outputfp); + + while ((buf=read_line(++disk_index))!=NULL) { + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + ops[nfs3proc]++; + + buf [strlen(buf)-1] = 0; + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + if (debug) + printf("read line disk_index %d %s\n", disk_index, buf); + fprintf (outputfp, "%s\n", buf); + + trace_status = NFS3ERR_RFS_MISS; + for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) { + reply_buf = read_line(i); + if (debug) + printf("searching for reply: read line %s\n", reply_buf); + if (!reply_buf) + break; + if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&reply_buf[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) { + fprintf(outputfp, "%s", reply_buf); + trace_status = find_reply_status(reply_buf); + if (debug) + fprintf(stderr, "reply found trace_status %d\n", find_reply_status(reply_buf)); + }; + } + } + + if (trace_status == NFS3ERR_RFS_MISS) { + fprintf (stderr, "%s no reply\n", buf); + fprintf(outputfp, "missing_reply\n"); + missing_reply_num ++; + } + + if (missing_reply_num > memory_trace_size/10 && missing_reply_num >100) { + fprintf (stderr, "missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size); + exit (0); + } + + memory_trace_size ++; + + if ((memory_trace_size%10000)==0) + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + }; + + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); + +} +/* This routine output all the write requests, together with their replies. It is used for + * analysis of write requests: appended bytes, overwrite bytes etc + */ +int pair_write (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + char * p; + int trace_status; + int pair_write_debug = 0; + int nfs3proc, msgid; + int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + int memory_trace_size = 0; + + while ((buf=read_line(++disk_index))!=NULL) { + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + ops[nfs3proc]++; + + if (!strstr(buf, "write")) + continue; + + buf [strlen(buf)-1] = 0; + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + if (pair_write_debug) + printf("read line disk_index %d %s\n", disk_index, buf); + + /* store the request to memory */ + //strcpy (memory_trace[memory_trace_size].line, buf); + //memory_trace[memory_trace_size].disk_index = disk_index; + + /* find and store the reply status and reply fhandle to memory */ + //memory_trace[memory_trace_size].trace_status = NFS3ERR_RFS_MISS; + trace_status = NFS3ERR_RFS_MISS; + for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) { + reply_buf = read_line(i); + if (pair_write_debug) + printf("searching for reply: read line %s\n", reply_buf); + if (!reply_buf) + break; + if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&reply_buf[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) { + int pre_size, size, count; + //memory_trace[memory_trace_size].trace_status = find_reply_status(reply_buf); + if (pair_write_debug) + printf("reply found trace_status %d\n", find_reply_status(reply_buf)); + //break; + trace_status = find_reply_status(reply_buf); + if (trace_status == NFS3_OK) { + p = strstr (p, "pre-size"); + RFS_ASSERT (p); + sscanf (p, "pre-size %x", &pre_size); + p += strlen("pre-size"); + p = strstr (p, "size"); + RFS_ASSERT (p); + sscanf (p, "size %x", &size); + p = strstr (p, "count"); + if (!p) + printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + RFS_ASSERT (p); + sscanf (p, "count %x", &count); + printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + break; + } + }; + } + } + //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS) + if (trace_status == NFS3ERR_RFS_MISS) { + printf ("%s no reply\n", buf); + missing_reply_num ++; + } + +#if 0 /* commented out by G. Jason Peng */ + if (missing_reply_num > memory_trace_size/10) { + printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size); + exit (0); + } +#endif + + memory_trace_size ++; + + /* + if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) { + fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES); + break; + } + */ + if ((memory_trace_size%10000)==0) + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + }; + + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); + +} + +int read_trace () +{ + char *buf; + char *reply_buf; + int i; + char * p; + int debug = 0; + memory_trace_ent_t * ent=NULL; + + start_profile (&read_trace_profile); + + while (!CYCLIC_FULL(memory_trace_index)) { + if (ent!=NULL && (ent->trace_status == NFS3ERR_RFS_MISS)) + buf = reply_buf; + if ((buf=read_line(++disk_index))==NULL) { +END: fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, CYCLIC_NUM(memory_trace_index), missing_reply_num ); + fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); + end_profile (&read_trace_profile); + return TRACE_FILE_END; + } + +#ifdef notdef + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + if (debug) + printf("read line disk_index %d %s\n", disk_index, buf); + + if (disk_index==1) { + trace_timestamp1 = get_timestamp (buf); + } else + trace_timestamp2 = get_timestamp (buf); +#endif + /* store the request to memory */ + ent = &(memory_trace[memory_trace_index.head]); + strcpy (ent->line, buf); + ent->disk_index = disk_index; + + if (MAX_COMMAND_REPLY_DISTANCE ==1) { + ent->trace_status == NFS3ERR_RFS_MISS; + } else { + reply_buf=read_line(++disk_index); + RFS_ASSERT (reply_buf); + if (!strcmp(reply_buf, "missing_reply\n")) { + ent->trace_status == NFS3ERR_RFS_MISS; + } else { + ent->trace_status = find_reply_status(reply_buf); + } + }; + + if (ent->trace_status == NFS3ERR_RFS_MISS) + missing_reply_num ++; + + if (MAX_COMMAND_REPLY_DISTANCE > 1) { + if ((missing_reply_num > disk_index/5) && (missing_reply_num > 100)) { + printf ("missing_reply_num %d too high for disk_index %d\n", missing_reply_num, disk_index); + exit (0); + } + } + + /* find and store the reply trace fhandle for create-class requests */ + if (ent->trace_status==NFS3_OK) { + if (strstr(buf, "create") || strstr(buf, "mkdir") + || (strstr(buf, "symlink") && (buf[TRACE_VERSION_POS]!='2')) + || strstr(buf, "mknod") ) { + p = find_reply_trace_fh(reply_buf); + memcpy(ent->reply_trace_fh, p, TRACE_FH_SIZE); + } else + memset(ent->reply_trace_fh, 0, TRACE_FH_SIZE); + } + + add_to_dep_tab(memory_trace_index.head); + + if (((disk_index+1)%20000)==0) { + fprintf(stderr, "%d disk trace parsed \n", disk_index+1); + }; + }; + + end_profile (&read_trace_profile); + return TRACE_BUF_FULL; +} +#else /* not defined REDUCE_MEMORY_TRACE_SIZE */ +int read_trace () +{ + FILE * fp; + char buf[1024]; + // char * t=buf; + int disk_index=0; + + fp = fopen(trace_file, "r"); + RFS_ASSERT (fp!=NULL); + while (fgets(buf, MAX_TRACE_LINE_LENGTH, fp)) { + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("strlen(buf) %d buf %s \n", strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + + /* store the request to memory */ + strcpy (memory_trace[memory_trace_size].line, buf); + memory_trace[memory_trace_size].disk_index = disk_index; + memory_trace_size ++; + + if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) { + fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES); + break; + } + if ((disk_index%100000)==0) + fprintf(stderr, "%d disk trace parsed, store %d trace lines to memory\n", disk_index, memory_trace_size); + disk_index ++; + } + + fprintf(stderr, "total %d disk lines %d memory lines \n", disk_index, memory_trace_size ); +} +#endif + + +#ifdef REDUCE_MEMORY_TRACE_SIZE +inline int disk_index_to_memory_index (int disk_index) +{ + static int memory_index = 0; + + RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index)); + RFS_ASSERT (memory_trace[memory_trace_index.tail].disk_index <= disk_index); + RFS_ASSERT (memory_trace[CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size)].disk_index >=disk_index); + if (disk_index > memory_trace[memory_index].disk_index) { + while (memory_trace[memory_index].disk_index < disk_index) { + memory_index = CYCLIC_ADD(memory_index,1,memory_trace_index.size); + } + }; + if (disk_index < memory_trace[memory_index].disk_index) { + while (memory_trace[memory_index].disk_index > disk_index) { + memory_index = CYCLIC_MINUS(memory_index,1,memory_trace_index.size); + } + }; + + RFS_ASSERT (disk_index == memory_trace[memory_index].disk_index); + return memory_index; +} +#else +#define disk_index_to_memory_index(disk_index) disk_index +#endif + +#define get_line_by_disk_index(disk_index) \ + memory_trace[disk_index_to_memory_index(disk_index)].line + +inline char * find_reply_line (char * command_line, int cur_disk_index) +{ + int i; + char * line; + char * p; + int request_memory_index = disk_index_to_memory_index (cur_disk_index); + for (i=request_memory_index+1; i<request_memory_index+MAX_COMMAND_REPLY_DISTANCE && i<MAX_MEMORY_TRACE_LINES; i++) { + line = memory_trace[i].line; + if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&line[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&line[TRACE_MSGID_POS], &command_line[TRACE_MSGID_POS], p-&(line[TRACE_MSGID_POS]))) + return line; + } + } + return NULL; +} + +inline int find_reply_status (char * line) +{ + char * p; + int i=0; + + //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R'); + p = line+TRACE_MSGID_POS+2; /* at least one letter for msgid and one letter for space */ + if (strstr(p, "OK")) + return NFS3_OK; + if (strstr(p, "lookup 2")) + return 0x2; + if (strstr(p, "create d")) + return 0xd; + if (strstr(p, "setattr 1")) + return 0x1; + if (strstr(p, "setattr 2712")) /* 10002 NFS3ERR_NOT_SYNC */ + return 0x2712; + if (strstr(p, "lookup d")) + return 0xd; + if (strstr(p, "read d")) + return 0xd; + if (strstr(p, "write d")) + return 0xd; + if (strstr(p, "write 46")) + return 0x46; + if (strstr(p, "getattr 46")) + return 0x46; + if (strstr(p, "mkdir d")) + return 0xd; + printf ("line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + printf ("!!!!!!!!!!!!!!!!!!!!!!!!\n"); + fprintf (stderr, "line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + fprintf (stderr, "!!!!!!!!!!!!!!!!!!!!!!!!\n"); +} + +inline int find_reply_status_old (char * line) +{ + char * p; + int i=0; + + //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R'); + if (!strstr(line, "OK")) { + p=strstr(line, " 6 read "); + if (p) { + p+=strlen(" 6 read "); + } else { + p = strstr (line, "status=XXX"); + RFS_ASSERT (p); + p--; + RFS_ASSERT (*p==' '); + while (*p==' ') + p--; + while (*p!=' ') { + p--; + } + p++; + } + sscanf (p, "%x", &i); + if ((i<=0) || (i>10000)) + printf("line %s\n", line); + RFS_ASSERT (i>0 && i<10009); + } + return i; +} + +inline char * find_reply_trace_fh (char * line) +{ + char * p; + p = strstr (line, "OK fh"); + if (!p) + printf ("find_reply_trace_fh line %s\n", line); + RFS_ASSERT (p); + return p+6; +} + +#ifndef NO_DEPENDENCY_TABLE +inline int disk_index_to_dep_index(int cur_dep_index, int disk_index) +{ + int i; + for (i=cur_dep_index; i>min_dep_index; i--) { + if (dep_tab[i].disk_index == disk_index) + return i; + } + RFS_ASSERT (0); +} +#endif + +inline int is_play_candidate (int dep_index) +{ + int proc = dep_tab[dep_index].proc; + int status = dep_tab[dep_index].status; + int trace_status = dep_tab[dep_index].trace_status; + +#ifndef TAKE_CARE_CREATE_MODE_BY_DAN + /* for a failed create in trace, trace_replay just ignore many time the trace create fail + * due to access control, but trace_play will success because our access control + * may be loose (all uid/gid is mapped to single one 513:513, so we just skip these requests + */ + if ((proc==CREATE || proc==MKDIR) && (trace_status!=NFS3_OK) && (status!=NFS3ERR_RFS_MISS)) { + if (dependency_debug) + printf ("disk[%d] ignore failed create/mkdir in trace, trace_status %d line %s", + dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line); + failed_create_command_num ++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_OTHER_FAILED_COMMAND + if (((trace_status == NFS3ERR_ACCES) && (proc==READ || proc==WRITE || proc==LOOKUP)) || + ((trace_status == NFS3ERR_PERM) && (proc==SETATTR)) ){ + if (dependency_debug) + printf ("disk[%d] ignore other failed command in trace, trace_status %d line %s", + dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line); + + failed_other_command_num ++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_SYMBOLIC_LINK + if ((dep_tab[dep_index].proc==READLINK) ) { /* send request */ + skipped_readlink_command_num ++; + return FALSE; + } +#endif +/* This is actually take care in get_nextop by checking fh_map error when dep_index==min_dep_index */ +#ifndef TAKE_CARE_CUSTOM_COMMAND + /* this line has a file handle which should belong to discard but it is not + * the file handle directly appears as parent directory in a lookup request + * the return value is NOENT, the parent directory should have been initialized + * but the initialization code just ignored all lookup request which didn't success + * including NOENT even though the parent directory is still valid. + */ +/* + if (( ((dep_tab[dep_index].disk_index==262213) || (dep_tab[dep_index].disk_index==214402)) + && !(strcmp(trace_file, "anon-lair62-011130-1100.txt")) + ) || + ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470)) + && !(strcmp(trace_file, "anon-lair62-011130-1000.txt")) + )) { + skipped_custom_command_num++; + return FALSE; + } +*/ + if (( ((dep_tab[dep_index].disk_index==423727) || (0)) + && !(strncmp(trace_file, "anon-lair62-011130-1500.txt", strlen("anon-lair62-011130-1500.txt"))) + ) || + ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470)) + && !(strcmp(trace_file, "anon-lair62-011130-1000.txt")) + )) { + skipped_custom_command_num++; + return FALSE; + } + /* this line is about the mkdir 116d9d originally in anon-lair62-011130-1400.txt */ + if (!strncmp(dep_tab[dep_index].line, "1007147245.194201", strlen("1007147245.194201"))) { + skipped_custom_command_num++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_FSSTAT_COMMAND + /* the file handle used in this command is not processed properly by pre-processing */ + if (proc==FSSTAT) { + char * trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line); + fh_map_t * fh = lookup_fh (trace_fh); + if (!fh) { + skipped_fsstat_command_num++; + return FALSE; + } + } +#endif + return TRUE; +} + +inline int is_dir_op (int proc) +{ + switch (proc) { + case MKDIR: + case CREATE: + case LINK: + case SYMLINK: + case MKNOD: + case REMOVE: + case RMDIR: + case RENAME: + return 1; + default: + return 0; + } +} + +inline int is_create_op (int proc) +{ + if (proc==CREATE || proc==MKDIR || proc==LINK || proc==SYMLINK || proc==MKNOD || proc==RENAME) + return 1; + return 0; +} + +inline int is_delete_op (int proc) +{ + if (proc==REMOVE || proc==RMDIR || proc==RENAME) + return 1; + return 0; +} + +static inline char * find_lead_trace_fh(int proc, char * line) +{ + char * p; + /* check the file handle availability */ + p = strstr (line, "fh"); + RFS_ASSERT (p); + p+=3; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line); + return p; +} + +inline char * find_another_trace_fh(int proc, char * line) +{ + char * p; + /* check the file handle availability */ + p = strstr (line, "fh2"); + RFS_ASSERT (p); + p+=4; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line); + return p; +} + +/* return the index of next request in dep_tab. + * Return -1 if there is no suitable request to send + */ +inline int get_nextop(void) +{ + int i,j, k; + int * t; + static int dep_index = -2; + char * line; + char * p; +#define INIT_MIN_WAIT_VALUE -999 + static int min_wait_fhandle_dep_index = INIT_MIN_WAIT_VALUE; + int proc; + int flag; + + if (min_wait_fhandle_dep_index == -999) + min_wait_fhandle_dep_index = dep_window_index.head; + + for (i=0; i<CYCLIC_NUM(dep_window_index); i++) { + dep_index = (dep_window_index.tail+i) % dep_window_index.size; + + proc = dep_tab[dep_index].proc; + flag = dep_tab[dep_index].flag; + + if (dependency_debug) + printf ("get_nextop check dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); +#ifdef NO_DEPENDENCY_TABLE + if (dep_tab[dep_index].flag == DEP_FLAG_INIT) { + if (is_play_candidate(dep_index)==TRUE) { + /* the trace_fh is the file handle for the operation directory, trace_fh_2 is other file handle + * used in the request */ + if (proc==LINK || proc==RENAME) { + dep_tab[dep_index].trace_fh = find_another_trace_fh (proc, dep_tab[dep_index].line); + dep_tab[dep_index].trace_fh_2 = find_lead_trace_fh(proc, dep_tab[dep_index].line); + dep_tab[dep_index].fh = 0; + dep_tab[dep_index].fh_2 = 0; + } else { + dep_tab[dep_index].trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line); + dep_tab[dep_index].fh = 0; + dep_tab[dep_index].fh_2 = (fh_map_t *)1; + }; + dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE; +#ifdef TIME_PLAY + dep_tab[dep_index].skip_sec = skip_sec; +#endif + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_CANDIDATE\n", dep_tab[dep_index].disk_index); + } else { + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_DONE; + continue; + } + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE) ) { + + if (!dep_tab[dep_index].fh) + dep_tab[dep_index].fh = lookup_fh (dep_tab[dep_index].trace_fh); + if (!dep_tab[dep_index].fh_2) + dep_tab[dep_index].fh_2 = lookup_fh (dep_tab[dep_index].trace_fh_2); + + /* need to wait for file handle */ + if ((!dep_tab[dep_index].fh) || (!dep_tab[dep_index].fh_2)) { + if (dependency_debug) + printf("disk[%d] can not lookup file handle\n", dep_tab[dep_index].disk_index); + if (dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) { + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_CANDIDATE to DEP_FLAG_WAIT_FHANDLE\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_FHANDLE; + sfs_gettime (&dep_tab[dep_index].start); + if (CYCLIC_LESS(dep_tab_index,dep_index,min_wait_fhandle_dep_index)) + min_wait_fhandle_dep_index = dep_index; + } else { + struct ladtime tmp; + if (dep_index==dep_window_index.tail) { + if (!profile_debug) + printf ("fh_path_map error disk[%d] state DEP_FLAG_WAIT_FHANDLE to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index); + fh_path_map_err_num ++; + dep_tab[dep_index].flag = DEP_FLAG_DONE; + continue; + } + sfs_gettime (&tmp); + SUBTIME (tmp, dep_tab[dep_index].start); +#define DEPENDENCY_TIMEOUT 5 +#ifdef TIME_PLAY + RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT + (skip_sec - dep_tab[dep_index].skip_sec)); +#else + if (tmp.sec >= DEPENDENCY_TIMEOUT) { + printf("dep_tab[%d].flag %d disk_index %d line %s\n", dep_index, + dep_tab[dep_index].flag, dep_tab[dep_index].disk_index, + dep_tab[dep_index].line); + } + RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT ); +#endif + } + continue; + } + + /* file handle ready, adjust_min_wait_fhandle_dep_index */ + if ((dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE)) { + if (dep_index == min_wait_fhandle_dep_index) { + min_wait_fhandle_dep_index = dep_window_index.head; + for (j=CYCLIC_ADD(dep_index,1,dep_window_index.size); CYCLIC_LESS(dep_window_index,j,dep_window_index.head); j++) { + if (dep_tab[j].flag ==DEP_FLAG_WAIT_FHANDLE) { + min_wait_fhandle_dep_index = j; + break; + } + } + } + } + if (dependency_debug) + printf("disk[%d] found file handle\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_FHANDLE_READY; + + /* the normal file operation can be executed now */ + if (!is_dir_op (dep_tab[dep_index].proc)) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + + if (dependency_debug) + printf("disk[%d] directory operation \n", dep_tab[dep_index].disk_index); + /* the directory operation need to lock the directory first */ + if (dep_tab[dep_index].fh->lock) { + if (dependency_debug) + printf ("disk[%d] state %d to DEP_FLAG_WAIT_DIRECTORY\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_DIRECTORY; + continue; + } + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_FHANDLE_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DIRECTORY)) { + int j = dep_tab[dep_index].fh - fh_map; + if (dependency_debug) { + printf ("dep_tab[%d].disk_index %d, fh_map[%d] lock=%d\n",dep_index, dep_tab[dep_index].disk_index, j, dep_tab[dep_index].fh->lock); + printf ("trace_fh %s path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path); + printf ("trace_fh %s path %s\n", fh_map[j].trace_fh, fh_map[j].path); + } + if ((dep_tab[dep_index].fh->lock) || ((proc==RENAME) && (dep_tab[dep_index].fh_2->lock)) ) { + if (dependency_debug) + printf ("continue to wait for directory lock\n"); + continue; + } + if (dependency_debug) + printf ("dep_tab[%d] disk index %d LOCK fh_map[%d] \n", dep_index, dep_tab[dep_index].disk_index, j); + dep_tab[dep_index].fh->lock = 1; + if (proc==RENAME) + dep_tab[dep_index].fh_2->lock = 1; + + /* the non-delete directory operation can proceed now */ + if (!is_delete_op (dep_tab[dep_index].proc)) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + + /* the delete operation can proceed if nobody ahead is waiting for fhandle */ + /* probably this condition is not strong enough */ +// if ((min_wait_fhandle_dep_index<dep_index) ) { + if (dep_index!=dep_window_index.tail) { + if (dependency_debug) + printf ("disk[%d] state %d to DEP_FLAG_WAIT_DELETE\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_DELETE; + continue; + } + dep_tab[dep_index].flag = DEP_FLAG_DIRECTORY_READY; + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_DIRECTORY_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DELETE)) { +// if (min_wait_fhandle_dep_index > dep_index) { + if (dep_index==dep_window_index.tail) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + } +#else /*NO_DEPENDENCY_TABLE undefined */ + /* this part of code will be invalid after CYCLIC buffer design */ + if (dep_tab[dep_index].flag == DEP_FLAG_INIT){ + for (j=0, t=&(dep_tab[dep_index].dep_ops[0]); + (j<dep_tab[dep_index].init_dep_num) && (dep_tab[dep_index].cur_dep_num>0); + j++, t++) { + if (*t !=-1) { + if (dep_tab[disk_index_to_dep_index(dep_index, *t)].flag == DEP_FLAG_DONE) { + /* The depended request has been finished */ + *t = -1; + dep_tab[dep_index].cur_dep_num --; + } + } + } + + if (dep_tab[dep_index].cur_dep_num == 0) { + return dep_index; + } + } +#endif + } + + if (dependency_debug) + printf ("get_nexop return -1\n"); + return -1; +} + +int check_timeout(void) +{ + static int biod_index = 0; + int i; + int dep_index; /* index into dep_tab */ + int proc; + sfs_op_type *op_ptr; /* per operation info */ + struct ladtime timeout; + + sfs_gettime (¤t); + + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + if (biod_reqp[biod_index].in_use==TRUE) { + timeout = biod_reqp[biod_index].timeout; + if ((current.sec>timeout.sec) || + ((current.sec==timeout.sec) && (current.usec>timeout.usec))) { + + dep_index = biod_reqp[biod_index].dep_tab_index; + proc = dep_tab[dep_index].proc; + op_ptr = &Ops[proc]; + op_ptr->results.timeout_calls++; + Ops[TOTAL].results.timeout_calls++; + + finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT); + + if (is_create_op(proc)) { + dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE; + printf ("resend dep_tab[%d], disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); + } + //RFS_ASSERT (!is_create_op(proc)); + + //printf ("timeout request: biod_reqp[%d].start %d:%d timeout %d:%d current %d:%d\n", biod_index, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, timeout.sec, timeout.usec, current.sec, current.usec); + } + } + } +} + +/* Allocate a biod_req entry to send and receive request dep_tab[dep_index] + * build the cross reference between dep_tab entry and biod_req entry + */ +struct biod_req * get_biod_req(int dep_index) /* index into dep_tab */ +{ + static int biod_index = 0; + int i; + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + if (!biod_reqp[biod_index].in_use) { + biod_reqp[biod_index].in_use = 1; + biod_reqp[biod_index].dep_tab_index = dep_index; + dep_tab[dep_index].biod_req_index = biod_index; + num_out_reqs++; + return &(biod_reqp[biod_index]); + } + } + return NULL; +} + +/* Return index into biod_reqp + * return -1 upon failure + */ +int lookup_biod_req (int xid) +{ + static int biod_index = 0; + int i; + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + /* give a NULL as timeout pointer may cause indefinitely block */ + if (biod_reqp[biod_index].xid == xid) { + return biod_index; + } + } + return -1; +} + +extern struct ladtime test_start; +void init_time_offset(void) +{ + struct ladtime tmp1; + struct ladtime tmp2; + + test_start.sec = 0; + test_start.usec = 0; + sfs_gettime (&tmp1); /* called at initial time: tmp1 = play_starttime */ +#ifdef SPEED_UP + DIVTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime / SCALE */ +#endif +#ifdef SLOW_DOWN + MULTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime * SCALE */ +#endif + + tmp2 = trace_starttime; /* tmp2 = trace_starttime */ + SUBTIME (tmp2, tmp1); /* tmp2 = trace_starttime - play_starttime *|/ SCALE */ + time_offset = tmp2; /* time_offset = trace_starttime - play_starttime *|/ SCALE */ +} + +/* initialize timestamp and proc field of dep_tab entry */ +void init_dep_tab_entry (int dep_index) +{ + char * line; + int version; + int nfsproc; + int msgid; + + //line = get_line_by_disk_index (dep_tab[dep_index].disk_index); + line = dep_tab[dep_index].line; + sscanf (line, "%d.%d", &(dep_tab[dep_index].timestamp.tv_sec), &(dep_tab[dep_index].timestamp.tv_usec)); + sscanf (&line[39], "%x %x", &msgid, &nfsproc); + if (line[TRACE_VERSION_POS]=='2') { + dep_tab[dep_index].proc = nfs2proc_to_rfsproc[nfsproc]; + RFS_ASSERT (nfsproc <18); + } else { + /* This is for debug purpose */ + if (line[TRACE_VERSION_POS] !='3') { + fprintf(stderr, "line[TRACE_VERSION_POS] %c line %s\n", line[TRACE_VERSION_POS], line); + line = get_line_by_disk_index (dep_tab[dep_index].disk_index-1); + if (!line) + line = get_line_by_disk_index (dep_tab[dep_index].disk_index-2); + RFS_ASSERT (line); + fprintf(stderr, "previousline %s\n", line); + } + RFS_ASSERT (line[TRACE_VERSION_POS] =='3'); + if (nfsproc >= NFS3_PROCEDURE_COUNT) { + fprintf(stderr, "proc %d line %s\n", nfsproc, line); + + } + RFS_ASSERT (nfsproc <NFS3_PROCEDURE_COUNT); + dep_tab[dep_index].proc = nfs3proc_to_rfsproc[nfsproc]; + } + RFS_ASSERT (dep_tab[dep_index].proc >= 0 && dep_tab[dep_index].proc < NOPS); + dep_tab[dep_index].flag = DEP_FLAG_INIT; +#ifndef REDUCE_MEMORY_TRACE_SIZE + dep_tab[dep_index].reply_line = find_reply_line (line, dep_tab[dep_index].disk_index); + if (dep_tab[dep_index].reply_line == NULL) { + //printf ("disk[%d] can not find the reply line, assume trace_status OK\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].trace_status = NFS3ERR_RFS_MISS; + missing_reply_num ++; + } else + dep_tab[dep_index].trace_status = find_reply_status (dep_tab[dep_index].reply_line); +#endif +} + +void adjust_play_window (int flag, int * poll_timeout) +{ + struct ladtime max_window_time; + static struct ladtime max_poll_time = {0, 2000, 0}; + struct ladtime t; + int i; + char * line; + cyclic_index_t old_dep_window_index = dep_window_index; + +#ifdef notdef + printf ("^^^^^^^^^^^^^^^ adjust_play_window, begin\n"); + CYCLIC_PRINT (dep_tab_index); + printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index); + CYCLIC_PRINT (dep_window_index); + CYCLIC_PRINT (memory_trace_index); + printf (" adjust_play_window, begin\n"); +#endif + + while ((!CYCLIC_EMPTY(dep_window_index)) && (dep_tab[dep_window_index.tail].flag == DEP_FLAG_DONE)) { +#ifdef notdef + //CYCLIC_PRINT (memory_trace_index); + //printf("MOVE_TAIL_TO memory_index %d\n", dep_tab[dep_tab_index.tail].memory_index); + RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index)); + RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head)); + printf("%d is done\n", dep_window_index.tail); +#endif + CYCLIC_MOVE_TAIL(dep_tab_index); + CYCLIC_MOVE_TAIL(dep_window_index); + +#ifdef notdef + CYCLIC_PRINT (dep_tab_index); + CYCLIC_PRINT (dep_window_index); + + if (! (dep_tab_index.tail == dep_window_index.tail)) { + CYCLIC_PRINT(dep_tab_index); + CYCLIC_PRINT(dep_window_index); + }; + RFS_ASSERT ( dep_tab_index.tail == dep_window_index.tail); +#endif + + if (!CYCLIC_EMPTY(dep_tab_index)) { +#ifdef notdef + RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index)); + if (!(CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head))) { + CYCLIC_PRINT(memory_trace_index); + CYCLIC_PRINT(dep_tab_index); + printf("dep_tab[head-1].memory_index, %d [tail].memory_index %d\n", + dep_tab[CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size)].memory_index, + dep_tab[dep_tab_index.tail].memory_index); + } + RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head)); +#endif + CYCLIC_SET_TAIL_TO(&memory_trace_index, dep_tab[dep_tab_index.tail].memory_index); + //printf ("set memory_trace_index to %d=%d, dep_tab_index.tail %d\n", memory_trace_index.tail, dep_tab[dep_tab_index.tail].memory_index, dep_tab_index.tail); + } else { + // CYCLIC_MOVE_TAIL (memory_trace_index); + } + } + + while (CYCLIC_EMPTY(dep_tab_index)) { + + if (disk_io_status == TRACE_FILE_END) + return; + else { + //printf ("************** ADJUST_PLAY_WINDOW sleep 1 s\n"); + //print_cyclic_buffers(); + pthread_yield(); + //usleep (1000); + } + } + + /* max_trace_window_time = current *|/ SCALE + trace_starttime */ + sfs_gettime (¤t); + +#ifdef TIME_PLAY +#ifdef SPEED_UP + MULTIME (current, PLAY_SCALE); +#endif +#ifdef SLOW_DOWN + DIVTIME (current, PLAY_SCALE); +#endif + ADDTIME (current, trace_starttime); + max_window_time = current; + + /* Right now it is not clear how to deal with the situation where MAX_PLAY_WINDOW is reached */ + if (CYCLIC_NUM(dep_window_index) == MAX_PLAY_WINDOW) { + //printf ("can not catch up the speed, dep_tab_size %d dep_window_max %d reach min_dep_index %d+MAX_PLAY_WINDOW\n", dep_tab_size, dep_window_max, min_dep_index); + //printf ("."); + can_not_catch_speed_num ++; + } + //RFS_ASSERT (dep_window_max < min_dep_index+MAX_PLAY_WINDOW); +#else + ADDTIME (current, trace_starttime); + max_window_time = current; + while ((CYCLIC_NUM(dep_window_index) < MAX_PLAY_WINDOW) && + (CYCLIC_NUM(dep_window_index) < CYCLIC_NUM(dep_tab_index)) ) { + CYCLIC_MOVE_HEAD(dep_window_index); + } +#endif + + if (flag == BUSY) + *poll_timeout = 0; + else if (CYCLIC_NUM(dep_window_index)==CYCLIC_NUM(dep_tab_index)) { + *poll_timeout = 1000000; /* poll_timeout set to 1 second for the last request */ + } else { +#ifdef TIME_PLAY + struct ladtime tmp; + struct ladtime tmp1; + tmp.sec = dep_tab[dep_window_index.head].timestamp.tv_sec; + tmp.usec = dep_tab[dep_window_index.head].timestamp.tv_usec; + if (adjust_play_window_debug>=2) + printf ("dep_tab[dep_window_index.head %d].timestamp %d:%d, max_window_time %d:%d\n", + dep_window_index.head, tmp.sec, tmp.usec, max_window_time.sec, max_window_time.usec); + + SUBTIME (tmp, max_window_time); +#ifdef SPEED_UP + DIVTIME (tmp, PLAY_SCALE); +#endif +#ifdef SLOW_DOWN + MULTIME (tmp, PLAY_SCALE); +#endif +/* + tmp1 = tmp; + + if (tmp.sec > max_poll_time.sec) { + + if (rfs_debug) + printf ("dep_tab[%d].timestamp %d:%d, max_window_time %d:%d\n", + dep_window_max, dep_tab[dep_window_max].timestamp.tv_sec, dep_tab[dep_window_max].timestamp.tv_usec, max_window_time.sec, max_window_time.usec); + printf ("skip %d seconds\n", tmp.sec-max_poll_time.sec); + SUBTIME (tmp, max_poll_time); + tmp.usec = 0; + skip_sec += tmp.sec; + SUBTIME (test_start, tmp); + tmp = max_poll_time; + } +*/ + + //RFS_ASSERT ((tmp.sec < 1000)); + if (tmp.sec > 1000) + tmp.sec = 1000; + if ((tmp.sec ==0) && (tmp.usec==0)) { + *poll_timeout = 0; + } else + *poll_timeout = tmp.sec*1000000+tmp.usec; +#else + /* + struct ladtime tmp; + struct ladtime tmp1; + tmp.sec = dep_tab[dep_window_max].timestamp.tv_sec; + tmp.usec = dep_tab[dep_window_max].timestamp.tv_usec; + tmp1.sec = dep_tab[dep_window_max-1].timestamp.tv_sec; + tmp1.usec = dep_tab[dep_window_max-1].timestamp.tv_usec; + SUBTIME (tmp, tmp1); + RFS_ASSERT ((tmp.sec < 1000)); + RFS_ASSERT ((tmp.sec>0) || ((tmp.sec==0) && (tmp.usec>0))); + *poll_timeout = tmp.sec*1000000+tmp.usec; + */ + + *poll_timeout = 100000; +#endif + } + if (rfs_debug) + printf ("adjust_play_window: flag %d min %d -> %d, max %d -> %d poll_timeout %d \n", + flag, old_dep_window_index.tail, dep_window_index.tail, old_dep_window_index.head, + dep_window_index.head, *poll_timeout); + +#ifdef notdef + printf ("^^^^^^^^^^^^^^^ adjust_play_window, end\n"); + CYCLIC_PRINT (dep_tab_index); + printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index); + CYCLIC_PRINT (dep_window_index); + CYCLIC_PRINT (memory_trace_index); + printf (" adjust_play_window, end\n\n"); +#endif + //CYCLIC_ASSERT(4); +} + +/* poll for usecs and receive, after receive one reply, + * return index in biod_reqp of the corresponding request + */ +int poll_and_get_reply (int usecs) +{ + int biod_index = -1; + int xid; + int error; + struct timeval zero_time = {0, 0}; /* Immediately return */ + + do { + error = biod_poll_wait (NFS_client, usecs); + switch (error) { + case -1: + if (errno == EINTR) { + error = 1; + continue; + } + if (rfs_debug) { + (void) fprintf(stderr, "biod_poll_wait error\n"); + perror (""); + (void) fflush(stderr); + } + break; + case 0: + break; + default: +#ifdef UDP + error = get_areply_udp (NFS_client, &xid, &zero_time); + // RFS_ASSERT (error!= RPC_TIMEOUT); /* we have polled and know there is data */ + // RFS_ASSERT (error!= RPC_CANTRECV); + RFS_ASSERT (error == RPC_SUCCESS); + + biod_index = lookup_biod_req (xid); + sfs_gettime (&(biod_reqp[biod_index].stop)); +#else + RFS_ASSERT (0); +#endif + } + } while (0); + return biod_index; +} + +void print_result(void) +{ + int i, j; + struct ladtime t; + int dep_index; + int avg_msecs; + unsigned long long tmp; + int avg_usecs; + + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stdout, "trace play result:\n"); + (void) fprintf(stdout, "\t percentage good_cnt bad_cnt timeout_cnt\telapsed time\t\t\taverage time\n"); + for (i=0; i<NOPS+1; i++) { + if (Ops[i].results.good_calls==0) { + avg_msecs = 0; + avg_usecs = 0; + } else { + tmp = Ops[i].results.time.sec*1000000 + Ops[i].results.time.usec; + avg_msecs = 0; + avg_usecs = tmp/Ops[i].results.good_calls; +/* + avg_msecs = (Ops[i].results.time.sec*1000 + Ops[i].results.time.usec/1000)/Ops[i].results.good_calls; + avg_usecs = (Ops[i].results.time.usec%1000)/Ops[i].results.good_calls; +*/ + } + (void) fprintf(stdout, "%11s\t%4.1f\t%4d\t%4d\t%4d\t\tsec %8d usec %8d \tusec %8d\n", + Ops[i].name, + (float)(100*Ops[i].results.good_calls)/(float)Ops[TOTAL].results.good_calls, + Ops[i].results.good_calls, Ops[i].results.bad_calls, Ops[i].results.timeout_calls, + Ops[i].results.time.sec, Ops[i].results.time.usec, avg_msecs*1000+avg_usecs); + } + (void) fflush (stdout); + } + +#if 0 /* commented out by G. Jason Peng */ + RFS_ASSERT (read_data_owe_GB==0); + printf("read_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",read_data_total_GB, read_data_total, read_data_owe_GB, read_data_owe, (read_data_owe)/(read_data_total/100), read_data_adjust_times); + printf("write_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",write_data_total_GB, write_data_total, write_data_owe_GB, write_data_owe, (write_data_owe)/(write_data_total/100), write_data_adjust_times); + printf("poll_timeout_0_num %d poll_timeout_pos_num %d\n", poll_timeout_0_num, poll_timeout_pos_num); + printf("failed_create_command_num_in_original_trace %d\nfailed_other_command_num_in_original_trace %d\nskipped_readlink_command_num %d\nskipped_custom_command_num %d\nfh_path_map_err_num %d\nskipped_fsstat_command_num %d\nmissing_reply_num %d\nrename_rmdir_noent_reply_num %d\nrmdir_not_empty_reply_num %d\nloose_access_control_reply_num %d\nlookup_err_due_to_rename %d\nlookup_err_due_to_parallel_remove %d\nlookup_eaccess_enoent_mismatch %d\nread_io_err_num %d\nstale_fhandle_err_num %d abnormal_EEXIST_num %d abnormal_ENOENT_num %d proper_reply_num %d run_stage_proper_reply_num %d\n", + failed_create_command_num, + failed_other_command_num, + skipped_readlink_command_num, + skipped_custom_command_num, + fh_path_map_err_num, + skipped_fsstat_command_num, + missing_reply_num, + rename_rmdir_noent_reply_num, + rmdir_not_empty_reply_num, + loose_access_control_reply_num, + lookup_err_due_to_rename_num, + lookup_err_due_to_parallel_remove_num, + lookup_eaccess_enoent_mismatch_num, + read_io_err_num, + stale_fhandle_err_num, + abnormal_EEXIST_num, + abnormal_ENOENT_num, + proper_reply_num, run_stage_proper_reply_num); +#endif + + clnt_destroy(NFS_client); + biod_term(); + +// print_dump(Client_num, Child_num); +} + +/* + * allocate and initialize client handles + */ +static int +init_rpc(void) +{ + int dummy = 0; + + /* + * Set up the client handles. We get them all before trying one + * out to insure that the client handle for LOOKUP class is allocated + * before calling op_getattr(). + */ + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname); + } + + NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent, + (uint32_t) NFS_PROGRAM, + (uint32_t) nfs_version, + RPC_ANYSOCK, &Nfs_timers[0]); + + if (NFS_client == ((CLIENT *) NULL)) { + return(-1); + } + + /* + * create credentials using the REAL uid + */ + NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid, + (int)Cur_gid, 0, NULL); + + + if (biod_init(dummy, dummy) == -1) { + return(-1); + } + + return(0); +} /* init_rpc */ + +void +init_counters(void) +{ + uint_t i; + uint_t start_msec; + + /* Ready to go - initialize operation counters */ + for (i = 0; i < NOPS + 1; i++) { + Ops[i].req_cnt = 0; + Ops[i].results.good_calls = 0; + Ops[i].results.bad_calls = 0; + Ops[i].results.timeout_calls = 0; // RFS + Ops[i].results.fast_calls = 0; + Ops[i].results.time.sec = 0; + Ops[i].results.time.usec = 0; + Ops[i].results.msec2 = 0; + } + + /* initialize timers and period variables */ + sfs_gettime(&Starttime); + Cur_time = Starttime; + start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000); + Previous_chkpnt_msec = start_msec; + Calls_this_period = 0; + Reqs_this_period = 0; + Sleep_msec_this_period = 0; + Calls_this_test = 0; + Reqs_this_test = 0; + Sleep_msec_this_test = 0; +} + +static char * +nfs3_strerror(int status) +{ + static char str[40]; + switch (status) { + case NFS3_OK: + (void) strcpy(str, "no error"); + break; + case NFS3ERR_PERM: + (void) strcpy(str, "Not owner"); + break; + case NFS3ERR_NOENT: + (void) strcpy(str, "No such file or directory"); + break; + case NFS3ERR_IO: + (void) strcpy(str, "I/O error"); + break; + case NFS3ERR_NXIO: + (void) strcpy(str, "No such device or address"); + break; + case NFS3ERR_ACCES: + (void) strcpy(str, "Permission denied"); + break; + case NFS3ERR_EXIST: + (void) strcpy(str, "File exists"); + break; + case NFS3ERR_XDEV: + (void) strcpy(str, "Cross-device link"); + break; + case NFS3ERR_NODEV: + (void) strcpy(str, "No such device"); + break; + case NFS3ERR_NOTDIR: + (void) strcpy(str, "Not a directory"); + break; + case NFS3ERR_ISDIR: + (void) strcpy(str, "Is a directory"); + break; + case NFS3ERR_INVAL: + (void) strcpy(str, "Invalid argument"); + break; + case NFS3ERR_FBIG: + (void) strcpy(str, "File too large"); + break; + case NFS3ERR_NOSPC: + (void) strcpy(str, "No space left on device"); + break; + case NFS3ERR_ROFS: + (void) strcpy(str, "Read-only file system"); + break; + case NFS3ERR_MLINK: + (void) strcpy(str, "Too many links"); + break; + case NFS3ERR_NAMETOOLONG: + (void) strcpy(str, "File name too long"); + break; + case NFS3ERR_NOTEMPTY: + (void) strcpy(str, "Directory not empty"); + break; + case NFS3ERR_DQUOT: + (void) strcpy(str, "Disc quota exceeded"); + break; + case NFS3ERR_STALE: + (void) strcpy(str, "Stale NFS file handle"); + break; + case NFS3ERR_REMOTE: + (void) strcpy(str, "Object is remote"); + break; + case NFS3ERR_BADHANDLE: + (void) strcpy(str, "Bad file handle"); + break; + case NFS3ERR_NOT_SYNC: + (void) strcpy(str, "Not sync write"); + break; + case NFS3ERR_BAD_COOKIE: + (void) strcpy(str, "Bad cookie"); + break; + case NFS3ERR_NOTSUPP: + (void) strcpy(str, "Operation not supported"); + break; + case NFS3ERR_TOOSMALL: + (void) strcpy(str, "Value too small"); + break; + case NFS3ERR_SERVERFAULT: + (void) strcpy(str, "Server fault"); + break; + case NFS3ERR_BADTYPE: + (void) strcpy(str, "Bad type"); + break; + case NFS3ERR_JUKEBOX: + (void) strcpy(str, "Jukebox"); + break; + case NFS3ERR_RFS_TIMEOUT: + (void) strcpy(str, "Timeout"); + break; + default: + (void) sprintf(str, "Unknown status %d", status); + break; + } + return (str); +} + +/* + * Check the gettimeofday() resolution. If the resolution + * is in chunks bigger than SFS_MIN_RES then the client + * does not have a usable resolution for running the + * benchmark. + */ +static void +check_clock(void) +{ + double time_res; + char tmp_hostname[HOSTNAME_LEN]; + + time_res = get_resolution(); + getmyhostname(tmp_hostname, HOSTNAME_LEN); + if( time_res > (double)SFS_MIN_RES ) + { + (void) fprintf(stderr, + "\n%s: Clock resolution too poor to obtain valid results.\n", + tmp_hostname); + (void) fprintf(stderr, + "%s: Clock resolution %f Micro seconds.\n", tmp_hostname, + time_res); + exit(175); + } + else + { + (void) fprintf(stderr, + "\n%s: Good clock resolution [ %f ] Micro seconds.\n", + tmp_hostname, time_res); + } +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns the resolution of the gettimeofday() function + * in microseconds. + */ +static double +get_resolution(void) +{ + double starttime, finishtime, besttime; + long j,delay; + int k; + + finishtime=time_so_far1(); /* Warm up the instruction cache */ + starttime=time_so_far1(); /* Warm up the instruction cache */ + delay=j=0; /* Warm up the data cache */ + for(k=0;k<10;k++) + { + while(1) + { + starttime=time_so_far1(); + for(j=0;j< delay;j++) + ; + finishtime=time_so_far1(); + if(starttime==finishtime) + delay++; + else + { + if(k==0) + besttime=(finishtime-starttime); + if((finishtime-starttime) < besttime) + besttime=(finishtime-starttime); + break; + } + } + } + return(besttime); +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns current result of gettimeofday() in microseconds. + */ +/************************************************************************/ +/* Time measurement routines. */ +/* Return time in microseconds */ +/************************************************************************/ + +static double +time_so_far1(void) +{ + /* For Windows the time_of_day() is useless. It increments in 55 */ + /* milli second increments. By using the Win32api one can get */ + /* access to the high performance measurement interfaces. */ + /* With this one can get back into the 8 to 9 microsecond */ + /* resolution. */ +#ifdef Windows + LARGE_INTEGER freq,counter; + double wintime; + double bigcounter; + + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&counter); + bigcounter=(double)counter.HighPart *(double)0xffffffff + + (double)counter.LowPart; + wintime = (double)(bigcounter/(double)freq.LowPart); + return((double)wintime*1000000.0); +#else +#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5) + struct timespec gp; + + if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1) + perror("getclock"); + return (( (double) (gp.tv_sec)*1000000.0) + + ( ((float)(gp.tv_nsec)) * 0.001 )); +#else + struct timeval tp; + + if (gettimeofday(&tp, (struct timezone *) NULL) == -1) + perror("gettimeofday"); + return ((double) (tp.tv_sec)*1000000.0) + + (((double) tp.tv_usec) ); +#endif +#endif +} + +static void +usage(void) +{ + fprintf(stderr, "trace play usage"); +} +extern void init_file_system (void) +{ + return; +} + +void show_fhandle (nfs_fh3 * fhp) +{ + struct knfs_fh * kfhp = (struct knfs_fh *)fhp; + + int dev; + + if (quiet_flag) + return; + + RFS_ASSERT (kfhp->fh_version == 1); + RFS_ASSERT (kfhp->fh_fsid_type == 0); + RFS_ASSERT (kfhp->fh_auth_type == 0); + + dev = ntohs(kfhp->fh_dev_major); + dev = dev<<8; + dev = dev + ntohs(kfhp->fh_dev_minor); + + /* kfhp->fh_dev_ino hold the inode number of export point of the mounted + * file system. For example, if /tmp/t1 is exported, /tmp/t1/t2 is mounted, + * then fh_dev_ino hold the inode number of t1, not t2 + */ + + switch (kfhp->fh_fileid_type) { + case 0: + printf("fh:type 0 root dev 0x%x dev_ino %d\n", dev, kfhp->fh_dev_ino); + break; + case 1: + printf("fh:type 1 %d %x dev %x dev_ino %x\n", + kfhp->fh_ino, kfhp->fh_generation, dev, kfhp->fh_dev_ino); + break; + case 2: + printf("fh:type2 %d %x dirino %d dev 0x%x dev_ino %x\n", + kfhp->fh_ino, kfhp->fh_generation, kfhp->fh_dirino, dev, kfhp->fh_dev_ino); + break; + default: + RFS_ASSERT (0); + } +} + +nfs_fh3 zero_fhandle; +int init_fh_map () +{ + memset (fh_map, 0, sizeof (fh_map)); + memset(fh_htable, 0, sizeof (fh_htable)); + memset (&zero_fhandle, 0, sizeof(nfs_fh3)); + printf ("SIZE of fh map %d KB\n", sizeof (fh_map)/1000); + fh_i = 0; +} + +int add_fh (int map_flag, char * trace_fh, char * path, nfs_fh3 * play_fh) +{ + char * old_trace_fh; + + /* first lookup if the entry for fh is already in the table */ + struct generic_entry * p; + + p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE); + if (p) { + RFS_ASSERT (fh_map[p->key3].flag = FH_MAP_FLAG_PARTIAL); + RFS_ASSERT (map_flag ==FH_MAP_FLAG_COMPLETE); + fh_map[p->key3].flag = map_flag; + //RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)); + if (memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)) { + int i; + printf ("fh_map[%d].trace_fh %s trace_fh %s", p->key3, fh_map[p->key3].trace_fh, trace_fh); + for (i=0; i<fh_i; i++) { + int * p1 = (int *)&(fh_map[i].play_fh); +#ifdef COMPRESS_TRACE_FH + int * p = (int *)fh_map[i].trace_fh; + printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); +#else + printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, fh_map[i].trace_fh, fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); + } +#endif + RFS_ASSERT (0); + } + RFS_ASSERT (!strcmp(fh_map[p->key3].path, path)); + /* It's possible that in fh-path-map, many trace_fh are corresponding to one path + * some of it may be the result of lookup after symlink, which is not handled + * properly as new created objects + */ +#ifdef TAKE_CARE_SYMBOLIC_LINK + RFS_ASSERT (!memcmp(&fh_map[p->key3].play_fh, &zero_fhandle, sizeof(nfs_fh3))); +#endif + memcpy (&fh_map[p->key3].play_fh, play_fh, sizeof (nfs_fh3)); + if ((fh_map_debug==1)) // || (stage ==TRACE_PLAY_STAGE)) + printf ("update the play_fh for trace_fh %s path %s \n", trace_fh, path); + return 0; + } + + fh_map[fh_i].flag = map_flag; + fh_map[fh_i].lock = 0; + strncpy(fh_map[fh_i].trace_fh, trace_fh, TRACE_FH_SIZE); + + RFS_ASSERT (strlen(path) < MAX_PLAY_PATH_SIZE); + strcpy (fh_map [fh_i].path, path); + if (map_flag==FH_MAP_FLAG_COMPLETE) + memcpy (&fh_map[fh_i].play_fh, play_fh, sizeof(nfs_fh3)); + else + memset (&fh_map[fh_i].play_fh, 0, sizeof(nfs_fh3)); + + if ((fh_map_debug==1)) { // || (stage ==TRACE_PLAY_STAGE)) { + printf ("insert trace_fh %s path %s play_fh:\n", trace_fh, path); + if (map_flag == FH_MAP_FLAG_COMPLETE) { + //show_fhandle(play_fh); + } else + printf("null\n"); + } + +/* + if (map_flag == FH_MAP_FLAG_DISCARD) + printf ("insert flag %d trace_fh %s path %s play_fh:\n", map_flag, trace_fh, path); +*/ + + generic_insert(trace_fh, TRACE_FH_SIZE, fh_i, fh_htable, FH_HTABLE_SIZE); + + fh_i = (fh_i+1); + RFS_ASSERT (fh_i < FH_MAP_SIZE); + + return 0; +}; + +inline fh_map_t * lookup_fh (char * trace_fh ) +{ + struct generic_entry * p; + p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE); + if (fh_map_debug==1) + printf ("lookup trace_fh %s\n", trace_fh); + + if (p) { + if (fh_map_debug==1) { + printf ("found: fh_i[%d] trace_fh %s path %s play_fh:\n", p->key3, fh_map[p->key3].trace_fh, fh_map[p->key3].path); + //show_fhandle(&fh_map[p->key3].play_fh); + } + RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)); + return (&(fh_map[p->key3])); + } else { + //printf ("lookup_fh %s not found\n", trace_fh); + if (stage != READ_DEP_TAB_STAGE && (fh_map_debug==1)) { + printf ("lookup not found trace_fh %s\n", trace_fh); + } + return NULL; + } + RFS_ASSERT (0); +} + +int delete_fh (char * trace_fh, int fh_map_index) +{ + generic_delete (trace_fh, TRACE_FH_SIZE, fh_map_index, fh_htable, FH_HTABLE_SIZE); + return 0; +}; + +int lookup_init_filesystem (nfs_fh3 * parent, char * name, nfs_fh3 * result) +{ + LOOKUP3args args; + LOOKUP3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + static int i=0; + + /* set up the arguments */ + (void) memcpy((char *) &args.what.dir, (char *) parent, + sizeof (nfs_fh3)); + args.what.name = name; + (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP, + xdr_LOOKUP3args, (char *) &args, + xdr_LOOKUP3res, (char *) &reply, + Nfs_timers[Init]); + sfs_gettime(&stop); + + if (rpc_stat !=RPC_SUCCESS) { + printf("rpc_stat %d\n", rpc_stat); + perror(""); + } + RFS_ASSERT (rpc_stat == RPC_SUCCESS); + (void) memcpy((char *) result, (char *) &reply.resok.object, sizeof (nfs_fh3)); + return (reply.status); +} + +void read_fh_map(char * fh_map_file) +{ + FILE * fp; + int i = 0; + char buf[1024]; + char trace_fh[TRACE_FH_SIZE]; + char intbuf[9]; + char * trace_path; + char * p; + int map_flag; +#define MAX_PATH_DEPTH 20 + nfs_fh3 parents[MAX_PATH_DEPTH]; + char * lookup_path_ptr[MAX_PATH_DEPTH]; + char lookup_path [MAX_PLAY_PATH_SIZE]; + int depth; + int new_dir_flag = 0; + int lineno = 0; + + depth = 0; + memset(lookup_path_ptr, 0, sizeof(lookup_path_ptr)); + memcpy(&parents[depth], &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3)); + strcpy(lookup_path, "/"); + lookup_path_ptr[depth]=&lookup_path[0]; + + fp = fopen(fh_map_file, "r"); + if (!fp) { + printf ("can not opern %s\n", fh_map_file); + perror("open"); + exit (0); + } + RFS_ASSERT (fp!=NULL); + + intbuf[8]=0; + + memset(buf, 0, sizeof(buf)); + while (fgets(buf, 1024, fp)) { + lineno ++; + if (fh_i % 10000==0) + printf("%d fh_map entry read\n", fh_i); + + RFS_ASSERT (buf[strlen(buf)-1]=='\n'); + buf[strlen(buf)-1]=0; + if (fh_map_debug) { + printf("%d fgets return %s\n", fh_i, buf); + printf("depth %d lookup_path %s\n", depth, lookup_path); + } + //for (i=0; i<=depth; i++) + //printf("lookup_path_ptr[%d] %s ", i, lookup_path_ptr[i]); + //printf("\n"); +#ifdef COMPRESS_TRACE_FH + for (i=0; i<TRACE_FH_SIZE/8; i++) { + strncpy(intbuf, buf+i*8, 8); + sscanf(intbuf, "%x", trace_fh+i*8); // maybe it should be 4, anyway we don't compress for now + } + trace_path = buf+TRACE_FH_SIZE*2+1; /* +1 the trace contains only initial file handle */ +#else + memcpy(trace_fh, buf, TRACE_FH_SIZE); + trace_path = buf + TRACE_FH_SIZE +1; +#endif +#ifdef TRACE_CONTAIN_LATER_FHANDLE + trace_path = +=2; /* +3 if the trace contains both initial and later created file handle */ +#endif + +#ifdef NO_DEPENDENCY_TABLE + if (!strncmp (trace_path, "DISCARD", 7) || + !strncmp (trace_path, "LN", 2) ) { + map_flag = FH_MAP_FLAG_DISCARD; + add_fh (map_flag, buf, trace_path, 0); + continue; + } +#endif + + p = trace_path+strlen(trace_path)-2; + while (*p!='/') + p--; + p++; + //RFS_ASSERT (p-trace_path<=strlen(lookup_path)+1); + //RFS_ASSERT (p>trace_path); + + if (strncmp(lookup_path, trace_path, p-trace_path)) { + printf("strncmp lookup_path %s trace_path %s for length %d\n", lookup_path, trace_path, p-trace_path); + } + RFS_ASSERT (!strncmp(lookup_path, trace_path, p-trace_path)); + //while (strncmp(lookup_path, trace_path, p-trace_path)) { /* one step deeper */ + while (strlen(lookup_path)>p-trace_path && depth>0) { + //printf("depth--\n"); + if (depth<=0) + printf ("lookup_path %s trace_path %s p-trace_path %d depth %d\n", lookup_path, trace_path, p-trace_path, depth); + RFS_ASSERT (depth>0); + *lookup_path_ptr[depth]=0; + lookup_path_ptr[depth]=0; + depth--; + } + RFS_ASSERT (strlen(lookup_path)==(p-trace_path) || (depth==0)); + + +#ifdef TRACE_CONTAIN_LATER_FHANDLE + if (buf[TRACE_FH_SIZE*2+1]=='Y') { + map_flag = FH_MAP_FLAG_COMPLETE; + } else { + map_flag = FH_MAP_FLAG_PARTIAL; + RFS_ASSERT (buf[TRACE_FH_SIZE*2+1]=='N'); + } +#else + map_flag = FH_MAP_FLAG_COMPLETE; +#endif + if ((*(p+strlen(p)-1))=='/') { + *(p+strlen(p)-1)=0; + new_dir_flag = 1; + } else + new_dir_flag = 0; + + if (map_flag == FH_MAP_FLAG_COMPLETE) { + int ret = lookup_init_filesystem (&parents[depth], p, &parents[depth+1]); + if (ret!=NFS3_OK) { + printf ("lineno %d %s\n", lineno, buf); + } + RFS_ASSERT (ret == NFS3_OK); + add_fh (map_flag, buf, trace_path, &parents[depth+1]); + } else + add_fh (map_flag, buf, trace_path, 0); + + if (new_dir_flag) { + /* the new fhandle is of a directory */ + lookup_path_ptr[depth+1] = lookup_path+strlen(lookup_path); + strcat (lookup_path, p); + strcat (lookup_path, "/"); + + //printf("depth++\n"); + depth++; + } + + memset(buf, 0, sizeof(buf)); + } + + if (fh_map_debug) { + for (i=0; i<fh_i; i++) { + int * p1 = (int *)&(fh_map[i].play_fh); +#ifdef COMPRESS_TRACE_FH + int * p = (int *)fh_map[i].trace_fh; + printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); +#else + printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, fh_map[i].trace_fh, fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); + } +#endif + + fprintf(stderr, "total %d requests \n", i); + } +} + +int f() +{ + return 1; +} + +inline free_biod_req (int biod_index) +{ + RFS_ASSERT (biod_reqp[biod_index].in_use == TRUE); + biod_reqp[biod_index].in_use = FALSE; + num_out_reqs --; +} + +inline void finish_request (int biod_index, int dep_index, int status) +{ + /* the ending operation, same as when a request time out */ + + dep_tab[dep_index].stop = biod_reqp[biod_index].stop; /* RFS: to dump data */ + free_biod_req (biod_index); + + dep_tab[dep_index].status = status; + if (event_order_index < EVENT_ORDER_SIZE) + event_order[event_order_index++] = -dep_tab[dep_index].disk_index; + + dep_tab[dep_index].flag = DEP_FLAG_DONE; + if (is_dir_op(dep_tab[dep_index].proc)) { + int j; + RFS_ASSERT (dep_tab[dep_index].fh->lock = 1); + dep_tab[dep_index].fh->lock = 0; + if (dep_tab[dep_index].proc==RENAME) + dep_tab[dep_index].fh_2->lock = 0; + j = dep_tab[dep_index].fh-fh_map; + if (dependency_debug) { + printf ("fh_map[%d] is UNlocked\n",j); + printf ("trace_fh %d path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path); + printf ("trace_fh %d path %s\n", fh_map[j].trace_fh, fh_map[j].path); + } + } +} + +/* the request argument may have pointers pointing to buffers, e.g. the name in lookup, + * the target of symlink, the write data */ +char arg_res[MAX_ARG_RES_SIZE]; +int poll_timeout = 0; /* timeout in usecs */ +char buf1 [MAX_BUF1_SIZE]; +char buf2 [MAX_BUF2_SIZE]; + +int execute_next_request () +{ + int dep_index; + int proc; + char * line; + struct biod_req * reqp; + sfs_op_type *op_ptr; /* per operation info */ + struct ladtime call_timeout; + static int last_print_time = -1; + + if (num_out_reqs == max_biod_reqs) { + return -1; + } + + start_profile (&valid_get_nextop_profile); + start_profile (&invalid_get_nextop_profile); + dep_index = get_nextop(); + if (dep_index == -1) { + end_profile (&invalid_get_nextop_profile); + return dep_index; + }; + end_profile (&valid_get_nextop_profile); + + start_profile (&prepare_argument_profile); + line = dep_tab[dep_index].line; + + end_profile(&total_profile); + if ((total_profile.in.tv_sec - last_print_time >= 10)) { + last_print_time = total_profile.in.tv_sec; + //fprintf (stderr, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE); + fprintf (stdout, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE); +/* + CYCLIC_PRINT (dep_tab_index); + { + int tmp = CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size); + printf("dep_tab_index.head-1 %d disk_index %d tail %d disk_index %d\n", tmp, dep_tab[tmp].disk_index, + dep_tab_index.tail, dep_tab[dep_tab_index.tail].disk_index); + } +*/ +#ifdef TIME_PLAY +#ifdef SPEED_UP +/* + if (can_not_catch_speed_num < 2000) { + PLAY_SCALE ++; + printf ("set PLAY_SCALE to %d\n", PLAY_SCALE); + }; + if (can_not_catch_speed_num > 50000) { + PLAY_SCALE /= 2; + } else { + if (can_not_catch_speed_num > 5000) { + PLAY_SCALE -= 2; + if (PLAY_SCALE < 1) + PLAY_SCALE = 1; + } + } +*/ +#endif + if ((total_profile.in.tv_sec > 100)) { + can_not_catch_speed_num_total += can_not_catch_speed_num; + } + can_not_catch_speed_num = 0; +#endif + } + if (rfs_debug) + printf ("processing dep_tab[%d] disk_index %d %s\n", dep_index, dep_tab[dep_index].disk_index, line); + + proc = dep_tab[dep_index].proc; + rfs_Ops[proc].setarg (dep_index, line, arg_res, buf1, buf2); + + op_ptr = &Ops[proc]; + reqp = get_biod_req (dep_index); + RFS_ASSERT (reqp); + +#ifdef notdef /* place to set request timeout. G. Jason Peng */ + call_timeout.sec = 2; //Nfs_timers[op_ptr->call_class].tv_sec; + call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; +#else + call_timeout.sec = 0; + call_timeout.usec = 500000; + //call_timeout.usec = 14000; + //call_timeout.usec = 13000; + //call_timeout.usec = 6000; + //call_timeout.usec = 8000; + //call_timeout.usec = 10000; +#endif + + /* make the call */ + sfs_gettime(&(reqp->start)); + end_profile (&prepare_argument_profile); + start_profile (&biod_clnt_call_profile); +#define REAL_PLAY +#ifdef REAL_PLAY + reqp->xid = biod_clnt_call(NFS_client, rfs_Ops[proc].nfsv3_proc, + rfs_Ops[proc].xdr_arg, arg_res); +#else + + reqp->xid = dep_index+1; /* just fake a message id and let it expire */ +#endif + RFS_ASSERT (reqp->xid != 0); + reqp->timeout = reqp->start; + ADDTIME (reqp->timeout, call_timeout); + dep_tab[dep_index].flag = DEP_FLAG_SENT; + if (event_order_index < EVENT_ORDER_SIZE) + event_order[event_order_index++] = dep_tab[dep_index].disk_index; + + dep_tab[dep_index].start = reqp->start; /* RFS: to dump data */ + end_profile (&biod_clnt_call_profile); +} + +void check_reply (int proc, int biod_index, int dep_index, int status, char * errmsg, int trace_status) +{ + if (((status!=trace_status)) && (status!=NFS3_OK) && (trace_status!=NFS3ERR_RFS_MISS)) { + if (!profile_debug) + printf ("receive problem reply, xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d command disk index %d\n", biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec, dep_tab[dep_index].disk_index); +#ifndef TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES + /* these files is not looked up and is not create/mkdir/symlink/link/mknod ed before they + * are refered by name through rename, remove + */ + if ((proc==RENAME || proc==REMOVE) && (status==NFS3ERR_NOENT) && (trace_status ==0)) { + /* current initialization doesnot take care of rename source, if there is no + * create or lookup before that source, the source object will not exist when + * rename occurs + */ + rename_rmdir_noent_reply_num++; + } else +#endif +#ifndef TAKE_CARE_SYMBOLIC_LINK + if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status==NFS3ERR_NOENT)) { + /* in the original trace, first lookup return NOENT, then symlink is executed, then lookup return OK + * the initialization considers only the lookup return OK and created the file in the initialization + * so in trace play the first lookup return OK + */ + RFS_ASSERT (1); + } else // if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) && (trace_status == 0)) { + /* trace_status could be EAGAIN */ + if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) ) { + /* due to similar reason as above, the initialization code initializes the symbolic link as a normal + * file already + */ + RFS_ASSERT (1); + } else +#endif +#ifndef TAKE_CARE_NOEMPTY_RMDIR + /* the remove packet seems got lost in the trace capture, so replay can not finish */ + if ((proc==RMDIR) && (status==NFS3ERR_NOTEMPTY)) { + RENAME3args args; + RENAME3res reply; /* the reply */ + RMDIR3args * rmdir_argp; + enum clnt_stat rpc_stat; /* result from RPC call */ + + rfs_Ops[proc].setarg (dep_index, dep_tab[dep_index].line, arg_res, buf1, buf2); + rmdir_argp = (RMDIR3args *)arg_res; + + memcpy(&args.from, &(rmdir_argp->object), sizeof (diropargs3)); + memcpy(&args.to.dir, &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3)); + args.from.name = buf1; /* the buf1 is already filled when parsing rmdir */ + args.to.name = buf2; + sprintf(buf2, "rmdir_%d_%s", dep_tab[dep_index].disk_index, rmdir_argp->object.name); + + rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME, + xdr_RENAME3args, (char *) &args, + xdr_RENAME3res, (char *) &reply, + Nfs_timers[Init]); + RFS_ASSERT (rpc_stat == RPC_SUCCESS); + if (reply.status!=NFS3_OK) + printf ("change rmdir into rename, reply.status %d\n", reply.status); + RFS_ASSERT (reply.status==NFS3_OK); + rmdir_not_empty_reply_num ++; +#endif +#ifndef TAKE_CARE_ACCESS_ERROR + } else if ((status==0) && (trace_status==NFS3ERR_ACCES)) { + loose_access_control_reply_num ++; +#endif +#ifdef NO_DEPENDENCY_TABLE + } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3_OK)) { + lookup_err_due_to_rename_num ++; + } else if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status == NFS3ERR_NOENT)) { + /* if there is a remove in front of the lookup, but it is + * actually executed later than the lookup + */ + lookup_err_due_to_parallel_remove_num ++; +#endif +#ifndef TAKE_CARE_LOOKUP_EACCESS_ENOENT_MISMATCH + /* if the looked return EACCESS in the trace, means the object still exists + * should have initialized, right not don't initialize it, hence play status + * could be ENOENT + */ + } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3ERR_ACCES)) { + lookup_eaccess_enoent_mismatch_num ++; +#endif +#ifdef TOLERANT_READ_IO_ERR + } else if ((proc==READ) && (status==NFS3ERR_IO) && (trace_status==NFS3_OK)) { + read_io_err_num ++; +#endif +#ifdef TOLERANT_STALE_FHANDLE_ERR + } else if ((status==NFS3ERR_STALE) && (trace_status==NFS3_OK)) { + printf ("!!!!!!! STALE FILE HANDLE \n"); + //sleep(1); + stale_fhandle_err_num ++; +#endif + } else { + int i; + for (i=dep_window_index.tail; CYCLIC_LESS(dep_window_index,i,dep_window_index.head); i++) + printf ("dep_tab[%d].disk_index %d, flag %d line %s\n", i, dep_tab[i].disk_index, dep_tab[i].flag, dep_tab[i].line); + if (status==EEXIST) { + abnormal_EEXIST_num ++; + } else if (status == ENOENT) { + abnormal_ENOENT_num ++; + } else + RFS_ASSERT (0); + } + } else { + proper_reply_num ++; + if (total_profile.in.tv_sec >= WARMUP_TIME) + run_stage_proper_reply_num ++; + } + +} + +/* return -1 if there is no reply being received + * return the dep_index if the corresponding reply has been received + */ +inline int receive_next_reply (int busy_flag) +{ + int dep_index; + int biod_index; + int proc; + char * line; + char * reply_line; + sfs_op_type *op_ptr; /* per operation info */ + int ret; + int status; + int trace_status; + char * errmsg; + + /* wait for reply */ + start_profile (&valid_poll_and_get_reply_profile); + start_profile (&invalid_poll_and_get_reply_profile); + + if (busy_flag == BUSY) { + poll_timeout = 0; + poll_timeout_0_num ++; + } else { + poll_timeout = 2000; /* 10000 or 2000 is a better number in non-debugging state */ + //poll_timeout = 0; /* 10000 or 2000 is a better number in non-debugging state */ + poll_timeout_pos_num ++; + } + + biod_index = poll_and_get_reply (poll_timeout); + if (biod_index==-1) { + end_profile (&invalid_poll_and_get_reply_profile); + return -1; + }; + end_profile (&valid_poll_and_get_reply_profile); + + start_profile (&decode_reply_profile); + /* check the corresponding request */ + dep_index = biod_reqp[biod_index].dep_tab_index; + if (biod_reqp[biod_index].in_use==1) { + RFS_ASSERT (dep_tab[dep_index].biod_req_index == biod_index); + } else { + printf ("biod_index %d reply received but the request has been time out\n", biod_index); + return -1; + } + + proc = dep_tab[dep_index].proc; + op_ptr = &Ops[proc]; + + if (dep_tab[dep_index].flag != DEP_FLAG_SENT) { + printf("dep_tab[%d].flag %d proc %d status %d start %d:%d stop %d:%d\n", + dep_index, dep_tab[dep_index].flag, proc, dep_tab[dep_index].status, + dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec, + dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec ); + printf ("received reply for timeout requests dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); + return dep_index; + } + RFS_ASSERT (dep_tab[dep_index].flag == DEP_FLAG_SENT); + + /* decode the reply */ + rfs_Ops[proc].setres (arg_res, buf1); + ret = proc_header (NFS_client, rfs_Ops[proc].xdr_res, arg_res); + RFS_ASSERT (ret == RPC_SUCCESS); + status = *((int *)arg_res); + errmsg = nfs3_strerror (status); + end_profile (&decode_reply_profile); + + start_profile (&check_reply_profile); + /* compare with the reply in the trace */ + line = dep_tab[dep_index].line; + reply_line = dep_tab[dep_index].reply_line; + trace_status = dep_tab[dep_index].trace_status; + + /* print the result, trace play progress indicator + if (rfs_debug || ( !profile_debug && ((dep_index %10000)==0) ) ) + fprintf (stdout, "dep_tab[%d], disk_index %d, receive reply, rpc_ret %d xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d \n", dep_index, dep_tab[dep_index].disk_index, ret, biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec); + */ + + /* error checking */ + check_reply (proc, biod_index, dep_index, status, errmsg, trace_status); + + /* free resources */ + finish_request (biod_index, dep_index, status); + + /* we set 100 seconds warm up time */ + if ((total_profile.in.tv_sec >= WARMUP_TIME)) { + /* get statistics */ + if (status == trace_status || (status==NFS3_OK && trace_status==NFS3ERR_RFS_MISS) ) { + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + } else { + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + sfs_elapsedtime (op_ptr, &(biod_reqp[biod_index].start), &(biod_reqp[biod_index].stop)); + end_profile (&check_reply_profile); + } + + //start_profile (&add_create_object_profile); + + if (trace_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==SYMLINK || proc==MKNOD)) { +#ifndef REDUCE_MEMORY_TRACE_SIZE + RFS_ASSERT (reply_line); +#endif + if (status!=NFS3_OK) { + /* commented out for 1022 */ + printf ("!!!!!! Should have been an ASSERTION FAILURE \n"); + RFS_ASSERT (proc==SYMLINK); + RFS_ASSERT (0); + } else { + if (proc!=SYMLINK || line[TRACE_VERSION_POS]!='2') + add_new_file_system_object(proc, dep_index, line, reply_line); + } + } + //end_profile (&add_create_object_profile); +} + +inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line) +{ + char * child_trace_fh; + fh_map_t * parent_entryp; + char component_name[MAX_PLAY_PATH_SIZE]; + char * parent_trace_fh; + char child_path[MAX_PLAY_PATH_SIZE]; + post_op_fh3 * post_op_fh3_child; + char * reply_trace_fh; + nfs_fh3 * child_fh3; + + parent_trace_fh = strstr (line, "fh"); + RFS_ASSERT (parent_trace_fh); + parent_trace_fh +=3; + parent_entryp = lookup_fh (parent_trace_fh); + RFS_ASSERT (parent_entryp); + parse_name (parent_trace_fh+65, component_name); + strcpy (child_path, parent_entryp->path); + strcat (child_path, "/"); + strcat (child_path, component_name); + + /* find the corresponding create request */ + //printf ("before find reply trace_fh reply_line %s\n", reply_line); +#ifdef REDUCE_MEMORY_TRACE_SIZE + reply_trace_fh = dep_tab[dep_index].reply_trace_fh; +#else + reply_trace_fh = find_reply_trace_fh (reply_line); +#endif + RFS_ASSERT (reply_trace_fh != NULL); + switch (proc) { + case CREATE: + RFS_ASSERT (((CREATE3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((CREATE3res *)arg_res)->res_u.ok.obj.handle; + break; + case MKDIR: + RFS_ASSERT (((MKDIR3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((MKDIR3res *)arg_res)->res_u.ok.obj.handle; + break; + case SYMLINK: + RFS_ASSERT (((SYMLINK3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((SYMLINK3res *)arg_res)->res_u.ok.obj.handle; + break; + case MKNOD: + RFS_ASSERT (((MKNOD3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((MKNOD3res *)arg_res)->res_u.ok.obj.handle; + break; + case LOOKUP: + RFS_ASSERT (proc==LOOKUP); + child_fh3 = &((LOOKUP3res *)arg_res)->res_u.ok.object; + break; + default: + RFS_ASSERT (0); + } +#ifndef REDUCE_MEMORY_TRACE_SIZE + RFS_ASSERT (reply_trace_fh[TRACE_FH_SIZE]==' '); +#endif + reply_trace_fh[TRACE_FH_SIZE] = 0; + add_fh (FH_MAP_FLAG_COMPLETE, reply_trace_fh, child_path, child_fh3); /* exist flag is not used now, set to 1 */ +#ifndef REDUCE_MEMORY_TRACE_SIZE + /* just to preserve the original reply line not changed */ + reply_trace_fh[TRACE_FH_SIZE] = ' '; +#endif +} + +/* initialize timestamp and proc field of dep_tab entry */ +void trace_play(void) +{ + + /* The flag to indicate whether trace_player is BUSY. Trace_player is BUSY + * when either there is request to send or there is reply being + * received. Otherwise it is IDLE. The timeout for polling replies + * is set to 0 when BUSY, it is set to the waiting time to the first + * request outside of current <min_dep_index, dep_window_max> window when IDLE. + */ + int busy_flag = BUSY; + //int dep_index; /* index into dependency table: dep_tab */ + //int biod_index; /* index into outstanding requests: biod_reqp */ + + disk_io_status = read_trace(); + RFS_ASSERT (!CYCLIC_EMPTY(dep_tab_index)); + CYCLIC_MOVE_HEAD(dep_window_index); + + adjust_play_window(busy_flag, &poll_timeout); + + start_profile (&total_profile); + while (!CYCLIC_EMPTY(dep_tab_index)) { + if ((total_profile.in.tv_sec > 600)) { + printf ("the process has run for more than 600 seconds, exit\n"); + goto END; + } + + if (busy_flag == IDLE) { + //start_profile (&check_timeout_profile); + check_timeout(); + //end_profile (&check_timeout_profile); + if (disk_io_status!=TRACE_FILE_END) { + disk_io_status = read_trace(); + }; + } + + //start_profile (&adjust_play_window_profile); + adjust_play_window (busy_flag, &poll_timeout); + if (rfs_debug) + printf("num_out_reqs %d\n", num_out_reqs); + busy_flag = IDLE; + //end_profile (&adjust_play_window_profile); + + start_profile (&execute_next_request_profile); + while (execute_next_request()!=-1) { + busy_flag = BUSY; + } + end_profile (&execute_next_request_profile); + + + start_profile (&receive_next_reply_profile); + /* actually the performance of two policy seems to be same */ +//#define SEND_HIGHER_PRIORITY_POLICY +#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY + +#ifdef SEND_HIGHER_PRIORITY_POLICY + receive_next_reply(IDLE); +#endif +#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY + busy_flag = IDLE; + while (receive_next_reply(busy_flag)!=-1) + busy_flag = BUSY; +#endif + end_profile (&receive_next_reply_profile); + + CYCLIC_ASSERT (0); + } + end_profile (&total_profile); + + RFS_ASSERT (disk_io_status == TRACE_FILE_END); + if (num_out_reqs !=0 ) { + printf ("num_out_reqs %d\n", num_out_reqs); + CYCLIC_PRINT(dep_tab_index); + } + RFS_ASSERT(num_out_reqs==0); + +END: + printf ("trace starttime %d, trace_end_time %d trace_duration %d\n", trace_timestamp1, trace_timestamp2, + trace_timestamp2 - trace_timestamp1); + printf ("can_not_catch_speed_num_total %d can_not_catch_speed_num_last_10_seconds %d", + can_not_catch_speed_num_total, can_not_catch_speed_num); + printf ("total_profile.about: %s\n", total_profile.about); + print_profile ("total_profile", &total_profile); + printf("\n"); + //print_profile ("check_timeout", &check_timeout_profile); + //printf("\n"); + //print_profile ("adjust_play_window", &adjust_play_window_profile); + //printf("\n"); + print_profile ("execute_next_request_profile", &execute_next_request_profile); + print_profile ("valid_get_nextop_profile", &valid_get_nextop_profile); + print_profile ("invalid_get_nextop_profile", &invalid_get_nextop_profile); + print_profile ("prepare_argument_profile", &prepare_argument_profile); + print_profile ("biod_clnt_call_profile", &biod_clnt_call_profile); + printf("\n"); + print_profile ("receive_next_reply_profile", &receive_next_reply_profile); + print_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile); + print_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile); + print_profile ("decode_reply_profile", &decode_reply_profile); + print_profile ("check_reply_profile", &check_reply_profile); + print_profile ("fgets_profile", &fgets_profile); + print_profile ("read_line_profile", &read_line_profile); + print_profile ("read_trace_profile", &read_trace_profile); + //print_profile ("add_create_object", &add_create_object_profile); + printf("\n"); + + printf ("dep_tab_index.tail %d dep_tab_index.head %d num_out_reqs %d\n", dep_tab_index.tail, dep_tab_index.head, num_out_reqs); +} + +int CYCLIC_SET_TAIL_TO(cyclic_index_t * index, int dest) +{ + cyclic_index_t indextmp, indextmp2; + int oldnum, num; + indextmp = *index; + indextmp2 = indextmp; + oldnum = CYCLIC_NUM(indextmp); + + if (! ((dest>=0) && (dest<indextmp.size))) { + CYCLIC_PRINT(indextmp); + printf("dest %d\n", dest); + } + RFS_ASSERT ((dest>=0) && (dest<indextmp.size)); + index->tail = dest; + indextmp2.tail = dest; + num = CYCLIC_NUM(indextmp2); + + if (num > oldnum) { + CYCLIC_PRINT(indextmp); + CYCLIC_PRINT(indextmp2); + printf("dest %d old_num %d num %d\n", dest, oldnum, num); + } + RFS_ASSERT (num <= oldnum); +} + +int flush_junk() +{ + int i; + for (i=0; i<500; i++) { + printf ("*************************************************************\n"); + } + fflush(stdout); +} + +int CYCLIC_ASSERT (int i) +{ + int j; + if (!(dep_tab_index.tail == dep_window_index.tail)) { + printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size); + printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size); + printf("pos %d\n", i); + flush_junk(); + sleep (10); + RFS_ASSERT (0); + }; + + if (!((dep_window_index.head == dep_tab_index.head) || + CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head ) )) { + printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size); + printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size); + printf("pos %d\n", i); + flush_junk(); + sleep (10); + RFS_ASSERT (0); + }; + for (i=0, j=0; i<max_biod_reqs; i++) { + if (biod_reqp[i].in_use == 1) + j++; + } + RFS_ASSERT (num_out_reqs==j); +/* + RFS_ASSERT ((dep_window_index.head == dep_tab_index.head) || + CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head )); +*/ +} + +/* sfs_c_chd.c */ diff --git a/TBBT/trace_play/sfs_c_chd.3thread.c b/TBBT/trace_play/sfs_c_chd.3thread.c new file mode 100644 index 0000000..195beb0 --- /dev/null +++ b/TBBT/trace_play/sfs_c_chd.3thread.c @@ -0,0 +1,3815 @@ +/* Try to change thread scheduling and uses three threads */ +#ifndef lint +static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * -------------------------- sfs_c_chd.c ------------------------- + * + * The sfs child. Routines to initialize child parameters, + * initialize test directories, and generate load. + * + *.Exported_Routines + * void child(int, float, int, char *); + * void init_fileinfo(void); + * void init_counters(void); + * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type, + * sfs_file_type); + * int check_access(struct *stat) + * int check_fh_access(); + * + *.Local_Routines + * void check_call_rate(void); + * void init_targets(void); + * void init_dirlayout(void); + * void init_rpc(void); + * void init_testdir(void); + * int do_op(void); + * int op(int); + * + *.Revision_History + * 21-Aug-92 Wittle randfh() uses working set files array. + * init_fileinfo() sets up working set. + * 02-Jul-92 Teelucksingh Target file size now based on peak load + * instead of BTDT. + * 04-Jan-92 Pawlowski Added raw data dump hooks. + * 16-Dec-91 Wittle Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" +#include "rfs_c_def.h" +#include "generic_hash.h" +#include "nfsd_nfsfh_cust.h" + +extern struct hostent *Server_hostent; + +#define PROB_SCALE 1000L +#define _M_MODULUS 2147483647L /* (2**31)-1 */ + +#define _GROUP_DIVISOR 500 +#define _FILES_PER_GROUP 4 +#define _MIN_GROUPS 12 +#define _WORKING_SET_AT_25_OPS_PER_SEC 975 + + +/* + * ----------------------- External Definitions ----------------------- + */ +extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *); +extern enum clnt_stat proc_header(CLIENT *cl, xdrproc_t xdr_results, void *results_ptr); +extern int biod_poll_wait(CLIENT *, uint32_t); +extern enum clnt_stat get_areply_udp (CLIENT * cl, uint32_t *xid, struct timeval *timeout); +extern char * parse_name (char * t, char * buf); + +/* forward definitions for local functions */ +static int init_rpc(void); + +/* RFS: forward definitions for local functions */ +void init_ops(void); +static void init_signal(); +extern void init_file_system (void); +extern void init_dep_tab (void); +static int read_trace(void); +static void read_fh_map(); +static void init_play (char * mount_point); +static void trace_play(void); +void print_result(void); +static int get_nextop(void); +static int check_timeout(void); +static struct biod_req * get_biod_req(int dep_tab_index); +static int lookup_biod_req (int xid); +static void init_time_offset(void); +void adjust_play_window (int flag, int * poll_timeout_arg); +static int poll_and_get_reply (int usecs); +static char * nfs3_strerror(int status); +static void check_clock(void); +static double time_so_far1(void); +static double get_resolution(void); +static void usage(void); +void init_dep_tab_entry (int dep_index); +extern inline fh_map_t * lookup_fh (char * trace_fh ); +static void finish_request (int biod_index, int dep_index, int status, int dep_flag); +static inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line); +static inline char * find_lead_trace_fh(int proc, char * line); +static inline char * find_reply_trace_fh (char * line); + +/* + * ------------- Per Child Load Generation Rate Variables ----------- + */ +static uint_t Calls_this_period; /* calls made during the current run period */ +static uint_t Calls_this_test; /* calls made during the test so far */ +static uint_t Reqs_this_period; /* reqs made during the current run period */ +static uint_t Reqs_this_test; /* reqs made during the test so far */ +static uint_t Sleep_msec_this_test; /* msec slept during the test so far */ +static uint_t Sleep_msec_this_period; +static uint_t Previous_chkpnt_msec; /* beginning time of current run period */ +static int Target_sleep_mspc; /* targeted sleep time per call */ + +static char io_buf[BUFSIZ]; /* io buffer for print out messages */ + +char * sfs_Myname; +int Log_fd; /* log fd */ +char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */ +int Validate = 0; /* fake variable */ +int Child_num = 0; /* fake: child index */ +int Tcp = 0; /* We implement UDP first */ +int Client_num = 1; /* fake: number of client */ +uid_t Real_uid; +gid_t Cur_gid; +uid_t Cur_uid; +/* as long as the inital value is different, then it's OK */ +int recv_num = 0; +int timeout_num = 0; +int send_num = 0; +int exit_flag = 0; +int async_rpc_sem; +int no_progress_flag = 0; +int num_out_reqs_statistics[MAX_OUTSTANDING_REQ+1]; +int num_out_reqs_statistics_at_timeout[MAX_OUTSTANDING_REQ+1]; + +/* + * ------------------------- SFS Child ------------------------- + */ + +static int nfs2proc_to_rfsproc[18] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17}; +static int nfs3proc_to_rfsproc[NFS3_PROCEDURE_COUNT] = {0, 1, 2, 4, 18, 5, 6, 8, 9, 14, + 13, 21, 10, 15, 11, 12, 16, 23, 17, 20, + 22, 19}; +void print_usage(int pos, int argc, char ** argv) +{ + int i; + printf("sfs3 hostname:mount_dir trace_file|stdin fh_map_file play_scale warmup_time(in seconds) \n"); + printf("sfs3 -pair_trace trace_file\n"); + printf("sfs3 -pair_write trace_file\n"); + printf("sfs3 -help\n"); + printf ("pos %d argc %d", pos, argc); + for (i=0; i<argc; i++) { + printf(" %s", argv[i]); + } + printf ("\n"); + exit; +} + +void print_cyclic_buffers () +{ + CYCLIC_PRINT(memory_trace_index); + CYCLIC_PRINT(dep_tab_index); + CYCLIC_PRINT(dep_window_index); +} + +void add_to_dep_tab(int i) +{ + char * trace_fh; + fh_map_t * fh_map_entry; + dep_tab_t * ent; + + trace_fh = strstr (memory_trace[i].line, "fh"); + if (!trace_fh) + printf ("memory_trace[%d].line %s\n", i, memory_trace[i].line); + RFS_ASSERT (trace_fh); + trace_fh += 3; + fh_map_entry = lookup_fh (trace_fh); + if (fh_map_entry && (fh_map_entry->flag==FH_MAP_FLAG_DISCARD) ) { + req_num_with_discard_fh ++; + return; + } + if (fh_map_entry) + req_num_with_init_fh ++; + else + req_num_with_new_fh ++; + + RFS_ASSERT (!CYCLIC_FULL(dep_tab_index)); + ent = &(dep_tab[dep_tab_index.head]); + + ent->disk_index = memory_trace[i].disk_index; + ent->memory_index = i; +#ifdef REDUCE_MEMORY_TRACE_SIZE + ent->trace_status = memory_trace[i].trace_status; + ent->reply_trace_fh = memory_trace[i].reply_trace_fh; +#endif + ent->line = memory_trace[i].line; + init_dep_tab_entry(dep_tab_index.head); + + if (rfs_debug && (i%100000)==0) + printf ("dep_tab[%d].disk_index %d = memory_trace[%d].disk_index %d\n", dep_tab_index.head, ent->disk_index, i, memory_trace[i].disk_index); + CYCLIC_MOVE_HEAD(memory_trace_index); + CYCLIC_MOVE_HEAD(dep_tab_index); +} + +void init_profile_variables () +{ + init_profile ("total_profile", &total_profile); + init_profile ("execute_next_request_profile", &execute_next_request_profile); + init_profile ("valid_get_nextop_profile", &valid_get_nextop_profile); + init_profile ("invalid_get_nextop_profile",&invalid_get_nextop_profile); + init_profile ("prepare_argument_profile", &prepare_argument_profile); + init_profile ("biod_clnt_call_profile", &biod_clnt_call_profile); + init_profile ("receive_next_reply_profile", &receive_next_reply_profile); + init_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile); + init_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile); + init_profile ("decode_reply_profile", &decode_reply_profile); + init_profile ("check_reply_profile", &check_reply_profile); + init_profile ("add_create_object_profile", &add_create_object_profile); + init_profile ("check_timeout_profile", &check_timeout_profile); + init_profile ("adjust_play_window_profile",&adjust_play_window_profile); + init_profile ("fgets_profile",&fgets_profile); + init_profile ("read_line_profile",&read_line_profile); + init_profile ("read_trace_profile",&read_trace_profile); +} + +static char trace_file[256]="anon-lair62-011130-1200.txt"; +int print_memory_usage() +{ + printf("size of fh_map_t %d size of fh_map %d\n", sizeof(fh_map_t), sizeof(fh_map)); + printf("sizeof dep_tab_t %d sizeof dep_tab %d\n", sizeof(dep_tab_t), sizeof(dep_tab)); + printf("size of memory_trace_ent_t %d sizeof memory_trace %d\n", sizeof(memory_trace_ent_t), sizeof(memory_trace)); + printf("size of CREATE3args %d\n", sizeof( CREATE3args)); + printf("size of MKDIR3args %d\n", sizeof( MKDIR3args)); + printf("size of READ3args %d\n", sizeof( READ3args)); + printf("size of WRITE3args %d\n", sizeof( WRITE3args)); + printf("size of RENAME3args %d\n", sizeof( RENAME3args)); + printf("size of GETATTR3args %d\n", sizeof( GETATTR3args)); + printf("size of SETATTR3args %d\n", sizeof( SETATTR3args)); + printf("size of LINK3args %d\n", sizeof( LINK3args)); + printf("size of SYMLINK3args %d\n", sizeof( SYMLINK3args)); + printf("size of MKNOD3args %d\n", sizeof( MKNOD3args)); + printf("size of RMDIR3args %d\n", sizeof( RMDIR3args)); + printf("size of REMOVE3args %d\n", sizeof( REMOVE3args)); + printf("size of LOOKUP3args %d\n", sizeof( LOOKUP3args)); + printf("size of READDIR3args %d\n", sizeof( READDIR3args)); + printf("size of READDIRPLUS3args %d\n", sizeof( READDIRPLUS3args)); + printf("size of FSSTAT3args %d\n", sizeof( FSSTAT3args)); + printf("size of FSINFO3args %d\n", sizeof( FSINFO3args)); + printf("size of COMMIT3args %d\n", sizeof( COMMIT3args)); + printf("size of ACCESS3args %d\n", sizeof( ACCESS3args)); + printf("size of READLINK3args %d\n", sizeof( READLINK3args)); + + +} + +void recv_thread() +{ + int last_print_time = -1; + int busy_flag; + + while (send_num ==0) { + usleep(1000); + } + + //while (!CYCLIC_EMPTY(dep_tab_index)) { + while (!exit_flag) { + if ((total_profile.in.tv_sec - last_print_time >= 10)) { + static int recv_num_before_10_seconds = 0; + last_print_time = total_profile.in.tv_sec; + fprintf (stdout, "<<<<< recv_thread recv_num %d time %d num_out_reqs %d \n", recv_num, total_profile.in.tv_sec, num_out_reqs); + if (recv_num == recv_num_before_10_seconds) { + no_progress_flag = 1; + RFS_ASSERT (0); + } else + recv_num_before_10_seconds = recv_num; + } + //start_profile (&check_timeout_profile); + check_timeout(); + //end_profile (&check_timeout_profile); + + pthread_yield(); + //usleep(1000); + start_profile (&receive_next_reply_profile); + /* actually the performance of two policy seems to be same */ +//#define SEND_HIGHER_PRIORITY_POLICY +#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY +#ifdef SEND_HIGHER_PRIORITY_POLICY + receive_next_reply(IDLE); +#endif +#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY + busy_flag = IDLE; + while (receive_next_reply(busy_flag)!=-1) { + busy_flag = BUSY; + } +#endif + end_profile (&receive_next_reply_profile); + } + printf ("<<<< recv thread EXIT\n"); + exit_flag = 2; +} + +int io_thread () +{ +/* number of seconds the I/O thread pauses after each time trying to read the requests */ +#define IO_THREAD_PAUSE_TIME 1 + + int i; + int j = 0; + + disk_io_status = read_trace (); + while (disk_io_status == TRACE_BUF_FULL) { + + usleep (10000); + if ((j++%1000)==0) { + printf("&&&&&&&&&& io thread, sleep %d seconds\n", j/100); + } + + disk_io_status = read_trace (); + //printf ("io_thread, after read_trace, disk_index %d\n", disk_index); + +#ifdef SEQUEN_READ + for (i=0; i<SEQUEN_READ_NUM; i++) { + add_to_dep_tab(CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size)); + } +#endif + //printf ("***************** IO THREAD SLEEP 1 s\n"); + //print_cyclic_buffers(); + //pthread_yield(); + } + RFS_ASSERT (disk_io_status == TRACE_FILE_END); + printf("io_thread EXIT\n"); +} + +int execute_thread() +{ + int i; + struct timeval playstart_time, playend_time; + + sleep (10); /* first let io_thread to run for a while */ + printf ("trace_play\n"); + + gettimeofday(&playstart_time, NULL); + + init_time_offset(); + trace_play (); + + gettimeofday(&playend_time, NULL); + + { + int usec, sec; + sec = playend_time.tv_sec - playstart_time.tv_sec; + usec = sec * 1000000 + (playend_time.tv_usec - playstart_time.tv_usec); + sec = usec / 1000000; + usec = usec % 1000000; + printf("Total play time: %d sec %d usec\n", sec, usec); + if (sec > WARMUP_TIME) + print_result(); + else + printf ("the trace play time %d is less than WARMUP_TIME %d, no statistical results\n"); + } +#ifdef RECV_THREAD + exit_flag = 1; + while (exit_flag == 1) { + usleep (1000); + } +#endif + + clnt_destroy(NFS_client); + biod_term(); +} + +int init_thread () +{ + pthread_attr_t attr; + int arg; + int ret = 0; + pthread_t io_thread_var; +#ifdef RECV_THREAD + pthread_t recv_thread_var; +#endif + pthread_t execute_thread_var; + + ret = pthread_attr_init (&attr); + if (ret!=0) { + perror("pthread_attr_init attr"); + return ret; + } +#ifdef IO_THREAD + ret = pthread_create (&(io_thread_var), &attr, &io_thread, (void *)&arg ); + if (ret!=0) { + perror("io_pthread_attr_init"); + return ret; + } +#endif + +#ifdef RECV_THREAD + ret = pthread_create (&(recv_thread_var), &attr, &recv_thread, (void *)&arg ); + if (ret!=0) { + perror("recv_pthread_attr_init"); + return ret; + } +#endif + +/* + ret = pthread_create (&(execute_thread_var), &attr, &execute_thread, (void *)&arg ); + if (ret!=0) { + perror("io_pthread_attr_init"); + return ret; + } +*/ +} + +void init_buffers() +{ + CYCLIC_INIT("memory_trace_index",memory_trace_index,MAX_MEMORY_TRACE_LINES); + CYCLIC_INIT("dep_tab_index ",dep_tab_index,DEP_TAB_SIZE); + CYCLIC_INIT("dep_window_index ",dep_window_index,DEP_TAB_SIZE); +} + +int main(int argc, char ** argv) +{ + extern char * optarg; + int i; + struct timeval in={1000000, 100}; + + init_profile_variables(); + if ((argc==1) || (argc==2 && !strcmp(argv[1],"-help"))) { + print_usage(0, argc, argv); + exit(0); + } + if (!strcmp(argv[1], "-pair_write")) { + if (argc==3) + strcpy(trace_file, argv[2]); + else + strcpy(trace_file, "stdin"); + pair_write(trace_file); + exit(0); + } + if (!strcmp(argv[1], "-pair_trace")) { + if (argc==3) + strcpy(trace_file, argv[2]); + else + strcpy(trace_file, "stdin"); + pair_trace(trace_file); + exit(0); + } + if (!strcmp(argv[1], "-check_aging")) { + if (argc!=3) { + print_usage(3, argc, argv); + exit(0); + } + strcpy(trace_file, argv[2]); + check_aging (trace_file); + exit(0); + } + if (!strcmp(argv[1], "-check_statistics")) { + if (argc!=3) { + print_usage(1, argc, argv); + exit(0); + } + strcpy(trace_file, argv[2]); + memset(fh_htable, 0, sizeof (fh_htable)); + check_statistics (trace_file); + exit(0); + } + + if (argc!=6) { + print_usage(2, argc, argv); + exit(0); + } + + PLAY_SCALE = atoi (argv[4]); + RFS_ASSERT (PLAY_SCALE >=1 && PLAY_SCALE <=10000); + + WARMUP_TIME = atoi (argv[5]); + RFS_ASSERT (WARMUP_TIME >=0 && WARMUP_TIME <=1000); + + print_memory_usage(); + check_clock(); + getmyhostname(lad_hostname, HOSTNAME_LEN); + + init_ops(); + /* + * Get the uid and gid information. + */ + Real_uid = getuid(); + Cur_gid = getgid(); + //Real_uid = 513; + //Cur_gid = 513; + + Nfs_timers = Nfs_udp_timers; + + init_semaphores(); + init_file_system (); + init_signal(); + init_play (argv[1]); + //init_play ("capella:/p5/RFSFS"); + init_fh_map(); + read_fh_map (argv[3]); + //read_fh_map ("fh-path-map-play"); + strcpy(trace_file, argv[2]); + +/* If ordered by TIMESTAMP, + * memory_trace_index.tail <= dep_tab_index.tail < dep_window_max <= + * dep_tab_index.head <= memory_trace_index.head + */ + + init_global_variables(); + init_buffers(); + init_thread(); + pthread_yield(); + execute_thread(); +} + +int init_global_variables() +{ + memset (num_out_reqs_statistics, 0, sizeof(num_out_reqs_statistics)); + memset (num_out_reqs_statistics_at_timeout, 0, sizeof(num_out_reqs_statistics_at_timeout)); +} + +int init_semaphores() +{ + async_rpc_sem = dest_and_init_sem (ASYNC_RPC_SEM_KEY, 1, "async_rpc_sem"); +} + +void init_ops (void) +{ + Ops = nfsv3_Ops; + nfs_version = NFS_V3; +} + +/* Set up the signal handlers for all signals */ +void init_signal() +{ +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + struct sigaction sig_act, old_sig_act; + + /* use XOPEN signal handling */ + + sig_act.sa_handler = generic_catcher; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + + /* signals handlers for signals used by sfs */ + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGINT"); + exit(135); + } + + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) { + perror("sigaction failed: SIGTERM"); + exit(137); + } +#else + /* signals handlers for signals used by sfs */ + (void) signal(SIGINT, sfs_cleanup); + // RFS (void) signal(SIGALRM, sfs_alarm); + (void) signal(SIGTERM, sfs_cleanup); +#endif +} + +void +init_play ( + char * mount_point) /* Mount point for remote FS */ +{ + char namebuf[NFS_MAXNAMLEN] = "trace_play"; /* unique name for this program */ + CLIENT * mount_client_ptr; /* Mount client handle */ + + if (!rfs_debug); + (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ); + + sfs_Myname = namebuf; + + /* + * May require root priv to perform bindresvport operation + */ + mount_client_ptr = lad_getmnt_hand(mount_point); + if (mount_client_ptr == NULL) { + exit(145); + } + + /* + * should be all done doing priv port stuff + */ + + if (init_rpc() == -1) { + (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname); + (void) generic_kill(0, SIGINT); + exit(146); + } + + + /* + * finish all priv bindresvport calls + * reset uid + */ + if (setuid(Real_uid) != (uid_t)0) { + (void) fprintf(stderr,"%s: %s%s", sfs_Myname, + "cannot perform setuid operation.\n", + "Do `make install` as root.\n"); + } + + init_mount_point(0, mount_point, mount_client_ptr); + + + /* + * Cleanup client handle for mount point + */ + clnt_destroy(mount_client_ptr); + + init_counters(); +} + +#ifdef REDUCE_MEMORY_TRACE_SIZE +void inline format_line (char * line_before, char * line) +{ + char * pv = line_before; + char * pl = line; + char * p; + int i; + + //printf("format_line before %s\n", line_before); + p = strstr (pv, "fh"); + while (p!=NULL) { + while (pv<=p) + *pl++ = *pv++; + *pl++ = *pv++; + if (*pv==2) { + *pl++ = *pv++; + } + *pl++ = *pv++; + i = 0; + while (*pv !=' ') { + RFS_ASSERT ((*pv >='0' && *pv <='9') || (*pv >='a' && *pv<='f')); + *pl++ = *pv++; + i++; + } + RFS_ASSERT ((i==48) || (i==40) || (i==24)); + while (i<48) { + *pl++ = '0'; + i++; + } + p = strstr (pv, "fh"); + } + while ((*pv)!=NULL) { + *pl++ = *pv++; + } + //printf("format_line after %s\n", line); +} + +char * read_line (int disk_index) +{ + static FILE * fp=NULL; + static int start=0; + static int start_disk_index=0; + int i; + static int finish_flag = 0; + static int varfh_flag = 0; +#define READ_LINE_BUF_SIZE (MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR+2) +#define SAFE_BYTES 1000 +#define READ_LINE_LENGTH (MAX_TRACE_LINE_LENGTH+SAFE_BYTES) + + static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH]; + char varfh_line_buf[READ_LINE_LENGTH]; + + start_profile (&read_line_profile); + + if (fp==NULL) { + if (strcmp(trace_file, "stdin")) { + fp = fopen(trace_file, "r"); + + if (strstr(trace_file, ".varfh")) { + varfh_flag = 1; + }; + if (strstr(trace_file, ".fmt1")) { + TRACE_COMMAND_REPLY_FLAG_POS += 12; + TRACE_VERSION_POS +=12; + TRACE_MSGID_POS +=12; + TRACE_FH_SIZE =48; + } + if (!fp) { + printf("can not open files %s\n", fp); + perror("open"); + } + } else { + fp = stdin; + } + RFS_ASSERT (fp!=NULL); + for (i=0; i<READ_LINE_BUF_SIZE; i++) { + start_profile(&fgets_profile); + if (varfh_flag) { + if (!fgets(varfh_line_buf, READ_LINE_LENGTH, fp)) { + RFS_ASSERT (0); + } + format_line (varfh_line_buf, line_buf[i]); + } else { + if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) { + RFS_ASSERT (0); + } + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]); + } + } + + if (disk_index > start_disk_index+READ_LINE_BUF_SIZE) { + printf ("disk_index %d start_disk_index %d READ_LINE_BUF_SIZE %d\n", + disk_index, start_disk_index, READ_LINE_BUF_SIZE); + } + RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE) + if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) { + if (finish_flag) { + return NULL; + } + start_profile(&fgets_profile); + if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) { + end_profile(&fgets_profile); + finish_flag = 1; + return NULL; + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]); + start = (start+1) % READ_LINE_BUF_SIZE; + start_disk_index ++; + } + RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE) + i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE; + +/* + if (!(strlen(line_buf[i])>80)) { + printf ("start %d start_disk_index %d disk_index %d strlen %d line_buf[%d] %s\n", start, start_disk_index, disk_index, strlen(line_buf[i]), i, line_buf[i]); + RFS_ASSERT (strlen(line_buf[i])>80); + } + if (!((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH))) + printf ("disk_index %d strlen %d line_buf[%d] %s\n", disk_index, strlen(line_buf[i]), i, line_buf[i]); + RFS_ASSERT ((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH)); +*/ + + end_profile (&read_line_profile); + return (line_buf[i]); +} + +#define OPS_FLAG_INC 0 +#define OPS_FLAG_PRINT 1 +int read_write_fh_statistics (int flag, char * buf, int timestamp) +{ + static FILE * fp = NULL; + char * p; + char c; + if (!fp) { + fp = fopen ("read_write_fh.output", "w"); + RFS_ASSERT (fp); + } + if (flag == OPS_FLAG_INC) { + p = strstr (buf, "fh"); + RFS_ASSERT (p); + c = p[40+3]; + p[40+3]=0; + fprintf(fp, "%s\n", p+3+24); + p[40+3]=c; + }; + if (flag == OPS_FLAG_PRINT) { + int disk_index = (int) buf; + fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + } +} + +int write_statistics(int flag, char * buf, char * reply_buf, int trace_status) +{ + static FILE * fp = NULL; + int pre_size, size, count; + char * p = reply_buf; + if (!fp) { + fp = fopen ("write.output", "w"); + RFS_ASSERT (fp); + } + if (flag == OPS_FLAG_INC) { + p = strstr (p, "pre-size"); + RFS_ASSERT (p); + sscanf (p, "pre-size %x", &pre_size); + p += strlen("pre-size"); + p = strstr (p, "size"); + RFS_ASSERT (p); + sscanf (p, "size %x", &size); + p = strstr (p, "count"); + if (!p) + fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + RFS_ASSERT (p); + sscanf (p, "count %x", &count); + fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + } + if (flag == OPS_FLAG_PRINT) { + int disk_index = (int) buf; + int timestamp = (int) reply_buf; + fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + } +} + +void ops_statistics (int ops_flag, int proc, int timestamp) +{ + static FILE * fp = NULL; + static struct { + int count; + int count_K; + int update_flag; + char name[32]; + } ops[NOPS]={{0, 0, 0, "NULL"},{0, 0, 0, "GETATTR"},{0, 0, 1, "SETATTR"},{0, 0, 0, "ROOT"}, + {0, 0, 0, "LOOKUP"},{0, 0, 0, "READLINK"},{0, 0, 0, "READ"},{0, 0, 1, "WRCACHE"}, + {0, 0, 1, "WRITE"}, {0, 0, 1, "CREATE"},{0, 0, 1, "REMOVE"},{0, 0, 1, "RENAME"}, + {0, 0, 1, "LINK"}, {0, 0, 1, "SYMLINK"},{0, 0, 1, "MKDIR"},{0, 0, 1, "RMDIR"}, + {0, 0, 0, "READDIR"},{0, 0, 0, "FSSTAT"},{0, 0, 0, "ACCESS"},{0, 0, 0, "COMMIT"}, + {0, 0, 0, "FSINFO"},{0, 0, 1, "MKNOD"}, {0, 0, 0, "PATHCONF"}, {0, 0, 0, "READDIRPLUS"}}; + + if (ops_flag == OPS_FLAG_INC) { + RFS_ASSERT (proc>=0 && proc<NOPS); + ops[proc].count ++; + if (ops[proc].count == 1000) { + ops[proc].count_K ++; + ops[proc].count = 0; + } + } + if (ops_flag == OPS_FLAG_PRINT) { + int i; + int update_K=0, update=0, total_K=0, total=0; + float f1, f2; + int disk_index = proc; + + if (!fp) { + fp = fopen ("mix.output", "w"); + RFS_ASSERT (fp); + } + for (i=0; i<NOPS; i++) { + total_K += ops[i].count_K; + total += ops[i].count; + if (ops[i].update_flag) { + update_K += ops[i].count_K; + update += ops[i].count; + } + } + update_K += update/1000; + update = update%1000; + total_K += total/1000; + total = total%1000; + + f1 = total_K; + f1 = f1*1000+total; + for (i=0; i<NOPS; i++) { + f2 = ops[i].count_K; + f2 = f2*1000+ops[i].count; + fprintf (fp, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1); + fprintf (stderr, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1); + } + f2 = update_K; + f2 = f2*1000+update; + fprintf (fp, " total %8d,%03d\n", total_K, total); + fprintf (stderr, " total %8d,%03d\n", total_K, total); + fprintf (fp, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1); + fprintf (stderr, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1); + fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + fprintf(stderr, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + } +} + + +void truncate_statistics (int flag, int proc, char * buf, char * reply_buf) +{ +#define TRACE_FH_SIZE2 16 +#define BLOCK_SIZE 4096 + static int no_size_num = 0; + static int have_size_num = 0; + static int equal_size_num = 0; + static int first_size_num = 0; + static int truncate_num = 0; + static int truncate_size = 0; + static int truncate_KB = 0; + static int truncate_block_num = 0; + static int padding_num = 0; + static int padding_KB = 0; + static int padding_size = 0; + static FILE * fp = NULL; + char * p; + char * trace_fh; + int pre_size, size, count; + struct generic_entry * ent; + + if (flag == OPS_FLAG_PRINT) { + int disk_index = proc; + int timestamp = (int)buf; + if (!fp) { + fp = fopen ("truncate.output", "w"); + RFS_ASSERT (fp); + } + truncate_KB += truncate_size/1000; + truncate_size %= 1000; + padding_KB += padding_size/1000; + padding_size %= 1000; + fprintf(fp, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB); + fprintf(stderr, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB); + return; + } + + p = strstr (&buf[TRACE_MSGID_POS], "fh"); + RFS_ASSERT (p); + p+=3; + trace_fh = p; + + if (proc==SETATTR) { + p = strstr (buf, " size "); + } else { + RFS_ASSERT (proc==WRITE); + p = strstr (reply_buf, " ftype 1 "); + RFS_ASSERT (p); + p = strstr (p, " size "); + RFS_ASSERT (p); + if (strstr(p, " ftype 1 ")) { + fprintf (fp, "#### %s%s\n", buf, reply_buf); + fprintf (stderr, "#### %s%s\n", buf, reply_buf); + } + RFS_ASSERT (!strstr(p, " ftype 1 ")); + } + if (!p) { + no_size_num ++; + return; + } + have_size_num ++; + + sscanf (p, " size %x", &size); + if (size <0 || size > 2000000000) { + fprintf (fp, "#### size too big %x %s %s\n", size, buf, reply_buf); + fprintf (stderr, "#### size too big %x %s %s\n", size, buf, reply_buf); + } + + RFS_ASSERT (size >=0 && size <2000000000); + ent = generic_lookup (trace_fh+24, TRACE_FH_SIZE2, 0, fh_htable, FH_HTABLE_SIZE); + if (ent) { + if (ent->key3 != size) { + if (proc==SETATTR) { + //printf ("%s\n", buf); + //printf ("size change fh %s pre-size %x size %x\n", trace_fh, ent->key3, size); + if (ent->key3 > size) { + truncate_num ++; + truncate_size += ent->key3 - size; + truncate_block_num += (ent->key3+BLOCK_SIZE-1)/BLOCK_SIZE; + if (size!=0) { + //fprintf (stderr, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf); + //fprintf (fp, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf); + truncate_block_num -= (size + BLOCK_SIZE-1)/BLOCK_SIZE; + } + if (truncate_size > 1000000000) { + truncate_KB += truncate_size/1000; + truncate_size %= 1000; + } + } else { + padding_num ++; + //printf ("%s\n", buf); + //printf ("padding fh %s pre-size %x size %x\n", trace_fh, ent->key3, size); + padding_size += size - ent->key3; + if (padding_size > 1000000000) { + padding_KB += padding_size/1000; + padding_size %= 1000; + } + } + } + ent->key3 = size; + }else + equal_size_num++; + } else { + generic_insert(trace_fh+24, TRACE_FH_SIZE2, size, fh_htable, FH_HTABLE_SIZE); + first_size_num ++; + } +}; + +int get_timestamp_usec (char * buf) +{ + char str[128]; + int ret; + strncpy(str, buf, 100); + RFS_ASSERT (str[10]=='.'); + RFS_ASSERT (str[17]==' '); + str[17]=0; + ret = atoi(&str[11]); + RFS_ASSERT (ret >=0 && ret <=999999); + return ret; +} + +int get_timestamp_sec (char * buf) +{ + char str[128]; + int ret; + strncpy(str, buf, 100); + RFS_ASSERT (str[10]=='.'); + str[10]=0; + ret = atoi(str); + RFS_ASSERT (ret >1000000000 && ret <2000000000); + return ret; +} + +int check_aging (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + int trace_status; + int debug = 0; + int nfs3proc, msgid, proc; + + while ((buf=read_line(++disk_index))!=NULL) { + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + + proc = nfs3proc_to_rfsproc[nfs3proc]; + ops_statistics (OPS_FLAG_INC, proc, -1); + + switch (proc) { + int off, count, size; + char * t; + case CREATE: printf("%s\n", "create"); break; + case MKDIR: printf("%s\n", "mkdir"); break; + case REMOVE: printf("%s\n", "remove"); break; + case RMDIR: printf("%s\n", "rmdir"); break; + case WRITE: + t = buf; + t = strstr (t, "off"); + RFS_ASSERT (t); + t+=4; + sscanf (t, "%x", &off); + RFS_ASSERT (off>=0 && off<0x7FFFFFFF) + t = strstr (t, "count"); + RFS_ASSERT (t); + t+=6; + sscanf (t, "%x", &count); + RFS_ASSERT (count <= 32768); + printf("%s off %x count %x\n", "write", off, count); + //printf("%s count %x\n", "write", count); + break; + case SETATTR: + t = strstr (buf, " size "); + if (t) { + sscanf (t, " size %x", &size); + printf ("%s size %x\n", "setattr", size); + } + } + if ((disk_index%10000)==0) { + fprintf(stderr, "%d disk trace passed\n", disk_index); + } + }; + + fprintf(stderr, "%d disk trace parsed\n", disk_index); + ops_statistics (OPS_FLAG_PRINT, disk_index, -1); +} + + +int check_statistics (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + char * p; + int trace_status; + int debug = 0; + int nfs3proc, msgid, proc; + static int last_timestamp_sec = -1; + int timestamp_sec; + int memory_trace_size = 0; + + while ((buf=read_line(++disk_index))!=NULL) { + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + timestamp_sec = get_timestamp_sec(buf); + + proc = nfs3proc_to_rfsproc[nfs3proc]; + ops_statistics (OPS_FLAG_INC, proc, -1); + + if (proc!= WRITE && proc!=SETATTR && proc!=READ) { + continue; + } + RFS_ASSERT (buf[strlen(buf)-1]=='\n'); + buf [strlen(buf)-1] = 0; + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + if (debug) + printf("read line disk_index %d %s\n", disk_index, buf); + + trace_status = NFS3ERR_RFS_MISS; + for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE; i++) { + reply_buf = read_line(i); + if (debug) + printf("searching for reply: read line %s\n", reply_buf); + if (!reply_buf) + break; + if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&reply_buf[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) { + trace_status = find_reply_status(reply_buf); + if (trace_status == NFS3_OK) { + if (proc==READ || proc==WRITE) + read_write_fh_statistics (OPS_FLAG_INC, buf, 0); + if (proc == WRITE) + write_statistics (OPS_FLAG_INC, buf, reply_buf, trace_status); + if (proc==WRITE || proc==SETATTR) + truncate_statistics (OPS_FLAG_INC, proc, buf, reply_buf); + } + }; + } + } + //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS) + if (trace_status == NFS3ERR_RFS_MISS) { + //printf ("%s no reply\n", buf); + missing_reply_num ++; + } + +#if 0 /* commented out by G. Jason Peng */ + if * + if ((missing_reply_num > memory_trace_size/10) && (missing_reply_num > 100)) { + printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size); + exit (0); + } +#endif + + memory_trace_size ++; + + if (last_timestamp_sec == -1) { + last_timestamp_sec = timestamp_sec; + } else if (timestamp_sec - last_timestamp_sec >=3600) { + ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec); + truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL); + read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec); + write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1); + last_timestamp_sec = timestamp_sec; + } +/* + if ((memory_trace_size%10000)==0) { + fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num); + ops_statistics (OPS_FLAG_PRINT, -1); + truncate_statistics (OPS_FLAG_PRINT, -1, NULL, NULL); + } +*/ + }; + + fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num); + ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec); + truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL); + read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec); + write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1); +} + + +/* This routine output all the requests, together with their replies */ +int pair_trace (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + char * p; + int trace_status; + int debug = 0; + int nfs3proc, msgid; + int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + int memory_trace_size = 0; + FILE * outputfp; + char output_file[1024]; + + strcpy (output_file, tracefile); + strcat (output_file, ".pair"); + outputfp = fopen (output_file, "w"); + RFS_ASSERT (outputfp); + + while ((buf=read_line(++disk_index))!=NULL) { + if (disk_index == 258) + f(); + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + ops[nfs3proc]++; + + buf [strlen(buf)-1] = 0; + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + if (debug) + printf("read line disk_index %d %s\n", disk_index, buf); + fprintf (outputfp, "%s\n", buf); + + trace_status = NFS3ERR_RFS_MISS; + for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) { + reply_buf = read_line(i); + if (debug) + printf("searching for reply: read line %s\n", reply_buf); + if (!reply_buf) + break; + if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&reply_buf[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) { + fprintf(outputfp, "%s", reply_buf); + trace_status = find_reply_status(reply_buf); + if (debug) + fprintf(stderr, "reply found trace_status %d\n", find_reply_status(reply_buf)); + break; + }; + } + } + + if (trace_status == NFS3ERR_RFS_MISS) { + fprintf (stderr, "%s no reply\n", buf); + fprintf(outputfp, "missing_reply\n"); + missing_reply_num ++; + } + + if (missing_reply_num > memory_trace_size/10 && missing_reply_num >100) { + fprintf (stderr, "missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size); + exit (0); + } + + memory_trace_size ++; + + if ((memory_trace_size%10000)==0) + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + }; + + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); + +} +/* This routine output all the write requests, together with their replies. It is used for + * analysis of write requests: appended bytes, overwrite bytes etc + */ +int pair_write (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + char * p; + int trace_status; + int pair_write_debug = 0; + int nfs3proc, msgid; + int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + int memory_trace_size = 0; + + while ((buf=read_line(++disk_index))!=NULL) { + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + ops[nfs3proc]++; + + if (!strstr(buf, "write")) + continue; + + buf [strlen(buf)-1] = 0; + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + if (pair_write_debug) + printf("read line disk_index %d %s\n", disk_index, buf); + + /* store the request to memory */ + //strcpy (memory_trace[memory_trace_size].line, buf); + //memory_trace[memory_trace_size].disk_index = disk_index; + + /* find and store the reply status and reply fhandle to memory */ + //memory_trace[memory_trace_size].trace_status = NFS3ERR_RFS_MISS; + trace_status = NFS3ERR_RFS_MISS; + for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) { + reply_buf = read_line(i); + if (pair_write_debug) + printf("searching for reply: read line %s\n", reply_buf); + if (!reply_buf) + break; + if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&reply_buf[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) { + int pre_size, size, count; + //memory_trace[memory_trace_size].trace_status = find_reply_status(reply_buf); + if (pair_write_debug) + printf("reply found trace_status %d\n", find_reply_status(reply_buf)); + //break; + trace_status = find_reply_status(reply_buf); + if (trace_status == NFS3_OK) { + p = strstr (p, "pre-size"); + RFS_ASSERT (p); + sscanf (p, "pre-size %x", &pre_size); + p += strlen("pre-size"); + p = strstr (p, "size"); + RFS_ASSERT (p); + sscanf (p, "size %x", &size); + p = strstr (p, "count"); + if (!p) + printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + RFS_ASSERT (p); + sscanf (p, "count %x", &count); + printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + break; + } + }; + } + } + //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS) + if (trace_status == NFS3ERR_RFS_MISS) { + printf ("%s no reply\n", buf); + missing_reply_num ++; + } + +#if 0 /* commented out by G. Jason Peng */ + if (missing_reply_num > memory_trace_size/10) { + printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size); + exit (0); + } +#endif + + memory_trace_size ++; + + /* + if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) { + fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES); + break; + } + */ + if ((memory_trace_size%10000)==0) + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + }; + + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); + +} + +#ifdef notdef +/* This function is not finished writing */ +int calculate_performance() +{ + char *buf; + char *reply_buf; + int i; + char * p; + int debug = 0; + +typedef struct { + struct timeval start; + struct timeval stop; + int trace_status; + int op; +} trace_performance_ent_t; + + struct timeval req_time; + struct timeval reply_time; + + trace_performance_ent_t * ent = NULL; + + while (!CYCLIC_FULL(memory_trace_index)) { + + if (ent!=NULL && (ent->trace_status == NFS3ERR_RFS_MISS)) + buf = reply_buf; + if ((buf=read_line(++disk_index))==NULL) { +END: fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, CYCLIC_NUM(memory_trace_index), missing_reply_num ); + fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); + end_profile (&read_trace_profile); + return TRACE_FILE_END; + } + + get_timestamp (&ent->req_time, buf); + if (MAX_COMMAND_REPLY_DISTANCE ==1) { + ent->trace_status == NFS3ERR_RFS_MISS; + } else { + reply_buf=read_line(++disk_index); + RFS_ASSERT (reply_buf); + if (!strcmp(reply_buf, "missing_reply\n")) { + ent->trace_status == NFS3ERR_RFS_MISS; + } else { + get_timestamp (&ent->reply_time, buf); + ent->trace_status = find_reply_status(reply_buf); + } + } + } +} +#endif + +int read_trace () +{ + char *buf; + char *reply_buf; + int i; + char * p; + int debug = 0; + memory_trace_ent_t * ent=NULL; + + start_profile (&read_trace_profile); + + while (!CYCLIC_FULL(memory_trace_index)) { + if (ent!=NULL && (ent->trace_status == NFS3ERR_RFS_MISS)) + buf = reply_buf; + if ((buf=read_line(++disk_index))==NULL) { +END: fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, CYCLIC_NUM(memory_trace_index), missing_reply_num ); + fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); + end_profile (&read_trace_profile); + return TRACE_FILE_END; + } + + if (rfs_debug) + printf ("disk_index %d %s\n", disk_index, buf); + + if (disk_index==0) { + trace_timestamp1 = get_timestamp_sec (buf); + trace_starttime.sec = get_timestamp_sec (buf); + trace_starttime.usec = get_timestamp_usec (buf); + trace_starttime.esec = 0; + printf ("trace starttime %d %d %d\n", trace_starttime.sec, trace_starttime.usec, trace_starttime.esec); + } else + trace_timestamp2 = get_timestamp_sec (buf); + + /* store the request to memory */ + ent = &(memory_trace[memory_trace_index.head]); + strcpy (ent->line, buf); + ent->disk_index = disk_index; + + if (MAX_COMMAND_REPLY_DISTANCE ==1) { + ent->trace_status == NFS3ERR_RFS_MISS; + } else { + reply_buf=read_line(++disk_index); + RFS_ASSERT (reply_buf); + if (!strcmp(reply_buf, "missing_reply\n")) { + ent->trace_status == NFS3ERR_RFS_MISS; + } else { + ent->trace_status = find_reply_status(reply_buf); + } + }; + + if (ent->trace_status == NFS3ERR_RFS_MISS) + missing_reply_num ++; + + if (MAX_COMMAND_REPLY_DISTANCE > 1) { + if ((missing_reply_num > disk_index/5) && (missing_reply_num > 100)) { + printf ("missing_reply_num %d too high for disk_index %d\n", missing_reply_num, disk_index); + exit (0); + } + } + + /* find and store the reply trace fhandle for create-class requests */ + if (ent->trace_status==NFS3_OK) { + if (strstr(buf, "create") || strstr(buf, "mkdir") + || (strstr(buf, "symlink") && (buf[TRACE_VERSION_POS]!='2')) + || strstr(buf, "mknod") ) { + p = find_reply_trace_fh(reply_buf); + if (p==NULL) { + printf("skip line disk_index %d %s \n", disk_index, buf); + continue; + } + memcpy(ent->reply_trace_fh, p, TRACE_FH_SIZE); + } else + memset(ent->reply_trace_fh, 0, TRACE_FH_SIZE); + } + + add_to_dep_tab(memory_trace_index.head); + + if (((disk_index+1)%20000)==0) { + fprintf(stderr, "%d disk trace parsed \n", disk_index+1); + }; + }; + + end_profile (&read_trace_profile); + return TRACE_BUF_FULL; +} +#else /* not defined REDUCE_MEMORY_TRACE_SIZE */ +int read_trace () +{ + FILE * fp; + char buf[1024]; + // char * t=buf; + int disk_index=0; + + fp = fopen(trace_file, "r"); + RFS_ASSERT (fp!=NULL); + while (fgets(buf, MAX_TRACE_LINE_LENGTH, fp)) { + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("strlen(buf) %d buf %s \n", strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + + /* store the request to memory */ + strcpy (memory_trace[memory_trace_size].line, buf); + memory_trace[memory_trace_size].disk_index = disk_index; + memory_trace_size ++; + + if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) { + fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES); + break; + } + if ((disk_index%100000)==0) + fprintf(stderr, "%d disk trace parsed, store %d trace lines to memory\n", disk_index, memory_trace_size); + disk_index ++; + } + + fprintf(stderr, "total %d disk lines %d memory lines \n", disk_index, memory_trace_size ); +} +#endif + + +#ifdef REDUCE_MEMORY_TRACE_SIZE +inline int disk_index_to_memory_index (int disk_index) +{ + static int memory_index = 0; + + RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index)); + RFS_ASSERT (memory_trace[memory_trace_index.tail].disk_index <= disk_index); + RFS_ASSERT (memory_trace[CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size)].disk_index >=disk_index); + if (disk_index > memory_trace[memory_index].disk_index) { + while (memory_trace[memory_index].disk_index < disk_index) { + memory_index = CYCLIC_ADD(memory_index,1,memory_trace_index.size); + } + }; + if (disk_index < memory_trace[memory_index].disk_index) { + while (memory_trace[memory_index].disk_index > disk_index) { + memory_index = CYCLIC_MINUS(memory_index,1,memory_trace_index.size); + } + }; + + RFS_ASSERT (disk_index == memory_trace[memory_index].disk_index); + return memory_index; +} +#else +#define disk_index_to_memory_index(disk_index) disk_index +#endif + +#define get_line_by_disk_index(disk_index) \ + memory_trace[disk_index_to_memory_index(disk_index)].line + +inline char * find_reply_line (char * command_line, int cur_disk_index) +{ + int i; + char * line; + char * p; + int request_memory_index = disk_index_to_memory_index (cur_disk_index); + for (i=request_memory_index+1; i<request_memory_index+MAX_COMMAND_REPLY_DISTANCE && i<MAX_MEMORY_TRACE_LINES; i++) { + line = memory_trace[i].line; + if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&line[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&line[TRACE_MSGID_POS], &command_line[TRACE_MSGID_POS], p-&(line[TRACE_MSGID_POS]))) + return line; + } + } + return NULL; +} + +inline int find_reply_status (char * line) +{ + char * p; + int i=0; + + if (rfs_debug) + printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + if (!(line[TRACE_COMMAND_REPLY_FLAG_POS]=='R')) { + printf ("disk_index %d %s\n", disk_index, line); + }; + RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R'); + p = line+TRACE_MSGID_POS+2; /* at least one letter for msgid and one letter for space */ + if (strstr(p, "OK")) + return NFS3_OK; + if (strstr(p, "lookup 2") || strstr(p, "lookup 2")) + return 0x2; + if (strstr(p, "create d")) + return 0xd; + if (strstr(p, "setattr 1")) + return 0x1; + if (strstr(p, "setattr 2712")) /* 10002 NFS3ERR_NOT_SYNC */ + return 0x2712; + if (strstr(p, "lookup d")) + return 0xd; + if (strstr(p, "read d")) + return 0xd; + if (strstr(p, "write d")) + return 0xd; + if (strstr(p, "write 46")) + return 0x46; + if (strstr(p, "getattr 46")) + return 0x46; + if (strstr(p, "mkdir d")) + return 0xd; + printf ("line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + printf ("!!!!!!!!!!!!!!!!!!!!!!!!\n"); + fprintf (stderr, "line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + fprintf (stderr, "!!!!!!!!!!!!!!!!!!!!!!!!\n"); +} + +inline int find_reply_status_old (char * line) +{ + char * p; + int i=0; + + //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R'); + if (!strstr(line, "OK")) { + p=strstr(line, " 6 read "); + if (p) { + p+=strlen(" 6 read "); + } else { + p = strstr (line, "status=XXX"); + RFS_ASSERT (p); + p--; + RFS_ASSERT (*p==' '); + while (*p==' ') + p--; + while (*p!=' ') { + p--; + } + p++; + } + sscanf (p, "%x", &i); + if ((i<=0) || (i>10000)) + printf("line %s\n", line); + RFS_ASSERT (i>0 && i<10009); + } + return i; +} + +inline char * find_reply_trace_fh (char * line) +{ + char * p; + p = strstr (line, "OK fh"); + if (!p) { + printf ("disk_index %d find_reply_trace_fh line %s\n", disk_index, line); + return NULL; + } else + return p+6; +} + +#ifndef NO_DEPENDENCY_TABLE +inline int disk_index_to_dep_index(int cur_dep_index, int disk_index) +{ + int i; + for (i=cur_dep_index; i>min_dep_index; i--) { + if (dep_tab[i].disk_index == disk_index) + return i; + } + RFS_ASSERT (0); +} +#endif + +inline int is_play_candidate (int dep_index) +{ + int proc = dep_tab[dep_index].proc; + int status = dep_tab[dep_index].status; + int trace_status = dep_tab[dep_index].trace_status; + +#ifndef TAKE_CARE_CREATE_MODE_BY_DAN + /* for a failed create in trace, trace_replay just ignore many time the trace create fail + * due to access control, but trace_play will success because our access control + * may be loose (all uid/gid is mapped to single one 513:513, so we just skip these requests + */ + if ((proc==CREATE || proc==MKDIR) && (trace_status!=NFS3_OK) && (status!=NFS3ERR_RFS_MISS)) { + if (dependency_debug) + printf ("disk[%d] ignore failed create/mkdir in trace, trace_status %d line %s", + dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line); + failed_create_command_num ++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_OTHER_FAILED_COMMAND + if (((trace_status == NFS3ERR_ACCES) && (proc==READ || proc==WRITE || proc==LOOKUP)) || + ((trace_status == NFS3ERR_PERM) && (proc==SETATTR)) ){ + if (dependency_debug) + printf ("disk[%d] ignore other failed command in trace, trace_status %d line %s", + dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line); + + failed_other_command_num ++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_SYMBOLIC_LINK + if ((dep_tab[dep_index].proc==READLINK) ) { /* send request */ + skipped_readlink_command_num ++; + return FALSE; + } +#endif +/* This is actually take care in get_nextop by checking fh_map error when dep_index==min_dep_index */ +#ifndef TAKE_CARE_CUSTOM_COMMAND + /* this line has a file handle which should belong to discard but it is not + * the file handle directly appears as parent directory in a lookup request + * the return value is NOENT, the parent directory should have been initialized + * but the initialization code just ignored all lookup request which didn't success + * including NOENT even though the parent directory is still valid. + */ +/* + if (( ((dep_tab[dep_index].disk_index==262213) || (dep_tab[dep_index].disk_index==214402)) + && !(strcmp(trace_file, "anon-lair62-011130-1100.txt")) + ) || + ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470)) + && !(strcmp(trace_file, "anon-lair62-011130-1000.txt")) + )) { + skipped_custom_command_num++; + return FALSE; + } +*/ + if (( ((dep_tab[dep_index].disk_index==423727) || (0)) + && !(strncmp(trace_file, "anon-lair62-011130-1500.txt", strlen("anon-lair62-011130-1500.txt"))) + ) || + ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470)) + && !(strcmp(trace_file, "anon-lair62-011130-1000.txt")) + )) { + skipped_custom_command_num++; + return FALSE; + } + /* this line is about the mkdir 116d9d originally in anon-lair62-011130-1400.txt */ + if (!strncmp(dep_tab[dep_index].line, "1007147245.194201", strlen("1007147245.194201"))) { + skipped_custom_command_num++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_FSSTAT_COMMAND + /* the file handle used in this command is not processed properly by pre-processing */ + if (proc==FSSTAT) { + char * trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line); + fh_map_t * fh = lookup_fh (trace_fh); + if (!fh) { + skipped_fsstat_command_num++; + return FALSE; + } + } +#endif + return TRUE; +} + +inline int is_dir_op (int proc) +{ + switch (proc) { + case MKDIR: + case CREATE: + case LINK: + case SYMLINK: + case MKNOD: + case REMOVE: + case RMDIR: + case RENAME: + return 1; + default: + return 0; + } +} + +inline int is_create_op (int proc) +{ + if (proc==CREATE || proc==MKDIR || proc==LINK || proc==SYMLINK || proc==MKNOD || proc==RENAME) + return 1; + return 0; +} + +inline int is_delete_op (int proc) +{ + if (proc==REMOVE || proc==RMDIR || proc==RENAME) + return 1; + return 0; +} + +static inline char * find_lead_trace_fh(int proc, char * line) +{ + char * p; + /* check the file handle availability */ + p = strstr (line, "fh"); + RFS_ASSERT (p); + p+=3; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line); + return p; +} + +inline char * find_another_trace_fh(int proc, char * line) +{ + char * p; + /* check the file handle availability */ + p = strstr (line, "fh2"); + RFS_ASSERT (p); + p+=4; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line); + return p; +} + +/* return the index of next request in dep_tab. + * Return -1 if there is no suitable request to send + */ +inline int get_nextop(void) +{ + int i,j, k; + int * t; + static int dep_index = -2; + char * line; + char * p; +#define INIT_MIN_WAIT_VALUE -999 + static int min_wait_fhandle_dep_index = INIT_MIN_WAIT_VALUE; + int proc; + int flag; + + if (min_wait_fhandle_dep_index == -999) + min_wait_fhandle_dep_index = dep_window_index.head; + + for (i=0; i<CYCLIC_NUM(dep_window_index); i++) { + dep_index = (dep_window_index.tail+i) % dep_window_index.size; + + proc = dep_tab[dep_index].proc; + flag = dep_tab[dep_index].flag; + + if (dependency_debug) + printf ("get_nextop check dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); +#ifdef NO_DEPENDENCY_TABLE + if (dep_tab[dep_index].flag == DEP_FLAG_INIT) { + if (is_play_candidate(dep_index)==TRUE) { + /* the trace_fh is the file handle for the operation directory, trace_fh_2 is other file handle + * used in the request */ + if (proc==LINK || proc==RENAME) { + dep_tab[dep_index].trace_fh = find_another_trace_fh (proc, dep_tab[dep_index].line); + dep_tab[dep_index].trace_fh_2 = find_lead_trace_fh(proc, dep_tab[dep_index].line); + dep_tab[dep_index].fh = 0; + dep_tab[dep_index].fh_2 = 0; + } else { + dep_tab[dep_index].trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line); + dep_tab[dep_index].fh = 0; + dep_tab[dep_index].fh_2 = (fh_map_t *)1; + }; + dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE; +#ifdef TIME_PLAY + dep_tab[dep_index].skip_sec = skip_sec; +#endif + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_CANDIDATE\n", dep_tab[dep_index].disk_index); + } else { + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_DONE; + continue; + } + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE) ) { + + if (!dep_tab[dep_index].fh) + dep_tab[dep_index].fh = lookup_fh (dep_tab[dep_index].trace_fh); + if (!dep_tab[dep_index].fh_2) + dep_tab[dep_index].fh_2 = lookup_fh (dep_tab[dep_index].trace_fh_2); + + /* need to wait for file handle */ + if ((!dep_tab[dep_index].fh) || (!dep_tab[dep_index].fh_2)) { + if (dependency_debug) + printf("disk[%d] can not lookup file handle\n", dep_tab[dep_index].disk_index); + if (dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) { + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_CANDIDATE to DEP_FLAG_WAIT_FHANDLE\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_FHANDLE; + sfs_gettime (&dep_tab[dep_index].start); + if (CYCLIC_LESS(dep_tab_index,dep_index,min_wait_fhandle_dep_index)) + min_wait_fhandle_dep_index = dep_index; + } else { + struct ladtime tmp; + if (dep_index==dep_window_index.tail) { + if (!profile_debug) + printf ("fh_path_map error disk[%d] state DEP_FLAG_WAIT_FHANDLE to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index); + fh_path_map_err_num ++; + dep_tab[dep_index].flag = DEP_FLAG_DONE; + continue; + } + sfs_gettime (&tmp); + SUBTIME (tmp, dep_tab[dep_index].start); +#define DEPENDENCY_TIMEOUT 50 +#ifdef TIME_PLAY + RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT + (skip_sec - dep_tab[dep_index].skip_sec)); +#else + if (tmp.sec >= DEPENDENCY_TIMEOUT) { + printf("dep_tab[%d].flag %d disk_index %d line %s\n", dep_index, + dep_tab[dep_index].flag, dep_tab[dep_index].disk_index, + dep_tab[dep_index].line); + } + RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT ); +#endif + } + continue; + } + + /* file handle ready, adjust_min_wait_fhandle_dep_index */ + if ((dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE)) { + if (dep_index == min_wait_fhandle_dep_index) { + min_wait_fhandle_dep_index = dep_window_index.head; + for (j=CYCLIC_ADD(dep_index,1,dep_window_index.size); CYCLIC_LESS(dep_window_index,j,dep_window_index.head); j++) { + if (dep_tab[j].flag ==DEP_FLAG_WAIT_FHANDLE) { + min_wait_fhandle_dep_index = j; + break; + } + } + } + } + if (dependency_debug) + printf("disk[%d] found file handle\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_FHANDLE_READY; + + /* the normal file operation can be executed now */ + if (!is_dir_op (dep_tab[dep_index].proc)) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + + if (dependency_debug) + printf("disk[%d] directory operation \n", dep_tab[dep_index].disk_index); + /* the directory operation need to lock the directory first */ + if (dep_tab[dep_index].fh->lock) { + if (dependency_debug) + printf ("disk[%d] state %d to DEP_FLAG_WAIT_DIRECTORY\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_DIRECTORY; + continue; + } + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_FHANDLE_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DIRECTORY)) { + int j = dep_tab[dep_index].fh - fh_map; + if (dependency_debug) { + printf ("dep_tab[%d].disk_index %d, fh_map[%d] lock=%d\n",dep_index, dep_tab[dep_index].disk_index, j, dep_tab[dep_index].fh->lock); + printf ("trace_fh %s path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path); + printf ("trace_fh %s path %s\n", fh_map[j].trace_fh, fh_map[j].path); + } + if ((dep_tab[dep_index].fh->lock) || ((proc==RENAME) && (dep_tab[dep_index].fh_2->lock)) ) { + if (dependency_debug) + printf ("continue to wait for directory lock\n"); + continue; + } + if (dependency_debug) + printf ("dep_tab[%d] disk index %d LOCK fh_map[%d] \n", dep_index, dep_tab[dep_index].disk_index, j); + dep_tab[dep_index].fh->lock = 1; + if (proc==RENAME) + dep_tab[dep_index].fh_2->lock = 1; + + /* the non-delete directory operation can proceed now */ + if (!is_delete_op (dep_tab[dep_index].proc)) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + + /* the delete operation can proceed if nobody ahead is waiting for fhandle */ + /* probably this condition is not strong enough */ +// if ((min_wait_fhandle_dep_index<dep_index) ) { + if (dep_index!=dep_window_index.tail) { + if (dependency_debug) + printf ("disk[%d] state %d to DEP_FLAG_WAIT_DELETE\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_DELETE; + continue; + } + dep_tab[dep_index].flag = DEP_FLAG_DIRECTORY_READY; + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_DIRECTORY_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DELETE)) { +// if (min_wait_fhandle_dep_index > dep_index) { + if (dep_index==dep_window_index.tail) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + } +#else /*NO_DEPENDENCY_TABLE undefined */ + /* this part of code will be invalid after CYCLIC buffer design */ + if (dep_tab[dep_index].flag == DEP_FLAG_INIT){ + for (j=0, t=&(dep_tab[dep_index].dep_ops[0]); + (j<dep_tab[dep_index].init_dep_num) && (dep_tab[dep_index].cur_dep_num>0); + j++, t++) { + if (*t !=-1) { + if (dep_tab[disk_index_to_dep_index(dep_index, *t)].flag == DEP_FLAG_DONE) { + /* The depended request has been finished */ + *t = -1; + dep_tab[dep_index].cur_dep_num --; + } + } + } + + if (dep_tab[dep_index].cur_dep_num == 0) { + return dep_index; + } + } +#endif + } + + if (dependency_debug) + printf ("get_nexop return -1\n"); + return -1; +} + +int check_timeout(void) +{ + static int biod_index = 0; + int i; + int dep_index; /* index into dep_tab */ + int proc; + sfs_op_type *op_ptr; /* per operation info */ + struct ladtime timeout; + struct ladtime current_time; + + sfs_gettime (¤t_time); + + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + if (biod_reqp[biod_index].in_use==TRUE) { + timeout = biod_reqp[biod_index].timeout; + if ((current_time.sec>timeout.sec) || + ((current_time.sec==timeout.sec) && (current_time.usec>timeout.usec))) { + + dep_index = biod_reqp[biod_index].dep_tab_index; + proc = dep_tab[dep_index].proc; + op_ptr = &Ops[proc]; + op_ptr->results.timeout_calls++; + Ops[TOTAL].results.timeout_calls++; + + + if (is_create_op(proc)) { + dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE; + printf ("resend dep_tab[%d], disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); + finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT, DEP_FLAG_CANDIDATE); + } else { + finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT, DEP_FLAG_DONE); + } + timeout_num ++; + num_out_reqs_statistics_at_timeout[num_out_reqs]++; + + //RFS_ASSERT (!is_create_op(proc)); + + if (per_packet_debug) + printf ("timeout request: disk_index %d, dep_index %d biod_reqp[%d].start %d:%d timeout %d:%d current %d:%d\n", dep_tab[dep_index].disk_index, dep_index, biod_index, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, timeout.sec, timeout.usec, current.sec, current.usec); + } + } + } +} + +/* Allocate a biod_req entry to send and receive request dep_tab[dep_index] + * build the cross reference between dep_tab entry and biod_req entry + */ +struct biod_req * get_biod_req(int dep_index) /* index into dep_tab */ +{ + static int biod_index = 0; + int i; + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + if (!biod_reqp[biod_index].in_use) { + biod_reqp[biod_index].in_use = 1; + biod_reqp[biod_index].dep_tab_index = dep_index; + dep_tab[dep_index].biod_req_index = biod_index; + num_out_reqs++; + return &(biod_reqp[biod_index]); + } + } + return NULL; +} + +/* Return index into biod_reqp + * return -1 upon failure + */ +int lookup_biod_req (int xid) +{ + static int biod_index = 0; + int i; + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + /* give a NULL as timeout pointer may cause indefinitely block */ + if ((biod_reqp[biod_index].in_use == TRUE) &&( biod_reqp[biod_index].xid == xid)) { + return biod_index; + } + } + return -1; +} + +extern struct ladtime test_start; +void init_time_offset(void) +{ + struct ladtime tmp1; + struct ladtime tmp2; + + test_start.sec = 0; + test_start.usec = 0; + sfs_gettime (&tmp1); /* called at initial time: tmp1 = play_starttime */ +#ifdef SPEED_UP + DIVTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime / SCALE */ +#endif +#ifdef SLOW_DOWN + MULTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime * SCALE */ +#endif + + tmp2 = trace_starttime; /* tmp2 = trace_starttime */ + SUBTIME (tmp2, tmp1); /* tmp2 = trace_starttime - play_starttime *|/ SCALE */ + time_offset = tmp2; /* time_offset = trace_starttime - play_starttime *|/ SCALE */ +} + +/* initialize timestamp and proc field of dep_tab entry */ +void init_dep_tab_entry (int dep_index) +{ + char * line; + int version; + int nfsproc; + int msgid; + + //line = get_line_by_disk_index (dep_tab[dep_index].disk_index); + line = dep_tab[dep_index].line; + sscanf (line, "%d.%d", &(dep_tab[dep_index].timestamp.tv_sec), &(dep_tab[dep_index].timestamp.tv_usec)); + sscanf (&line[TRACE_MSGID_POS], "%x %x", &msgid, &nfsproc); + //printf ("msgid %x nfsproc %x\n", msgid, nfsproc); + if (line[TRACE_VERSION_POS]=='2') { + dep_tab[dep_index].proc = nfs2proc_to_rfsproc[nfsproc]; + RFS_ASSERT (nfsproc <18); + } else { + /* This is for debug purpose */ + if (line[TRACE_VERSION_POS] !='3') { + fprintf(stderr, "line[TRACE_VERSION_POS] %c line %s\n", line[TRACE_VERSION_POS], line); + line = get_line_by_disk_index (dep_tab[dep_index].disk_index-1); + if (!line) + line = get_line_by_disk_index (dep_tab[dep_index].disk_index-2); + RFS_ASSERT (line); + fprintf(stderr, "previousline %s\n", line); + } + RFS_ASSERT (line[TRACE_VERSION_POS] =='3'); + if (nfsproc >= NFS3_PROCEDURE_COUNT) { + fprintf(stderr, "proc %d line %s\n", nfsproc, line); + + } + RFS_ASSERT (nfsproc <NFS3_PROCEDURE_COUNT); + dep_tab[dep_index].proc = nfs3proc_to_rfsproc[nfsproc]; + } + RFS_ASSERT (dep_tab[dep_index].proc >= 0 && dep_tab[dep_index].proc < NOPS); + dep_tab[dep_index].flag = DEP_FLAG_INIT; +} + +void adjust_play_window (int flag, int * poll_timeout_arg) +{ + struct ladtime max_window_time; + static struct ladtime max_poll_time = {0, 2000, 0}; + struct ladtime t; + int i; + char * line; + cyclic_index_t old_dep_window_index = dep_window_index; + +#ifdef notdef + printf ("^^^^^^^^^^^^^^^ adjust_play_window, begin\n"); + CYCLIC_PRINT (dep_tab_index); + printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index); + CYCLIC_PRINT (dep_window_index); + CYCLIC_PRINT (memory_trace_index); + printf (" adjust_play_window, begin\n"); +#endif + + while ((!CYCLIC_EMPTY(dep_window_index)) && (dep_tab[dep_window_index.tail].flag == DEP_FLAG_DONE)) { +#ifdef notdef + //CYCLIC_PRINT (memory_trace_index); + //printf("MOVE_TAIL_TO memory_index %d\n", dep_tab[dep_tab_index.tail].memory_index); + RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index)); + RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head)); + printf("%d is done\n", dep_window_index.tail); +#endif + CYCLIC_MOVE_TAIL(dep_tab_index); + CYCLIC_MOVE_TAIL(dep_window_index); + +#ifdef notdef + CYCLIC_PRINT (dep_tab_index); + CYCLIC_PRINT (dep_window_index); + + if (! (dep_tab_index.tail == dep_window_index.tail)) { + CYCLIC_PRINT(dep_tab_index); + CYCLIC_PRINT(dep_window_index); + }; + RFS_ASSERT ( dep_tab_index.tail == dep_window_index.tail); +#endif + + if (!CYCLIC_EMPTY(dep_tab_index)) { +#ifdef notdef + RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index)); + if (!(CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head))) { + CYCLIC_PRINT(memory_trace_index); + CYCLIC_PRINT(dep_tab_index); + printf("dep_tab[head-1].memory_index, %d [tail].memory_index %d\n", + dep_tab[CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size)].memory_index, + dep_tab[dep_tab_index.tail].memory_index); + } + RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head)); +#endif + CYCLIC_SET_TAIL_TO(&memory_trace_index, dep_tab[dep_tab_index.tail].memory_index); + //printf ("set memory_trace_index to %d=%d, dep_tab_index.tail %d\n", memory_trace_index.tail, dep_tab[dep_tab_index.tail].memory_index, dep_tab_index.tail); + } else { + // CYCLIC_MOVE_TAIL (memory_trace_index); + } + } + + while (CYCLIC_EMPTY(dep_tab_index)) { + + if (disk_io_status == TRACE_FILE_END) + return; + else { + //printf ("************** ADJUST_PLAY_WINDOW sleep 1 s\n"); + //print_cyclic_buffers(); + //pthread_yield(); + //usleep (1000); + } + } + + /* max_trace_window_time = current *|/ SCALE + trace_starttime */ + sfs_gettime (¤t); + +#ifdef TIME_PLAY +#ifdef SPEED_UP + MULTIME (current, PLAY_SCALE); +#endif +#ifdef SLOW_DOWN + DIVTIME (current, PLAY_SCALE); +#endif + ADDTIME (current, trace_starttime); + max_window_time = current; + + /* Right now it is not clear how to deal with the situation where MAX_PLAY_WINDOW is reached */ + if (CYCLIC_NUM(dep_window_index) == MAX_PLAY_WINDOW) { + //printf ("can not catch up the speed, dep_tab_size %d dep_window_max %d reach min_dep_index %d+MAX_PLAY_WINDOW\n", dep_tab_size, dep_window_max, min_dep_index); + //printf ("."); + can_not_catch_speed_num ++; + } + while ((CYCLIC_NUM(dep_window_index) < MAX_PLAY_WINDOW) && + (CYCLIC_NUM(dep_window_index) < CYCLIC_NUM(dep_tab_index)) ) { + struct ladtime t; + int dep_index = (dep_window_index.tail+i) % dep_window_index.size; + t.sec = dep_tab[dep_index].timestamp.tv_sec; + t.usec = dep_tab[dep_index].timestamp.tv_usec; + if ((t.sec>max_window_time.sec)||(t.sec==max_window_time.sec && t.usec>max_window_time.usec)) + break; + CYCLIC_MOVE_HEAD(dep_window_index); + } +#else + ADDTIME (current, trace_starttime); + max_window_time = current; + while ((CYCLIC_NUM(dep_window_index) < MAX_PLAY_WINDOW) && + (CYCLIC_NUM(dep_window_index) < CYCLIC_NUM(dep_tab_index)) ) { + CYCLIC_MOVE_HEAD(dep_window_index); + } +#endif + + if (flag == BUSY) + *poll_timeout_arg = 0; + else if (CYCLIC_NUM(dep_window_index)==CYCLIC_NUM(dep_tab_index)) { + *poll_timeout_arg = 1000; /* poll_timeout set to 1 second for the last request */ + } else { +#ifdef TIME_PLAY + struct ladtime tmp; + struct ladtime tmp1; + tmp.sec = dep_tab[dep_window_index.head].timestamp.tv_sec; + tmp.usec = dep_tab[dep_window_index.head].timestamp.tv_usec; + if (adjust_play_window_debug>=2) + printf ("dep_tab[dep_window_index.head %d].timestamp %d:%d, max_window_time %d:%d\n", + dep_window_index.head, tmp.sec, tmp.usec, max_window_time.sec, max_window_time.usec); + + SUBTIME (tmp, max_window_time); +#ifdef SPEED_UP + DIVTIME (tmp, PLAY_SCALE); +#endif +#ifdef SLOW_DOWN + MULTIME (tmp, PLAY_SCALE); +#endif +/* + tmp1 = tmp; + + if (tmp.sec > max_poll_time.sec) { + + if (rfs_debug) + printf ("dep_tab[%d].timestamp %d:%d, max_window_time %d:%d\n", + dep_window_max, dep_tab[dep_window_max].timestamp.tv_sec, dep_tab[dep_window_max].timestamp.tv_usec, max_window_time.sec, max_window_time.usec); + printf ("skip %d seconds\n", tmp.sec-max_poll_time.sec); + SUBTIME (tmp, max_poll_time); + tmp.usec = 0; + skip_sec += tmp.sec; + SUBTIME (test_start, tmp); + tmp = max_poll_time; + } +*/ + + //RFS_ASSERT ((tmp.sec < 1000)); + if (tmp.sec > 1000) + tmp.sec = 1000; + if ((tmp.sec ==0) && (tmp.usec==0)) { + *poll_timeout_arg = 0; + } else + *poll_timeout_arg = tmp.sec*1000000+tmp.usec; +#else + /* + struct ladtime tmp; + struct ladtime tmp1; + tmp.sec = dep_tab[dep_window_max].timestamp.tv_sec; + tmp.usec = dep_tab[dep_window_max].timestamp.tv_usec; + tmp1.sec = dep_tab[dep_window_max-1].timestamp.tv_sec; + tmp1.usec = dep_tab[dep_window_max-1].timestamp.tv_usec; + SUBTIME (tmp, tmp1); + RFS_ASSERT ((tmp.sec < 1000)); + RFS_ASSERT ((tmp.sec>0) || ((tmp.sec==0) && (tmp.usec>0))); + *poll_timeout = tmp.sec*1000000+tmp.usec; + */ + + *poll_timeout_arg = 100000; +#endif + } + if (rfs_debug) + printf ("adjust_play_window: flag %d min %d -> %d, max %d -> %d poll_timeout_arg %d \n", + flag, old_dep_window_index.tail, dep_window_index.tail, old_dep_window_index.head, + dep_window_index.head, *poll_timeout_arg); + +#ifdef notdef + printf ("^^^^^^^^^^^^^^^ adjust_play_window, end\n"); + CYCLIC_PRINT (dep_tab_index); + printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index); + CYCLIC_PRINT (dep_window_index); + CYCLIC_PRINT (memory_trace_index); + printf (" adjust_play_window, end\n\n"); +#endif + //CYCLIC_ASSERT(4); +} + +/* poll for usecs and receive, after receive one reply, + * return index in biod_reqp of the corresponding request + */ +int poll_and_get_reply (int usecs) +{ + int biod_index = -1; + int xid; + int error; + struct timeval zero_time = {0, 0}; /* Immediately return */ + +#ifdef RECV_THREAD + //printf("recv thread waitsem 1\n"); + waitsem (async_rpc_sem); + //printf("recv thread got sem 1\n"); +#endif + do { + error = biod_poll_wait (NFS_client, usecs); + switch (error) { + case -1: + if (errno == EINTR) { + error = 1; + continue; + } + if (rfs_debug) { + (void) fprintf(stderr, "biod_poll_wait error\n"); + perror (""); + (void) fflush(stderr); + } + break; + case 0: + break; + default: +#ifdef UDP + //printf("recv thread waitsem 2\n"); + //waitsem (async_rpc_sem); + //printf("recv thread got sem 2\n"); + error = get_areply_udp (NFS_client, &xid, &zero_time); + //postsem (async_rpc_sem); + //printf("recv thread postsem 2\n"); + // RFS_ASSERT (error!= RPC_TIMEOUT); /* we have polled and know there is data */ + // RFS_ASSERT (error!= RPC_CANTRECV); + RFS_ASSERT (error == RPC_SUCCESS); + + biod_index = lookup_biod_req (xid); + sfs_gettime (&(biod_reqp[biod_index].stop)); +#else + RFS_ASSERT (0); +#endif + } + } while (0); +#ifdef RECV_THREAD + postsem (async_rpc_sem); + //printf("recv thread postsem 1\n"); +#endif + return biod_index; +} + +void print_result(void) +{ + int i, j; + struct ladtime t; + int dep_index; + int avg_msecs; + unsigned long long tmp; + int avg_usecs; + + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stdout, "trace play result:\n"); + (void) fprintf(stdout, "\t percentage good_cnt bad_cnt timeout_cnt\telapsed time\t\t\taverage time\n"); + for (i=0; i<NOPS+1; i++) { + if (Ops[i].results.good_calls==0) { + avg_msecs = 0; + avg_usecs = 0; + } else { + tmp = Ops[i].results.time.sec*1000000 + Ops[i].results.time.usec; + avg_msecs = 0; + avg_usecs = tmp/Ops[i].results.good_calls; +/* + avg_msecs = (Ops[i].results.time.sec*1000 + Ops[i].results.time.usec/1000)/Ops[i].results.good_calls; + avg_usecs = (Ops[i].results.time.usec%1000)/Ops[i].results.good_calls; +*/ + } + (void) fprintf(stdout, "%11s\t%4.1f\t%4d\t%4d\t%4d\t\tsec %8d usec %8d \tusec %8d\n", + Ops[i].name, + (float)(100*Ops[i].results.good_calls)/(float)Ops[TOTAL].results.good_calls, + Ops[i].results.good_calls, Ops[i].results.bad_calls, Ops[i].results.timeout_calls, + Ops[i].results.time.sec, Ops[i].results.time.usec, avg_msecs*1000+avg_usecs); + } + (void) fflush (stdout); + } + +#if 0 /* commented out by G. Jason Peng */ + RFS_ASSERT (read_data_owe_GB==0); + printf("read_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",read_data_total_GB, read_data_total, read_data_owe_GB, read_data_owe, (read_data_owe)/(read_data_total/100), read_data_adjust_times); + printf("write_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",write_data_total_GB, write_data_total, write_data_owe_GB, write_data_owe, (write_data_owe)/(write_data_total/100), write_data_adjust_times); + printf("poll_timeout_0_num %d poll_timeout_pos_num %d\n", poll_timeout_0_num, poll_timeout_pos_num); + printf("failed_create_command_num_in_original_trace %d\nfailed_other_command_num_in_original_trace %d\nskipped_readlink_command_num %d\nskipped_custom_command_num %d\nfh_path_map_err_num %d\nskipped_fsstat_command_num %d\nmissing_reply_num %d\nrename_rmdir_noent_reply_num %d\nrmdir_not_empty_reply_num %d\nloose_access_control_reply_num %d\nlookup_err_due_to_rename %d\nlookup_err_due_to_parallel_remove %d\nlookup_eaccess_enoent_mismatch %d\nread_io_err_num %d\nstale_fhandle_err_num %d abnormal_EEXIST_num %d abnormal_ENOENT_num %d proper_reply_num %d run_stage_proper_reply_num %d\n", + failed_create_command_num, + failed_other_command_num, + skipped_readlink_command_num, + skipped_custom_command_num, + fh_path_map_err_num, + skipped_fsstat_command_num, + missing_reply_num, + rename_rmdir_noent_reply_num, + rmdir_not_empty_reply_num, + loose_access_control_reply_num, + lookup_err_due_to_rename_num, + lookup_err_due_to_parallel_remove_num, + lookup_eaccess_enoent_mismatch_num, + read_io_err_num, + stale_fhandle_err_num, + abnormal_EEXIST_num, + abnormal_ENOENT_num, + proper_reply_num, run_stage_proper_reply_num); +#endif + +// print_dump(Client_num, Child_num); +} + +/* + * allocate and initialize client handles + */ +static int +init_rpc(void) +{ + int dummy = 0; + + /* + * Set up the client handles. We get them all before trying one + * out to insure that the client handle for LOOKUP class is allocated + * before calling op_getattr(). + */ + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname); + } + + NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent, + (uint32_t) NFS_PROGRAM, + (uint32_t) nfs_version, + RPC_ANYSOCK, &Nfs_timers[0]); + + if (NFS_client == ((CLIENT *) NULL)) { + return(-1); + } + + /* + * create credentials using the REAL uid + */ + NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid, + (int)Cur_gid, 0, NULL); + + if (biod_init(dummy, dummy) == -1) { + return(-1); + } + + return(0); +} /* init_rpc */ + +void +init_counters(void) +{ + uint_t i; + uint_t start_msec; + + /* Ready to go - initialize operation counters */ + for (i = 0; i < NOPS + 1; i++) { + Ops[i].req_cnt = 0; + Ops[i].results.good_calls = 0; + Ops[i].results.bad_calls = 0; + Ops[i].results.timeout_calls = 0; // RFS + Ops[i].results.fast_calls = 0; + Ops[i].results.time.sec = 0; + Ops[i].results.time.usec = 0; + Ops[i].results.msec2 = 0; + } + + /* initialize timers and period variables */ + sfs_gettime(&Starttime); + Cur_time = Starttime; + start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000); + Previous_chkpnt_msec = start_msec; + Calls_this_period = 0; + Reqs_this_period = 0; + Sleep_msec_this_period = 0; + Calls_this_test = 0; + Reqs_this_test = 0; + Sleep_msec_this_test = 0; +} + +static char * +nfs3_strerror(int status) +{ + static char str[40]; + switch (status) { + case NFS3_OK: + (void) strcpy(str, "no error"); + break; + case NFS3ERR_PERM: + (void) strcpy(str, "Not owner"); + break; + case NFS3ERR_NOENT: + (void) strcpy(str, "No such file or directory"); + break; + case NFS3ERR_IO: + (void) strcpy(str, "I/O error"); + break; + case NFS3ERR_NXIO: + (void) strcpy(str, "No such device or address"); + break; + case NFS3ERR_ACCES: + (void) strcpy(str, "Permission denied"); + break; + case NFS3ERR_EXIST: + (void) strcpy(str, "File exists"); + break; + case NFS3ERR_XDEV: + (void) strcpy(str, "Cross-device link"); + break; + case NFS3ERR_NODEV: + (void) strcpy(str, "No such device"); + break; + case NFS3ERR_NOTDIR: + (void) strcpy(str, "Not a directory"); + break; + case NFS3ERR_ISDIR: + (void) strcpy(str, "Is a directory"); + break; + case NFS3ERR_INVAL: + (void) strcpy(str, "Invalid argument"); + break; + case NFS3ERR_FBIG: + (void) strcpy(str, "File too large"); + break; + case NFS3ERR_NOSPC: + (void) strcpy(str, "No space left on device"); + break; + case NFS3ERR_ROFS: + (void) strcpy(str, "Read-only file system"); + break; + case NFS3ERR_MLINK: + (void) strcpy(str, "Too many links"); + break; + case NFS3ERR_NAMETOOLONG: + (void) strcpy(str, "File name too long"); + break; + case NFS3ERR_NOTEMPTY: + (void) strcpy(str, "Directory not empty"); + break; + case NFS3ERR_DQUOT: + (void) strcpy(str, "Disc quota exceeded"); + break; + case NFS3ERR_STALE: + (void) strcpy(str, "Stale NFS file handle"); + break; + case NFS3ERR_REMOTE: + (void) strcpy(str, "Object is remote"); + break; + case NFS3ERR_BADHANDLE: + (void) strcpy(str, "Bad file handle"); + break; + case NFS3ERR_NOT_SYNC: + (void) strcpy(str, "Not sync write"); + break; + case NFS3ERR_BAD_COOKIE: + (void) strcpy(str, "Bad cookie"); + break; + case NFS3ERR_NOTSUPP: + (void) strcpy(str, "Operation not supported"); + break; + case NFS3ERR_TOOSMALL: + (void) strcpy(str, "Value too small"); + break; + case NFS3ERR_SERVERFAULT: + (void) strcpy(str, "Server fault"); + break; + case NFS3ERR_BADTYPE: + (void) strcpy(str, "Bad type"); + break; + case NFS3ERR_JUKEBOX: + (void) strcpy(str, "Jukebox"); + break; + case NFS3ERR_RFS_TIMEOUT: + (void) strcpy(str, "Timeout"); + break; + default: + (void) sprintf(str, "Unknown status %d", status); + break; + } + return (str); +} + +/* + * Check the gettimeofday() resolution. If the resolution + * is in chunks bigger than SFS_MIN_RES then the client + * does not have a usable resolution for running the + * benchmark. + */ +static void +check_clock(void) +{ + double time_res; + char tmp_hostname[HOSTNAME_LEN]; + + time_res = get_resolution(); + getmyhostname(tmp_hostname, HOSTNAME_LEN); + if( time_res > (double)SFS_MIN_RES ) + { + (void) fprintf(stderr, + "\n%s: Clock resolution too poor to obtain valid results.\n", + tmp_hostname); + (void) fprintf(stderr, + "%s: Clock resolution %f Micro seconds.\n", tmp_hostname, + time_res); + exit(175); + } + else + { + (void) fprintf(stderr, + "\n%s: Good clock resolution [ %f ] Micro seconds.\n", + tmp_hostname, time_res); + } +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns the resolution of the gettimeofday() function + * in microseconds. + */ +static double +get_resolution(void) +{ + double starttime, finishtime, besttime; + long j,delay; + int k; + + finishtime=time_so_far1(); /* Warm up the instruction cache */ + starttime=time_so_far1(); /* Warm up the instruction cache */ + delay=j=0; /* Warm up the data cache */ + for(k=0;k<10;k++) + { + while(1) + { + starttime=time_so_far1(); + for(j=0;j< delay;j++) + ; + finishtime=time_so_far1(); + if(starttime==finishtime) + delay++; + else + { + if(k==0) + besttime=(finishtime-starttime); + if((finishtime-starttime) < besttime) + besttime=(finishtime-starttime); + break; + } + } + } + return(besttime); +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns current result of gettimeofday() in microseconds. + */ +/************************************************************************/ +/* Time measurement routines. */ +/* Return time in microseconds */ +/************************************************************************/ + +static double +time_so_far1(void) +{ + /* For Windows the time_of_day() is useless. It increments in 55 */ + /* milli second increments. By using the Win32api one can get */ + /* access to the high performance measurement interfaces. */ + /* With this one can get back into the 8 to 9 microsecond */ + /* resolution. */ +#ifdef Windows + LARGE_INTEGER freq,counter; + double wintime; + double bigcounter; + + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&counter); + bigcounter=(double)counter.HighPart *(double)0xffffffff + + (double)counter.LowPart; + wintime = (double)(bigcounter/(double)freq.LowPart); + return((double)wintime*1000000.0); +#else +#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5) + struct timespec gp; + + if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1) + perror("getclock"); + return (( (double) (gp.tv_sec)*1000000.0) + + ( ((float)(gp.tv_nsec)) * 0.001 )); +#else + struct timeval tp; + + if (gettimeofday(&tp, (struct timezone *) NULL) == -1) + perror("gettimeofday"); + return ((double) (tp.tv_sec)*1000000.0) + + (((double) tp.tv_usec) ); +#endif +#endif +} + +static void +usage(void) +{ + fprintf(stderr, "trace play usage"); +} +extern void init_file_system (void) +{ + return; +} + +void show_fhandle (nfs_fh3 * fhp) +{ + struct knfs_fh * kfhp = (struct knfs_fh *)fhp; + + int dev; + + if (quiet_flag) + return; + + RFS_ASSERT (kfhp->fh_version == 1); + RFS_ASSERT (kfhp->fh_fsid_type == 0); + RFS_ASSERT (kfhp->fh_auth_type == 0); + + dev = ntohs(kfhp->fh_dev_major); + dev = dev<<8; + dev = dev + ntohs(kfhp->fh_dev_minor); + + /* kfhp->fh_dev_ino hold the inode number of export point of the mounted + * file system. For example, if /tmp/t1 is exported, /tmp/t1/t2 is mounted, + * then fh_dev_ino hold the inode number of t1, not t2 + */ + + switch (kfhp->fh_fileid_type) { + case 0: + printf("fh:type 0 root dev 0x%x dev_ino %d\n", dev, kfhp->fh_dev_ino); + break; + case 1: + printf("fh:type 1 %d %x dev %x dev_ino %x\n", + kfhp->fh_ino, kfhp->fh_generation, dev, kfhp->fh_dev_ino); + break; + case 2: + printf("fh:type2 %d %x dirino %d dev 0x%x dev_ino %x\n", + kfhp->fh_ino, kfhp->fh_generation, kfhp->fh_dirino, dev, kfhp->fh_dev_ino); + break; + default: + RFS_ASSERT (0); + } +} + +nfs_fh3 zero_fhandle; +int init_fh_map () +{ + memset (fh_map, 0, sizeof (fh_map)); + memset(fh_htable, 0, sizeof (fh_htable)); + memset (&zero_fhandle, 0, sizeof(nfs_fh3)); + printf ("SIZE of fh map %d KB\n", sizeof (fh_map)/1000); + fh_i = 0; +} + +int add_fh (int map_flag, char * trace_fh, char * path, nfs_fh3 * play_fh) +{ + char * old_trace_fh; + + /* first lookup if the entry for fh is already in the table */ + struct generic_entry * p; + + p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE); + if (p) { + RFS_ASSERT (fh_map[p->key3].flag = FH_MAP_FLAG_PARTIAL); + RFS_ASSERT (map_flag ==FH_MAP_FLAG_COMPLETE); + fh_map[p->key3].flag = map_flag; + //RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)); + if (memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)) { + int i; + printf ("fh_map[%d].trace_fh %s trace_fh %s", p->key3, fh_map[p->key3].trace_fh, trace_fh); + for (i=0; i<fh_i; i++) { + int * p1 = (int *)&(fh_map[i].play_fh); +#ifdef COMPRESS_TRACE_FH + int * p = (int *)fh_map[i].trace_fh; + printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); +#else + printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, fh_map[i].trace_fh, fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); + } +#endif + RFS_ASSERT (0); + } + RFS_ASSERT (!strcmp(fh_map[p->key3].path, path)); + /* It's possible that in fh-path-map, many trace_fh are corresponding to one path + * some of it may be the result of lookup after symlink, which is not handled + * properly as new created objects + */ +#ifdef TAKE_CARE_SYMBOLIC_LINK + RFS_ASSERT (!memcmp(&fh_map[p->key3].play_fh, &zero_fhandle, sizeof(nfs_fh3))); +#endif + memcpy (&fh_map[p->key3].play_fh, play_fh, sizeof (nfs_fh3)); + if ((fh_map_debug==1)) // || (stage ==TRACE_PLAY_STAGE)) + printf ("update the play_fh for trace_fh %s path %s \n", trace_fh, path); + return 0; + } + + fh_map[fh_i].flag = map_flag; + fh_map[fh_i].lock = 0; + strncpy(fh_map[fh_i].trace_fh, trace_fh, TRACE_FH_SIZE); + + RFS_ASSERT (strlen(path) < MAX_PLAY_PATH_SIZE); + strcpy (fh_map [fh_i].path, path); + if (map_flag==FH_MAP_FLAG_COMPLETE) + memcpy (&fh_map[fh_i].play_fh, play_fh, sizeof(nfs_fh3)); + else + memset (&fh_map[fh_i].play_fh, 0, sizeof(nfs_fh3)); + + if ((fh_map_debug==1)) { // || (stage ==TRACE_PLAY_STAGE)) { + printf ("insert trace_fh %s path %s play_fh:\n", trace_fh, path); + if (map_flag == FH_MAP_FLAG_COMPLETE) { + //show_fhandle(play_fh); + } else + printf("null\n"); + } + +/* + if (map_flag == FH_MAP_FLAG_DISCARD) + printf ("insert flag %d trace_fh %s path %s play_fh:\n", map_flag, trace_fh, path); +*/ + + generic_insert(trace_fh, TRACE_FH_SIZE, fh_i, fh_htable, FH_HTABLE_SIZE); + + fh_i = (fh_i+1); + RFS_ASSERT (fh_i < FH_MAP_SIZE); + + return 0; +}; + +inline fh_map_t * lookup_fh (char * trace_fh ) +{ + struct generic_entry * p; + p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE); + if (fh_map_debug==1) + printf ("lookup trace_fh %s\n", trace_fh); + + if (p) { + if (fh_map_debug==1) { + printf ("found: fh_i[%d] trace_fh %s path %s play_fh:\n", p->key3, fh_map[p->key3].trace_fh, fh_map[p->key3].path); + //show_fhandle(&fh_map[p->key3].play_fh); + } + RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)); + return (&(fh_map[p->key3])); + } else { + //printf ("lookup_fh %s not found\n", trace_fh); + if (stage != READ_DEP_TAB_STAGE && (fh_map_debug==1)) { + printf ("lookup not found trace_fh %s\n", trace_fh); + } + return NULL; + } + RFS_ASSERT (0); +} + +int delete_fh (char * trace_fh, int fh_map_index) +{ + generic_delete (trace_fh, TRACE_FH_SIZE, fh_map_index, fh_htable, FH_HTABLE_SIZE); + return 0; +}; + +int lookup_init_filesystem (nfs_fh3 * parent, char * name, nfs_fh3 * result) +{ + LOOKUP3args args; + LOOKUP3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + static int i=0; + + /* set up the arguments */ + (void) memcpy((char *) &args.what.dir, (char *) parent, + sizeof (nfs_fh3)); + args.what.name = name; + (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP, + xdr_LOOKUP3args, (char *) &args, + xdr_LOOKUP3res, (char *) &reply, + Nfs_timers[Init]); + sfs_gettime(&stop); + + if (rpc_stat !=RPC_SUCCESS) { + printf("rpc_stat %d\n", rpc_stat); + perror(""); + } + RFS_ASSERT (rpc_stat == RPC_SUCCESS); + (void) memcpy((char *) result, (char *) &reply.resok.object, sizeof (nfs_fh3)); + return (reply.status); +} + +void read_fh_map(char * fh_map_file) +{ + FILE * fp; + int i = 0; + char buf[1024]; + char trace_fh[MAX_TRACE_FH_SIZE]; + char intbuf[9]; + char * trace_path; + char * p; + int map_flag; +#define MAX_PATH_DEPTH 20 + nfs_fh3 parents[MAX_PATH_DEPTH]; + char * lookup_path_ptr[MAX_PATH_DEPTH]; + char lookup_path [MAX_PLAY_PATH_SIZE]; + int depth; + int new_dir_flag = 0; + int lineno = 0; + + depth = 0; + memset(lookup_path_ptr, 0, sizeof(lookup_path_ptr)); + memcpy(&parents[depth], &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3)); + strcpy(lookup_path, "/"); + lookup_path_ptr[depth]=&lookup_path[0]; + + fp = fopen(fh_map_file, "r"); + if (!fp) { + printf ("can not opern %s\n", fh_map_file); + perror("open"); + exit (0); + } + RFS_ASSERT (fp!=NULL); + if (strstr(fh_map_file, "fmt1")) { + TRACE_FH_SIZE = 48; + } + + intbuf[8]=0; + + memset(buf, 0, sizeof(buf)); + while (fgets(buf, 1024, fp)) { + lineno ++; + if (fh_i % 10000==0) + printf("%d fh_map entry read\n", fh_i); + + RFS_ASSERT (buf[strlen(buf)-1]=='\n'); + buf[strlen(buf)-1]=0; + if (fh_map_debug) { + printf("%d fgets return %s\n", fh_i, buf); + printf("depth %d lookup_path %s\n", depth, lookup_path); + } + //for (i=0; i<=depth; i++) + //printf("lookup_path_ptr[%d] %s ", i, lookup_path_ptr[i]); + //printf("\n"); +#ifdef COMPRESS_TRACE_FH + for (i=0; i<TRACE_FH_SIZE/8; i++) { + strncpy(intbuf, buf+i*8, 8); + sscanf(intbuf, "%x", trace_fh+i*8); // maybe it should be 4, anyway we don't compress for now + } + trace_path = buf+TRACE_FH_SIZE*2+1; /* +1 the trace contains only initial file handle */ +#else + memcpy(trace_fh, buf, TRACE_FH_SIZE); + trace_path = buf + TRACE_FH_SIZE +1; +#endif +#ifdef TRACE_CONTAIN_LATER_FHANDLE + trace_path = +=2; /* +3 if the trace contains both initial and later created file handle */ +#endif + +#ifdef NO_DEPENDENCY_TABLE + if (!strncmp (trace_path, "DISCARD", 7) || + !strncmp (trace_path, "LN", 2) ) { + map_flag = FH_MAP_FLAG_DISCARD; + add_fh (map_flag, buf, trace_path, 0); + continue; + } +#endif + + p = trace_path+strlen(trace_path)-2; + while (*p!='/') + p--; + p++; + //RFS_ASSERT (p-trace_path<=strlen(lookup_path)+1); + //RFS_ASSERT (p>trace_path); + + if (strncmp(lookup_path, trace_path, p-trace_path)) { + printf("strncmp lookup_path %s trace_path %s for length %d\n", lookup_path, trace_path, p-trace_path); + } + RFS_ASSERT (!strncmp(lookup_path, trace_path, p-trace_path)); + //while (strncmp(lookup_path, trace_path, p-trace_path)) { /* one step deeper */ + while (strlen(lookup_path)>p-trace_path && depth>0) { + //printf("depth--\n"); + if (depth<=0) + printf ("lookup_path %s trace_path %s p-trace_path %d depth %d\n", lookup_path, trace_path, p-trace_path, depth); + RFS_ASSERT (depth>0); + *lookup_path_ptr[depth]=0; + lookup_path_ptr[depth]=0; + depth--; + } + RFS_ASSERT (strlen(lookup_path)==(p-trace_path) || (depth==0)); + + +#ifdef TRACE_CONTAIN_LATER_FHANDLE + if (buf[TRACE_FH_SIZE*2+1]=='Y') { + map_flag = FH_MAP_FLAG_COMPLETE; + } else { + map_flag = FH_MAP_FLAG_PARTIAL; + RFS_ASSERT (buf[TRACE_FH_SIZE*2+1]=='N'); + } +#else + map_flag = FH_MAP_FLAG_COMPLETE; +#endif + if ((*(p+strlen(p)-1))=='/') { + *(p+strlen(p)-1)=0; + new_dir_flag = 1; + } else + new_dir_flag = 0; + + if (map_flag == FH_MAP_FLAG_COMPLETE) { + int ret = lookup_init_filesystem (&parents[depth], p, &parents[depth+1]); + if (ret!=NFS3_OK) { + printf ("lineno %d %s\n", lineno, buf); + } + RFS_ASSERT (ret == NFS3_OK); + add_fh (map_flag, buf, trace_path, &parents[depth+1]); + } else + add_fh (map_flag, buf, trace_path, 0); + + if (new_dir_flag) { + /* the new fhandle is of a directory */ + lookup_path_ptr[depth+1] = lookup_path+strlen(lookup_path); + strcat (lookup_path, p); + strcat (lookup_path, "/"); + + //printf("depth++\n"); + depth++; + } + + memset(buf, 0, sizeof(buf)); + } + + if (fh_map_debug) { + for (i=0; i<fh_i; i++) { + int * p1 = (int *)&(fh_map[i].play_fh); +#ifdef COMPRESS_TRACE_FH + int * p = (int *)fh_map[i].trace_fh; + printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); +#else + printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, fh_map[i].trace_fh, fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); + } +#endif + + fprintf(stderr, "total %d requests \n", i); + } +} + +int f() +{ + return 1; +} + +inline free_biod_req (int biod_index) +{ + RFS_ASSERT (biod_reqp[biod_index].in_use == TRUE); + biod_reqp[biod_index].in_use = FALSE; + num_out_reqs --; +} + +void finish_request (int biod_index, int dep_index, int status, int dep_flag) +{ + static int i = 0; + /* the ending operation, same as when a request time out */ + + dep_tab[dep_index].stop = biod_reqp[biod_index].stop; /* RFS: to dump data */ + free_biod_req (biod_index); + + dep_tab[dep_index].status = status; + + if (event_order_index < EVENT_ORDER_SIZE) + event_order[event_order_index++] = -dep_tab[dep_index].disk_index; + + dep_tab[dep_index].flag = dep_flag; + if (is_dir_op(dep_tab[dep_index].proc)) { + int j; + RFS_ASSERT (dep_tab[dep_index].fh->lock = 1); + dep_tab[dep_index].fh->lock = 0; + if (dep_tab[dep_index].proc==RENAME) + dep_tab[dep_index].fh_2->lock = 0; + j = dep_tab[dep_index].fh-fh_map; + if (dependency_debug) { + printf ("fh_map[%d] is UNlocked\n",j); + printf ("trace_fh %d path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path); + printf ("trace_fh %d path %s\n", fh_map[j].trace_fh, fh_map[j].path); + } + } +} + +/* the request argument may have pointers pointing to buffers, e.g. the name in lookup, + * the target of symlink, the write data */ +char arg_res[MAX_ARG_RES_SIZE]; +char buf1 [MAX_BUF1_SIZE]; +char buf2 [MAX_BUF2_SIZE]; + +int execute_next_request () +{ + int dep_index; + int proc; + char * line; + struct biod_req * reqp; + sfs_op_type *op_ptr; /* per operation info */ + struct ladtime call_timeout; + static int last_print_time = -1; + + if (num_out_reqs == max_biod_reqs) { + return -1; + } + + start_profile (&valid_get_nextop_profile); + start_profile (&invalid_get_nextop_profile); + dep_index = get_nextop(); + if (dep_index == -1) { + end_profile (&invalid_get_nextop_profile); + return dep_index; + }; + end_profile (&valid_get_nextop_profile); + + start_profile (&prepare_argument_profile); + line = dep_tab[dep_index].line; + + if (per_packet_debug) + fprintf (stdout, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE); + + end_profile(&total_profile); + if ((total_profile.in.tv_sec - last_print_time >= 10)) { + last_print_time = total_profile.in.tv_sec; + //fprintf (stdout, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE); +/* + CYCLIC_PRINT (dep_tab_index); + { + int tmp = CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size); + printf("dep_tab_index.head-1 %d disk_index %d tail %d disk_index %d\n", tmp, dep_tab[tmp].disk_index, + dep_tab_index.tail, dep_tab[dep_tab_index.tail].disk_index); + } +*/ +#ifdef TIME_PLAY +#ifdef SPEED_UP +/* + if (can_not_catch_speed_num < 2000) { + PLAY_SCALE ++; + printf ("set PLAY_SCALE to %d\n", PLAY_SCALE); + }; + if (can_not_catch_speed_num > 50000) { + PLAY_SCALE /= 2; + } else { + if (can_not_catch_speed_num > 5000) { + PLAY_SCALE -= 2; + if (PLAY_SCALE < 1) + PLAY_SCALE = 1; + } + } +*/ +#endif + if ((total_profile.in.tv_sec > 100)) { + can_not_catch_speed_num_total += can_not_catch_speed_num; + } + can_not_catch_speed_num = 0; +#endif + } + if (rfs_debug) + printf ("processing dep_tab[%d] disk_index %d %s\n", dep_index, dep_tab[dep_index].disk_index, line); + + proc = dep_tab[dep_index].proc; + rfs_Ops[proc].setarg (dep_index, line, arg_res, buf1, buf2); + + op_ptr = &Ops[proc]; + reqp = get_biod_req (dep_index); + RFS_ASSERT (reqp); + +#ifdef notdef /* place to set request timeout. G. Jason Peng */ + call_timeout.sec = 2; //Nfs_timers[op_ptr->call_class].tv_sec; + call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; +#else + call_timeout.sec = 0; + call_timeout.usec = 300000; + //call_timeout.usec = 14000; + //call_timeout.usec = 13000; + //call_timeout.usec = 6000; + //call_timeout.usec = 8000; + //call_timeout.usec = 10000; +#endif + + /* make the call */ + sfs_gettime(&(reqp->start)); + end_profile (&prepare_argument_profile); + start_profile (&biod_clnt_call_profile); +#define REAL_PLAY +#ifdef REAL_PLAY + +#ifdef RECV_THREAD + //printf ("send thread waitsem\n"); + waitsem(async_rpc_sem); + //printf ("send thread got sem\n"); +#endif + reqp->xid = biod_clnt_call(NFS_client, rfs_Ops[proc].nfsv3_proc, + rfs_Ops[proc].xdr_arg, arg_res); +#ifdef RECV_THREAD + postsem(async_rpc_sem); + //printf ("send thread postsem\n"); +#endif + +#else // REAL_PLAY + reqp->xid = dep_index+1; /* just fake a message id and let it expire */ +#endif + RFS_ASSERT (reqp->xid != 0); + reqp->timeout = reqp->start; + ADDTIME (reqp->timeout, call_timeout); + dep_tab[dep_index].flag = DEP_FLAG_SENT; + if (event_order_index < EVENT_ORDER_SIZE) + event_order[event_order_index++] = dep_tab[dep_index].disk_index; + + dep_tab[dep_index].start = reqp->start; /* RFS: to dump data */ + end_profile (&biod_clnt_call_profile); + + send_num ++; +} + +void check_reply (int proc, int biod_index, int dep_index, int status, char * errmsg, int trace_status) +{ + if (((status!=trace_status)) && (status!=NFS3_OK) && (trace_status!=NFS3ERR_RFS_MISS)) { + if (!profile_debug) + printf ("receive problem reply, xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d command disk index %d\n", biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec, dep_tab[dep_index].disk_index); +#ifndef TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES + /* these files is not looked up and is not create/mkdir/symlink/link/mknod ed before they + * are refered by name through rename, remove + */ + if ((proc==RENAME || proc==REMOVE) && (status==NFS3ERR_NOENT) && (trace_status ==0)) { + /* current initialization doesnot take care of rename source, if there is no + * create or lookup before that source, the source object will not exist when + * rename occurs + */ + rename_rmdir_noent_reply_num++; + } else +#endif +#ifndef TAKE_CARE_SYMBOLIC_LINK + if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status==NFS3ERR_NOENT)) { + /* in the original trace, first lookup return NOENT, then symlink is executed, then lookup return OK + * the initialization considers only the lookup return OK and created the file in the initialization + * so in trace play the first lookup return OK + */ + RFS_ASSERT (1); + } else // if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) && (trace_status == 0)) { + /* trace_status could be EAGAIN */ + if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) ) { + /* due to similar reason as above, the initialization code initializes the symbolic link as a normal + * file already + */ + RFS_ASSERT (1); + } else +#endif +#ifndef TAKE_CARE_NOEMPTY_RMDIR + /* the remove packet seems got lost in the trace capture, so replay can not finish */ + if ((proc==RMDIR) && (status==NFS3ERR_NOTEMPTY)) { + RENAME3args args; + RENAME3res reply; /* the reply */ + RMDIR3args * rmdir_argp; + enum clnt_stat rpc_stat; /* result from RPC call */ + + rfs_Ops[proc].setarg (dep_index, dep_tab[dep_index].line, arg_res, buf1, buf2); + rmdir_argp = (RMDIR3args *)arg_res; + + memcpy(&args.from, &(rmdir_argp->object), sizeof (diropargs3)); + memcpy(&args.to.dir, &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3)); + args.from.name = buf1; /* the buf1 is already filled when parsing rmdir */ + args.to.name = buf2; + sprintf(buf2, "rmdir_%d_%s", dep_tab[dep_index].disk_index, rmdir_argp->object.name); + + rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME, + xdr_RENAME3args, (char *) &args, + xdr_RENAME3res, (char *) &reply, + Nfs_timers[Init]); + RFS_ASSERT (rpc_stat == RPC_SUCCESS); + if (reply.status!=NFS3_OK) + printf ("change rmdir into rename, reply.status %d\n", reply.status); + RFS_ASSERT (reply.status==NFS3_OK); + rmdir_not_empty_reply_num ++; +#endif +#ifndef TAKE_CARE_ACCESS_ERROR + } else if ((status==0) && (trace_status==NFS3ERR_ACCES)) { + loose_access_control_reply_num ++; +#endif +#ifdef NO_DEPENDENCY_TABLE + } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3_OK)) { + lookup_err_due_to_rename_num ++; + } else if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status == NFS3ERR_NOENT)) { + /* if there is a remove in front of the lookup, but it is + * actually executed later than the lookup + */ + lookup_err_due_to_parallel_remove_num ++; +#endif +#ifndef TAKE_CARE_LOOKUP_EACCESS_ENOENT_MISMATCH + /* if the looked return EACCESS in the trace, means the object still exists + * should have initialized, right not don't initialize it, hence play status + * could be ENOENT + */ + } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3ERR_ACCES)) { + lookup_eaccess_enoent_mismatch_num ++; +#endif +#ifdef TOLERANT_READ_IO_ERR + } else if ((proc==READ) && (status==NFS3ERR_IO) && (trace_status==NFS3_OK)) { + read_io_err_num ++; +#endif +#ifdef TOLERANT_STALE_FHANDLE_ERR + } else if ((status==NFS3ERR_STALE) && (trace_status==NFS3_OK)) { + printf ("!!!!!!! STALE FILE HANDLE \n"); + //sleep(1); + stale_fhandle_err_num ++; +#endif + } else { + int i; + for (i=dep_window_index.tail; CYCLIC_LESS(dep_window_index,i,dep_window_index.head); i++) { + if (dep_tab[i].flag!=1) + printf ("dep_tab[%d].disk_index %d, flag %d line %s\n", i, dep_tab[i].disk_index, dep_tab[i].flag, dep_tab[i].line); + } + + if (status==EEXIST) { + abnormal_EEXIST_num ++; + } else if (status == ENOENT) { + abnormal_ENOENT_num ++; + } else { + printf ("!!!!!!!!!!!!!1 should fail\n"); + //RFS_ASSERT (0); + } + } + } else { + proper_reply_num ++; + if (total_profile.in.tv_sec >= WARMUP_TIME) + run_stage_proper_reply_num ++; + } + +} + +/* return -1 if there is no reply being received + * return the dep_index if the corresponding reply has been received + */ +int receive_next_reply (int busy_flag) +{ + int dep_index; + int biod_index; + int proc; + char * line; + char * reply_line; + sfs_op_type *op_ptr; /* per operation info */ + int ret; + int status; + int trace_status; + char * errmsg; + int poll_timeout = 0; /* timeout in usecs */ + + /* wait for reply */ + start_profile (&valid_poll_and_get_reply_profile); + start_profile (&invalid_poll_and_get_reply_profile); + + if (busy_flag == BUSY) { + poll_timeout = 0; + poll_timeout_0_num ++; + } else { + poll_timeout = 2000; /* 10000 or 2000 is a better number in non-debugging state */ + //poll_timeout = 0; /* 10000 or 2000 is a better number in non-debugging state */ + poll_timeout_pos_num ++; + } + + biod_index = poll_and_get_reply (poll_timeout); + if (biod_index==-1) { + end_profile (&invalid_poll_and_get_reply_profile); + return -1; + }; + end_profile (&valid_poll_and_get_reply_profile); + + start_profile (&decode_reply_profile); + /* check the corresponding request */ + dep_index = biod_reqp[biod_index].dep_tab_index; + if (biod_reqp[biod_index].in_use==1) { + RFS_ASSERT (dep_tab[dep_index].biod_req_index == biod_index); + } else { + printf ("biod_index %d reply received but the request has been time out\n", biod_index); + return -1; + } + + proc = dep_tab[dep_index].proc; + op_ptr = &Ops[proc]; + + if (dep_tab[dep_index].flag != DEP_FLAG_SENT) { + printf("dep_tab[%d].flag %d proc %d status %d start %d:%d stop %d:%d\n", + dep_index, dep_tab[dep_index].flag, proc, dep_tab[dep_index].status, + dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec, + dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec ); + printf ("received reply for timeout requests dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); + return dep_index; + } + RFS_ASSERT (dep_tab[dep_index].flag == DEP_FLAG_SENT); + + /* decode the reply */ + rfs_Ops[proc].setres (arg_res, buf1); + ret = proc_header (NFS_client, rfs_Ops[proc].xdr_res, arg_res); + RFS_ASSERT (ret == RPC_SUCCESS); + status = *((int *)arg_res); + errmsg = nfs3_strerror (status); + end_profile (&decode_reply_profile); + + start_profile (&check_reply_profile); + /* compare with the reply in the trace */ + line = dep_tab[dep_index].line; + reply_line = dep_tab[dep_index].reply_line; + trace_status = dep_tab[dep_index].trace_status; + + if (per_packet_debug || rfs_debug ) + fprintf (stdout, "dep_tab[%d], disk_index %d, receive reply, rpc_ret %d xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d \n", dep_index, dep_tab[dep_index].disk_index, ret, biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec); + + /* error checking */ + check_reply (proc, biod_index, dep_index, status, errmsg, trace_status); + + /* free resources */ + finish_request (biod_index, dep_index, status, DEP_FLAG_DONE); + recv_num ++; + + /* we set 100 seconds warm up time */ + if ((total_profile.in.tv_sec >= WARMUP_TIME)) { + /* get statistics */ + if (status == trace_status || (status==NFS3_OK && trace_status==NFS3ERR_RFS_MISS) ) { + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + } else { + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + sfs_elapsedtime (op_ptr, &(biod_reqp[biod_index].start), &(biod_reqp[biod_index].stop)); + end_profile (&check_reply_profile); + } + + //start_profile (&add_create_object_profile); + + if (trace_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==SYMLINK || proc==MKNOD)) { +#ifndef REDUCE_MEMORY_TRACE_SIZE + RFS_ASSERT (reply_line); +#endif + if (status!=NFS3_OK) { + /* commented out for 1022 */ + printf ("!!!!!! Should have been an ASSERTION FAILURE \n"); + RFS_ASSERT (proc==SYMLINK); + RFS_ASSERT (0); + } else { + if (proc!=SYMLINK || line[TRACE_VERSION_POS]!='2') + add_new_file_system_object(proc, dep_index, line, reply_line); + } + } + //end_profile (&add_create_object_profile); +} + +inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line) +{ + char * child_trace_fh; + fh_map_t * parent_entryp; + char component_name[MAX_PLAY_PATH_SIZE]; + char * parent_trace_fh; + char child_path[MAX_PLAY_PATH_SIZE]; + post_op_fh3 * post_op_fh3_child; + char * reply_trace_fh; + nfs_fh3 * child_fh3; + + parent_trace_fh = strstr (line, "fh"); + RFS_ASSERT (parent_trace_fh); + parent_trace_fh +=3; + parent_entryp = lookup_fh (parent_trace_fh); + RFS_ASSERT (parent_entryp); + parse_name (parent_trace_fh+65, component_name); + strcpy (child_path, parent_entryp->path); + strcat (child_path, "/"); + strcat (child_path, component_name); + + /* find the corresponding create request */ + //printf ("before find reply trace_fh reply_line %s\n", reply_line); +#ifdef REDUCE_MEMORY_TRACE_SIZE + reply_trace_fh = dep_tab[dep_index].reply_trace_fh; +#else + reply_trace_fh = find_reply_trace_fh (reply_line); +#endif + RFS_ASSERT (reply_trace_fh != NULL); + switch (proc) { + case CREATE: + RFS_ASSERT (((CREATE3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((CREATE3res *)arg_res)->res_u.ok.obj.handle; + break; + case MKDIR: + RFS_ASSERT (((MKDIR3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((MKDIR3res *)arg_res)->res_u.ok.obj.handle; + break; + case SYMLINK: + RFS_ASSERT (((SYMLINK3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((SYMLINK3res *)arg_res)->res_u.ok.obj.handle; + break; + case MKNOD: + RFS_ASSERT (((MKNOD3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((MKNOD3res *)arg_res)->res_u.ok.obj.handle; + break; + case LOOKUP: + RFS_ASSERT (proc==LOOKUP); + child_fh3 = &((LOOKUP3res *)arg_res)->res_u.ok.object; + break; + default: + RFS_ASSERT (0); + } +#ifndef REDUCE_MEMORY_TRACE_SIZE + RFS_ASSERT (reply_trace_fh[TRACE_FH_SIZE]==' '); +#endif + reply_trace_fh[TRACE_FH_SIZE] = 0; + add_fh (FH_MAP_FLAG_COMPLETE, reply_trace_fh, child_path, child_fh3); /* exist flag is not used now, set to 1 */ +#ifndef REDUCE_MEMORY_TRACE_SIZE + /* just to preserve the original reply line not changed */ + reply_trace_fh[TRACE_FH_SIZE] = ' '; +#endif +} + +/* initialize timestamp and proc field of dep_tab entry */ +void trace_play(void) +{ + + /* The flag to indicate whether trace_player is BUSY. Trace_player is BUSY + * when either there is request to send or there is reply being + * received. Otherwise it is IDLE. The timeout for polling replies + * is set to 0 when BUSY, it is set to the waiting time to the first + * request outside of current <min_dep_index, dep_window_max> window when IDLE. + */ + int busy_flag = BUSY; + //int dep_index; /* index into dependency table: dep_tab */ + //int biod_index; /* index into outstanding requests: biod_reqp */ + static int last_print_time = -1; + int poll_timeout = 0; + +#ifndef IO_THREAD + disk_io_status = read_trace(); +#endif + + RFS_ASSERT (!CYCLIC_EMPTY(dep_tab_index)); + CYCLIC_MOVE_HEAD(dep_window_index); + + adjust_play_window(busy_flag, &poll_timeout); + + start_profile (&total_profile); + while (!CYCLIC_EMPTY(dep_tab_index)) { + end_profile(&total_profile); + if ((total_profile.in.tv_sec - last_print_time >= 10)) { + int i; + + last_print_time = total_profile.in.tv_sec; + fprintf (stdout, ">>>> sendng thread: time %d send_num %d recv_num %d timeout_num %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, send_num, recv_num, timeout_num, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE); + for (i=0; i<=MAX_OUTSTANDING_REQ; i++) { + if (num_out_reqs_statistics[i]!=0) { + printf("num_out_req[%d]=%d,", i, num_out_reqs_statistics[i]); + num_out_reqs_statistics[i]=0; + } + } + printf("\n"); + for (i=0; i<=MAX_OUTSTANDING_REQ; i++) { + if (num_out_reqs_statistics_at_timeout[i]!=0) { + printf("num_out_req_at_timeout[%d]=%d,", i, num_out_reqs_statistics_at_timeout[i]); + num_out_reqs_statistics_at_timeout[i]=0; + } + } + printf("\n"); + // CYCLIC_PRINT(dep_tab_index); + } + + if ((total_profile.in.tv_sec > 6000)) { + printf ("the process has run for more than 600 seconds, exit\n"); + goto END; + } + + if (busy_flag == IDLE) { +#ifndef RECV_THREAD + //start_profile (&check_timeout_profile); + check_timeout(); + //end_profile (&check_timeout_profile); +#endif +#ifndef IO_THREAD + if (disk_io_status!=TRACE_FILE_END) { + disk_io_status = read_trace(); + }; +#endif + } + + //start_profile (&adjust_play_window_profile); + adjust_play_window (busy_flag, &poll_timeout); + if (rfs_debug) + printf("num_out_reqs %d\n", num_out_reqs); + num_out_reqs_statistics[num_out_reqs]++; + busy_flag = IDLE; + //end_profile (&adjust_play_window_profile); + + start_profile (&execute_next_request_profile); + while (execute_next_request()!=-1) { + busy_flag = BUSY; + } + end_profile (&execute_next_request_profile); + +#ifndef RECV_THREAD + start_profile (&receive_next_reply_profile); + /* actually the performance of two policy seems to be same */ +//#define SEND_HIGHER_PRIORITY_POLICY +#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY + +#ifdef SEND_HIGHER_PRIORITY_POLICY + receive_next_reply(IDLE); +#endif +#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY + busy_flag = IDLE; + while (receive_next_reply(busy_flag)!=-1) + busy_flag = BUSY; +#endif + end_profile (&receive_next_reply_profile); +#endif + CYCLIC_ASSERT (0); + } + end_profile (&total_profile); + + RFS_ASSERT (disk_io_status == TRACE_FILE_END); + if (num_out_reqs !=0 ) { + printf ("num_out_reqs %d\n", num_out_reqs); + CYCLIC_PRINT(dep_tab_index); + } + RFS_ASSERT(num_out_reqs==0); + +END: + printf ("trace starttime %d, trace_end_time %d trace_duration %d\n", trace_timestamp1, trace_timestamp2, + trace_timestamp2 - trace_timestamp1); + printf ("can_not_catch_speed_num_total %d can_not_catch_speed_num_last_10_seconds %d", + can_not_catch_speed_num_total, can_not_catch_speed_num); + printf ("total_profile.about: %s\n", total_profile.about); + print_profile ("total_profile", &total_profile); + printf("\n"); + //print_profile ("check_timeout", &check_timeout_profile); + //printf("\n"); + //print_profile ("adjust_play_window", &adjust_play_window_profile); + //printf("\n"); + print_profile ("execute_next_request_profile", &execute_next_request_profile); + print_profile ("valid_get_nextop_profile", &valid_get_nextop_profile); + print_profile ("invalid_get_nextop_profile", &invalid_get_nextop_profile); + print_profile ("prepare_argument_profile", &prepare_argument_profile); + print_profile ("biod_clnt_call_profile", &biod_clnt_call_profile); + printf("\n"); + print_profile ("receive_next_reply_profile", &receive_next_reply_profile); + print_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile); + print_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile); + print_profile ("decode_reply_profile", &decode_reply_profile); + print_profile ("check_reply_profile", &check_reply_profile); + print_profile ("fgets_profile", &fgets_profile); + print_profile ("read_line_profile", &read_line_profile); + print_profile ("read_trace_profile", &read_trace_profile); + //print_profile ("add_create_object", &add_create_object_profile); + printf("\n"); + + printf ("dep_tab_index.tail %d dep_tab_index.head %d num_out_reqs %d\n", dep_tab_index.tail, dep_tab_index.head, num_out_reqs); +} + + +int CYCLIC_SET_TAIL_TO(cyclic_index_t * index, int dest) +{ + cyclic_index_t indextmp, indextmp2; + int oldnum, num; + indextmp = *index; + indextmp2 = indextmp; + oldnum = CYCLIC_NUM(indextmp); + + if (! ((dest>=0) && (dest<indextmp.size))) { + CYCLIC_PRINT(indextmp); + printf("dest %d\n", dest); + } + RFS_ASSERT ((dest>=0) && (dest<indextmp.size)); + index->tail = dest; + indextmp2.tail = dest; + num = CYCLIC_NUM(indextmp2); + + if (num > oldnum) { + CYCLIC_PRINT(indextmp); + CYCLIC_PRINT(indextmp2); + printf("dest %d old_num %d num %d\n", dest, oldnum, num); + } + RFS_ASSERT (num <= oldnum); +} + +int flush_junk() +{ + int i; + for (i=0; i<500; i++) { + printf ("*************************************************************\n"); + } + fflush(stdout); +} + +int CYCLIC_ASSERT (int i) +{ + int j; + if (!(dep_tab_index.tail == dep_window_index.tail)) { + printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size); + printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size); + printf("pos %d\n", i); + flush_junk(); + sleep (10); + RFS_ASSERT (0); + }; + + if (!((dep_window_index.head == dep_tab_index.head) || + CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head ) )) { + printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size); + printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size); + printf("pos %d\n", i); + flush_junk(); + sleep (10); + RFS_ASSERT (0); + }; + for (i=0, j=0; i<max_biod_reqs; i++) { + if (biod_reqp[i].in_use == 1) + j++; + } +#ifndef RECV_THREAD + RFS_ASSERT (num_out_reqs==j); +#endif +/* + RFS_ASSERT ((dep_window_index.head == dep_tab_index.head) || + CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head )); +*/ +} + +/* sfs_c_chd.c */ diff --git a/TBBT/trace_play/sfs_c_chd.c b/TBBT/trace_play/sfs_c_chd.c new file mode 120000 index 0000000..0c356df --- /dev/null +++ b/TBBT/trace_play/sfs_c_chd.c @@ -0,0 +1 @@ +sfs_c_chd.3thread.c \ No newline at end of file diff --git a/TBBT/trace_play/sfs_c_chd.c.old b/TBBT/trace_play/sfs_c_chd.c.old new file mode 100644 index 0000000..1d2f423 --- /dev/null +++ b/TBBT/trace_play/sfs_c_chd.c.old @@ -0,0 +1,2457 @@ +#ifndef lint +static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * -------------------------- sfs_c_chd.c ------------------------- + * + * The sfs child. Routines to initialize child parameters, + * initialize test directories, and generate load. + * + *.Exported_Routines + * void child(int, float, int, char *); + * void init_fileinfo(void); + * void init_counters(void); + * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type, + * sfs_file_type); + * int check_access(struct *stat) + * int check_fh_access(); + * + *.Local_Routines + * void check_call_rate(void); + * void init_targets(void); + * void init_dirlayout(void); + * void init_rpc(void); + * void init_testdir(void); + * int do_op(void); + * int op(int); + * + *.Revision_History + * 21-Aug-92 Wittle randfh() uses working set files array. + * init_fileinfo() sets up working set. + * 02-Jul-92 Teelucksingh Target file size now based on peak load + * instead of BTDT. + * 04-Jan-92 Pawlowski Added raw data dump hooks. + * 16-Dec-91 Wittle Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" +#include "rfs_c_def.h" +#include "generic_hash.h" +#include "nfsd_nfsfh_cust.h" + +extern struct hostent *Server_hostent; + +#define PROB_SCALE 1000L +#define _M_MODULUS 2147483647L /* (2**31)-1 */ + +#define _GROUP_DIVISOR 500 +#define _FILES_PER_GROUP 4 +#define _MIN_GROUPS 12 +#define _WORKING_SET_AT_25_OPS_PER_SEC 975 + +/* + * ----------------------- External Definitions ----------------------- + */ +extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *); +extern enum clnt_stat proc_header(CLIENT *cl, xdrproc_t xdr_results, void *results_ptr); +extern int biod_poll_wait(CLIENT *, uint32_t); +extern enum clnt_stat get_areply_udp (CLIENT * cl, uint32_t *xid, struct timeval *timeout); +extern char * parse_name (char * t, char * buf); + +/* forward definitions for local functions */ +static int init_rpc(void); + +/* RFS: forward definitions for local functions */ +void init_ops(void); +static void init_signal(); +extern void init_file_system (void); +extern void init_dep_tab (void); +static void read_trace(char * trace_file); +static void read_fh_map(); +static void init_play (char * mount_point); +static void trace_play(void); +static void print_result(void); +static int get_nextop(void); +static int check_timeout(void); +static struct biod_req * get_biod_req(int dep_tab_index); +static int lookup_biod_req (int xid); +static void init_time_offset(void); +static void adjust_play_window (int flag, int * poll_timeout); +static int poll_and_get_reply (int usecs); +static char * nfs3_strerror(int status); +static void check_clock(void); +static double time_so_far1(void); +static double get_resolution(void); +static void usage(void); +void init_dep_tab_entry (int dep_index); +extern inline fh_map_t * lookup_fh (char * trace_fh ); +static inline void finish_request (int biod_index, int dep_index, int status); +static inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line); +static inline char * find_lead_trace_fh(int proc, char * line); + +/* + * ------------- Per Child Load Generation Rate Variables ----------- + */ +static uint_t Calls_this_period; /* calls made during the current run period */ +static uint_t Calls_this_test; /* calls made during the test so far */ +static uint_t Reqs_this_period; /* reqs made during the current run period */ +static uint_t Reqs_this_test; /* reqs made during the test so far */ +static uint_t Sleep_msec_this_test; /* msec slept during the test so far */ +static uint_t Sleep_msec_this_period; +static uint_t Previous_chkpnt_msec; /* beginning time of current run period */ +static int Target_sleep_mspc; /* targeted sleep time per call */ + +static char io_buf[BUFSIZ]; /* io buffer for print out messages */ + +char * sfs_Myname; +int Log_fd; /* log fd */ +char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */ +int Validate = 0; /* fake variable */ +int Child_num = 0; /* fake: child index */ +int Tcp = 0; /* We implement UDP first */ +int Client_num = 1; /* fake: number of client */ +uid_t Real_uid; +gid_t Cur_gid; +uid_t Cur_uid; +/* + * ------------------------- SFS Child ------------------------- + */ + +void print_usage() +{ + printf("sfs3 hostname:mount_dir trace_file fh_map_file\n"); + exit; +} + +void read_dep_tab() +{ +#ifdef NO_DEPENDENCY_TABLE + int i; + char * line; + char * trace_fh; + fh_map_t * fh_map_entry; + int req_num_with_new_fh = 0; + int req_num_with_discard_fh = 0; + int req_num_with_init_fh =0; + + for (i=0; i<memory_trace_size; i++) { + line = memory_trace[i].line; + if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='C') { + trace_fh = strstr (line, "fh"); + RFS_ASSERT (trace_fh); + trace_fh += 3; + fh_map_entry = lookup_fh (trace_fh); + if (fh_map_entry && (fh_map_entry->flag==FH_MAP_FLAG_DISCARD) ) { + req_num_with_discard_fh ++; + continue; + } + if (fh_map_entry) + req_num_with_init_fh ++; + else + req_num_with_new_fh ++; + + dep_tab[dep_tab_size].disk_index = memory_trace[i].disk_index; + dep_tab[dep_tab_size].line = memory_trace[i].line; + if ((dep_tab_size%100000)==0) + printf ("dep_tab[%d].disk_index %d = memory_trace[%d].disk_index %d\n", dep_tab_size, dep_tab[dep_tab_size].disk_index, i, memory_trace[i].disk_index); + dep_tab_size ++; + } + } +#else + RFS_ASSERT (0); +#endif + printf ("read_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); +} + +void init_profile() +{ + memset (&total_profile, 0, sizeof(total_profile)); + + memset (&execute_next_request_profile, 0, sizeof(execute_next_request_profile)); + memset (&valid_get_nextop_profile, 0, sizeof(valid_get_nextop_profile)); + memset (&invalid_get_nextop_profile, 0, sizeof(invalid_get_nextop_profile)); + memset (&prepare_argument_profile, 0, sizeof(prepare_argument_profile)); + memset (&biod_clnt_call_profile, 0, sizeof(biod_clnt_call_profile)); + + memset (&receive_next_reply_profile, 0, sizeof(receive_next_reply_profile)); + memset (&valid_poll_and_get_reply_profile, 0, sizeof(valid_poll_and_get_reply_profile)); + memset (&invalid_poll_and_get_reply_profile, 0, sizeof(invalid_poll_and_get_reply_profile)); + memset (&decode_reply_profile, 0, sizeof(decode_reply_profile)); + memset (&check_reply_profile, 0, sizeof(check_reply_profile)); + memset (&add_create_object_profile, 0, sizeof(add_create_object_profile)); + + memset (&check_timeout_profile, 0, sizeof(check_timeout_profile)); + memset (&adjust_play_window_profile, 0, sizeof(adjust_play_window_profile)); +} + +static char trace_file[256]="anon-lair62-011130-1200.txt"; +int print_memory_usage() +{ + printf("size of fh_map_t %d size of fh_map %d\n", sizeof(fh_map_t), sizeof(fh_map)); + printf("sizeof dep_tab_t %d sizeof dep_tab %d\n", sizeof(dep_tab_t), sizeof(dep_tab)); + printf("size of memory_trace_entry_t %d sizeof memory_trace %d\n", sizeof(memory_trace_entry_t), sizeof(memory_trace)); + printf("size of CREATE3args %d\n", sizeof( CREATE3args)); + printf("size of MKDIR3args %d\n", sizeof( MKDIR3args)); + printf("size of READ3args %d\n", sizeof( READ3args)); + printf("size of WRITE3args %d\n", sizeof( WRITE3args)); + printf("size of RENAME3args %d\n", sizeof( RENAME3args)); + printf("size of GETATTR3args %d\n", sizeof( GETATTR3args)); + printf("size of SETATTR3args %d\n", sizeof( SETATTR3args)); + printf("size of LINK3args %d\n", sizeof( LINK3args)); + printf("size of SYMLINK3args %d\n", sizeof( SYMLINK3args)); + printf("size of MKNOD3args %d\n", sizeof( MKNOD3args)); + printf("size of RMDIR3args %d\n", sizeof( RMDIR3args)); + printf("size of REMOVE3args %d\n", sizeof( REMOVE3args)); + printf("size of LOOKUP3args %d\n", sizeof( LOOKUP3args)); + printf("size of READDIR3args %d\n", sizeof( READDIR3args)); + printf("size of READDIRPLUS3args %d\n", sizeof( READDIRPLUS3args)); + printf("size of FSSTAT3args %d\n", sizeof( FSSTAT3args)); + printf("size of FSINFO3args %d\n", sizeof( FSINFO3args)); + printf("size of COMMIT3args %d\n", sizeof( COMMIT3args)); + printf("size of ACCESS3args %d\n", sizeof( ACCESS3args)); + printf("size of READLINK3args %d\n", sizeof( READLINK3args)); + + +} +int main(int argc, char ** argv) +{ + extern char * optarg; + int i; + int memory_trace_size; + + if (argc==2 && !strcmp(argv[1],"-help")) { + print_usage(); + exit(0); + } + print_memory_usage(); + check_clock(); + getmyhostname(lad_hostname, HOSTNAME_LEN); + + init_ops(); + /* + * Get the uid and gid information. + */ + Real_uid = getuid(); + Cur_gid = getgid(); + //Real_uid = 513; + //Cur_gid = 513; + + Nfs_timers = Nfs_udp_timers; + + init_file_system (); + + init_signal(); + init_play (argv[1]); + //init_play ("capella:/p5/RFSFS"); + init_profile(); + init_fh_map(); + //read_fh_map (argv[3]); + read_fh_map ("fh-path-map-play"); + init_dep_tab(); /* and dep_tab_size */ + //read_trace (argv[2]); + //read_trace ("anon-lair62-011130-1000.txt"); + strcpy(trace_file, argv[2]); + read_trace (trace_file); + stage = READ_DEP_TAB_STAGE; + read_dep_tab(); + + for (i=0; i<dep_tab_size; i++) { + RFS_ASSERT (dep_tab[i].flag == DEP_FLAG_FREE) + init_dep_tab_entry (i); + } + stage = TRACE_PLAY_STAGE; + init_time_offset(); + printf ("trace_play\n"); + trace_play (); + print_result(); + printf("ffff\n"); +} + +void init_ops (void) +{ + Ops = nfsv3_Ops; + nfs_version = NFS_V3; +} + +/* Set up the signal handlers for all signals */ +void init_signal() +{ +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + struct sigaction sig_act, old_sig_act; + + /* use XOPEN signal handling */ + + sig_act.sa_handler = generic_catcher; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + + /* signals handlers for signals used by sfs */ + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGINT"); + exit(135); + } + + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) { + perror("sigaction failed: SIGTERM"); + exit(137); + } +#else + /* signals handlers for signals used by sfs */ + (void) signal(SIGINT, sfs_cleanup); + // RFS (void) signal(SIGALRM, sfs_alarm); + (void) signal(SIGTERM, sfs_cleanup); +#endif +} + +void +init_play ( + char * mount_point) /* Mount point for remote FS */ +{ + char namebuf[NFS_MAXNAMLEN] = "trace_play"; /* unique name for this program */ + CLIENT * mount_client_ptr; /* Mount client handle */ + + if (!rfs_debug); + (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ); + + sfs_Myname = namebuf; + + /* + * May require root priv to perform bindresvport operation + */ + mount_client_ptr = lad_getmnt_hand(mount_point); + if (mount_client_ptr == NULL) { + exit(145); + } + + /* + * should be all done doing priv port stuff + */ + + if (init_rpc() == -1) { + (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname); + (void) generic_kill(0, SIGINT); + exit(146); + } + + + /* + * finish all priv bindresvport calls + * reset uid + */ + if (setuid(Real_uid) != (uid_t)0) { + (void) fprintf(stderr,"%s: %s%s", sfs_Myname, + "cannot perform setuid operation.\n", + "Do `make install` as root.\n"); + } + + init_mount_point(0, mount_point, mount_client_ptr); + + + /* + * Cleanup client handle for mount point + */ + clnt_destroy(mount_client_ptr); + + init_counters(); + + +} + +void read_trace (char * tracefile) +{ + FILE * fp; + char buf[1024]; + // char * t=buf; + int disk_index=0; + + fp = fopen(tracefile, "r"); + RFS_ASSERT (fp!=NULL); + while (fgets(buf, 1024, fp)) { + //printf ("buf: %s buf[36] %c\n", buf, buf[36]); + //if (buf[36]=='C' || strstr(buf, "create") || strstr(buf, "lookup")) { +#ifdef REDUCE_MEMORY_TRACE_SIZE + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]=='C' || strstr(buf, "create") || + strstr(buf, "mkdir") || strstr(buf, "symlink") || strstr(buf, "mknod") || strstr(buf, "lookup")) { +#endif + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("strlen(buf) %d buf %s \n", strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + + /* store the request to memory */ + strcpy (memory_trace[memory_trace_size].line, buf); + memory_trace[memory_trace_size].disk_index = disk_index; + memory_trace_size ++; + + if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) { + fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES); + break; + } + if ((disk_index%100000)==0) + fprintf(stderr, "%d disk trace parsed, store %d trace lines to memory\n", disk_index, memory_trace_size); +#ifdef REDUCE_MEMORY_TRACE_SIZE + } else { + RFS_ASSERT (buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R'); + } +#endif + disk_index ++; + }; + + fprintf(stderr, "total %d disk lines %d memory lines \n", disk_index, memory_trace_size ); + +} + + +#ifdef REDUCE_MEMORY_TRACE_SIZE +inline int disk_index_to_memory_index (int disk_index) +{ + static int memory_index = 0; + if (disk_index > memory_trace[memory_index].disk_index) { + while (memory_trace[memory_index].disk_index < disk_index) { + memory_index++; + RFS_ASSERT (memory_index < MAX_MEMORY_TRACE_LINES); + } + }; + if (disk_index < memory_trace[memory_index].disk_index) { + while (memory_trace[memory_index].disk_index > disk_index) { + memory_index--; + RFS_ASSERT (memory_index >=0); + } + }; + + RFS_ASSERT (disk_index == memory_trace[memory_index].disk_index); + return memory_index; +} +#else +#define disk_index_to_memory_index(disk_index) disk_index +#endif + +#define get_line_by_disk_index(disk_index) \ + memory_trace[disk_index_to_memory_index(disk_index)].line + +inline char * find_reply_line (char * command_line, int cur_disk_index) +{ + int i; + char * line; + char * p; + int request_memory_index = disk_index_to_memory_index (cur_disk_index); + for (i=request_memory_index+1; i<request_memory_index+MAX_COMMAND_REPLY_DISTANCE && i<MAX_MEMORY_TRACE_LINES; i++) { + line = memory_trace[i].line; + if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&line[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&line[TRACE_MSGID_POS], &command_line[TRACE_MSGID_POS], p-&(line[TRACE_MSGID_POS]))) + return line; + } + } + return NULL; +} + +inline int find_reply_status (char * line) +{ + char * p; + int i=0; + + //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R'); + p = line+TRACE_MSGID_POS+2; /* at least one letter for msgid and one letter for space */ + if (strstr(p, "OK")) + return NFS3_OK; + if (strstr(p, "lookup 2")) + return 0x2; + if (strstr(p, "create d")) + return 0xd; + if (strstr(p, "setattr 1")) + return 0x1; + if (strstr(p, "lookup d")) + return 0xd; + if (strstr(p, "read d")) + return 0xd; + if (strstr(p, "write d")) + return 0xd; + if (strstr(p, "mkdir d")) + return 0xd; + printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + RFS_ASSERT (0); +} + +inline int find_reply_status_old (char * line) +{ + char * p; + int i=0; + + //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R'); + if (!strstr(line, "OK")) { + p=strstr(line, " 6 read "); + if (p) { + p+=strlen(" 6 read "); + } else { + p = strstr (line, "status=XXX"); + RFS_ASSERT (p); + p--; + RFS_ASSERT (*p==' '); + while (*p==' ') + p--; + while (*p!=' ') { + p--; + } + p++; + } + sscanf (p, "%x", &i); + if ((i<=0) || (i>10000)) + printf("line %s\n", line); + RFS_ASSERT (i>0 && i<10009); + } + return i; +} + +inline char * find_reply_trace_fh (char * line) +{ + char * p; + p = strstr (line, "OK fh"); + if (!p) + printf ("find_reply_trace_fh line %s\n", line); + RFS_ASSERT (p); + return p+6; +} + +inline int disk_index_to_dep_index(int cur_dep_index, int disk_index) +{ + int i; + for (i=cur_dep_index; i>min_dep_index; i--) { + if (dep_tab[i].disk_index == disk_index) + return i; + } + RFS_ASSERT (0); +} + +inline int is_play_candidate (int dep_index) +{ + int proc = dep_tab[dep_index].proc; + int status = dep_tab[dep_index].status; + int trace_status = dep_tab[dep_index].trace_status; + +#ifndef TAKE_CARE_CREATE_MODE_BY_DAN + /* for a failed create in trace, trace_replay just ignore many time the trace create fail + * due to access control, but trace_play will success because our access control + * may be loose (all uid/gid is mapped to single one 513:513, so we just skip these requests + */ + if ((proc==CREATE || proc==MKDIR) && (trace_status!=NFS3_OK) && (status!=NFS3ERR_RFS_MISS)) { + if (dependency_debug) + printf ("disk[%d] ignore failed create/mkdir in trace, trace_status %d line %s", + dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line); + failed_create_command_num ++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_OTHER_FAILED_COMMAND + if (((trace_status == NFS3ERR_ACCES) && (proc==READ || proc==WRITE || proc==LOOKUP)) || + ((trace_status == NFS3ERR_PERM) && (proc==SETATTR)) ){ + if (dependency_debug) + printf ("disk[%d] ignore other failed command in trace, trace_status %d line %s", + dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line); + + failed_other_command_num ++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_SYMBOLIC_LINK + if ((dep_tab[dep_index].proc==READLINK) ) { /* send request */ + skipped_readlink_command_num ++; + return FALSE; + } +#endif +#define TAKE_CARE_CUSTOM_COMMAND +/* This is actually take care in get_nextop by checking fh_map error when dep_index==min_dep_index */ +#ifndef TAKE_CARE_CUSTOM_COMMAND + /* this line has a file handle which should belong to discard but it is not + * the file handle directly appears as parent directory in a lookup request + * the return value is NOENT, the parent directory should have been initialized + * but the initialization code just ignored all lookup request which didn't success + * including NOENT even though the parent directory is still valid. + */ + if (( ((dep_tab[dep_index].disk_index==262213) || (dep_tab[dep_index].disk_index==214402)) + && !(strcmp(trace_file, "anon-lair62-011130-1100.txt")) + ) || + ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470)) + && !(strcmp(trace_file, "anon-lair62-011130-1000.txt")) + )) { + skipped_custom_command_num++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_FSSTAT_COMMAND + /* the file handle used in this command is not processed properly by pre-processing */ + if (proc==FSSTAT) { + char * trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line); + fh_map_t * fh = lookup_fh (trace_fh); + if (!fh) { + skipped_fsstat_command_num++; + return FALSE; + } + } +#endif + return TRUE; +} + +inline int is_dir_op (int proc) +{ + switch (proc) { + case MKDIR: + case CREATE: + case LINK: + case SYMLINK: + case MKNOD: + case REMOVE: + case RMDIR: + case RENAME: + return 1; + default: + return 0; + } +} + +inline int is_create_op (int proc) +{ + if (proc==CREATE || proc==MKDIR || proc==LINK || proc==SYMLINK || proc==MKNOD || proc==RENAME) + return 1; + return 0; +} + +inline int is_delete_op (int proc) +{ + if (proc==REMOVE || proc==RMDIR || proc==RENAME) + return 1; + return 0; +} + +static inline char * find_lead_trace_fh(int proc, char * line) +{ + char * p; + /* check the file handle availability */ + p = strstr (line, "fh"); + RFS_ASSERT (p); + p+=3; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line); + return p; +} + +inline char * find_another_trace_fh(int proc, char * line) +{ + char * p; + /* check the file handle availability */ + p = strstr (line, "fh2"); + RFS_ASSERT (p); + p+=4; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line); + return p; +} + +/* return the index of next request in dep_tab. + * Return -1 if there is no suitable request to send + */ +inline int get_nextop(void) +{ + int i,j, k; + int * t; + static int dep_index = -2; + char * line; + char * p; + static int min_wait_fhandle_dep_index = DEP_TAB_SIZE; + int proc; + int flag; + + //if (dep_index < min_dep_index-1) + // dep_index = min_dep_index-1; + + dep_index = min_dep_index-1; + for (i=0; i<max_dep_index-min_dep_index; i++) { + dep_index ++; + if (dep_index == max_dep_index) { + dep_index = min_dep_index; + } + + proc = dep_tab[dep_index].proc; + flag = dep_tab[dep_index].flag; + + if (dependency_debug) + printf ("get_nextop check dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); +#ifdef NO_DEPENDENCY_TABLE + if (dep_tab[dep_index].flag == DEP_FLAG_INIT) { + if (is_play_candidate(dep_index)==TRUE) { + /* the trace_fh is the file handle for the operation directory, trace_fh_2 is other file handle + * used in the request */ + if (proc==LINK || proc==RENAME) { + dep_tab[dep_index].trace_fh = find_another_trace_fh (proc, dep_tab[dep_index].line); + dep_tab[dep_index].trace_fh_2 = find_lead_trace_fh(proc, dep_tab[dep_index].line); + dep_tab[dep_index].fh = 0; + dep_tab[dep_index].fh_2 = 0; + } else { + dep_tab[dep_index].trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line); + dep_tab[dep_index].fh = 0; + dep_tab[dep_index].fh_2 = (fh_map_t *)1; + }; + dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE; +#ifdef TIME_PLAY + dep_tab[dep_index].skip_sec = skip_sec; +#endif + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_CANDIDATE\n", dep_tab[dep_index].disk_index); + } else { + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_DONE; + continue; + } + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE) ) { + + if (!dep_tab[dep_index].fh) + dep_tab[dep_index].fh = lookup_fh (dep_tab[dep_index].trace_fh); + if (!dep_tab[dep_index].fh_2) + dep_tab[dep_index].fh_2 = lookup_fh (dep_tab[dep_index].trace_fh_2); + + /* need to wait for file handle */ + if ((!dep_tab[dep_index].fh) || (!dep_tab[dep_index].fh_2)) { + if (dependency_debug) + printf("disk[%d] can not lookup file handle\n", dep_tab[dep_index].disk_index); + if (dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) { + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_CANDIDATE to DEP_FLAG_WAIT_FHANDLE\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_FHANDLE; + sfs_gettime (&dep_tab[dep_index].start); + if (dep_index < min_wait_fhandle_dep_index) + min_wait_fhandle_dep_index = dep_index; + } else { + struct ladtime tmp; + if (dep_index==min_dep_index) { + if (!profile_debug) + printf ("fh_path_map error disk[%d] state DEP_FLAG_WAIT_FHANDLE to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index); + fh_path_map_err_num ++; + dep_tab[dep_index].flag = DEP_FLAG_DONE; + continue; + } + sfs_gettime (&tmp); + SUBTIME (tmp, dep_tab[dep_index].start); +#define DEPENDENCY_TIMEOUT 5 +#ifdef TIME_PLAY + RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT + (skip_sec - dep_tab[dep_index].skip_sec)); +#else + if (tmp.sec >= DEPENDENCY_TIMEOUT) { + printf("dep_tab[%d].flag %d disk_index %d line %s\n", dep_index, + dep_tab[dep_index].flag, dep_tab[dep_index].disk_index, + dep_tab[dep_index].line); + } + RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT ); +#endif + } + continue; + } + + /* file handle ready, adjust_min_wait_fhandle_dep_index */ + if ((dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE)) { + if (dep_index == min_wait_fhandle_dep_index) { + min_wait_fhandle_dep_index = dep_tab_size; + for (j=dep_index+1; j<max_dep_index; j++) { + if (dep_tab[j].flag ==DEP_FLAG_WAIT_FHANDLE) { + min_wait_fhandle_dep_index = j; + break; + } + } + } + } + if (dependency_debug) + printf("disk[%d] found file handle\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_FHANDLE_READY; + + /* the normal file operation can be executed now */ + if (!is_dir_op (dep_tab[dep_index].proc)) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + + if (dependency_debug) + printf("disk[%d] directory operation \n", dep_tab[dep_index].disk_index); + /* the directory operation need to lock the directory first */ + if (dep_tab[dep_index].fh->lock) { + if (dependency_debug) + printf ("disk[%d] state %d to DEP_FLAG_WAIT_DIRECTORY\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_DIRECTORY; + continue; + } + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_FHANDLE_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DIRECTORY)) { + int j = dep_tab[dep_index].fh - fh_map; + if (dependency_debug) { + printf ("dep_tab[%d].disk_index %d, fh_map[%d] lock=%d\n",dep_index, dep_tab[dep_index].disk_index, j, dep_tab[dep_index].fh->lock); + printf ("trace_fh %s path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path); + printf ("trace_fh %s path %s\n", fh_map[j].trace_fh, fh_map[j].path); + } + if ((dep_tab[dep_index].fh->lock) || ((proc==RENAME) && (dep_tab[dep_index].fh_2->lock)) ) { + if (dependency_debug) + printf ("continue to wait for directory lock\n"); + continue; + } + if (dependency_debug) + printf ("dep_tab[%d] disk index %d LOCK fh_map[%d] \n", dep_index, dep_tab[dep_index].disk_index, j); + dep_tab[dep_index].fh->lock = 1; + if (proc==RENAME) + dep_tab[dep_index].fh_2->lock = 1; + + /* the non-delete directory operation can proceed now */ + if (!is_delete_op (dep_tab[dep_index].proc)) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + + /* the delete operation can proceed if nobody ahead is waiting for fhandle */ + /* probably this condition is not strong enough */ +// if ((min_wait_fhandle_dep_index<dep_index) ) { + if (dep_index!=min_dep_index) { + if (dependency_debug) + printf ("disk[%d] state %d to DEP_FLAG_WAIT_DELETE\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_DELETE; + continue; + } + dep_tab[dep_index].flag = DEP_FLAG_DIRECTORY_READY; + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_DIRECTORY_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DELETE)) { +// if (min_wait_fhandle_dep_index > dep_index) { + if (dep_index==min_dep_index) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + } +#else + if (dep_tab[dep_index].flag == DEP_FLAG_INIT){ + for (j=0, t=&(dep_tab[dep_index].dep_ops[0]); + (j<dep_tab[dep_index].init_dep_num) && (dep_tab[dep_index].cur_dep_num>0); + j++, t++) { + if (*t !=-1) { + if (dep_tab[disk_index_to_dep_index(dep_index, *t)].flag == DEP_FLAG_DONE) { + /* The depended request has been finished */ + *t = -1; + dep_tab[dep_index].cur_dep_num --; + } + } + } + + if (dep_tab[dep_index].cur_dep_num == 0) { + return dep_index; + } + } +#endif + } + + if (dependency_debug) + printf ("get_nexop return -1\n"); + return -1; +} + +int check_timeout(void) +{ + static int biod_index = 0; + int i; + int dep_index; /* index into dep_tab */ + int proc; + sfs_op_type *op_ptr; /* per operation info */ + struct ladtime timeout; + + sfs_gettime (¤t); + + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + if (biod_reqp[biod_index].in_use==TRUE) { + timeout = biod_reqp[biod_index].timeout; + if ((current.sec>timeout.sec) || + ((current.sec==timeout.sec) && (current.usec>timeout.usec))) { + + dep_index = biod_reqp[biod_index].dep_tab_index; + proc = dep_tab[dep_index].proc; + op_ptr = &Ops[proc]; + op_ptr->results.timeout_calls++; + Ops[TOTAL].results.timeout_calls++; + + finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT); + + if (is_create_op(proc)) { + dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE; + printf ("resend dep_tab[%d], disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); + } + //RFS_ASSERT (!is_create_op(proc)); + + //printf ("timeout request: biod_reqp[%d].start %d:%d timeout %d:%d current %d:%d\n", biod_index, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, timeout.sec, timeout.usec, current.sec, current.usec); + } + } + } +} + +/* Allocate a biod_req entry to send and receive request dep_tab[dep_index] + * build the cross reference between dep_tab entry and biod_req entry + */ +struct biod_req * get_biod_req(int dep_index) /* index into dep_tab */ +{ + static int biod_index = 0; + int i; + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + if (!biod_reqp[biod_index].in_use) { + biod_reqp[biod_index].in_use = 1; + biod_reqp[biod_index].dep_tab_index = dep_index; + dep_tab[dep_index].biod_req_index = biod_index; + return &(biod_reqp[biod_index]); + } + } + return NULL; +} + +/* Return index into biod_reqp + * return -1 upon failure + */ +int lookup_biod_req (int xid) +{ + static int biod_index = 0; + int i; + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + /* give a NULL as timeout pointer may cause indefinitely block */ + if (biod_reqp[biod_index].xid == xid) { + return biod_index; + } + } + return -1; +} + +extern struct ladtime test_start; +void init_time_offset(void) +{ + struct ladtime tmp1; + struct ladtime tmp2; + + test_start.sec = 0; + test_start.usec = 0; + sfs_gettime (&tmp1); /* called at initial time: tmp1 = play_starttime */ +#ifdef SPEED_UP + DIVTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime / SCALE */ +#endif +#ifdef SLOW_DOWN + MULTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime * SCALE */ +#endif + + tmp2 = trace_starttime; /* tmp2 = trace_starttime */ + SUBTIME (tmp2, tmp1); /* tmp2 = trace_starttime - play_starttime *|/ SCALE */ + time_offset = tmp2; /* time_offset = trace_starttime - play_starttime *|/ SCALE */ +} + +/* initialize timestamp and proc field of dep_tab entry */ +void init_dep_tab_entry (int dep_index) +{ + static int nfs2proc_to_rfsproc[18] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17}; + static int nfs3proc_to_rfsproc[NFS3_PROCEDURE_COUNT] = {0, 1, 2, 4, 18, 5, 6, 8, 9, 14, + 13, 21, 10, 15, 11, 12, 16, 23, 17, 20, + 22, 19}; + char * line; + int version; + int nfsproc; + int msgid; + + //line = get_line_by_disk_index (dep_tab[dep_index].disk_index); + line = dep_tab[dep_index].line; + sscanf (line, "%d.%d", &(dep_tab[dep_index].timestamp.tv_sec), &(dep_tab[dep_index].timestamp.tv_usec)); + sscanf (&line[39], "%x %x", &msgid, &nfsproc); + if (line[TRACE_VERSION_POS]=='2') { + dep_tab[dep_index].proc = nfs2proc_to_rfsproc[nfsproc]; + RFS_ASSERT (nfsproc <18); + } else { + /* This is for debug purpose */ + if (line[TRACE_VERSION_POS] !='3') { + fprintf(stderr, "line[TRACE_VERSION_POS] %c line %s\n", line[TRACE_VERSION_POS], line); + line = get_line_by_disk_index (dep_tab[dep_index].disk_index-1); + if (!line) + line = get_line_by_disk_index (dep_tab[dep_index].disk_index-2); + RFS_ASSERT (line); + fprintf(stderr, "previousline %s\n", line); + } + RFS_ASSERT (line[TRACE_VERSION_POS] =='3'); + if (nfsproc >= NFS3_PROCEDURE_COUNT) { + fprintf(stderr, "proc %d line %s\n", nfsproc, line); + + } + RFS_ASSERT (nfsproc <NFS3_PROCEDURE_COUNT); + dep_tab[dep_index].proc = nfs3proc_to_rfsproc[nfsproc]; + } + RFS_ASSERT (dep_tab[dep_index].proc >= 0 && dep_tab[dep_index].proc < NOPS); + dep_tab[dep_index].flag = DEP_FLAG_INIT; + dep_tab[dep_index].reply_line = find_reply_line (line, dep_tab[dep_index].disk_index); + if (dep_tab[dep_index].reply_line == NULL) { + //printf ("disk[%d] can not find the reply line, assume trace_status OK\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].trace_status = NFS3ERR_RFS_MISS; + missing_reply_num ++; + } else + dep_tab[dep_index].trace_status = find_reply_status (dep_tab[dep_index].reply_line); +} + +static void adjust_play_window (int flag, int * poll_timeout) +{ + struct ladtime max_window_time; + static struct ladtime max_poll_time = {0, 2000, 0}; + struct ladtime t; + int old_min = min_dep_index; + int old_max = max_dep_index; + int i; + char * line; + + for (; (dep_tab[min_dep_index].flag == DEP_FLAG_DONE) && (min_dep_index<dep_tab_size); min_dep_index++) + ; + RFS_ASSERT (min_dep_index <= max_dep_index); + + /* max_trace_window_time = current *|/ SCALE + trace_starttime */ + sfs_gettime (¤t); + +#ifdef TIME_PLAY +#ifdef SPEED_UP + MULTIME (current, PLAY_SCALE); +#endif +#ifdef SLOW_DOWN + DIVTIME (current, PLAY_SCALE); +#endif + ADDTIME (current, trace_starttime); + max_window_time = current; + + while ((max_dep_index<min_dep_index+MAX_PLAY_WINDOW) && (max_dep_index<dep_tab_size)) + { + t.sec = dep_tab[max_dep_index].timestamp.tv_sec; + t.usec = dep_tab[max_dep_index].timestamp.tv_usec; + + if (adjust_play_window_debug) + printf ("max_window_time sec %d usec %d : max_dep_index %d, t.sec %d t.usec %d\n", max_window_time.sec, max_window_time.usec, max_dep_index, t.sec, t.usec ); + + if ((t.sec>max_window_time.sec)||(t.sec==max_window_time.sec && t.usec>max_window_time.usec)) + break; + + max_dep_index++; + } + + /* Right now it is not clear how to deal with the situation where MAX_PLAY_WINDOW is reached */ + if (max_dep_index == min_dep_index+MAX_PLAY_WINDOW) { + //printf ("can not catch up the speed, max_dep_index %d reach min_dep_index %d+MAX_PLAY_WINDOW\n", max_dep_index, min_dep_index); + //printf ("."); + can_not_catch_speed_num ++; + } + //RFS_ASSERT (max_dep_index < min_dep_index+MAX_PLAY_WINDOW); +#else + ADDTIME (current, trace_starttime); + max_window_time = current; + max_dep_index = min_dep_index + MAX_PLAY_WINDOW; + if (max_dep_index >dep_tab_size) + max_dep_index = dep_tab_size; +#endif + + if (flag == BUSY) + *poll_timeout = 0; + else if (max_dep_index == dep_tab_size) { + *poll_timeout = 1000000; /* poll_timeout set to 1 second for the last request */ + } else { +#ifdef TIME_PLAY + struct ladtime tmp; + struct ladtime tmp1; + tmp.sec = dep_tab[max_dep_index].timestamp.tv_sec; + tmp.usec = dep_tab[max_dep_index].timestamp.tv_usec; + if (adjust_play_window_debug) + printf ("dep_tab[max_dep_index %d].timestamp %d:%d, max_window_time %d:%d\n", + max_dep_index, tmp.sec, tmp.usec, max_window_time.sec, max_window_time.usec); + + SUBTIME (tmp, max_window_time); +#ifdef SPEED_UP + DIVTIME (tmp, PLAY_SCALE); +#endif +#ifdef SLOW_DOWN + MULTIME (tmp, PLAY_SCALE); +#endif + tmp1 = tmp; + + if (tmp.sec > max_poll_time.sec) { + + if (rfs_debug) + printf ("dep_tab[%d].timestamp %d:%d, max_window_time %d:%d\n", + max_dep_index, dep_tab[max_dep_index].timestamp.tv_sec, dep_tab[max_dep_index].timestamp.tv_usec, max_window_time.sec, max_window_time.usec); + printf ("skip %d seconds\n", tmp.sec-max_poll_time.sec); + SUBTIME (tmp, max_poll_time); + tmp.usec = 0; + skip_sec += tmp.sec; + SUBTIME (test_start, tmp); + tmp = max_poll_time; + } + + RFS_ASSERT ((tmp.sec < 1000)); + if ((tmp.sec ==0) && (tmp.usec==0)) { + *poll_timeout = 0; + } else + *poll_timeout = tmp.sec*1000000+tmp.usec; +#else + /* + struct ladtime tmp; + struct ladtime tmp1; + tmp.sec = dep_tab[max_dep_index].timestamp.tv_sec; + tmp.usec = dep_tab[max_dep_index].timestamp.tv_usec; + tmp1.sec = dep_tab[max_dep_index-1].timestamp.tv_sec; + tmp1.usec = dep_tab[max_dep_index-1].timestamp.tv_usec; + SUBTIME (tmp, tmp1); + RFS_ASSERT ((tmp.sec < 1000)); + RFS_ASSERT ((tmp.sec>0) || ((tmp.sec==0) && (tmp.usec>0))); + *poll_timeout = tmp.sec*1000000+tmp.usec; + */ + + *poll_timeout = 100000; +#endif + } + if (rfs_debug) + printf ("adjust_play_window: flag %d min %d -> %d, max %d -> %d poll_timeout %d \n", flag, old_min, min_dep_index, old_max, max_dep_index, *poll_timeout); +} + +/* poll for usecs and receive, after receive one reply, + * return index in biod_reqp of the corresponding request + */ +int poll_and_get_reply (int usecs) +{ + int biod_index = -1; + int xid; + int error; + struct timeval zero_time = {0, 0}; /* Immediately return */ + + do { + error = biod_poll_wait (NFS_client, usecs); + switch (error) { + case -1: + if (errno == EINTR) { + error = 1; + continue; + } + if (rfs_debug) { + (void) fprintf(stderr, "biod_poll_wait error\n"); + perror (""); + (void) fflush(stderr); + } + break; + case 0: + break; + default: +#ifdef UDP + error = get_areply_udp (NFS_client, &xid, &zero_time); + // RFS_ASSERT (error!= RPC_TIMEOUT); /* we have polled and know there is data */ + // RFS_ASSERT (error!= RPC_CANTRECV); + RFS_ASSERT (error == RPC_SUCCESS); + + biod_index = lookup_biod_req (xid); + sfs_gettime (&(biod_reqp[biod_index].stop)); +#else + RFS_ASSERT (0); +#endif + } + } while (0); + return biod_index; +} + +void print_result(void) +{ + int i, j; + struct ladtime t; + int dep_index; + int avg_msecs; + unsigned long long tmp; + int avg_usecs; + + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stdout, "trace play result:\n"); + (void) fprintf(stdout, "\t percentage good_cnt bad_cnt timeout_cnt\telapsed time\t\t\taverage time\n"); + for (i=0; i<NOPS+1; i++) { + if (Ops[i].results.good_calls==0) { + avg_msecs = 0; + avg_usecs = 0; + } else { + tmp = Ops[i].results.time.sec*1000000 + Ops[i].results.time.usec; + avg_msecs = 0; + avg_usecs = tmp/Ops[i].results.good_calls; +/* + avg_msecs = (Ops[i].results.time.sec*1000 + Ops[i].results.time.usec/1000)/Ops[i].results.good_calls; + avg_usecs = (Ops[i].results.time.usec%1000)/Ops[i].results.good_calls; +*/ + } + (void) fprintf(stdout, "%11s\t%4.1f\t%4d\t%4d\t%4d\t\tsec %8d usec %8d \tusec %8d\n", + Ops[i].name, + (float)(100*Ops[i].results.good_calls)/(float)Ops[TOTAL].results.good_calls, + Ops[i].results.good_calls, Ops[i].results.bad_calls, Ops[i].results.timeout_calls, + Ops[i].results.time.sec, Ops[i].results.time.usec, avg_msecs*1000+avg_usecs); + } + (void) fflush (stdout); + } + + RFS_ASSERT (read_data_owe_GB==0); + printf("read_data_bytes %d owe %d GB and %d bytes, %d percent, adjusted %d times \n",read_data_total, read_data_owe_GB, read_data_owe, (read_data_owe_GB*1073741824+read_data_owe)/(read_data_total/100), read_data_adjust_times); + printf("write_data_bytes %d owe %d GB and %d bytes, %d percent, adjusted %d times \n",write_data_total, write_data_owe_GB, write_data_owe, (write_data_owe_GB*1073741824+write_data_owe)/(write_data_total/100), write_data_adjust_times); + printf("poll_timeout_0_num %d poll_timeout_pos_num %d\n", poll_timeout_0_num, poll_timeout_pos_num); + printf("failed_create_command_num %d\nfailed_other_command_num %d\nskipped_readlink_command_num %d\nskipped_custom_command_num %d\nfh_path_map_err_num %d\nskipped_fsstat_command_num %d\nmissing_reply_num %d\nrename_rmdir_noent_reply_num %d\nrmdir_not_empty_reply_num %d\nloose_access_control_reply_num %d\nlookup_err_due_to_rename %d\nlookup_eaccess_enoent_mismatch %d\nread_io_err_num %d\nproper_reply_num %d\n", + failed_create_command_num, + failed_other_command_num, + skipped_readlink_command_num, + skipped_custom_command_num, + fh_path_map_err_num, + skipped_fsstat_command_num, + missing_reply_num, + rename_rmdir_noent_reply_num, + rmdir_not_empty_reply_num, + loose_access_control_reply_num, + lookup_err_due_to_rename_num, + lookup_eaccess_enoent_mismatch_num, + read_io_err_num, + proper_reply_num); + + clnt_destroy(NFS_client); + biod_term(); + +// print_dump(Client_num, Child_num); + return; + dep_index = 0; + printf ("[%4d] %s \tstart %4d:%6d \n", + dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec); + for (i=1; i<dep_tab_size*2; i++) { + dep_index = event_order[i]; + if (dep_index >0) + printf ("[%4d] %s \tstart %4d:%6d \n", + dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec); + else { + dep_index = -dep_index; + t=dep_tab[dep_index].stop; + SUBTIME (t, dep_tab[dep_index].start); + printf ("\t\t\t\t\t[%4d] %s stop %4d:%6d\tinterval %4d:%6d %s\n", + dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec, t.sec, t.usec, nfs3_strerror(dep_tab[dep_index].status)); + } + } +/* + + for (i=0, j=0; i<dep_tab_size || j<dep_tab_size; ) { + if ((i==dep_tab_size) || + (LARGERTIME(dep_tab[i].start, dep_tab[j].stop) && j<dep_tab_size)) { + t=dep_tab[j].stop; + SUBTIME (t, dep_tab[j].start); + printf ("dep_tab[%d].proc %s stop %d:%d interval %d:%d status %s\n", + j, Ops[dep_tab[j].proc].name, dep_tab[j].stop.sec, dep_tab[j].stop.usec, t.sec, t.usec, nfs3_strerror(dep_tab[j].status)); + j++; + } else { + printf ("dep_tab[%d].proc %s start %d:%d \n", + i, Ops[dep_tab[i].proc].name, dep_tab[i].start.sec, dep_tab[i].start.usec); + i++; + } + } +*/ +} + +/* + * allocate and initialize client handles + */ +static int +init_rpc(void) +{ + int dummy = 0; + + /* + * Set up the client handles. We get them all before trying one + * out to insure that the client handle for LOOKUP class is allocated + * before calling op_getattr(). + */ + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname); + } + + NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent, + (uint32_t) NFS_PROGRAM, + (uint32_t) nfs_version, + RPC_ANYSOCK, &Nfs_timers[0]); + + if (NFS_client == ((CLIENT *) NULL)) { + return(-1); + } + + /* + * create credentials using the REAL uid + */ + NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid, + (int)Cur_gid, 0, NULL); + + + if (biod_init(dummy, dummy) == -1) { + return(-1); + } + + return(0); +} /* init_rpc */ + +void +init_counters(void) +{ + uint_t i; + uint_t start_msec; + + /* Ready to go - initialize operation counters */ + for (i = 0; i < NOPS + 1; i++) { + Ops[i].req_cnt = 0; + Ops[i].results.good_calls = 0; + Ops[i].results.bad_calls = 0; + Ops[i].results.timeout_calls = 0; // RFS + Ops[i].results.fast_calls = 0; + Ops[i].results.time.sec = 0; + Ops[i].results.time.usec = 0; + Ops[i].results.msec2 = 0; + } + + /* initialize timers and period variables */ + sfs_gettime(&Starttime); + Cur_time = Starttime; + start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000); + Previous_chkpnt_msec = start_msec; + Calls_this_period = 0; + Reqs_this_period = 0; + Sleep_msec_this_period = 0; + Calls_this_test = 0; + Reqs_this_test = 0; + Sleep_msec_this_test = 0; +} + +static char * +nfs3_strerror(int status) +{ + static char str[40]; + switch (status) { + case NFS3_OK: + (void) strcpy(str, "no error"); + break; + case NFS3ERR_PERM: + (void) strcpy(str, "Not owner"); + break; + case NFS3ERR_NOENT: + (void) strcpy(str, "No such file or directory"); + break; + case NFS3ERR_IO: + (void) strcpy(str, "I/O error"); + break; + case NFS3ERR_NXIO: + (void) strcpy(str, "No such device or address"); + break; + case NFS3ERR_ACCES: + (void) strcpy(str, "Permission denied"); + break; + case NFS3ERR_EXIST: + (void) strcpy(str, "File exists"); + break; + case NFS3ERR_XDEV: + (void) strcpy(str, "Cross-device link"); + break; + case NFS3ERR_NODEV: + (void) strcpy(str, "No such device"); + break; + case NFS3ERR_NOTDIR: + (void) strcpy(str, "Not a directory"); + break; + case NFS3ERR_ISDIR: + (void) strcpy(str, "Is a directory"); + break; + case NFS3ERR_INVAL: + (void) strcpy(str, "Invalid argument"); + break; + case NFS3ERR_FBIG: + (void) strcpy(str, "File too large"); + break; + case NFS3ERR_NOSPC: + (void) strcpy(str, "No space left on device"); + break; + case NFS3ERR_ROFS: + (void) strcpy(str, "Read-only file system"); + break; + case NFS3ERR_MLINK: + (void) strcpy(str, "Too many links"); + break; + case NFS3ERR_NAMETOOLONG: + (void) strcpy(str, "File name too long"); + break; + case NFS3ERR_NOTEMPTY: + (void) strcpy(str, "Directory not empty"); + break; + case NFS3ERR_DQUOT: + (void) strcpy(str, "Disc quota exceeded"); + break; + case NFS3ERR_STALE: + (void) strcpy(str, "Stale NFS file handle"); + break; + case NFS3ERR_REMOTE: + (void) strcpy(str, "Object is remote"); + break; + case NFS3ERR_BADHANDLE: + (void) strcpy(str, "Bad file handle"); + break; + case NFS3ERR_NOT_SYNC: + (void) strcpy(str, "Not sync write"); + break; + case NFS3ERR_BAD_COOKIE: + (void) strcpy(str, "Bad cookie"); + break; + case NFS3ERR_NOTSUPP: + (void) strcpy(str, "Operation not supported"); + break; + case NFS3ERR_TOOSMALL: + (void) strcpy(str, "Value too small"); + break; + case NFS3ERR_SERVERFAULT: + (void) strcpy(str, "Server fault"); + break; + case NFS3ERR_BADTYPE: + (void) strcpy(str, "Bad type"); + break; + case NFS3ERR_JUKEBOX: + (void) strcpy(str, "Jukebox"); + break; + case NFS3ERR_RFS_TIMEOUT: + (void) strcpy(str, "Timeout"); + break; + default: + (void) sprintf(str, "Unknown status %d", status); + break; + } + return (str); +} + +/* + * Check the gettimeofday() resolution. If the resolution + * is in chunks bigger than SFS_MIN_RES then the client + * does not have a usable resolution for running the + * benchmark. + */ +static void +check_clock(void) +{ + double time_res; + char tmp_hostname[HOSTNAME_LEN]; + + time_res = get_resolution(); + getmyhostname(tmp_hostname, HOSTNAME_LEN); + if( time_res > (double)SFS_MIN_RES ) + { + (void) fprintf(stderr, + "\n%s: Clock resolution too poor to obtain valid results.\n", + tmp_hostname); + (void) fprintf(stderr, + "%s: Clock resolution %f Micro seconds.\n", tmp_hostname, + time_res); + exit(175); + } + else + { + (void) fprintf(stderr, + "\n%s: Good clock resolution [ %f ] Micro seconds.\n", + tmp_hostname, time_res); + } +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns the resolution of the gettimeofday() function + * in microseconds. + */ +static double +get_resolution(void) +{ + double starttime, finishtime, besttime; + long j,delay; + int k; + + finishtime=time_so_far1(); /* Warm up the instruction cache */ + starttime=time_so_far1(); /* Warm up the instruction cache */ + delay=j=0; /* Warm up the data cache */ + for(k=0;k<10;k++) + { + while(1) + { + starttime=time_so_far1(); + for(j=0;j< delay;j++) + ; + finishtime=time_so_far1(); + if(starttime==finishtime) + delay++; + else + { + if(k==0) + besttime=(finishtime-starttime); + if((finishtime-starttime) < besttime) + besttime=(finishtime-starttime); + break; + } + } + } + return(besttime); +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns current result of gettimeofday() in microseconds. + */ +/************************************************************************/ +/* Time measurement routines. */ +/* Return time in microseconds */ +/************************************************************************/ + +static double +time_so_far1(void) +{ + /* For Windows the time_of_day() is useless. It increments in 55 */ + /* milli second increments. By using the Win32api one can get */ + /* access to the high performance measurement interfaces. */ + /* With this one can get back into the 8 to 9 microsecond */ + /* resolution. */ +#ifdef Windows + LARGE_INTEGER freq,counter; + double wintime; + double bigcounter; + + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&counter); + bigcounter=(double)counter.HighPart *(double)0xffffffff + + (double)counter.LowPart; + wintime = (double)(bigcounter/(double)freq.LowPart); + return((double)wintime*1000000.0); +#else +#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5) + struct timespec gp; + + if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1) + perror("getclock"); + return (( (double) (gp.tv_sec)*1000000.0) + + ( ((float)(gp.tv_nsec)) * 0.001 )); +#else + struct timeval tp; + + if (gettimeofday(&tp, (struct timezone *) NULL) == -1) + perror("gettimeofday"); + return ((double) (tp.tv_sec)*1000000.0) + + (((double) tp.tv_usec) ); +#endif +#endif +} + +static void +usage(void) +{ + fprintf(stderr, "trace play usage"); +} +extern void init_file_system (void) +{ + return; +} + +extern void init_dep_tab (void) +{ + int i; + memset (&dep_tab, 0, sizeof(dep_tab)); +#ifdef notdef + dep_tab[0].disk_index = 0; + dep_tab[1].disk_index = 2; + dep_tab[2].disk_index = 3; + dep_tab[3].disk_index = 5; + dep_tab[4].disk_index = 7; + dep_tab[5].disk_index = 9; + dep_tab[6].disk_index = 11; + dep_tab[7].disk_index = 12; + dep_tab[8].disk_index = 15; + dep_tab[9].disk_index = 17; + dep_tab[10].disk_index = 18; + dep_tab[11].disk_index = 20; + dep_tab_size = 12; + //dep_tab_size = 2; +#endif +} + +extern void init_dep_tab_old (void) +{ + int i; + + Cur_file_ptr = &Export_dir; + Cur_uid = Real_uid; + + for (i=0; i<5; i++) { + dep_tab[i].flag = DEP_FLAG_INIT; + dep_tab[i].proc = CREATE; + dep_tab[i].timestamp.tv_sec = trace_starttime.sec; + dep_tab[i].timestamp.tv_usec = trace_starttime.usec+i*10; + dep_tab[i].init_dep_num = 0; + dep_tab[i].cur_dep_num = 0; + } + + for (i=5; i<10; i++) { + dep_tab[i].flag = DEP_FLAG_INIT; + dep_tab[i].proc = CREATE; + dep_tab[i].timestamp.tv_sec = trace_starttime.sec+i; + dep_tab[i].timestamp.tv_usec = trace_starttime.usec; + dep_tab[i].init_dep_num = 0; + dep_tab[i].cur_dep_num = 0; + } + + dep_tab[2].init_dep_num = 2; + dep_tab[2].cur_dep_num = 2; + dep_tab[2].dep_ops[0] = 0; + dep_tab[2].dep_ops[1] = 1; + + // printf ("trace_starttime (%d %d)\n", trace_starttime.sec, trace_starttime.usec); + + /* + for (i=2; i<4; i++) { + dep_tab[i].flag = DEP_FLAG_INIT; + dep_tab[i].proc = CREATE; + dep_tab[i].timestamp.tv_sec = trace_starttime.sec+i*10; + dep_tab[i].timestamp.tv_usec = trace_starttime.usec; + dep_tab[i].init_dep_num = 0; + dep_tab[i].cur_dep_num = 0; + } + */ + + dep_tab_size = 10; + + for (i=0; i<dep_tab_size; i++) + { + printf("dep_tab[%d].timestamp (%d %d)\n", i, dep_tab[i].timestamp.tv_sec, dep_tab[i].timestamp.tv_usec); + } +} + +void show_fhandle (nfs_fh3 * fhp) +{ + struct knfs_fh * kfhp = (struct knfs_fh *)fhp; + + int dev; + + if (quiet_flag) + return; + + RFS_ASSERT (kfhp->fh_version == 1); + RFS_ASSERT (kfhp->fh_fsid_type == 0); + RFS_ASSERT (kfhp->fh_auth_type == 0); + + dev = ntohs(kfhp->fh_dev_major); + dev = dev<<8; + dev = dev + ntohs(kfhp->fh_dev_minor); + + /* kfhp->fh_dev_ino hold the inode number of export point of the mounted + * file system. For example, if /tmp/t1 is exported, /tmp/t1/t2 is mounted, + * then fh_dev_ino hold the inode number of t1, not t2 + */ + + switch (kfhp->fh_fileid_type) { + case 0: + printf("fh:type 0 root dev 0x%x dev_ino %d\n", dev, kfhp->fh_dev_ino); + break; + case 1: + printf("fh:type 1 %d %x dev %x dev_ino %x\n", + kfhp->fh_ino, kfhp->fh_generation, dev, kfhp->fh_dev_ino); + break; + case 2: + printf("fh:type2 %d %x dirino %d dev 0x%x dev_ino %x\n", + kfhp->fh_ino, kfhp->fh_generation, kfhp->fh_dirino, dev, kfhp->fh_dev_ino); + break; + default: + RFS_ASSERT (0); + } +} + +nfs_fh3 zero_fhandle; +int init_fh_map () +{ + memset (fh_map, 0, sizeof (fh_map)); + memset(fh_htable, 0, sizeof (fh_htable)); + memset (&zero_fhandle, 0, sizeof(nfs_fh3)); + printf ("SIZE of fh map %d KB\n", sizeof (fh_map)/1000); + fh_i = 0; +} + +int add_fh (int map_flag, char * trace_fh, char * path, nfs_fh3 * play_fh) +{ + char * old_trace_fh; + + /* first lookup if the entry for fh is already in the table */ + struct generic_entry * p; + + p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE); + if (p) { + RFS_ASSERT (fh_map[p->key3].flag = FH_MAP_FLAG_PARTIAL); + RFS_ASSERT (map_flag ==FH_MAP_FLAG_COMPLETE); + fh_map[p->key3].flag = map_flag; + //RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)); + if (memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)) { + int i; + printf ("fh_map[%d].trace_fh %s trace_fh %s", p->key3, fh_map[p->key3].trace_fh, trace_fh); + for (i=0; i<fh_i; i++) { + int * p1 = (int *)&(fh_map[i].play_fh); +#ifdef COMPRESS_TRACE_FH + int * p = (int *)fh_map[i].trace_fh; + printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); +#else + printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, fh_map[i].trace_fh, fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); + } +#endif + RFS_ASSERT (0); + } + RFS_ASSERT (!strcmp(fh_map[p->key3].path, path)); + /* It's possible that in fh-path-map, many trace_fh are corresponding to one path + * some of it may be the result of lookup after symlink, which is not handled + * properly as new created objects + */ +#ifdef TAKE_CARE_SYMBOLIC_LINK + RFS_ASSERT (!memcmp(&fh_map[p->key3].play_fh, &zero_fhandle, sizeof(nfs_fh3))); +#endif + memcpy (&fh_map[p->key3].play_fh, play_fh, sizeof (nfs_fh3)); + if ((fh_map_debug==1)) // || (stage ==TRACE_PLAY_STAGE)) + printf ("update the play_fh for trace_fh %s path %s \n", trace_fh, path); + return 0; + } + + fh_map[fh_i].flag = map_flag; + fh_map[fh_i].lock = 0; + strncpy(fh_map[fh_i].trace_fh, trace_fh, TRACE_FH_SIZE); + + RFS_ASSERT (strlen(path) < MAX_PLAY_PATH_SIZE); + strcpy (fh_map [fh_i].path, path); + if (map_flag==FH_MAP_FLAG_COMPLETE) + memcpy (&fh_map[fh_i].play_fh, play_fh, sizeof(nfs_fh3)); + else + memset (&fh_map[fh_i].play_fh, 0, sizeof(nfs_fh3)); + + if ((fh_map_debug==1)) { // || (stage ==TRACE_PLAY_STAGE)) { + printf ("insert trace_fh %s path %s play_fh:\n", trace_fh, path); + if (map_flag == FH_MAP_FLAG_COMPLETE) { + //show_fhandle(play_fh); + } else + printf("null\n"); + } + +/* + if (map_flag == FH_MAP_FLAG_DISCARD) + printf ("insert flag %d trace_fh %s path %s play_fh:\n", map_flag, trace_fh, path); +*/ + + generic_insert(trace_fh, TRACE_FH_SIZE, fh_i, fh_htable, FH_HTABLE_SIZE); + + fh_i = (fh_i+1); + RFS_ASSERT (fh_i < FH_MAP_SIZE); + + return 0; +}; + +inline fh_map_t * lookup_fh (char * trace_fh ) +{ + struct generic_entry * p; + p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE); + if (fh_map_debug==1) + printf ("lookup trace_fh %s\n", trace_fh); + + if (p) { + if (fh_map_debug==1) { + printf ("found: fh_i[%d] trace_fh %s path %s play_fh:\n", p->key3, fh_map[p->key3].trace_fh, fh_map[p->key3].path); + //show_fhandle(&fh_map[p->key3].play_fh); + } + RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)); + return (&(fh_map[p->key3])); + } else { + //printf ("lookup_fh %s not found\n", trace_fh); + if (stage != READ_DEP_TAB_STAGE && (fh_map_debug==1)) { + printf ("lookup not found trace_fh %s\n", trace_fh); + } + return NULL; + } + RFS_ASSERT (0); +} + +int delete_fh (char * trace_fh, int fh_map_index) +{ + generic_delete (trace_fh, TRACE_FH_SIZE, fh_map_index, fh_htable, FH_HTABLE_SIZE); + return 0; +}; + +void lookup_init_filesystem (nfs_fh3 * parent, char * name, nfs_fh3 * result) +{ + LOOKUP3args args; + LOOKUP3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + static int i=0; + + /* set up the arguments */ + (void) memcpy((char *) &args.what.dir, (char *) parent, + sizeof (nfs_fh3)); + args.what.name = name; + (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP, + xdr_LOOKUP3args, (char *) &args, + xdr_LOOKUP3res, (char *) &reply, + Nfs_timers[Init]); + sfs_gettime(&stop); + + if (rpc_stat !=RPC_SUCCESS) { + printf("rpc_stat %d\n", rpc_stat); + perror(""); + } + RFS_ASSERT (rpc_stat == RPC_SUCCESS); + RFS_ASSERT (reply.status == NFS3_OK); + (void) memcpy((char *) result, (char *) &reply.resok.object, sizeof (nfs_fh3)); +} + +void read_fh_map(char * fh_map_file) +{ + FILE * fp; + int i = 0; + char buf[1024]; + char trace_fh[TRACE_FH_SIZE]; + char intbuf[9]; + char * trace_path; + char * p; + int map_flag; +#define MAX_PATH_DEPTH 20 + nfs_fh3 parents[MAX_PATH_DEPTH]; + char * lookup_path_ptr[MAX_PATH_DEPTH]; + char lookup_path [MAX_PLAY_PATH_SIZE]; + int depth; + int new_dir_flag = 0; + + depth = 0; + memset(lookup_path_ptr, 0, sizeof(lookup_path_ptr)); + memcpy(&parents[depth], &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3)); + strcpy(lookup_path, "/"); + lookup_path_ptr[depth]=&lookup_path[0]; + + fp = fopen(fh_map_file, "r"); + RFS_ASSERT (fp!=NULL); + + intbuf[8]=0; + + memset(buf, 0, sizeof(buf)); + while (fgets(buf, 1024, fp)) { + + if (fh_i % 10000==0) + printf("%d fh_map entry read\n", fh_i); + + RFS_ASSERT (buf[strlen(buf)-1]=='\n'); + buf[strlen(buf)-1]=0; + if (fh_map_debug) { + printf("%d fgets return %s\n", fh_i, buf); + printf("depth %d lookup_path %s\n", depth, lookup_path); + } + //for (i=0; i<=depth; i++) + //printf("lookup_path_ptr[%d] %s ", i, lookup_path_ptr[i]); + //printf("\n"); +#ifdef COMPRESS_TRACE_FH + for (i=0; i<TRACE_FH_SIZE/8; i++) { + strncpy(intbuf, buf+i*8, 8); + sscanf(intbuf, "%x", trace_fh+i*8); // maybe it should be 4, anyway we don't compress for now + } + trace_path = buf+TRACE_FH_SIZE*2+1; /* +1 the trace contains only initial file handle */ +#else + memcpy(trace_fh, buf, TRACE_FH_SIZE); + trace_path = buf + TRACE_FH_SIZE +1; +#endif +#ifdef TRACE_CONTAIN_LATER_FHANDLE + trace_path = +=2; /* +3 if the trace contains both initial and later created file handle */ +#endif + +#ifdef NO_DEPENDENCY_TABLE + if (!strncmp (trace_path, "DISCARD", 7)) { + map_flag = FH_MAP_FLAG_DISCARD; + add_fh (map_flag, buf, trace_path, 0); + continue; + } +#endif + + p = trace_path+strlen(trace_path)-2; + while (*p!='/') + p--; + p++; + //RFS_ASSERT (p-trace_path<=strlen(lookup_path)+1); + //RFS_ASSERT (p>trace_path); + + if (strncmp(lookup_path, trace_path, p-trace_path)) { + printf("strncmp lookup_path %s trace_path %s for length %d\n", lookup_path, trace_path, p-trace_path); + } + RFS_ASSERT (!strncmp(lookup_path, trace_path, p-trace_path)); + //while (strncmp(lookup_path, trace_path, p-trace_path)) { /* one step deeper */ + while (strlen(lookup_path)>p-trace_path && depth>0) { + //printf("depth--\n"); + if (depth<=0) + printf ("lookup_path %s trace_path %s p-trace_path %d depth %d\n", lookup_path, trace_path, p-trace_path, depth); + RFS_ASSERT (depth>0); + *lookup_path_ptr[depth]=0; + lookup_path_ptr[depth]=0; + depth--; + } + RFS_ASSERT (strlen(lookup_path)==(p-trace_path) || (depth==0)); + + +#ifdef TRACE_CONTAIN_LATER_FHANDLE + if (buf[TRACE_FH_SIZE*2+1]=='Y') { + map_flag = FH_MAP_FLAG_COMPLETE; + } else { + map_flag = FH_MAP_FLAG_PARTIAL; + RFS_ASSERT (buf[TRACE_FH_SIZE*2+1]=='N'); + } +#else + map_flag = FH_MAP_FLAG_COMPLETE; +#endif + if ((*(p+strlen(p)-1))=='/') { + *(p+strlen(p)-1)=0; + new_dir_flag = 1; + } else + new_dir_flag = 0; + + if (map_flag == FH_MAP_FLAG_COMPLETE) { + lookup_init_filesystem (&parents[depth], p, &parents[depth+1]); + add_fh (map_flag, buf, trace_path, &parents[depth+1]); + } else + add_fh (map_flag, buf, trace_path, 0); + + if (new_dir_flag) { + /* the new fhandle is of a directory */ + lookup_path_ptr[depth+1] = lookup_path+strlen(lookup_path); + strcat (lookup_path, p); + strcat (lookup_path, "/"); + + //printf("depth++\n"); + depth++; + } + + memset(buf, 0, sizeof(buf)); + } + + if (fh_map_debug) { + for (i=0; i<fh_i; i++) { + int * p1 = (int *)&(fh_map[i].play_fh); +#ifdef COMPRESS_TRACE_FH + int * p = (int *)fh_map[i].trace_fh; + printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); +#else + printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, fh_map[i].trace_fh, fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); + } +#endif + + fprintf(stderr, "total %d requests \n", i); + } +} + +int f() +{ + return 1; +} + +inline void finish_request (int biod_index, int dep_index, int status) +{ + /* the ending operation, same as when a request time out */ + dep_tab[dep_index].stop = biod_reqp[biod_index].stop; /* RFS: to dump data */ + dep_tab[dep_index].status = status; + event_order[event_order_index++] = -dep_index; + biod_reqp[biod_index].in_use = FALSE; + dep_tab[dep_index].flag = DEP_FLAG_DONE; + if (is_dir_op(dep_tab[dep_index].proc)) { + int j; + RFS_ASSERT (dep_tab[dep_index].fh->lock = 1); + dep_tab[dep_index].fh->lock = 0; + if (dep_tab[dep_index].proc==RENAME) + dep_tab[dep_index].fh_2->lock = 0; + j = dep_tab[dep_index].fh-fh_map; + if (dependency_debug) { + printf ("fh_map[%d] is UNlocked\n",j); + printf ("trace_fh %d path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path); + printf ("trace_fh %d path %s\n", fh_map[j].trace_fh, fh_map[j].path); + } + } + num_out_reqs --; +} + +/* the request argument may have pointers pointing to buffers, e.g. the name in lookup, + * the target of symlink, the write data */ +char arg_res[MAX_ARG_RES_SIZE]; +int poll_timeout = 0; /* timeout in usecs */ +char buf1 [MAX_BUF1_SIZE]; +char buf2 [MAX_BUF2_SIZE]; + +int execute_next_request () +{ + int dep_index; + int proc; + char * line; + struct biod_req * reqp; + sfs_op_type *op_ptr; /* per operation info */ + struct ladtime call_timeout; + + start_profile (&valid_get_nextop_profile); + start_profile (&invalid_get_nextop_profile); + dep_index = get_nextop(); + if (dep_index == -1) { + end_profile (&invalid_get_nextop_profile); + return dep_index; + }; + end_profile (&valid_get_nextop_profile); + + start_profile (&prepare_argument_profile); + line = dep_tab[dep_index].line; + if ((dep_index%(10000))==0) { +#ifndef TIME_PLAY + fprintf (stderr, "processing dep_tab[%d] disk_index %d num_out_reqs %d \n", dep_index, dep_tab[dep_index].disk_index, num_out_reqs); +#else + fprintf (stderr, "processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE); +#ifdef SPEED_UP + if (can_not_catch_speed_num < 2000) { + PLAY_SCALE ++; + printf ("set PLAY_SCALE to %d\n", PLAY_SCALE); + }; + if (can_not_catch_speed_num > 50000) { + PLAY_SCALE /= 2; + } else { + if (can_not_catch_speed_num > 5000) { + PLAY_SCALE -= 2; + if (PLAY_SCALE < 1) + PLAY_SCALE = 1; + } + } +#endif + can_not_catch_speed_num = 0; +#endif + } + if (rfs_debug) + printf ("processing dep_tab[%d] disk_index %d %s\n", dep_index, dep_tab[dep_index].disk_index, line); + + proc = dep_tab[dep_index].proc; + rfs_Ops[proc].setarg (dep_index, line, arg_res, buf1, buf2); + + op_ptr = &Ops[proc]; + reqp = get_biod_req (dep_index); + RFS_ASSERT (reqp); + + call_timeout.sec = 2; //Nfs_timers[op_ptr->call_class].tv_sec; + call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; + + /* make the call */ + sfs_gettime(&(reqp->start)); + end_profile (&prepare_argument_profile); + start_profile (&biod_clnt_call_profile); +#define REAL_PLAY +#ifdef REAL_PLAY + reqp->xid = biod_clnt_call(NFS_client, rfs_Ops[proc].nfsv3_proc, + rfs_Ops[proc].xdr_arg, arg_res); +#else + + reqp->xid = dep_index+1; /* just fake a message id and let it expire */ +#endif + if (reqp->xid != 0) { + reqp->timeout = reqp->start; + ADDTIME (reqp->timeout, call_timeout); + num_out_reqs++; + dep_tab[dep_index].flag = DEP_FLAG_SENT; + event_order[event_order_index++] = dep_index; + } else + RFS_ASSERT (0); + + dep_tab[dep_index].start = reqp->start; /* RFS: to dump data */ + end_profile (&biod_clnt_call_profile); +} + +inline void check_reply (int proc, int biod_index, int dep_index, int status, char * errmsg, int trace_status) +{ + if (((status!=trace_status)) && (trace_status!=NFS3ERR_RFS_MISS)) { + if (!profile_debug) + printf ("receive problem reply, xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d command disk index %d\n", biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec, dep_tab[dep_index].disk_index); +#ifndef TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES + /* these files is not looked up and is not create/mkdir/symlink/link/mknod ed before they + * are refered by name through rename, remove + */ + if ((proc==RENAME || proc==REMOVE) && (status==NFS3ERR_NOENT) && (trace_status ==0)) { + /* current initialization doesnot take care of rename source, if there is no + * create or lookup before that source, the source object will not exist when + * rename occurs + */ + rename_rmdir_noent_reply_num++; + } else +#endif +#ifndef TAKE_CARE_SYMBOLIC_LINK + if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status==NFS3ERR_NOENT)) { + /* in the original trace, first lookup return NOENT, then symlink is executed, then lookup return OK + * the initialization considers only the lookup return OK and created the file in the initialization + * so in trace play the first lookup return OK + */ + RFS_ASSERT (1); + } else if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) && (trace_status == 0)) { + /* due to similar reason as above, the initialization code initializes the symbolic link as a normal + * file already + */ + RFS_ASSERT (1); + } else +#endif +#ifndef TAKE_CARE_NOEMPTY_RMDIR + /* the remove packet seems got lost in the trace capture, so replay can not finish */ + if ((proc==RMDIR) && (status==NFS3ERR_NOTEMPTY)) { + RENAME3args args; + RENAME3res reply; /* the reply */ + RMDIR3args * rmdir_argp; + enum clnt_stat rpc_stat; /* result from RPC call */ + + rfs_Ops[proc].setarg (dep_index, dep_tab[dep_index].line, arg_res, buf1, buf2); + rmdir_argp = (RMDIR3args *)arg_res; + + memcpy(&args.from, &(rmdir_argp->object), sizeof (diropargs3)); + memcpy(&args.to.dir, &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3)); + args.from.name = buf1; /* the buf1 is already filled when parsing rmdir */ + args.to.name = buf2; + sprintf(buf2, "rmdir_%d_%s", dep_tab[dep_index].disk_index, rmdir_argp->object.name); + + rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME, + xdr_RENAME3args, (char *) &args, + xdr_RENAME3res, (char *) &reply, + Nfs_timers[Init]); + RFS_ASSERT (rpc_stat == RPC_SUCCESS); + if (reply.status!=NFS3_OK) + printf ("change rmdir into rename, reply.status %d\n", reply.status); + RFS_ASSERT (reply.status==NFS3_OK); + rmdir_not_empty_reply_num ++; +#endif +#ifndef TAKE_CARE_ACCESS_ERROR + } else if ((status==0) && (trace_status==NFS3ERR_ACCES)) { + loose_access_control_reply_num ++; +#endif +#ifdef NO_DEPENDENCY_TABLE + } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3_OK)) { + lookup_err_due_to_rename_num ++; +#endif +#ifndef TAKE_CARE_LOOKUP_EACCESS_ENOENT_MISMATCH + /* if the looked return EACCESS in the trace, means the object still exists + * should have initialized, right not don't initialize it, hence play status + * could be ENOENT + */ + } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3ERR_ACCES)) { + lookup_eaccess_enoent_mismatch_num ++; +#endif +#ifdef TOLERANT_READ_IO_ERR + } else if ((proc==READ) && (status==NFS3ERR_IO) && (trace_status==NFS3_OK)) { + read_io_err_num ++; +#endif + } else { + int i; + for (i=min_dep_index; i<max_dep_index; i++) + printf ("dep_tab[%d].disk_index %d, flag %d line %s\n", i, dep_tab[i].disk_index, dep_tab[i].flag, dep_tab[i].line); + RFS_ASSERT (0); + } + } else + proper_reply_num ++; + +} + +/* return -1 if there is no reply being received + * return the dep_index if the corresponding reply has been received + */ +inline int receive_next_reply (int busy_flag) +{ + int dep_index; + int biod_index; + int proc; + char * line; + char * reply_line; + sfs_op_type *op_ptr; /* per operation info */ + int ret; + int status; + int trace_status; + char * errmsg; + + /* wait for reply */ + start_profile (&valid_poll_and_get_reply_profile); + start_profile (&invalid_poll_and_get_reply_profile); + + if (busy_flag == BUSY) { + poll_timeout = 0; + poll_timeout_0_num ++; + } else { + poll_timeout = 10000; /* 10000 or 2000 is a better number in non-debugging state */ + poll_timeout_pos_num ++; + } + + biod_index = poll_and_get_reply (poll_timeout); + if (biod_index==-1) { + end_profile (&invalid_poll_and_get_reply_profile); + return -1; + }; + end_profile (&valid_poll_and_get_reply_profile); + + start_profile (&decode_reply_profile); + /* check the corresponding request */ + dep_index = biod_reqp[biod_index].dep_tab_index; + proc = dep_tab[dep_index].proc; + op_ptr = &Ops[proc]; + + if (dep_tab[dep_index].flag != DEP_FLAG_SENT) { + printf("dep_tab[%d].flag %d proc %d status %d start %d:%d stop %d:%d\n", + dep_index, dep_tab[dep_index].flag, proc, dep_tab[dep_index].status, + dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec, + dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec ); + printf ("received reply for timeout requests dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); + return dep_index; + } + RFS_ASSERT (dep_tab[dep_index].flag == DEP_FLAG_SENT); + + /* decode the reply */ + rfs_Ops[proc].setres (arg_res, buf1); + ret = proc_header (NFS_client, rfs_Ops[proc].xdr_res, arg_res); + RFS_ASSERT (ret == RPC_SUCCESS); + status = *((int *)arg_res); + errmsg = nfs3_strerror (status); + end_profile (&decode_reply_profile); + + start_profile (&check_reply_profile); + /* compare with the reply in the trace */ + line = dep_tab[dep_index].line; + reply_line = dep_tab[dep_index].reply_line; + trace_status = dep_tab[dep_index].trace_status; + + /* print the result, trace play progress indicator */ + if (rfs_debug || ( !profile_debug && ((dep_index %10000)==0) ) ) + fprintf (stdout, "dep_tab[%d], disk_index %d, receive reply, rpc_ret %d xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d \n", dep_index, dep_tab[dep_index].disk_index, ret, biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec); + + /* error checking */ + check_reply (proc, biod_index, dep_index, status, errmsg, trace_status); + + /* free resources */ + finish_request (biod_index, dep_index, status); + + /* get statistics */ + if (status == trace_status) { + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + } else { + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + sfs_elapsedtime (op_ptr, &(biod_reqp[biod_index].start), &(biod_reqp[biod_index].stop)); + end_profile (&check_reply_profile); + + //start_profile (&add_create_object_profile); +/* +#ifndef TAKE_CARE_SYMBOLIC_LINK + if (trace_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==MKNOD)) { +#else +#endif +*/ + if (trace_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==SYMLINK || proc==MKNOD)) { + RFS_ASSERT (reply_line); + if (status!=NFS3_OK) { + RFS_ASSERT (proc==SYMLINK); + } else { + if (proc!=SYMLINK || line[TRACE_VERSION_POS]!='2') + add_new_file_system_object(proc, dep_index, line, reply_line); + } + } + //end_profile (&add_create_object_profile); +} + +inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line) +{ + char * child_trace_fh; + fh_map_t * parent_entryp; + char component_name[MAX_PLAY_PATH_SIZE]; + char * parent_trace_fh; + char child_path[MAX_PLAY_PATH_SIZE]; + post_op_fh3 * post_op_fh3_child; + char * reply_trace_fh; + nfs_fh3 * child_fh3; + + parent_trace_fh = strstr (line, "fh"); + RFS_ASSERT (parent_trace_fh); + parent_trace_fh +=3; + parent_entryp = lookup_fh (parent_trace_fh); + RFS_ASSERT (parent_entryp); + parse_name (parent_trace_fh+65, component_name); + strcpy (child_path, parent_entryp->path); + strcat (child_path, "/"); + strcat (child_path, component_name); + + /* find the corresponding create request */ + //printf ("before find reply trace_fh reply_line %s\n", reply_line); + reply_trace_fh = find_reply_trace_fh (reply_line); + RFS_ASSERT (reply_trace_fh != NULL); + switch (proc) { + case CREATE: + RFS_ASSERT (((CREATE3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((CREATE3res *)arg_res)->res_u.ok.obj.handle; + break; + case MKDIR: + RFS_ASSERT (((MKDIR3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((MKDIR3res *)arg_res)->res_u.ok.obj.handle; + break; + case SYMLINK: + RFS_ASSERT (((SYMLINK3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((SYMLINK3res *)arg_res)->res_u.ok.obj.handle; + break; + case MKNOD: + RFS_ASSERT (((MKNOD3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((MKNOD3res *)arg_res)->res_u.ok.obj.handle; + break; + case LOOKUP: + RFS_ASSERT (proc==LOOKUP); + child_fh3 = &((LOOKUP3res *)arg_res)->res_u.ok.object; + break; + default: + RFS_ASSERT (0); + } + RFS_ASSERT (reply_trace_fh[TRACE_FH_SIZE]==' '); + reply_trace_fh[TRACE_FH_SIZE] = 0; + add_fh (FH_MAP_FLAG_COMPLETE, reply_trace_fh, child_path, child_fh3); /* exist flag is not used now, set to 1 */ + reply_trace_fh[TRACE_FH_SIZE] = ' '; +} + +/* initialize timestamp and proc field of dep_tab entry */ +void trace_play(void) +{ + + /* The flag to indicate whether trace_player is BUSY. Trace_player is BUSY + * when either there is request to send or there is reply being + * received. Otherwise it is IDLE. The timeout for polling replies + * is set to 0 when BUSY, it is set to the waiting time to the first + * request outside of current <min_dep_index, max_dep_index> window when IDLE. + */ + int busy_flag = BUSY; + + //int dep_index; /* index into dependency table: dep_tab */ + //int biod_index; /* index into outstanding requests: biod_reqp */ + + int count = 0; + min_dep_index = 0; + max_dep_index = 0; + adjust_play_window(busy_flag, &poll_timeout); + + start_profile (&total_profile); + while ((min_dep_index<dep_tab_size) || (num_out_reqs>0)) { + + if (busy_flag == IDLE) { + //start_profile (&check_timeout_profile); + check_timeout(); + //end_profile (&check_timeout_profile); + } + + //start_profile (&adjust_play_window_profile); + //adjust_play_window (flag, &poll_timeout); + //adjust_play_window (flag+(max_dep_index-min_dep_index), &poll_timeout); + adjust_play_window (busy_flag, &poll_timeout); + if (rfs_debug) + printf("num_out_reqs %d\n", num_out_reqs); + busy_flag = IDLE; + //end_profile (&adjust_play_window_profile); + + start_profile (&execute_next_request_profile); + while (execute_next_request()!=-1) + busy_flag = BUSY; + end_profile (&execute_next_request_profile); + + start_profile (&receive_next_reply_profile); + /* actually the performance of two policy seems to be same */ +//#define SEND_HIGHER_PRIORITY_POLICY +#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY + +#ifdef SEND_HIGHER_PRIORITY_POLICY + receive_next_reply(IDLE); +#endif +#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY + busy_flag = IDLE; + while (receive_next_reply(busy_flag)!=-1) + busy_flag = BUSY; +#endif + end_profile (&receive_next_reply_profile); + } + end_profile (&total_profile); + + print_profile ("total_profile", &total_profile); + printf("\n"); + //print_profile ("check_timeout", &check_timeout_profile); + //printf("\n"); + //print_profile ("adjust_play_window", &adjust_play_window_profile); + //printf("\n"); + print_profile ("execute_next_request_profile", &execute_next_request_profile); + print_profile ("valid_get_nextop_profile", &valid_get_nextop_profile); + print_profile ("invalid_get_nextop_profile", &invalid_get_nextop_profile); + print_profile ("prepare_argument", &prepare_argument_profile); + print_profile ("biod_clnt_call", &biod_clnt_call_profile); + printf("\n"); + print_profile ("receive_next_reply", &receive_next_reply_profile); + print_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile); + print_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile); + print_profile ("decode_reply", &decode_reply_profile); + print_profile ("check_reply", &check_reply_profile); + //print_profile ("add_create_object", &add_create_object_profile); + printf("\n"); + + printf ("min_dep_index %d dep_tab_size %d num_out_reqs %d\n", min_dep_index, dep_tab_size, num_out_reqs); +} +/* sfs_c_chd.c */ diff --git a/TBBT/trace_play/sfs_c_chd.c.old.2 b/TBBT/trace_play/sfs_c_chd.c.old.2 new file mode 100644 index 0000000..a8c71f7 --- /dev/null +++ b/TBBT/trace_play/sfs_c_chd.c.old.2 @@ -0,0 +1,2294 @@ +#ifndef lint +static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * -------------------------- sfs_c_chd.c ------------------------- + * + * The sfs child. Routines to initialize child parameters, + * initialize test directories, and generate load. + * + *.Exported_Routines + * void child(int, float, int, char *); + * void init_fileinfo(void); + * void init_counters(void); + * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type, + * sfs_file_type); + * int check_access(struct *stat) + * int check_fh_access(); + * + *.Local_Routines + * void check_call_rate(void); + * void init_targets(void); + * void init_dirlayout(void); + * void init_rpc(void); + * void init_testdir(void); + * int do_op(void); + * int op(int); + * + *.Revision_History + * 21-Aug-92 Wittle randfh() uses working set files array. + * init_fileinfo() sets up working set. + * 02-Jul-92 Teelucksingh Target file size now based on peak load + * instead of BTDT. + * 04-Jan-92 Pawlowski Added raw data dump hooks. + * 16-Dec-91 Wittle Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" +#include "rfs_c_def.h" +#include "generic_hash.h" +#include "nfsd_nfsfh_cust.h" + +extern struct hostent *Server_hostent; + +#define PROB_SCALE 1000L +#define _M_MODULUS 2147483647L /* (2**31)-1 */ + +#define _GROUP_DIVISOR 500 +#define _FILES_PER_GROUP 4 +#define _MIN_GROUPS 12 +#define _WORKING_SET_AT_25_OPS_PER_SEC 975 + +/* + * ----------------------- External Definitions ----------------------- + */ +extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *); +extern enum clnt_stat proc_header(CLIENT *cl, xdrproc_t xdr_results, void *results_ptr); +extern int biod_poll_wait(CLIENT *, uint32_t); +extern enum clnt_stat get_areply_udp (CLIENT * cl, uint32_t *xid, struct timeval *timeout); +extern char * parse_name (char * t, char * buf); + +/* forward definitions for local functions */ +static int init_rpc(void); + +/* RFS: forward definitions for local functions */ +void init_ops(void); +static void init_signal(); +extern void init_file_system (void); +extern void init_dep_tab (void); +static void read_trace(char * trace_file); +static void read_fh_map(); +static void init_play (char * mount_point); +static void trace_play(void); +static void print_result(void); +static int get_nextop(void); +static int check_timeout(void); +static struct biod_req * get_biod_req(int dep_tab_index); +static int lookup_biod_req (int xid); +static void init_time_offset(void); +static void adjust_play_window (int flag, int * poll_timeout); +static int poll_and_get_reply (int usecs); +static char * nfs3_strerror(int status); +static void check_clock(void); +static double time_so_far1(void); +static double get_resolution(void); +static void usage(void); +void init_dep_tab_entry (int dep_index); +extern fh_map_t * lookup_fh (char * trace_fh ); +static inline void finish_request (int biod_index, int dep_index, int status); +static void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line); + +/* + * ------------- Per Child Load Generation Rate Variables ----------- + */ +static uint_t Calls_this_period; /* calls made during the current run period */ +static uint_t Calls_this_test; /* calls made during the test so far */ +static uint_t Reqs_this_period; /* reqs made during the current run period */ +static uint_t Reqs_this_test; /* reqs made during the test so far */ +static uint_t Sleep_msec_this_test; /* msec slept during the test so far */ +static uint_t Sleep_msec_this_period; +static uint_t Previous_chkpnt_msec; /* beginning time of current run period */ +static int Target_sleep_mspc; /* targeted sleep time per call */ + +static char io_buf[BUFSIZ]; /* io buffer for print out messages */ + +char * sfs_Myname; +int Log_fd; /* log fd */ +char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */ +int Validate = 0; /* fake variable */ +int Child_num = 0; /* fake: child index */ +int Tcp = 0; /* We implement UDP first */ +int Client_num = 1; /* fake: number of client */ +uid_t Real_uid; +gid_t Cur_gid; +uid_t Cur_uid; +/* + * ------------------------- SFS Child ------------------------- + */ + +void print_usage() +{ + printf("sfs3 hostname:mount_dir trace_file fh_map_file\n"); + exit; +} + +void read_dep_tab() +{ +#ifdef NO_DEPENDENCY_TABLE + int i; + char * line; + char * trace_fh; + fh_map_t * fh_map_entry; + int req_num_with_new_fh = 0; + int req_num_with_discard_fh = 0; + int req_num_with_init_fh =0; + + for (i=0; i<memory_trace_size; i++) { + line = memory_trace[i].line; + if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='C') { + trace_fh = strstr (line, "fh"); + RFS_ASSERT (trace_fh); + trace_fh += 3; + fh_map_entry = lookup_fh (trace_fh); + if (fh_map_entry && (fh_map_entry->flag==FH_MAP_FLAG_DISCARD) ) { + req_num_with_discard_fh ++; + continue; + } + if (fh_map_entry) + req_num_with_init_fh ++; + else + req_num_with_new_fh ++; + + dep_tab[dep_tab_size].disk_index = memory_trace[i].disk_index; + dep_tab[dep_tab_size].line = memory_trace[i].line; + if ((dep_tab_size%100000)==0) + printf ("dep_tab[%d].disk_index %d = memory_trace[%d].disk_index %d\n", dep_tab_size, dep_tab[dep_tab_size].disk_index, i, memory_trace[i].disk_index); + dep_tab_size ++; + } + } +#else + RFS_ASSERT (0); +#endif + printf ("read_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); +} + +void init_profile() +{ + memset (&total_profile, 0, sizeof(total_profile)); + + memset (&execute_next_request_profile, 0, sizeof(execute_next_request_profile)); + memset (&valid_get_nextop_profile, 0, sizeof(valid_get_nextop_profile)); + memset (&invalid_get_nextop_profile, 0, sizeof(invalid_get_nextop_profile)); + memset (&prepare_argument_profile, 0, sizeof(prepare_argument_profile)); + memset (&biod_clnt_call_profile, 0, sizeof(biod_clnt_call_profile)); + + memset (&receive_next_reply_profile, 0, sizeof(receive_next_reply_profile)); + memset (&valid_poll_and_get_reply_profile, 0, sizeof(valid_poll_and_get_reply_profile)); + memset (&invalid_poll_and_get_reply_profile, 0, sizeof(invalid_poll_and_get_reply_profile)); + memset (&decode_reply_profile, 0, sizeof(decode_reply_profile)); + memset (&check_reply_profile, 0, sizeof(check_reply_profile)); + memset (&add_create_object_profile, 0, sizeof(add_create_object_profile)); + + memset (&check_timeout_profile, 0, sizeof(check_timeout_profile)); + memset (&adjust_play_window_profile, 0, sizeof(adjust_play_window_profile)); +} + +int main(int argc, char ** argv) +{ + extern char * optarg; + char tracefile[256]; + int i; + int memory_trace_size; + + if (argc==2 && !strcmp(argv[1],"-help")) { + print_usage(); + exit(0); + } + + check_clock(); + getmyhostname(lad_hostname, HOSTNAME_LEN); + + init_ops(); + /* + * Get the uid and gid information. + */ + Real_uid = getuid(); + Cur_gid = getgid(); + //Real_uid = 513; + //Cur_gid = 513; + + Nfs_timers = Nfs_udp_timers; + + init_file_system (); + + init_signal(); + //init_play (argv[1]); + init_play ("capella:/p5/RFSFS"); + init_profile(); + init_fh_map(); + //read_fh_map (argv[3]); + read_fh_map ("fh-path-map"); + init_dep_tab(); /* and dep_tab_size */ + //read_trace (argv[2]); + read_trace ("anon-lair62-011130-1100.txt"); + stage = READ_DEP_TAB_STAGE; + read_dep_tab(); + + for (i=0; i<dep_tab_size; i++) { + RFS_ASSERT (dep_tab[i].flag == DEP_FLAG_FREE) + init_dep_tab_entry (i); + } + stage = TRACE_PLAY_STAGE; + init_time_offset(); + trace_play (); + print_result(); + printf("ffff\n"); +} + +void init_ops (void) +{ + Ops = nfsv3_Ops; + nfs_version = NFS_V3; +} + +/* Set up the signal handlers for all signals */ +void init_signal() +{ +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + struct sigaction sig_act, old_sig_act; + + /* use XOPEN signal handling */ + + sig_act.sa_handler = generic_catcher; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + + /* signals handlers for signals used by sfs */ + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGINT"); + exit(135); + } + + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) { + perror("sigaction failed: SIGTERM"); + exit(137); + } +#else + /* signals handlers for signals used by sfs */ + (void) signal(SIGINT, sfs_cleanup); + // RFS (void) signal(SIGALRM, sfs_alarm); + (void) signal(SIGTERM, sfs_cleanup); +#endif +} + +void +init_play ( + char * mount_point) /* Mount point for remote FS */ +{ + char namebuf[NFS_MAXNAMLEN] = "trace_play"; /* unique name for this program */ + CLIENT * mount_client_ptr; /* Mount client handle */ + + if (!rfs_debug); + (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ); + + sfs_Myname = namebuf; + + /* + * May require root priv to perform bindresvport operation + */ + mount_client_ptr = lad_getmnt_hand(mount_point); + if (mount_client_ptr == NULL) { + exit(145); + } + + /* + * should be all done doing priv port stuff + */ + + if (init_rpc() == -1) { + (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname); + (void) generic_kill(0, SIGINT); + exit(146); + } + + + /* + * finish all priv bindresvport calls + * reset uid + */ + if (setuid(Real_uid) != (uid_t)0) { + (void) fprintf(stderr,"%s: %s%s", sfs_Myname, + "cannot perform setuid operation.\n", + "Do `make install` as root.\n"); + } + + init_mount_point(0, mount_point, mount_client_ptr); + + + /* + * Cleanup client handle for mount point + */ + clnt_destroy(mount_client_ptr); + + init_counters(); + + +} + +void read_trace (char * tracefile) +{ + FILE * fp; + char buf[1024]; + // char * t=buf; + int disk_index=0; + + fp = fopen(tracefile, "r"); + RFS_ASSERT (fp!=NULL); + while (fgets(buf, 1024, fp)) { + //printf ("buf: %s buf[36] %c\n", buf, buf[36]); + //if (buf[36]=='C' || strstr(buf, "create") || strstr(buf, "lookup")) { +#ifdef REDUCE_MEMORY_TRACE_SIZE + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]=='C' || strstr(buf, "create") || + strstr(buf, "mkdir") || strstr(buf, "symlink") || strstr(buf, "mknod") || strstr(buf, "lookup")) { +#endif + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("strlen(buf) %d buf %s \n", strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + + /* store the request to memory */ + strcpy (memory_trace[memory_trace_size].line, buf); + memory_trace[memory_trace_size].disk_index = disk_index; + memory_trace_size ++; + + if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) { + printf ("memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES); + break; + } + if ((disk_index%100000)==0) + fprintf(stderr, "%d disk trace parsed, store %d trace lines to memory\n", disk_index, memory_trace_size); +#ifdef REDUCE_MEMORY_TRACE_SIZE + } else { + RFS_ASSERT (buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R'); + } +#endif + disk_index ++; + }; + + fprintf(stderr, "total %d disk lines %d memory lines \n", disk_index, memory_trace_size ); + +} + + +#ifdef REDUCE_MEMORY_TRACE_SIZE +inline int disk_index_to_memory_index (int disk_index) +{ + static int memory_index = 0; + if (disk_index > memory_trace[memory_index].disk_index) { + while (memory_trace[memory_index].disk_index < disk_index) { + memory_index++; + RFS_ASSERT (memory_index < MAX_MEMORY_TRACE_LINES); + } + }; + if (disk_index < memory_trace[memory_index].disk_index) { + while (memory_trace[memory_index].disk_index > disk_index) { + memory_index--; + RFS_ASSERT (memory_index >=0); + } + }; + + RFS_ASSERT (disk_index == memory_trace[memory_index].disk_index); + return memory_index; +} +#else +#define disk_index_to_memory_index(disk_index) disk_index +#endif + +#define get_line_by_disk_index(disk_index) \ + memory_trace[disk_index_to_memory_index(disk_index)].line + +inline char * find_reply_line (char * command_line, int cur_disk_index) +{ + int i; + char * line; + char * p; + int request_memory_index = disk_index_to_memory_index (cur_disk_index); + for (i=request_memory_index+1; i<request_memory_index+MAX_COMMAND_REPLY_DISTANCE && i<MAX_MEMORY_TRACE_LINES; i++) { + line = memory_trace[i].line; + if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&line[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&line[TRACE_MSGID_POS], &command_line[TRACE_MSGID_POS], p-&(line[TRACE_MSGID_POS]))) + return line; + } + } + return NULL; +} + +inline int find_reply_status (char * line) +{ + char * p; + int i=0; + + //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R'); + if (!strstr(line, "OK")) { + p=strstr(line, " 6 read "); + if (p) { + p+=strlen(" 6 read "); + } else { + p = strstr (line, "status=XXX"); + RFS_ASSERT (p); + p--; + RFS_ASSERT (*p==' '); + while (*p==' ') + p--; + while (*p!=' ') { + p--; + } + p++; + } + sscanf (p, "%x", &i); + if ((i<=0) || (i>10000)) + printf("line %s\n", line); + RFS_ASSERT (i>0 && i<10009); + } + return i; +} + +inline char * find_reply_trace_fh (char * line) +{ + char * p; + p = strstr (line, "OK fh"); + if (!p) + printf ("find_reply_trace_fh line %s\n", line); + RFS_ASSERT (p); + return p+6; +} + +inline int disk_index_to_dep_index(int cur_dep_index, int disk_index) +{ + int i; + for (i=cur_dep_index; i>min_dep_index; i--) { + if (dep_tab[i].disk_index == disk_index) + return i; + } + RFS_ASSERT (0); +} + +inline int is_play_candidate (int dep_index) +{ +#ifndef TAKE_CARE_CREATE_MODE_BY_DAN + int trace_reply_status; + char * reply_line; + if ((dep_tab[dep_index].proc==CREATE)) { + /* for a failed create in trace, trace_replay just ignore */ + reply_line = find_reply_line (dep_tab[dep_index].line, dep_tab[dep_index].disk_index); + if (reply_line == NULL) { + if (dependency_debug) + printf ("disk[%d] can not find the reply line, assume trace_reply_status OK\n", dep_tab[dep_index].disk_index); + trace_reply_status = NFS3_OK; + } else + trace_reply_status = find_reply_status (reply_line); + /* many time the trace create fail due to access control, but trace_play will success because our access control + * may be loose (all uid/gid is mapped to single one 513:513, so we just skip these requests + */ + if ((dep_tab[dep_index].proc == CREATE) && (trace_reply_status!=NFS3_OK)) { + if (dependency_debug) + printf ("disk[%d] ignore failed create in trace, trace_reply_status %d line %s", dep_tab[dep_index].disk_index, trace_reply_status, dep_tab[dep_index].line); + failed_create_command_num ++; + return FALSE; + } + } +#endif +#ifndef TAKE_CARE_SYMBOLIC_LINK + if ((dep_tab[dep_index].proc==READLINK)) { /* send request */ + skipped_readlink_command_num ++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_CUSTOM_COMMAND + /* this line has a file handle which should belong to discard but it is not + * the file handle directly appears as parent directory in a lookup request + * the return value is NOENT, the parent directory should have been initialized + * but the initialization code just ignored all lookup request which didn't success + * including NOENT even though the parent directory is still valid. + */ + if ((dep_tab[dep_index].disk_index==262213 || + dep_tab[dep_index].disk_index==214402 )) { + skipped_custom_command_num++; + return FALSE; + } +#endif + return TRUE; +} + +inline int is_dir_op (int proc) +{ + switch (proc) { + case MKDIR: + case CREATE: + case LINK: + case SYMLINK: + case MKNOD: + case REMOVE: + case RMDIR: + case RENAME: + return 1; + default: + return 0; + } +} + +inline int is_create_op (int proc) +{ + if (proc==CREATE || proc==MKDIR || proc==LINK || proc==SYMLINK || proc==MKNOD || proc==RENAME) + return 1; + return 0; +} + +inline int is_delete_op (int proc) +{ + if (proc==REMOVE || proc==RMDIR || proc==RENAME) + return 1; + return 0; +} + +inline char * find_lead_trace_fh(int proc, char * line) +{ + char * p; + /* check the file handle availability */ + p = strstr (line, "fh"); + RFS_ASSERT (p); + p+=3; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line); + return p; +} + +inline char * find_another_trace_fh(int proc, char * line) +{ + char * p; + /* check the file handle availability */ + p = strstr (line, "fh2"); + RFS_ASSERT (p); + p+=4; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line); + return p; +} + +/* return the index of next request in dep_tab. + * Return -1 if there is no suitable request to send + */ +inline int get_nextop(void) +{ + int i,j, k; + int * t; + static int dep_index = -2; + char * line; + char * p; + static int min_wait_fhandle_dep_index = DEP_TAB_SIZE; + int proc; + int flag; + + //if (dep_index < min_dep_index-1) + // dep_index = min_dep_index-1; + + dep_index = min_dep_index-1; + for (i=0; i<max_dep_index-min_dep_index; i++) { + dep_index ++; + if (dep_index == max_dep_index) { + dep_index = min_dep_index; + } + + proc = dep_tab[dep_index].proc; + flag = dep_tab[dep_index].flag; + + if (dependency_debug) + printf ("get_nextop check dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); +#ifdef NO_DEPENDENCY_TABLE + if (dep_tab[dep_index].flag == DEP_FLAG_INIT) { + if (is_play_candidate(dep_index)==TRUE) { + /* the trace_fh is the file handle for the operation directory, trace_fh_2 is other file handle + * used in the request */ + if (proc==LINK || proc==RENAME) { + dep_tab[dep_index].trace_fh = find_another_trace_fh (proc, dep_tab[dep_index].line); + dep_tab[dep_index].trace_fh_2 = find_lead_trace_fh(proc, dep_tab[dep_index].line); + dep_tab[dep_index].fh = 0; + dep_tab[dep_index].fh_2 = 0; + } else { + dep_tab[dep_index].trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line); + dep_tab[dep_index].fh = 0; + dep_tab[dep_index].fh_2 = (fh_map_t *)1; + }; + dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE; +#ifdef TIME_PLAY + dep_tab[dep_index].skip_sec = skip_sec; +#endif + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_CANDIDATE\n", dep_tab[dep_index].disk_index); + } else { + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_DONE; + continue; + } + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE) ) { + + if (!dep_tab[dep_index].fh) + dep_tab[dep_index].fh = lookup_fh (dep_tab[dep_index].trace_fh); + if (!dep_tab[dep_index].fh_2) + dep_tab[dep_index].fh_2 = lookup_fh (dep_tab[dep_index].trace_fh_2); + + /* need to wait for file handle */ + if ((!dep_tab[dep_index].fh) || (!dep_tab[dep_index].fh_2)) { + if (dependency_debug) + printf("disk[%d] can not lookup file handle\n", dep_tab[dep_index].disk_index); + if (dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) { + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_CANDIDATE to DEP_FLAG_WAIT_FHANDLE\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_FHANDLE; + sfs_gettime (&dep_tab[dep_index].start); + if (dep_index < min_wait_fhandle_dep_index) + min_wait_fhandle_dep_index = dep_index; + } else { + struct ladtime tmp; + sfs_gettime (&tmp); + SUBTIME (tmp, dep_tab[dep_index].start); +#ifdef TIME_PLAY + RFS_ASSERT (tmp.sec < 20 + (skip_sec - dep_tab[dep_index].skip_sec)); +#else + RFS_ASSERT (tmp.sec < 20 ); +#endif + } + continue; + } + + /* file handle ready, adjust_min_wait_fhandle_dep_index */ + if ((dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE)) { + if (dep_index == min_wait_fhandle_dep_index) { + min_wait_fhandle_dep_index = dep_tab_size; + for (j=dep_index+1; j<max_dep_index; j++) { + if (dep_tab[j].flag ==DEP_FLAG_WAIT_FHANDLE) { + min_wait_fhandle_dep_index = j; + break; + } + } + } + } + if (dependency_debug) + printf("disk[%d] found file handle\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_FHANDLE_READY; + + /* the normal file operation can be executed now */ + if (!is_dir_op (dep_tab[dep_index].proc)) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + + if (dependency_debug) + printf("disk[%d] directory operation \n", dep_tab[dep_index].disk_index); + /* the directory operation need to lock the directory first */ + if (dep_tab[dep_index].fh->lock) { + if (dependency_debug) + printf ("disk[%d] state %d to DEP_FLAG_WAIT_DIRECTORY\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_DIRECTORY; + continue; + } + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_FHANDLE_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DIRECTORY)) { + int j = dep_tab[dep_index].fh - fh_map; + if (dependency_debug) { + printf ("dep_tab[%d].disk_index %d, fh_map[%d] lock=%d\n",dep_index, dep_tab[dep_index].disk_index, j, dep_tab[dep_index].fh->lock); + printf ("trace_fh %s path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path); + printf ("trace_fh %s path %s\n", fh_map[j].trace_fh, fh_map[j].path); + } + if ((dep_tab[dep_index].fh->lock) || ((proc==RENAME) && (dep_tab[dep_index].fh_2->lock)) ) { + if (dependency_debug) + printf ("continue to wait for directory lock\n"); + continue; + } + if (dependency_debug) + printf ("dep_tab[%d] disk index %d LOCK fh_map[%d] \n", dep_index, dep_tab[dep_index].disk_index, j); + dep_tab[dep_index].fh->lock = 1; + if (proc==RENAME) + dep_tab[dep_index].fh_2->lock = 1; + + /* the non-delete directory operation can proceed now */ + if (!is_delete_op (dep_tab[dep_index].proc)) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + + /* the delete operation can proceed if nobody ahead is waiting for fhandle */ + /* probably this condition is not strong enough */ +// if ((min_wait_fhandle_dep_index<dep_index) ) { + if (dep_index!=min_dep_index) { + if (dependency_debug) + printf ("disk[%d] state %d to DEP_FLAG_WAIT_DELETE\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_DELETE; + continue; + } + dep_tab[dep_index].flag = DEP_FLAG_DIRECTORY_READY; + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_DIRECTORY_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DELETE)) { +// if (min_wait_fhandle_dep_index > dep_index) { + if (dep_index==min_dep_index) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + } +#else + if (dep_tab[dep_index].flag == DEP_FLAG_INIT){ + for (j=0, t=&(dep_tab[dep_index].dep_ops[0]); + (j<dep_tab[dep_index].init_dep_num) && (dep_tab[dep_index].cur_dep_num>0); + j++, t++) { + if (*t !=-1) { + if (dep_tab[disk_index_to_dep_index(dep_index, *t)].flag == DEP_FLAG_DONE) { /* The depended request has been finished */ + *t = -1; + dep_tab[dep_index].cur_dep_num --; + } + } + } + + if (dep_tab[dep_index].cur_dep_num == 0) { + return dep_index; + } + } +#endif + } + + if (dependency_debug) + printf ("get_nexop return -1\n"); + return -1; +} + +int check_timeout(void) +{ + static int biod_index = 0; + int i; + int dep_index; /* index into dep_tab */ + int proc; + sfs_op_type *op_ptr; /* per operation info */ + struct ladtime timeout; + + sfs_gettime (¤t); + + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + if (biod_reqp[biod_index].in_use==TRUE) { + timeout = biod_reqp[biod_index].timeout; + if ((current.sec>timeout.sec) || + ((current.sec==timeout.sec) && (current.usec>timeout.usec))) { + + dep_index = biod_reqp[biod_index].dep_tab_index; + proc = dep_tab[dep_index].proc; + op_ptr = &Ops[proc]; + op_ptr->results.timeout_calls++; + Ops[TOTAL].results.timeout_calls++; + + finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT); + + if (is_create_op(proc)) { + dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE; + printf ("resend dep_tab[%d], disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); + } + //RFS_ASSERT (!is_create_op(proc)); + + //printf ("timeout request: biod_reqp[%d].start %d:%d timeout %d:%d current %d:%d\n", biod_index, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, timeout.sec, timeout.usec, current.sec, current.usec); + } + } + } +} + +/* Allocate a biod_req entry to send and receive request dep_tab[dep_index] + * build the cross reference between dep_tab entry and biod_req entry + */ +struct biod_req * get_biod_req(int dep_index) /* index into dep_tab */ +{ + static int biod_index = 0; + int i; + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + if (!biod_reqp[biod_index].in_use) { + biod_reqp[biod_index].in_use = 1; + biod_reqp[biod_index].dep_tab_index = dep_index; + dep_tab[dep_index].biod_req_index = biod_index; + return &(biod_reqp[biod_index]); + } + } + return NULL; +} + +/* Return index into biod_reqp + * return -1 upon failure + */ +int lookup_biod_req (int xid) +{ + static int biod_index = 0; + int i; + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + /* give a NULL as timeout pointer may cause indefinitely block */ + if (biod_reqp[biod_index].xid == xid) { + return biod_index; + } + } + return -1; +} + +extern struct ladtime test_start; +void init_time_offset(void) +{ + struct ladtime tmp1; + struct ladtime tmp2; + + test_start.sec = 0; + test_start.usec = 0; + sfs_gettime (&tmp1); /* called at initial time: tmp1 = play_starttime */ +#ifdef SPEED_UP + DIVTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime / SCALE */ +#endif +#ifdef SLOW_DOWN + MULTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime * SCALE */ +#endif + + tmp2 = trace_starttime; /* tmp2 = trace_starttime */ + SUBTIME (tmp2, tmp1); /* tmp2 = trace_starttime - play_starttime *|/ SCALE */ + time_offset = tmp2; /* time_offset = trace_starttime - play_starttime *|/ SCALE */ +} + +/* initialize timestamp and proc field of dep_tab entry */ +void init_dep_tab_entry (int dep_index) +{ + static int nfs2proc_to_rfsproc[18] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17}; + static int nfs3proc_to_rfsproc[NFS3_PROCEDURE_COUNT] = {0, 1, 2, 4, 18, 5, 6, 8, 9, 14, + 13, 21, 10, 15, 11, 12, 16, 23, 17, 20, + 22, 19}; + char * line; + int version; + int nfsproc; + int msgid; + + //line = get_line_by_disk_index (dep_tab[dep_index].disk_index); + line = dep_tab[dep_index].line; + sscanf (line, "%d.%d", &(dep_tab[dep_index].timestamp.tv_sec), &(dep_tab[dep_index].timestamp.tv_usec)); + sscanf (&line[39], "%x %x", &msgid, &nfsproc); + if (line[TRACE_VERSION_POS]=='2') { + dep_tab[dep_index].proc = nfs2proc_to_rfsproc[nfsproc]; + RFS_ASSERT (nfsproc <18); + } else { + /* This is for debug purpose */ + if (line[TRACE_VERSION_POS] !='3') { + fprintf(stderr, "line[TRACE_VERSION_POS] %c line %s\n", line[TRACE_VERSION_POS], line); + line = get_line_by_disk_index (dep_tab[dep_index].disk_index-1); + if (!line) + line = get_line_by_disk_index (dep_tab[dep_index].disk_index-2); + RFS_ASSERT (line); + fprintf(stderr, "previousline %s\n", line); + } + RFS_ASSERT (line[TRACE_VERSION_POS] =='3'); + if (nfsproc >= NFS3_PROCEDURE_COUNT) { + fprintf(stderr, "proc %d line %s\n", nfsproc, line); + + } + RFS_ASSERT (nfsproc <NFS3_PROCEDURE_COUNT); + dep_tab[dep_index].proc = nfs3proc_to_rfsproc[nfsproc]; + } + RFS_ASSERT (dep_tab[dep_index].proc >= 0 && dep_tab[dep_index].proc < NOPS); + dep_tab[dep_index].flag = DEP_FLAG_INIT; +} + +/* initialize status, reply status, reply lines in one shot is probably better ???????????????? + +static void adjust_play_window (int flag, int * poll_timeout) +{ + struct ladtime max_window_time; + static struct ladtime max_poll_time = {0, 2000, 0}; + struct ladtime t; + int old_min = min_dep_index; + int old_max = max_dep_index; + int i; + char * line; + + for (; (dep_tab[min_dep_index].flag == DEP_FLAG_DONE) && (min_dep_index<dep_tab_size); min_dep_index++) + ; + RFS_ASSERT (min_dep_index <= max_dep_index); + + /* max_trace_window_time = current *|/ SCALE + trace_starttime */ + sfs_gettime (¤t); + +#ifdef TIME_PLAY +#ifdef SPEED_UP + MULTIME (current, PLAY_SCALE); +#endif +#ifdef SLOW_DOWN + DIVTIME (current, PLAY_SCALE); +#endif + ADDTIME (current, trace_starttime); + max_window_time = current; + + while ((max_dep_index<min_dep_index+MAX_PLAY_WINDOW) && (max_dep_index<dep_tab_size)) + { + t.sec = dep_tab[max_dep_index].timestamp.tv_sec; + t.usec = dep_tab[max_dep_index].timestamp.tv_usec; + + if (adjust_play_window_debug) + printf ("max_window_time sec %d usec %d : max_dep_index %d, t.sec %d t.usec %d\n", max_window_time.sec, max_window_time.usec, max_dep_index, t.sec, t.usec ); + + if ((t.sec>max_window_time.sec)||(t.sec==max_window_time.sec && t.usec>max_window_time.usec)) + break; + + max_dep_index++; + } + + /* Right now it is not clear how to deal with the situation where MAX_PLAY_WINDOW is reached */ + if (max_dep_index == min_dep_index+MAX_PLAY_WINDOW) { + //printf ("can not catch up the speed, max_dep_index %d reach min_dep_index %d+MAX_PLAY_WINDOW\n", max_dep_index, min_dep_index); + //printf ("."); + can_not_catch_speed_num ++; + } + //RFS_ASSERT (max_dep_index < min_dep_index+MAX_PLAY_WINDOW); +#else + ADDTIME (current, trace_starttime); + max_window_time = current; + max_dep_index = min_dep_index + MAX_PLAY_WINDOW; + if (max_dep_index >dep_tab_size) + max_dep_index = dep_tab_size; +#endif + + if (flag == BUSY) + *poll_timeout = 0; + else if (max_dep_index == dep_tab_size) { + *poll_timeout = 1000000; /* poll_timeout set to 1 second for the last request */ + } else { +#ifdef TIME_PLAY + struct ladtime tmp; + struct ladtime tmp1; + tmp.sec = dep_tab[max_dep_index].timestamp.tv_sec; + tmp.usec = dep_tab[max_dep_index].timestamp.tv_usec; + if (adjust_play_window_debug) + printf ("dep_tab[max_dep_index %d].timestamp %d:%d, max_window_time %d:%d\n", + max_dep_index, tmp.sec, tmp.usec, max_window_time.sec, max_window_time.usec); + + SUBTIME (tmp, max_window_time); +#ifdef SPEED_UP + DIVTIME (tmp, PLAY_SCALE); +#endif +#ifdef SLOW_DOWN + MULTIME (tmp, PLAY_SCALE); +#endif + tmp1 = tmp; + + if (tmp.sec > max_poll_time.sec) { + + if (rfs_debug) + printf ("dep_tab[%d].timestamp %d:%d, max_window_time %d:%d\n", + max_dep_index, dep_tab[max_dep_index].timestamp.tv_sec, dep_tab[max_dep_index].timestamp.tv_usec, max_window_time.sec, max_window_time.usec); + printf ("skip %d seconds\n", tmp.sec-max_poll_time.sec); + SUBTIME (tmp, max_poll_time); + tmp.usec = 0; + skip_sec += tmp.sec; + SUBTIME (test_start, tmp); + tmp = max_poll_time; + } + + RFS_ASSERT ((tmp.sec < 1000)); + if ((tmp.sec ==0) && (tmp.usec==0)) { + *poll_timeout = 0; + } else + *poll_timeout = tmp.sec*1000000+tmp.usec; +#else + /* + struct ladtime tmp; + struct ladtime tmp1; + tmp.sec = dep_tab[max_dep_index].timestamp.tv_sec; + tmp.usec = dep_tab[max_dep_index].timestamp.tv_usec; + tmp1.sec = dep_tab[max_dep_index-1].timestamp.tv_sec; + tmp1.usec = dep_tab[max_dep_index-1].timestamp.tv_usec; + SUBTIME (tmp, tmp1); + RFS_ASSERT ((tmp.sec < 1000)); + RFS_ASSERT ((tmp.sec>0) || ((tmp.sec==0) && (tmp.usec>0))); + *poll_timeout = tmp.sec*1000000+tmp.usec; + */ + + *poll_timeout = 100000; +#endif + } + if (rfs_debug) + printf ("adjust_play_window: flag %d min %d -> %d, max %d -> %d poll_timeout %d \n", flag, old_min, min_dep_index, old_max, max_dep_index, *poll_timeout); +} + +/* poll for usecs and receive, after receive one reply, + * return index in biod_reqp of the corresponding request + */ +int poll_and_get_reply (int usecs) +{ + int biod_index = -1; + int xid; + int error; + struct timeval zero_time = {0, 0}; /* Immediately return */ + + do { + error = biod_poll_wait (NFS_client, usecs); + switch (error) { + case -1: + if (errno == EINTR) { + error = 1; + continue; + } + if (rfs_debug) { + (void) fprintf(stderr, "biod_poll_wait error\n"); + perror (""); + (void) fflush(stderr); + } + break; + case 0: + break; + default: +#ifdef UDP + error = get_areply_udp (NFS_client, &xid, &zero_time); + // RFS_ASSERT (error!= RPC_TIMEOUT); /* we have polled and know there is data */ + // RFS_ASSERT (error!= RPC_CANTRECV); + RFS_ASSERT (error == RPC_SUCCESS); + + biod_index = lookup_biod_req (xid); + sfs_gettime (&(biod_reqp[biod_index].stop)); +#else + RFS_ASSERT (0); +#endif + } + } while (0); + return biod_index; +} + +void print_result(void) +{ + int i, j; + struct ladtime t; + int dep_index; + int avg_msecs; + unsigned long long tmp; + int avg_usecs; + + printf("read_data_bytes %d owe %d GB and %d bytes, adjusted %d times \n",read_data_total, read_data_owe_GB, read_data_owe, read_data_adjust_times); + printf("write_data_bytes %d owe %d GB and %d bytes, adjusted %d times \n",write_data_total, write_data_owe_GB, write_data_owe, write_data_adjust_times); + printf("failed_create_command_num %d skipped_readlink_command_num %d skipped_custom_command_num %d\n missing_reply_num %d rename_rmdir_noent_reply_num %d rmdir_not_empty_reply_num %d\n loose_access_control_reply_num %d, proper_reply_num %d, lookup_err_due_to_rename %d\n", + failed_create_command_num, skipped_readlink_command_num, skipped_custom_command_num, + missing_reply_num, rename_rmdir_noent_reply_num, rmdir_not_empty_reply_num, + loose_access_control_reply_num, lookup_err_due_to_rename_num, proper_reply_num ); + + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stdout, "trace play result:\n"); + (void) fprintf(stdout, "\t percentage good_cnt bad_cnt timeout_cnt\telapsed time\t\t\taverage time\n"); + for (i=0; i<NOPS+1; i++) { + if (Ops[i].results.good_calls==0) { + avg_msecs = 0; + avg_usecs = 0; + } else { + tmp = Ops[i].results.time.sec*1000000 + Ops[i].results.time.usec; + avg_msecs = 0; + avg_usecs = tmp/Ops[i].results.good_calls; +/* + avg_msecs = (Ops[i].results.time.sec*1000 + Ops[i].results.time.usec/1000)/Ops[i].results.good_calls; + avg_usecs = (Ops[i].results.time.usec%1000)/Ops[i].results.good_calls; +*/ + } + (void) fprintf(stdout, "%11s\t%4.1f\t%4d\t%4d\t%4d\t\tsec %8d usec %8d \tusec %8d\n", + Ops[i].name, + (float)(100*Ops[i].results.good_calls)/(float)Ops[TOTAL].results.good_calls, + Ops[i].results.good_calls, Ops[i].results.bad_calls, Ops[i].results.timeout_calls, + Ops[i].results.time.sec, Ops[i].results.time.usec, avg_msecs*1000+avg_usecs); + } + (void) fflush (stdout); + } + + clnt_destroy(NFS_client); + biod_term(); + +// print_dump(Client_num, Child_num); + return; + dep_index = 0; + printf ("[%4d] %s \tstart %4d:%6d \n", + dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec); + for (i=1; i<dep_tab_size*2; i++) { + dep_index = event_order[i]; + if (dep_index >0) + printf ("[%4d] %s \tstart %4d:%6d \n", + dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec); + else { + dep_index = -dep_index; + t=dep_tab[dep_index].stop; + SUBTIME (t, dep_tab[dep_index].start); + printf ("\t\t\t\t\t[%4d] %s stop %4d:%6d\tinterval %4d:%6d %s\n", + dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec, t.sec, t.usec, nfs3_strerror(dep_tab[dep_index].status)); + } + } +/* + + for (i=0, j=0; i<dep_tab_size || j<dep_tab_size; ) { + if ((i==dep_tab_size) || + (LARGERTIME(dep_tab[i].start, dep_tab[j].stop) && j<dep_tab_size)) { + t=dep_tab[j].stop; + SUBTIME (t, dep_tab[j].start); + printf ("dep_tab[%d].proc %s stop %d:%d interval %d:%d status %s\n", + j, Ops[dep_tab[j].proc].name, dep_tab[j].stop.sec, dep_tab[j].stop.usec, t.sec, t.usec, nfs3_strerror(dep_tab[j].status)); + j++; + } else { + printf ("dep_tab[%d].proc %s start %d:%d \n", + i, Ops[dep_tab[i].proc].name, dep_tab[i].start.sec, dep_tab[i].start.usec); + i++; + } + } +*/ +} + +/* + * allocate and initialize client handles + */ +static int +init_rpc(void) +{ + int dummy = 0; + + /* + * Set up the client handles. We get them all before trying one + * out to insure that the client handle for LOOKUP class is allocated + * before calling op_getattr(). + */ + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname); + } + + NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent, + (uint32_t) NFS_PROGRAM, + (uint32_t) nfs_version, + RPC_ANYSOCK, &Nfs_timers[0]); + + if (NFS_client == ((CLIENT *) NULL)) { + return(-1); + } + + /* + * create credentials using the REAL uid + */ + NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid, + (int)Cur_gid, 0, NULL); + + + if (biod_init(dummy, dummy) == -1) { + return(-1); + } + + return(0); +} /* init_rpc */ + +void +init_counters(void) +{ + uint_t i; + uint_t start_msec; + + /* Ready to go - initialize operation counters */ + for (i = 0; i < NOPS + 1; i++) { + Ops[i].req_cnt = 0; + Ops[i].results.good_calls = 0; + Ops[i].results.bad_calls = 0; + Ops[i].results.timeout_calls = 0; // RFS + Ops[i].results.fast_calls = 0; + Ops[i].results.time.sec = 0; + Ops[i].results.time.usec = 0; + Ops[i].results.msec2 = 0; + } + + /* initialize timers and period variables */ + sfs_gettime(&Starttime); + Cur_time = Starttime; + start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000); + Previous_chkpnt_msec = start_msec; + Calls_this_period = 0; + Reqs_this_period = 0; + Sleep_msec_this_period = 0; + Calls_this_test = 0; + Reqs_this_test = 0; + Sleep_msec_this_test = 0; +} + +static char * +nfs3_strerror(int status) +{ + static char str[40]; + switch (status) { + case NFS3_OK: + (void) strcpy(str, "no error"); + break; + case NFS3ERR_PERM: + (void) strcpy(str, "Not owner"); + break; + case NFS3ERR_NOENT: + (void) strcpy(str, "No such file or directory"); + break; + case NFS3ERR_IO: + (void) strcpy(str, "I/O error"); + break; + case NFS3ERR_NXIO: + (void) strcpy(str, "No such device or address"); + break; + case NFS3ERR_ACCES: + (void) strcpy(str, "Permission denied"); + break; + case NFS3ERR_EXIST: + (void) strcpy(str, "File exists"); + break; + case NFS3ERR_XDEV: + (void) strcpy(str, "Cross-device link"); + break; + case NFS3ERR_NODEV: + (void) strcpy(str, "No such device"); + break; + case NFS3ERR_NOTDIR: + (void) strcpy(str, "Not a directory"); + break; + case NFS3ERR_ISDIR: + (void) strcpy(str, "Is a directory"); + break; + case NFS3ERR_INVAL: + (void) strcpy(str, "Invalid argument"); + break; + case NFS3ERR_FBIG: + (void) strcpy(str, "File too large"); + break; + case NFS3ERR_NOSPC: + (void) strcpy(str, "No space left on device"); + break; + case NFS3ERR_ROFS: + (void) strcpy(str, "Read-only file system"); + break; + case NFS3ERR_MLINK: + (void) strcpy(str, "Too many links"); + break; + case NFS3ERR_NAMETOOLONG: + (void) strcpy(str, "File name too long"); + break; + case NFS3ERR_NOTEMPTY: + (void) strcpy(str, "Directory not empty"); + break; + case NFS3ERR_DQUOT: + (void) strcpy(str, "Disc quota exceeded"); + break; + case NFS3ERR_STALE: + (void) strcpy(str, "Stale NFS file handle"); + break; + case NFS3ERR_REMOTE: + (void) strcpy(str, "Object is remote"); + break; + case NFS3ERR_BADHANDLE: + (void) strcpy(str, "Bad file handle"); + break; + case NFS3ERR_NOT_SYNC: + (void) strcpy(str, "Not sync write"); + break; + case NFS3ERR_BAD_COOKIE: + (void) strcpy(str, "Bad cookie"); + break; + case NFS3ERR_NOTSUPP: + (void) strcpy(str, "Operation not supported"); + break; + case NFS3ERR_TOOSMALL: + (void) strcpy(str, "Value too small"); + break; + case NFS3ERR_SERVERFAULT: + (void) strcpy(str, "Server fault"); + break; + case NFS3ERR_BADTYPE: + (void) strcpy(str, "Bad type"); + break; + case NFS3ERR_JUKEBOX: + (void) strcpy(str, "Jukebox"); + break; + case NFS3ERR_RFS_TIMEOUT: + (void) strcpy(str, "Timeout"); + break; + default: + (void) sprintf(str, "Unknown status %d", status); + break; + } + return (str); +} + +/* + * Check the gettimeofday() resolution. If the resolution + * is in chunks bigger than SFS_MIN_RES then the client + * does not have a usable resolution for running the + * benchmark. + */ +static void +check_clock(void) +{ + double time_res; + char tmp_hostname[HOSTNAME_LEN]; + + time_res = get_resolution(); + getmyhostname(tmp_hostname, HOSTNAME_LEN); + if( time_res > (double)SFS_MIN_RES ) + { + (void) fprintf(stderr, + "\n%s: Clock resolution too poor to obtain valid results.\n", + tmp_hostname); + (void) fprintf(stderr, + "%s: Clock resolution %f Micro seconds.\n", tmp_hostname, + time_res); + exit(175); + } + else + { + (void) fprintf(stderr, + "\n%s: Good clock resolution [ %f ] Micro seconds.\n", + tmp_hostname, time_res); + } +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns the resolution of the gettimeofday() function + * in microseconds. + */ +static double +get_resolution(void) +{ + double starttime, finishtime, besttime; + long j,delay; + int k; + + finishtime=time_so_far1(); /* Warm up the instruction cache */ + starttime=time_so_far1(); /* Warm up the instruction cache */ + delay=j=0; /* Warm up the data cache */ + for(k=0;k<10;k++) + { + while(1) + { + starttime=time_so_far1(); + for(j=0;j< delay;j++) + ; + finishtime=time_so_far1(); + if(starttime==finishtime) + delay++; + else + { + if(k==0) + besttime=(finishtime-starttime); + if((finishtime-starttime) < besttime) + besttime=(finishtime-starttime); + break; + } + } + } + return(besttime); +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns current result of gettimeofday() in microseconds. + */ +/************************************************************************/ +/* Time measurement routines. */ +/* Return time in microseconds */ +/************************************************************************/ + +static double +time_so_far1(void) +{ + /* For Windows the time_of_day() is useless. It increments in 55 */ + /* milli second increments. By using the Win32api one can get */ + /* access to the high performance measurement interfaces. */ + /* With this one can get back into the 8 to 9 microsecond */ + /* resolution. */ +#ifdef Windows + LARGE_INTEGER freq,counter; + double wintime; + double bigcounter; + + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&counter); + bigcounter=(double)counter.HighPart *(double)0xffffffff + + (double)counter.LowPart; + wintime = (double)(bigcounter/(double)freq.LowPart); + return((double)wintime*1000000.0); +#else +#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5) + struct timespec gp; + + if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1) + perror("getclock"); + return (( (double) (gp.tv_sec)*1000000.0) + + ( ((float)(gp.tv_nsec)) * 0.001 )); +#else + struct timeval tp; + + if (gettimeofday(&tp, (struct timezone *) NULL) == -1) + perror("gettimeofday"); + return ((double) (tp.tv_sec)*1000000.0) + + (((double) tp.tv_usec) ); +#endif +#endif +} + +static void +usage(void) +{ + fprintf(stderr, "trace play usage"); +} +extern void init_file_system (void) +{ + return; +} + +extern void init_dep_tab (void) +{ + int i; + memset (&dep_tab, 0, sizeof(dep_tab)); +#ifdef notdef + dep_tab[0].disk_index = 0; + dep_tab[1].disk_index = 2; + dep_tab[2].disk_index = 3; + dep_tab[3].disk_index = 5; + dep_tab[4].disk_index = 7; + dep_tab[5].disk_index = 9; + dep_tab[6].disk_index = 11; + dep_tab[7].disk_index = 12; + dep_tab[8].disk_index = 15; + dep_tab[9].disk_index = 17; + dep_tab[10].disk_index = 18; + dep_tab[11].disk_index = 20; + dep_tab_size = 12; + //dep_tab_size = 2; +#endif +} + +extern void init_dep_tab_old (void) +{ + int i; + + Cur_file_ptr = &Export_dir; + Cur_uid = Real_uid; + + for (i=0; i<5; i++) { + dep_tab[i].flag = DEP_FLAG_INIT; + dep_tab[i].proc = CREATE; + dep_tab[i].timestamp.tv_sec = trace_starttime.sec; + dep_tab[i].timestamp.tv_usec = trace_starttime.usec+i*10; + dep_tab[i].init_dep_num = 0; + dep_tab[i].cur_dep_num = 0; + } + + for (i=5; i<10; i++) { + dep_tab[i].flag = DEP_FLAG_INIT; + dep_tab[i].proc = CREATE; + dep_tab[i].timestamp.tv_sec = trace_starttime.sec+i; + dep_tab[i].timestamp.tv_usec = trace_starttime.usec; + dep_tab[i].init_dep_num = 0; + dep_tab[i].cur_dep_num = 0; + } + + dep_tab[2].init_dep_num = 2; + dep_tab[2].cur_dep_num = 2; + dep_tab[2].dep_ops[0] = 0; + dep_tab[2].dep_ops[1] = 1; + + // printf ("trace_starttime (%d %d)\n", trace_starttime.sec, trace_starttime.usec); + + /* + for (i=2; i<4; i++) { + dep_tab[i].flag = DEP_FLAG_INIT; + dep_tab[i].proc = CREATE; + dep_tab[i].timestamp.tv_sec = trace_starttime.sec+i*10; + dep_tab[i].timestamp.tv_usec = trace_starttime.usec; + dep_tab[i].init_dep_num = 0; + dep_tab[i].cur_dep_num = 0; + } + */ + + dep_tab_size = 10; + + for (i=0; i<dep_tab_size; i++) + { + printf("dep_tab[%d].timestamp (%d %d)\n", i, dep_tab[i].timestamp.tv_sec, dep_tab[i].timestamp.tv_usec); + } +} + +void show_fhandle (nfs_fh3 * fhp) +{ + struct knfs_fh * kfhp = (struct knfs_fh *)fhp; + + int dev; + + if (quiet_flag) + return; + + RFS_ASSERT (kfhp->fh_version == 1); + RFS_ASSERT (kfhp->fh_fsid_type == 0); + RFS_ASSERT (kfhp->fh_auth_type == 0); + + dev = ntohs(kfhp->fh_dev_major); + dev = dev<<8; + dev = dev + ntohs(kfhp->fh_dev_minor); + + /* kfhp->fh_dev_ino hold the inode number of export point of the mounted + * file system. For example, if /tmp/t1 is exported, /tmp/t1/t2 is mounted, + * then fh_dev_ino hold the inode number of t1, not t2 + */ + + switch (kfhp->fh_fileid_type) { + case 0: + printf("fh:type 0 root dev 0x%x dev_ino %d\n", dev, kfhp->fh_dev_ino); + break; + case 1: + printf("fh:type 1 %d %x dev %x dev_ino %x\n", + kfhp->fh_ino, kfhp->fh_generation, dev, kfhp->fh_dev_ino); + break; + case 2: + printf("fh:type2 %d %x dirino %d dev 0x%x dev_ino %x\n", + kfhp->fh_ino, kfhp->fh_generation, kfhp->fh_dirino, dev, kfhp->fh_dev_ino); + break; + default: + RFS_ASSERT (0); + } +} + +nfs_fh3 zero_fhandle; +int init_fh_map () +{ + memset (fh_map, 0, sizeof (fh_map)); + memset(fh_htable, 0, sizeof (fh_htable)); + memset (&zero_fhandle, 0, sizeof(nfs_fh3)); + printf ("SIZE of fh map %d KB\n", sizeof (fh_map)/1000); + fh_i = 0; +} + +int add_fh (int map_flag, char * trace_fh, char * path, nfs_fh3 * play_fh) +{ + char * old_trace_fh; + + /* first lookup if the entry for fh is already in the table */ + struct generic_entry * p; + + p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE); + if (p) { + RFS_ASSERT (fh_map[p->key3].flag = FH_MAP_FLAG_PARTIAL); + RFS_ASSERT (map_flag ==FH_MAP_FLAG_COMPLETE); + fh_map[p->key3].flag = map_flag; + RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)); + RFS_ASSERT (!strcmp(fh_map[p->key3].path, path)); + RFS_ASSERT (!memcmp(&fh_map[p->key3].play_fh, &zero_fhandle, sizeof(nfs_fh3))); + memcpy (&fh_map[p->key3].play_fh, play_fh, sizeof (nfs_fh3)); + if ((fh_map_debug==1)) // || (stage ==TRACE_PLAY_STAGE)) + printf ("update the play_fh for trace_fh %s path %s \n", trace_fh, path); + return 0; + } + + fh_map[fh_i].flag = map_flag; + fh_map[fh_i].lock = 0; + memcpy(fh_map[fh_i].trace_fh, trace_fh, TRACE_FH_SIZE); + + RFS_ASSERT (strlen(path) < MAX_PLAY_PATH_SIZE); + strcpy (fh_map [fh_i].path, path); + if (map_flag==FH_MAP_FLAG_COMPLETE) + memcpy (&fh_map[fh_i].play_fh, play_fh, sizeof(nfs_fh3)); + else + memset (&fh_map[fh_i].play_fh, 0, sizeof(nfs_fh3)); + + if ((fh_map_debug==1)) { // || (stage ==TRACE_PLAY_STAGE)) { + printf ("insert trace_fh %s path %s play_fh:\n", trace_fh, path); + if (map_flag == FH_MAP_FLAG_COMPLETE) + show_fhandle(play_fh); + else + printf("null\n"); + } + +/* + if (map_flag == FH_MAP_FLAG_DISCARD) + printf ("insert flag %d trace_fh %s path %s play_fh:\n", map_flag, trace_fh, path); +*/ + + generic_insert(trace_fh, TRACE_FH_SIZE, fh_i, fh_htable, FH_HTABLE_SIZE); + + fh_i = (fh_i+1); + RFS_ASSERT (fh_i < FH_MAP_SIZE); + + return 0; +}; + +fh_map_t * lookup_fh (char * trace_fh ) +{ + struct generic_entry * p; + p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE); + if (fh_map_debug==1) + printf ("lookup trace_fh %s\n", trace_fh); + + if (p) { + if (fh_map_debug==1) { + printf ("found: fh_i %d path %s play_fh:", p->key3, fh_map[p->key3].path); + show_fhandle(&fh_map[p->key3].play_fh); + } + return (&(fh_map[p->key3])); + } else { + //printf ("lookup_fh %s not found\n", trace_fh); + if (stage != READ_DEP_TAB_STAGE && (fh_map_debug==1)) { + printf ("lookup not found trace_fh %s\n", trace_fh); + } + return NULL; + } + RFS_ASSERT (0); +} + +int delete_fh (char * trace_fh, int fh_map_index) +{ + generic_delete (trace_fh, TRACE_FH_SIZE, fh_map_index, fh_htable, FH_HTABLE_SIZE); + return 0; +}; + +void lookup_init_filesystem (nfs_fh3 * parent, char * name, nfs_fh3 * result) +{ + LOOKUP3args args; + LOOKUP3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + static int i=0; + + /* set up the arguments */ + (void) memcpy((char *) &args.what.dir, (char *) parent, + sizeof (nfs_fh3)); + args.what.name = name; + (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP, + xdr_LOOKUP3args, (char *) &args, + xdr_LOOKUP3res, (char *) &reply, + Nfs_timers[Init]); + sfs_gettime(&stop); + + if (rpc_stat !=RPC_SUCCESS) { + printf("rpc_stat %d\n", rpc_stat); + perror(""); + } + RFS_ASSERT (rpc_stat == RPC_SUCCESS); + RFS_ASSERT (reply.status == NFS3_OK); + (void) memcpy((char *) result, (char *) &reply.resok.object, sizeof (nfs_fh3)); +} + +void read_fh_map(char * fh_map_file) +{ + FILE * fp; + int i = 0; + char buf[1024]; + char trace_fh[TRACE_FH_SIZE]; + char intbuf[9]; + char * trace_path; + char * p; + int map_flag; +#define MAX_PATH_DEPTH 20 + nfs_fh3 parents[MAX_PATH_DEPTH]; + char * lookup_path_ptr[MAX_PATH_DEPTH]; + char lookup_path [MAX_PLAY_PATH_SIZE]; + int depth; + int new_dir_flag = 0; + + depth = 0; + memset(lookup_path_ptr, 0, sizeof(lookup_path_ptr)); + memcpy(&parents[depth], &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3)); + strcpy(lookup_path, "/"); + lookup_path_ptr[depth]=&lookup_path[0]; + + fp = fopen(fh_map_file, "r"); + RFS_ASSERT (fp!=NULL); + + intbuf[8]=0; + + memset(buf, 0, sizeof(buf)); + while (fgets(buf, 1024, fp)) { + + if (fh_i % 10000==0) + printf("%d fh_map entry read\n", fh_i); + + RFS_ASSERT (buf[strlen(buf)-1]=='\n'); + buf[strlen(buf)-1]=0; + if (fh_map_debug) { + printf("%d fgets return %s\n", fh_i, buf); + printf("depth %d lookup_path %s\n", depth, lookup_path); + } + //for (i=0; i<=depth; i++) + //printf("lookup_path_ptr[%d] %s ", i, lookup_path_ptr[i]); + //printf("\n"); +#ifdef COMPRESS_TRACE_FH + for (i=0; i<TRACE_FH_SIZE/8; i++) { + strncpy(intbuf, buf+i*8, 8); + sscanf(intbuf, "%x", trace_fh+i*8); // maybe it should be 4, anyway we don't compress for now + } + trace_path = buf+TRACE_FH_SIZE*2+1; /* +1 the trace contains only initial file handle */ +#else + memcpy(trace_fh, buf, TRACE_FH_SIZE); + trace_path = buf + TRACE_FH_SIZE +1; +#endif +#ifdef TRACE_CONTAIN_LATER_FHANDLE + trace_path = +=2; /* +3 if the trace contains both initial and later created file handle */ +#endif + +#ifdef NO_DEPENDENCY_TABLE + if (!strncmp (trace_path, "DISCARD", 7)) { + map_flag = FH_MAP_FLAG_DISCARD; + add_fh (map_flag, buf, trace_path, 0); + continue; + } +#endif + + p = trace_path+strlen(trace_path)-2; + while (*p!='/') + p--; + p++; + //RFS_ASSERT (p-trace_path<=strlen(lookup_path)+1); + //RFS_ASSERT (p>trace_path); + + if (strncmp(lookup_path, trace_path, p-trace_path)) { + printf("strncmp lookup_path %s trace_path %s for length %d\n", lookup_path, trace_path, p-trace_path); + } + RFS_ASSERT (!strncmp(lookup_path, trace_path, p-trace_path)); + //while (strncmp(lookup_path, trace_path, p-trace_path)) { /* one step deeper */ + while (strlen(lookup_path)>p-trace_path && depth>0) { + //printf("depth--\n"); + if (depth<=0) + printf ("lookup_path %s trace_path %s p-trace_path %d depth %d\n", lookup_path, trace_path, p-trace_path, depth); + RFS_ASSERT (depth>0); + *lookup_path_ptr[depth]=0; + lookup_path_ptr[depth]=0; + depth--; + } + RFS_ASSERT (strlen(lookup_path)==(p-trace_path) || (depth==0)); + + +#ifdef TRACE_CONTAIN_LATER_FHANDLE + if (buf[TRACE_FH_SIZE*2+1]=='Y') { + map_flag = FH_MAP_FLAG_COMPLETE; + } else { + map_flag = FH_MAP_FLAG_PARTIAL; + RFS_ASSERT (buf[TRACE_FH_SIZE*2+1]=='N'); + } +#else + map_flag = FH_MAP_FLAG_COMPLETE; +#endif + if ((*(p+strlen(p)-1))=='/') { + *(p+strlen(p)-1)=0; + new_dir_flag = 1; + } else + new_dir_flag = 0; + + if (map_flag == FH_MAP_FLAG_COMPLETE) { + lookup_init_filesystem (&parents[depth], p, &parents[depth+1]); + add_fh (map_flag, buf, trace_path, &parents[depth+1]); + } else + add_fh (map_flag, buf, trace_path, 0); + + if (new_dir_flag) { + /* the new fhandle is of a directory */ + lookup_path_ptr[depth+1] = lookup_path+strlen(lookup_path); + strcat (lookup_path, p); + strcat (lookup_path, "/"); + + //printf("depth++\n"); + depth++; + } + + memset(buf, 0, sizeof(buf)); + } + + if (fh_map_debug) { + for (i=0; i<fh_i; i++) { + int * p1 = (int *)&(fh_map[i].play_fh); +#ifdef COMPRESS_TRACE_FH + int * p = (int *)fh_map[i].trace_fh; + printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); +#else + printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, fh_map[i].trace_fh, fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); + } +#endif + + fprintf(stderr, "total %d requests \n", i); + } +} + +int f() +{ + return 1; +} + +inline void finish_request (int biod_index, int dep_index, int status) +{ + /* the ending operation, same as when a request time out */ + dep_tab[dep_index].stop = biod_reqp[biod_index].stop; /* RFS: to dump data */ + dep_tab[dep_index].status = status; + event_order[event_order_index++] = -dep_index; + biod_reqp[biod_index].in_use = FALSE; + dep_tab[dep_index].flag = DEP_FLAG_DONE; + if (is_dir_op(dep_tab[dep_index].proc)) { + int j; + RFS_ASSERT (dep_tab[dep_index].fh->lock = 1); + dep_tab[dep_index].fh->lock = 0; + if (dep_tab[dep_index].proc==RENAME) + dep_tab[dep_index].fh_2->lock = 0; + j = dep_tab[dep_index].fh-fh_map; + if (dependency_debug) { + printf ("fh_map[%d] is UNlocked\n",j); + printf ("trace_fh %d path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path); + printf ("trace_fh %d path %s\n", fh_map[j].trace_fh, fh_map[j].path); + } + } + num_out_reqs --; +} + +/* the request argument may have pointers pointing to buffers, e.g. the name in lookup, + * the target of symlink, the write data */ +char arg_res[MAX_ARG_RES_SIZE]; +int poll_timeout = 0; /* timeout in usecs */ +char buf1 [MAX_BUF1_SIZE]; +char buf2 [MAX_BUF2_SIZE]; +#define NFS3_REPLY_MISS -1 + +int execute_next_request () +{ + int dep_index; + int proc; + char * line; + struct biod_req * reqp; + sfs_op_type *op_ptr; /* per operation info */ + struct ladtime call_timeout; + + start_profile (&valid_get_nextop_profile); + start_profile (&invalid_get_nextop_profile); + dep_index = get_nextop(); + if (dep_index == -1) { + end_profile (&invalid_get_nextop_profile); + return dep_index; + }; + end_profile (&valid_get_nextop_profile); + + start_profile (&prepare_argument_profile); + line = dep_tab[dep_index].line; + if ((dep_index%(10000))==0) { +#ifndef TIME_PLAY + fprintf (stderr, "processing dep_tab[%d] disk_index %d num_out_reqs %d \n", dep_index, dep_tab[dep_index].disk_index, num_out_reqs); +#else + fprintf (stderr, "processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE); +#ifdef SPEED_UP + if (can_not_catch_speed_num < 2000) { + PLAY_SCALE ++; + printf ("set PLAY_SCALE to %d\n", PLAY_SCALE); + }; + if (can_not_catch_speed_num > 50000) { + PLAY_SCALE /= 2; + } else { + if (can_not_catch_speed_num > 5000) { + PLAY_SCALE -= 2; + if (PLAY_SCALE < 1) + PLAY_SCALE = 1; + } + } +#endif + can_not_catch_speed_num = 0; +#endif + } + if (rfs_debug) + printf ("processing dep_tab[%d] disk_index %d %s\n", dep_index, dep_tab[dep_index].disk_index, line); + + proc = dep_tab[dep_index].proc; + rfs_Ops[proc].setarg (dep_index, line, arg_res, buf1, buf2); + + op_ptr = &Ops[proc]; + reqp = get_biod_req (dep_index); + RFS_ASSERT (reqp); + + call_timeout.sec = 4; //Nfs_timers[op_ptr->call_class].tv_sec; + call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; + + /* make the call */ + sfs_gettime(&(reqp->start)); + end_profile (&prepare_argument_profile); + start_profile (&biod_clnt_call_profile); +#define REAL_PLAY +#ifdef REAL_PLAY + reqp->xid = biod_clnt_call(NFS_client, rfs_Ops[proc].nfsv3_proc, + rfs_Ops[proc].xdr_arg, arg_res); +#else + + reqp->xid = dep_index+1; /* just fake a message id and let it expire */ +#endif + if (reqp->xid != 0) { + reqp->timeout = reqp->start; + ADDTIME (reqp->timeout, call_timeout); + num_out_reqs++; + dep_tab[dep_index].flag = DEP_FLAG_SENT; + event_order[event_order_index++] = dep_index; + } else + RFS_ASSERT (0); + + dep_tab[dep_index].start = reqp->start; /* RFS: to dump data */ + end_profile (&biod_clnt_call_profile); +} + +void check_reply (int proc, int biod_index, int dep_index, int status, char * errmsg, int trace_reply_status) +{ + if (((status!=trace_reply_status)) && (trace_reply_status!=NFS3_REPLY_MISS)) { + if (rfs_debug) + printf ("receive problem reply, xid %x nfs_ret %d %s trace_reply_status %d start %d:%d stop %d:%d command disk index %d\n", biod_reqp[biod_index].xid, status, errmsg, trace_reply_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec, dep_tab[dep_index].disk_index); +#ifndef TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES + /* these files is not looked up and is not create/mkdir/symlink/link/mknod ed before they + * are refered by name through rename, remove + */ + if ((proc==RENAME || proc==REMOVE) && (status==NFS3ERR_NOENT) && (trace_reply_status ==0)) { + /* current initialization doesnot take care of rename source, if there is no + * create or lookup before that source, the source object will not exist when + * rename occurs + */ + RFS_ASSERT (1); + rename_rmdir_noent_reply_num++; + } else +#endif +#ifndef TAKE_CARE_SYMBOLIC_LINK + if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_reply_status==NFS3ERR_NOENT)) { + /* in the original trace, first lookup return NOENT, then symlink is executed, then lookup return OK + * the initialization considers only the lookup return OK and created the file in the initialization + * so in trace play the first lookup return OK + */ + RFS_ASSERT (1); + } else if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) && (trace_reply_status == 0)) { + /* due to similar reason as above, the initialization code initializes the symbolic link as a normal + * file already + */ + RFS_ASSERT (1); + } else +#endif +#ifndef TAKE_CARE_NOEMPTY_RMDIR + /* the remove packet seems got lost in the trace capture, so replay can not finish */ + if ((proc==RMDIR) && (status==NFS3ERR_NOTEMPTY)) { + RENAME3args args; + RENAME3res reply; /* the reply */ + RMDIR3args * rmdir_argp; + enum clnt_stat rpc_stat; /* result from RPC call */ + + rfs_Ops[proc].setarg (dep_index, dep_tab[dep_index].line, arg_res, buf1, buf2); + rmdir_argp = (RMDIR3args *)arg_res; + + memcpy(&args.from, &(rmdir_argp->object), sizeof (diropargs3)); + memcpy(&args.to.dir, &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3)); + args.from.name = buf1; /* the buf1 is already filled when parsing rmdir */ + args.to.name = buf2; + sprintf(buf2, "rmdir_%d_%s", dep_tab[dep_index].disk_index, rmdir_argp->object.name); + + rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME, + xdr_RENAME3args, (char *) &args, + xdr_RENAME3res, (char *) &reply, + Nfs_timers[Init]); + RFS_ASSERT (rpc_stat == RPC_SUCCESS); + if (reply.status!=NFS3_OK) + printf ("change rmdir into rename, reply.status %d\n", reply.status); + RFS_ASSERT (reply.status==NFS3_OK); + rmdir_not_empty_reply_num ++; +#endif +#ifndef TAKE_CARE_ACCESS_ERROR + } else if ((status==0) && (trace_reply_status==NFS3ERR_ACCES)) { + loose_access_control_reply_num ++; +#endif +#ifdef NO_DEPENDENCY_TABLE + } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_reply_status==NFS3_OK)) { + lookup_err_due_to_rename_num ++; +#endif + } else { + int i; + for (i=min_dep_index; i<max_dep_index; i++) + printf ("dep_tab[%d].disk_index %d, flag %d line %s\n", i, dep_tab[i].disk_index, dep_tab[i].flag, dep_tab[i].line); + RFS_ASSERT (0); + } + } else + proper_reply_num ++; + +} + +/* return -1 if there is no reply being received + * return the dep_index if the corresponding reply has been received + */ +int receive_next_reply (int busy_flag) +{ + int dep_index; + int biod_index; + int proc; + char * line; + char * reply_line; + sfs_op_type *op_ptr; /* per operation info */ + int ret; + int status; + int trace_reply_status; + char * errmsg; + + /* wait for reply */ + start_profile (&valid_poll_and_get_reply_profile); + start_profile (&invalid_poll_and_get_reply_profile); +/* + if (busy_flag == IDLE) + poll_timeout = 0; + else + poll_timeout = 10000; +*/ + biod_index = poll_and_get_reply (poll_timeout); + if (biod_index==-1) { + end_profile (&invalid_poll_and_get_reply_profile); + return -1; + }; + end_profile (&valid_poll_and_get_reply_profile); + + start_profile (&decode_reply_profile); + /* check the corresponding request */ + dep_index = biod_reqp[biod_index].dep_tab_index; + proc = dep_tab[dep_index].proc; + op_ptr = &Ops[proc]; + + if (dep_tab[dep_index].flag != DEP_FLAG_SENT) { + printf("dep_tab[%d].flag %d proc %d status %d start %d:%d stop %d:%d\n", + dep_index, dep_tab[dep_index].flag, proc, dep_tab[dep_index].status, + dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec, + dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec ); + printf ("received reply for timeout requests dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); + return dep_index; + } + RFS_ASSERT (dep_tab[dep_index].flag == DEP_FLAG_SENT); + + /* decode the reply */ + rfs_Ops[proc].setres (arg_res, buf1); + ret = proc_header (NFS_client, rfs_Ops[proc].xdr_res, arg_res); + RFS_ASSERT (ret == RPC_SUCCESS); + status = *((int *)arg_res); + errmsg = nfs3_strerror (status); + end_profile (&decode_reply_profile); + + start_profile (&check_reply_profile); + /* compare with the reply in the trace */ + line = dep_tab[dep_index].line; + reply_line = find_reply_line (line, dep_tab[dep_index].disk_index); + if (reply_line == NULL) { + //printf ("disk[%d] can not find the reply line, assume trace_reply_status OK\n", dep_tab[dep_index].disk_index); + trace_reply_status = NFS3_REPLY_MISS; + missing_reply_num ++; + } else + trace_reply_status = find_reply_status (reply_line); + + /* print the result, trace play progress indicator */ + if ((dep_index %10000)==0 || rfs_debug) + fprintf (stdout, "dep_tab[%d], disk_index %d, receive reply, rpc_ret %d xid %x nfs_ret %d %s trace_reply_status %d start %d:%d stop %d:%d \n", dep_index, dep_tab[dep_index].disk_index, ret, biod_reqp[biod_index].xid, status, errmsg, trace_reply_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec); + + /* error checking */ + check_reply (proc, biod_index, dep_index, status, errmsg, trace_reply_status); + + /* free resources */ + finish_request (biod_index, dep_index, status); + + /* get statistics */ + if (status == trace_reply_status) { + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + } else { + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + sfs_elapsedtime (op_ptr, &(biod_reqp[biod_index].start), &(biod_reqp[biod_index].stop)); + end_profile (&check_reply_profile); + + //start_profile (&add_create_object_profile); +#ifndef TAKE_CARE_SYMBOLIC_LINK + if (trace_reply_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==MKNOD)) { +#else + if (trace_reply_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==SYMLINK || proc==MKNOD)) { +#endif + RFS_ASSERT (status == NFS_OK); + RFS_ASSERT (reply_line); + add_new_file_system_object(proc, dep_index, line, reply_line); + } + //end_profile (&add_create_object_profile); +} + +void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line) +{ + char * child_trace_fh; + fh_map_t * parent_entryp; + char component_name[MAX_PLAY_PATH_SIZE]; + char * parent_trace_fh; + char child_path[MAX_PLAY_PATH_SIZE]; + post_op_fh3 * post_op_fh3_child; + char * reply_trace_fh; + nfs_fh3 * child_fh3; + + parent_trace_fh = strstr (line, "fh"); + RFS_ASSERT (parent_trace_fh); + parent_trace_fh +=3; + parent_entryp = lookup_fh (parent_trace_fh); + RFS_ASSERT (parent_entryp); + parse_name (parent_trace_fh+65, component_name); + strcpy (child_path, parent_entryp->path); + strcat (child_path, "/"); + strcat (child_path, component_name); + + /* find the corresponding create request */ + //printf ("before find reply trace_fh reply_line %s\n", reply_line); + reply_trace_fh = find_reply_trace_fh (reply_line); + RFS_ASSERT (reply_trace_fh != NULL); + switch (proc) { + case CREATE: + RFS_ASSERT (((CREATE3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((CREATE3res *)arg_res)->res_u.ok.obj.handle; + break; + case MKDIR: + RFS_ASSERT (((MKDIR3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((MKDIR3res *)arg_res)->res_u.ok.obj.handle; + break; + case SYMLINK: + RFS_ASSERT (((SYMLINK3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((SYMLINK3res *)arg_res)->res_u.ok.obj.handle; + break; + case MKNOD: + RFS_ASSERT (((MKNOD3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((MKNOD3res *)arg_res)->res_u.ok.obj.handle; + break; + case LOOKUP: + RFS_ASSERT (proc==LOOKUP); + child_fh3 = &((LOOKUP3res *)arg_res)->res_u.ok.object; + break; + default: + RFS_ASSERT (0); + } + RFS_ASSERT (reply_trace_fh[TRACE_FH_SIZE]==' '); + reply_trace_fh[TRACE_FH_SIZE] = 0; + add_fh (FH_MAP_FLAG_COMPLETE, reply_trace_fh, child_path, child_fh3); /* exist flag is not used now, set to 1 */ + reply_trace_fh[TRACE_FH_SIZE] = ' '; +} + +/* initialize timestamp and proc field of dep_tab entry */ +void trace_play(void) +{ + + /* The flag to indicate whether trace_player is BUSY. Trace_player is BUSY + * when either there is request to send or there is reply being + * received. Otherwise it is IDLE. The timeout for polling replies + * is set to 0 when BUSY, it is set to the waiting time to the first + * request outside of current <min_dep_index, max_dep_index> window when IDLE. + */ + int busy_flag = BUSY; + + //int dep_index; /* index into dependency table: dep_tab */ + //int biod_index; /* index into outstanding requests: biod_reqp */ + + int count = 0; + min_dep_index = 0; + max_dep_index = 0; + adjust_play_window(busy_flag, &poll_timeout); + + start_profile (&total_profile); + while ((min_dep_index<dep_tab_size) || (num_out_reqs>0)) { + + if (busy_flag == IDLE) { + //start_profile (&check_timeout_profile); + check_timeout(); + //end_profile (&check_timeout_profile); + } + + //start_profile (&adjust_play_window_profile); + //adjust_play_window (flag, &poll_timeout); + //adjust_play_window (flag+(max_dep_index-min_dep_index), &poll_timeout); + adjust_play_window (busy_flag, &poll_timeout); + if (rfs_debug) + printf("num_out_reqs %d\n", num_out_reqs); + busy_flag = IDLE; + //end_profile (&adjust_play_window_profile); + + start_profile (&execute_next_request_profile); + while (execute_next_request()!=-1) + busy_flag = BUSY; + end_profile (&execute_next_request_profile); + + start_profile (&receive_next_reply_profile); + while (receive_next_reply(busy_flag)!=-1) + busy_flag = BUSY; + end_profile (&receive_next_reply_profile); + } + end_profile (&total_profile); + + print_profile ("total_profile", &total_profile); + printf("\n"); + print_profile ("check_timeout", &check_timeout_profile); + printf("\n"); + print_profile ("adjust_play_window", &adjust_play_window_profile); + printf("\n"); + print_profile ("execute_next_request_profile", &execute_next_request_profile); + print_profile ("valid_get_nextop_profile", &valid_get_nextop_profile); + print_profile ("invalid_get_nextop_profile", &invalid_get_nextop_profile); + print_profile ("prepare_argument", &prepare_argument_profile); + print_profile ("biod_clnt_call", &biod_clnt_call_profile); + printf("\n"); + print_profile ("receive_next_reply", &receive_next_reply_profile); + print_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile); + print_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile); + print_profile ("decode_reply", &decode_reply_profile); + print_profile ("check_reply", &check_reply_profile); + print_profile ("add_create_object", &add_create_object_profile); + printf("\n"); + + printf ("min_dep_index %d dep_tab_size %d num_out_reqs %d\n", min_dep_index, dep_tab_size, num_out_reqs); +} +/* sfs_c_chd.c */ diff --git a/TBBT/trace_play/sfs_c_chd.c.org b/TBBT/trace_play/sfs_c_chd.c.org new file mode 100644 index 0000000..27a122c --- /dev/null +++ b/TBBT/trace_play/sfs_c_chd.c.org @@ -0,0 +1,2866 @@ +#ifndef lint +static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * -------------------------- sfs_c_chd.c ------------------------- + * + * The sfs child. Routines to initialize child parameters, + * initialize test directories, and generate load. + * + *.Exported_Routines + * void child(int, float, int, char *); + * void init_fileinfo(void); + * void init_counters(void); + * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type, + * sfs_file_type); + * int check_access(struct *stat) + * int check_fh_access(); + * + *.Local_Routines + * void check_call_rate(void); + * void init_targets(void); + * void init_dirlayout(void); + * void init_rpc(void); + * void init_testdir(void); + * int do_op(void); + * int op(int); + * + *.Revision_History + * 21-Aug-92 Wittle randfh() uses working set files array. + * init_fileinfo() sets up working set. + * 02-Jul-92 Teelucksingh Target file size now based on peak load + * instead of BTDT. + * 04-Jan-92 Pawlowski Added raw data dump hooks. + * 16-Dec-91 Wittle Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + +extern struct hostent *Server_hostent; + +#define PROB_SCALE 1000L +#define _M_MODULUS 2147483647L /* (2**31)-1 */ + +#define _GROUP_DIVISOR 500 +#define _FILES_PER_GROUP 4 +#define _MIN_GROUPS 12 +#define _WORKING_SET_AT_25_OPS_PER_SEC 975 + +/* + * ----------------------- External Definitions ----------------------- + */ + +/* forward definitions for local functions */ +static void check_call_rate(void); +static void init_targets(void); +static int init_rpc(void); +static void init_testdir(void); +static int do_op(void); +static int op(int); +static void init_dirlayout(void); + + +/* + * ------------------- File Set Size Control ------------------------- + */ +static uint_t Setattr_borrowed = 0; /* setattr op used for file truncate */ +static uint_t Create_borrowed = 0; /* create op used for file truncate */ + +/* + * ------------- Per Child Load Generation Rate Variables ----------- + */ +static float Child_call_load; /* per child call/sec rate */ +static float Child_req_load; /* per child req/sec rate */ +static uint_t Calls_this_period; /* calls made during the current run period */ +static uint_t Calls_this_test; /* calls made during the test so far */ +static uint_t Reqs_this_period; /* reqs made during the current run period */ +static uint_t Reqs_this_test; /* reqs made during the test so far */ +static uint_t Sleep_msec_this_test; /* msec slept during the test so far */ +static uint_t Sleep_msec_this_period; +static uint_t Previous_chkpnt_msec; /* beginning time of current run period */ +static int Target_sleep_mspc; /* targeted sleep time per call */ +static int Measurement_in_progress = 0; + +static sfs_work_set_type Dir_working_set; +static sfs_work_set_type Io_working_set; +static sfs_work_set_type Non_io_working_set; +static sfs_work_set_type Symlink_working_set; + +static uint_t Files_created = 0; /* unique integer part of file names */ +static char io_buf[BUFSIZ]; +int generations = + (_WORKING_SET_AT_25_OPS_PER_SEC/_GROUP_DIVISOR) * _MIN_GROUPS; +/* + * ------------------------- SFS Child ------------------------- + */ + + +/* + * Child number 'child_num'. Initialize internal data structure and + * the test directory, then notify parent (through log file) that we + * are ready to start generating 'load' calls per second into the current + * working directory, or optionally, into the directories specified by + * 'argc' and 'argv'. Wait for the start signal, and then generate load + * until we complete all our goal for calls or until the run time expires, + * depending on the 'Timed_run' flag. The run time expires when the parent + * sends the stop signal. + */ +void +child( + int child_num, + int children, + float load, + int argc, + char *argv[]) +{ + char namebuf[NFS_MAXNAMLEN]; /* unique name for this program */ + char *nameptr; + int i; /* general use */ + int op_count; /* ops completed during each request */ + uint_t rand_sleep_msec; /* random sleep msec between calls */ + uint_t current_msec; /* current test time in msecs */ + double previous_pcnt; + CLIENT * mount_client_ptr; /* Mount client handle */ + char * mount_point; /* Mount point for remote FS */ + int Saveerrno; + int mnt_argc; + + struct ladtime elapsed_time; /* Current_time - Start_time */ + sfs_results_report_type report; /* final results log */ + + (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ); + + /* Change my name for error logging */ + if ((nameptr = strrchr(sfs_Myname, '/')) != NULL) + sfs_Myname = ++nameptr; + (void) sprintf(namebuf, "%s%d", sfs_Myname, child_num); + sfs_Myname = namebuf; + Child_call_load = load; + Current_test_phase = Mount_phase; + + /* Seed the random number generator based on my child number */ + + /* + * Note: If random seeds are allocated by the prime client + * then this code must change. + */ + sfs_srandom((int)(load + Child_num + 1)); + + /* Setup user and group information */ + Cur_uid = Real_uid; + + /* + * Initialize call and request targets. + * Calls are the Over-The-Wire (OTW) operations that occur due to + * each request. A request may cause one or more calls. + * Initialize the child file info and mount the remote test directory. + * Set up the rpc and biod structures. + */ + init_targets(); + init_fileinfo(); + init_dirlayout(); + + /* + * Mount points list: + * If the mount point list is equal to the number of procs (P), the + * mount point for child M is the M'th entry in the list. + * If the mount point list is greater than the number of procs (P), the + * mount point for client N child M is ((N - 1) * P) + M + */ + if (argc == children) + mnt_argc = Child_num; + else + mnt_argc = (Client_num - 1) * children + Child_num; + + if (mnt_argc >= argc) { + (void) fprintf(stderr, +"%s: Invalid mount point list: required %d only specified %d mount points\n", + sfs_Myname, mnt_argc + 1, argc); + (void) generic_kill(0, SIGINT); + exit(181); + } + + mount_point = argv[mnt_argc]; + + /* + * May require root priv to perform bindresvport operation + */ + mount_client_ptr = lad_getmnt_hand(mount_point); + if (mount_client_ptr == NULL) { + exit(145); + } + + /* + * should be all done doing priv port stuff + */ + + if (init_rpc() == -1) { + (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname); + (void) generic_kill(0, SIGINT); + exit(146); + } + + /* + * finish all priv bindresvport calls + * reset uid + */ + if (setuid(Real_uid) != (uid_t)0) { + (void) fprintf(stderr,"%s: %s%s", sfs_Myname, + "cannot perform setuid operation.\n", + "Do `make install` as root.\n"); + } + + init_mount_point(Child_num, mount_point, mount_client_ptr); + + /* + * Cleanup client handle for mount point + */ + clnt_destroy(mount_client_ptr); + + /* + * Tell parent I'm ready to initialize my test directory, + * wait for the go ahead signal. + */ + if (write(Log_fd, "x", 1) != 1) { + (void) fprintf(stderr, "%s: can't write to synchronization file %s", + sfs_Myname, Logname); + (void) generic_kill(0, SIGINT); + exit(147); + } + (void) pause(); + + if (DEBUG_CHILD_GENERAL) { + if (Timed_run) { + if (Prime_client) { + (void) fprintf(stderr, + "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n", + Child_num, Child_call_load, Child_req_load, + Runtime - MULTICLIENT_OFFSET); + } + else { + (void) fprintf(stderr, + "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n", + Child_num, Child_call_load, Child_req_load, Runtime); + } + } else { + (void) fprintf(stderr, + "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d calls\n", + Child_num, Child_call_load, Child_req_load, + Ops[TOTAL].target_calls); + } + (void) fflush(stderr); + } + + /* Initialize the test directory */ + Current_test_phase = Populate_phase; + init_testdir(); + + /* + * activate the biod behaviour if desired + */ + if (Biod_max_outstanding_reads > 0 || Biod_max_outstanding_writes > 0) { + biod_turn_on(); + } + + /* + * Tell parent I'm ready to start test, wait for the go ahead signal. + */ + if (write(Log_fd, "x", 1) != 1) { + (void) fprintf(stderr, "%s: can't write to synchronization file %s\n", + sfs_Myname, Logname); + (void) generic_kill(0, SIGINT); + exit(148); + } + (void) pause(); + + if (DEBUG_CHILD_GENERAL) { + if (Timed_run) { + if (Prime_client) { + (void) fprintf(stderr, + "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n", + Child_num, Child_call_load, Child_req_load, + Runtime - MULTICLIENT_OFFSET); + } + else { + (void) fprintf(stderr, + "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n", + Child_num, Child_call_load, Child_req_load, Runtime); + } + } else { + (void) fprintf(stderr, + "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d calls\n", + Child_num, Child_call_load, Child_req_load, + Ops[TOTAL].target_calls); + } + (void) fflush(stderr); + } + + + /* Start the warmup phase; initialize operation counters */ + Current_test_phase = Warmup_phase; + init_counters(); + Measurement_in_progress = 0; + + /* + * Compute the average sleep time per call. + * Start off with the assumption that we can sleep half the time. + * Note: using msec-per-call to adjust sleeping time + * limits benchmark load rates to less than 1000 calls-per-sec-per-child. + */ + Target_sleep_mspc = (int) (((1000.0 / Child_call_load) / 2.0) + .5); + + /* + * Occasionally, check to see if ops are being generating at the + * correct rate. During the warmup phase, checks are made every 2 seconds. + * Hopefully, this will allow the test to reach steady state before the + * warmup phase ends. During the timed test run, checks are made every + * 10 seconds. The switch is made when we receive the start signal. + */ + Msec_per_period = DEFAULT_WARM_RATE_CHECK * 1000; + + /* Loop generating load */ + while ((Timed_run && Runtime) || + (!Timed_run && + (Ops[TOTAL].results.good_calls < Ops[TOTAL].target_calls))) { + + if (start_run_phase) { + init_counters(); + Measurement_in_progress = 1; + /* + * Progress is checked every 10 seconds during the test run. + */ + Msec_per_period = DEFAULT_RUN_RATE_CHECK * 1000; + + start_run_phase = 0; + } + + /* Do an NFS operation, unless we need to sleep for the whole period. */ + if (Target_sleep_mspc < Msec_per_period) + op_count = do_op(); + else + op_count = 0; + + /* if the call was successful, add op_count to the period total. */ + if (op_count > 0) { + Calls_this_period += op_count; + Reqs_this_period++; + } + + /* + * If the call was successful, + * or we need to sleep for the whole period, + * sleep for a while before doing the next op. + */ + if ((op_count > 0) || (Target_sleep_mspc >= Msec_per_period)) { + /* + * Sleep for the whole period or + * for a random (positive) time period in the range + * (Target_sleep_mspc +- 1/2(Target_sleep_mspc)). + */ + if (Target_sleep_mspc >= Msec_per_period) + rand_sleep_msec = Msec_per_period; + else if (Target_sleep_mspc >= 1) + rand_sleep_msec = (Target_sleep_mspc >> 1) + + (sfs_random() % Target_sleep_mspc); + else + rand_sleep_msec = 0; + + if (rand_sleep_msec != 0) { + if (DEBUG_CHILD_TIMING) { + (void) fprintf(stderr, "Child %d sleep for %d msec\n", + Child_num, rand_sleep_msec); + (void) fflush(stderr); + } + Sleep_msec_this_period += msec_sleep(rand_sleep_msec); + } + } + + /* + * See if it's time to check our progress. + * If an operation was just performed, then Cur_time was updated + * in the op routine; otherwise we need to get Cur_time. + */ + if (op_count <= 0) { + sfs_gettime(&Cur_time); + } + + current_msec = (Cur_time.sec * 1000) + (Cur_time.usec / 1000); + if (DEBUG_CHILD_XPOINT) { + (void) fprintf(stderr, "cur=%d prev=%d per=%d\n", + current_msec, Previous_chkpnt_msec, Msec_per_period); + } + + if ((current_msec - Previous_chkpnt_msec) > Msec_per_period) { + check_call_rate(); + } + + } /* end while more calls to make */ + + /* + * We are done generating our part of the load. + * Store total time in last slot of counts array. + * + * The last slot has the wall clock time of all the load generation. + * Individual slots have the wall clock time spent just for the op + * gen routine. + */ + sfs_gettime(&Cur_time); + Measurement_in_progress = 0; + elapsed_time.sec = Cur_time.sec; + elapsed_time.usec = Cur_time.usec; + SUBTIME(elapsed_time, Starttime); + + Ops[TOTAL].results.time.sec = elapsed_time.sec; + Ops[TOTAL].results.time.usec = elapsed_time.usec; + + if (DEBUG_CHILD_FILES) { + (void) fprintf(stderr, + "%s: max fss %d KB min fss %d KB\n", + sfs_Myname, Most_fss_bytes, Least_fss_bytes); + (void) fflush(stderr); + } + + if (DEBUG_CHILD_FILES) { + (void) fprintf(stderr, "Child %d Files:\n", Child_num); + for (i = 0; i < Num_io_files; i++) + (void) fprintf(stderr, "Io[%d] use %d xfer %d\n", + i, Io_files[i].use_cnt, Io_files[i].xfer_cnt); + for (i = 0; i < Num_non_io_files; i++) + (void) fprintf(stderr, "Non_io[%d] use %d xfer %d\n", + i, Non_io_files[i].use_cnt, + Non_io_files[i].xfer_cnt); + for (i = 0; i < Num_dir_files; i++) + (void) fprintf(stderr, "Dir[%d] use %d xfer %d\n", + i, Dirs[i].use_cnt, Dirs[i].xfer_cnt); + for (i = 0; i < Num_symlink_files; i++) + (void) fprintf(stderr, "Sym[%d] use %d xfer %d\n", + i, Symlinks[i].use_cnt, Symlinks[i].xfer_cnt); + (void) fflush(stderr); + } + + if (DEBUG_CHILD_SETUP) { + int j, group_size, offset, index, tot; + for (i = 0; i < Io_working_set.access_group_cnt; i++) { + group_size = Io_working_set.access_group_size; + if (i < (Num_working_io_files - + ((Num_working_io_files / Io_working_set.access_group_cnt) + * Io_working_set.access_group_cnt))) + group_size += 1; + tot = 0; + for (j = 0; j < group_size; j++) { + offset = i + (j * Io_working_set.access_group_cnt); + index = Io_working_set.entries[offset].index; + tot += Io_files[index].use_cnt; + (void) fprintf(stderr, "Working[%d] use %d xfer %d\n", + offset, Io_files[index].use_cnt, + Io_files[index].xfer_cnt); + } + (void) fprintf(stderr, "Group %d total use %d\n", i, tot); + } + (void) fflush(stderr); + } + + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stderr, "Child %d Ops:\n", Child_num); + + previous_pcnt = 0.0; + (void) fprintf(stderr, + " calls reqs\n"); + (void) fprintf(stderr, + " trgt actl trgt actl bad no trgt actl trgt actl\n"); + (void) fprintf(stderr, + " name mix mix cnt cnt cnt cnt mix mix cnt cnt\n"); + + for (i = 0; i < NOPS + 1; i++) { + (void) fprintf(stderr, + "%11s %4d %4.1f %5d %5d %4d %3d %4.1f %4.1f %6d %6d\n", + Ops[i].name, Ops[i].mix_pcnt, + (float) (100 * Ops[i].results.good_calls) + / (float) Ops[TOTAL].results.good_calls, + Ops[i].target_calls, Ops[i].results.good_calls, + Ops[i].results.bad_calls, Ops[i].no_calls, + Ops[i].req_pcnt - previous_pcnt, + (float) (100 * Ops[i].req_cnt) / (float) Ops[TOTAL].req_cnt, + Ops[i].target_reqs, Ops[i].req_cnt); + previous_pcnt = Ops[i].req_pcnt; + } + (void) fflush(stderr); + } + + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stderr, "Child %d made %d of %d calls in %ld sec\n", + Child_num, Ops[TOTAL].results.good_calls, + Ops[TOTAL].target_calls, + Ops[TOTAL].results.time.sec); + (void) fflush(stderr); + } + + clnt_destroy(NFS_client); + biod_term(); + + /* write stats to log file (append mode) */ + report.version = nfs_version; + for (i = 0; i < NOPS + 1; i++) { + report.results_buf[i] = Ops[i].results; + } + report.total_fss_bytes = Total_fss_bytes; + report.least_fss_bytes = Least_fss_bytes; + report.most_fss_bytes = Most_fss_bytes; + report.base_fss_bytes = Base_fss_bytes; + + if (write(Log_fd, (char *) &report, sizeof(report)) == -1) { + Saveerrno = errno; + (void) fprintf(stderr, "%s: can't write to synchronization file %s ", + sfs_Myname, Logname); + errno = Saveerrno; + perror(Logname); + (void) generic_kill(0, SIGINT); + exit(149); + } + (void) close(Log_fd); + + print_dump(Client_num, Child_num); + +} /* child */ + + +/* + * -------------------- Call Target Initialization -------------------- + */ + +/* + * Initialize call and request targets. + */ +static void +init_targets(void) +{ + int call_target; /* total number of calls to make */ + int req_target; /* total number of reqs to make */ + int32_t equal_mix; /* equal mix of operations */ + int32_t slack; /* calls leftover after % mix */ + int randnum; /* a random number */ + int32_t i; /* general use */ + double total_req_pcnt; + double previous_pcnt; + int nops_used = 0; + + + /* + * Compute number of target calls for each operation. + * These are used to estimate the number of filehandles + * that will be used for each type of operation. + */ + call_target = Ops[TOTAL].target_calls; + Ops[TOTAL].target_calls = 0; + + for (i = 0; i < NOPS; i++) { + Ops[i].target_calls = (Ops[i].mix_pcnt * call_target) / 100; + Ops[TOTAL].target_calls += Ops[i].target_calls; + if (Ops[i].mix_pcnt != 0) + nops_used++; + } + + /* Put left over calls into the heavier mix operations. */ + slack = call_target - Ops[TOTAL].target_calls; + equal_mix = (100 / nops_used) / 2; + while (slack > 0) { + randnum = sfs_random() % NOPS; + if (Ops[randnum].mix_pcnt != 0 && Ops[randnum].mix_pcnt >= equal_mix) { + Ops[randnum].target_calls++; + Ops[TOTAL].target_calls++; + slack--; + } + } + + /* + * compute request targets (based on suggestions from M. Molloy, HP) + */ + + /* compute total of target requests, based on weighted ops */ + total_req_pcnt = 0.0; + for (i = 0; i < NOPS ; i++) { + switch (i) { + case READ: + total_req_pcnt += ((double) Ops[i].mix_pcnt) + / Io_dist_ptr->avg_ops_per_read_req; + break; + case WRITE: + total_req_pcnt += ((double) Ops[i].mix_pcnt) + / Io_dist_ptr->avg_ops_per_write_req; + break; + case COMMIT: /* Commits never generate requests */ + break; + default: + total_req_pcnt += (double) Ops[i].mix_pcnt; + break; + } + } + + /* + * compute cumulative frequency distribution percentile for each op. + * This code assumes that the NULLCALL does not generate multiple + * OTW operations per request. + */ + previous_pcnt = 0.0; + for (i = 0; i < NOPS; i++) { + switch (i) { + case READ: + Ops[i].req_pcnt = previous_pcnt + + (((100.0 * (double) Ops[i].mix_pcnt) + / Io_dist_ptr->avg_ops_per_read_req) + / total_req_pcnt); + break; + case WRITE: + Ops[i].req_pcnt = previous_pcnt + + (((100.0 * (double) Ops[i].mix_pcnt) + / Io_dist_ptr->avg_ops_per_write_req) + / total_req_pcnt); + break; + case COMMIT: /* Commits never generate requests */ + Ops[i].req_pcnt = previous_pcnt; + break; + default: + Ops[i].req_pcnt = previous_pcnt + + ((100.0 * (double) Ops[i].mix_pcnt) + / total_req_pcnt); + break; + } + previous_pcnt = Ops[i].req_pcnt; + } + /* force last bucket to 100 */ + Ops[NOPS-1].req_pcnt = 100; + + /* compute the req load rate */ + Child_req_load = (total_req_pcnt * Child_call_load) / 100.0; + + /* + * Compute number of target reqs for each operation. + * These are used for debugging purposes. + */ + req_target = (total_req_pcnt * Ops[TOTAL].target_calls) / 100; + Ops[TOTAL].target_reqs = 0; + + previous_pcnt = 0.0; + for (i = 0; i < NOPS; i++) { + Ops[i].target_reqs = 0; + if (Ops[i].mix_pcnt != 0) { + Ops[i].target_reqs = ((Ops[i].req_pcnt - previous_pcnt) * + req_target) / 100; + } + Ops[TOTAL].target_reqs += Ops[i].target_reqs; + previous_pcnt = Ops[i].req_pcnt; + } + + /* Put left over reqs into the heavier mix operations. */ + slack = req_target - Ops[TOTAL].target_reqs; + equal_mix = (100 / nops_used) / 2; + while (slack > 0) { + randnum = sfs_random() % NOPS; + if (Ops[randnum].target_reqs != 0 && + Ops[randnum].req_pcnt >= equal_mix) { + Ops[randnum].target_reqs++; + Ops[TOTAL].target_reqs++; + slack--; + } + } + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stderr, + " Op\t Op mix\tCalls\t\t Req mix\t Reqs\t\n"); + previous_pcnt = 0.0; + for (i = 0; i < NOPS; i++) { + (void) fprintf(stderr, "%8s\t%8d\t%5d\t\t%8.2f\t%5d\n", + Ops[i].name, + Ops[i].mix_pcnt, Ops[i].target_calls, + Ops[i].req_pcnt - previous_pcnt, + Ops[i].target_reqs); + previous_pcnt = Ops[i].req_pcnt; + } + } +} /* init_targets */ + + +/* + * ----------------------- File Set Initialization ----------------------- + */ + +static file_array_initialized = 0; +static int file_size_array[100]; + +/* + * For a value between 0-99, return a size based on distribution + */ +static int +get_file_size(int i) +{ + if (i < 0 || i > 99) + return (0); + + if (file_array_initialized == 0) { + int j, k; + + for (j = 0, k = 0; j < 100; j++) { + if (j >= Default_file_size_dist[k].pcnt && + Default_file_size_dist[k + 1].size != 0) + k++; + file_size_array[j] = Default_file_size_dist[k].size * 1024; + } + file_array_initialized++; + } + return (file_size_array[i]); +} + +/* + * allocate and initialize the various file information structures. + */ +void +init_fileinfo(void) +{ + int i, index; + int j; + int group_size, group_cnt; + int range, previous_range; + int next_value; + double lambda; + double e_to_the_lambda; + double cumulative_ratio; + int num_non_io_to_init; + int io_file_num = 0; + int files_per_generation; + sfs_fh_data *fh_datap; + + + /* + * Zero number of files created used to create unique names + */ + Files_created = 0; + + /* + * Dirs - Initialize the files info structure. + * Directories must come first, in initializing test dirs we + * need to make sure that any files deleted are no full directories + */ + Num_dir_files = + Num_dirs + /* exist: readdir, rmdir */ + Ops[MKDIR].target_calls + /* non-exist: mkdir */ + Ops[RMDIR].target_calls; /* empty dir to be removed */ + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "%s: allocate %d directories\n", + sfs_Myname, Num_dir_files); + (void) fflush(stderr); + } + Dirs = (sfs_fh_type *) calloc(Num_dir_files, sizeof(sfs_fh_type)); + + if (Dirs == (sfs_fh_type *) 0) { + (void) fprintf(stderr,"%s: init_fileinfo dir calloc %d bytes failed", + sfs_Myname, Num_dir_files * sizeof(sfs_fh_type)); + (void) generic_kill(0, SIGINT); + exit(150); + } + for (i = 0; i < Num_dir_files; i++) { + Dirs[i].working_set = 0; + Dirs[i].state = Nonexistent; + if (i <= (Num_dirs + Ops[RMDIR].target_calls)) { + Dirs[i].initialize = 1; + Dirs[i].fh_data = (sfs_fh_data *)0; + } + Dirs[i].unique_num = i; + } + + /* Working Set Directory Files - Initialize the working files array. */ + Num_working_dirs = Num_dir_files; + Dir_working_set.entries = (sfs_work_fh_type *) + calloc(Num_working_dirs, + sizeof(sfs_work_fh_type)); + if (Dir_working_set.entries == (sfs_work_fh_type *) 0) { + (void) fprintf(stderr,"%s: init_fileinfo wdir calloc %d bytes failed", + sfs_Myname, Num_working_dirs * sizeof(sfs_work_fh_type)); + (void) generic_kill(0, SIGINT); + exit(151); + } + + /* + * Dirs are accessed uniformly. See Non_io_files for a description. + */ + if (init_rand_range(Num_dir_files)) { + (void) fprintf(stderr, "%s: init_fileinfo dir init_rand_range failed", + sfs_Myname); + (void) generic_kill(0, SIGINT); + exit(183); + } + + for (i = 0; i < Num_working_dirs; i++) { + if (Num_working_dirs != Num_dir_files) { + /* generate a random subset */ + index = rand_range(i); + } else { + /* match the working set one-to-one with the files */ + index = i; + } + + Dirs[index].working_set = 1; + Dir_working_set.entries[i].index = index; + Dir_working_set.entries[i].range = i + 1; + } + Dir_working_set.access_group_size = Num_working_dirs; + Dir_working_set.access_group_cnt = 1; + + Dir_working_set.max_range = Num_working_dirs; + + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "\nDir size=%d cnt=%d max=%d\n", + Dir_working_set.access_group_size, + Dir_working_set.access_group_cnt, + Dir_working_set.max_range); + (void) fflush(stderr); + } + + + /* + * I/o Files - Initialize the files info structure to Num_io_files. + */ + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "%s: allocate %d i/o files, %d working\n", + sfs_Myname, Num_io_files, Num_working_io_files); + (void) fflush(stderr); + } + + Io_files = (sfs_fh_type *) calloc(Num_io_files, sizeof(sfs_fh_type)); + if (Io_files == (sfs_fh_type *) 0) { + (void) fprintf(stderr,"%s: init_fileinfo %d io files calloc %d bytes failed", + sfs_Myname, Num_io_files, + Num_io_files * sizeof(sfs_fh_type)); + (void) generic_kill(0, SIGINT); + exit(152); + } + io_file_num = 0; + for (i = 0; i < Num_io_files; i++) { + Io_files[i].working_set = 0; + Io_files[i].state = Nonexistent; + Io_files[i].initialize = 1; + Io_files[i].size = get_file_size(io_file_num % 100); + Io_files[i].unique_num = Files_created++; + /* Memory allocation for the fh_data will be done later. */ + Io_files[i].fh_data = (sfs_fh_data *)0; + io_file_num++; + } + + /* + * Working Set I/o Files - Initialize the working files array. + * Only Access_percent of the Io_files are put into the working set. + */ + Io_working_set.entries = (sfs_work_fh_type *) + calloc(Num_working_io_files, + sizeof(sfs_work_fh_type)); + if (Io_working_set.entries == (sfs_work_fh_type *) 0) { + (void) fprintf(stderr,"%s: init_fileinfo wio calloc %d bytes failed", + sfs_Myname, Num_working_io_files * sizeof(sfs_work_fh_type)); + (void) generic_kill(0, SIGINT); + exit(153); + } + + + if (DEBUG_CHILD_FILES) { + (void) fprintf(stderr, "working_set: "); + (void) fflush(stderr); + } + + /* + * For now, the access distribution is poisson. See below. + */ +/* #define UNIFORM_ACCESS */ +#define POISSON_ACCESS + +#ifdef UNIFORM_ACCESS + /* + * With a uniform access distribution, there is no need for access + * groups. + * Hopefully SPEC-SFS will agree on a non-uniform access function. + * (see below for an example using a poisson distribution). + */ + if (init_rand_range(Num_io_files)) { + (void) fprintf(stderr, "%s: init_fileinfo io init_rand_range failed", + sfs_Myname); + (void) generic_kill(0, SIGINT); + exit(184); + } + + for (i = 0; i < Num_working_io_files; i++) { + if (Num_working_io_files != Num_io_files) { + /* generate a random subset */ + index = rand_range(i); + } else { + /* match the working set one-to-one with the files */ + index = i; + } + Io_files[index].working_set = 1; + Io_working_set.entries[i].index = index; + Io_working_set.entries[i].range = i + 1; + + if (DEBUG_CHILD_FILES) { + (void) fprintf(stderr, "%d,", index); + (void) fflush(stderr); + } + } + Io_working_set.access_group_size = Num_working_io_files; + Io_working_set.access_group_cnt = 1; + Io_working_set.max_range = Num_working_io_files; + + if (DEBUG_CHILD_FILES) { + (void) fprintf(stderr, "\nIo size=%d cnt=%d max=%d\n", + Io_working_set.access_group_size, + Io_working_set.access_group_cnt, + Io_working_set.max_range); + (void) fflush(stderr); + } + +#endif /* ! UNIFORM_ACCESS */ +#ifdef POISSON_ACCESS + + /* + * The working set is partitioned into access groups of Access_group_size + * files. Each group is assigned a probability of being accessed. + * This is implemented as a cumulative distribution table, with + * variable probabilities for each group. The distribution function + * is used to generate a sequence of values, one for each group. + * Each group is assigned a 'range' value that is the sum of all + * previous range values, plus the next value in the distribution + * sequence. Thus, the probability of choosing any particular group + * is equal to the relative height of the distribution curve at the + * point represented by that group. + * The choice is made by generating a random number in the range + * 0 up to (the sum of all values in the distribution sequence - 1), + * and finding the group with the greatest range value less than + * the random number. + * Once a group is chosen, a random number in the range + * 1 - Access_group_size is used to pick an entry from within the group. + * The entry chosen points to a file in the Io_files array. + * If the file at Io_files[index] is eligible for the operation, + * then it is accessed, otherwise, the access group is searched + * sequentially (mod Access_group_size with wrap-around) until an + * eligible file is found. + * Access_group_size is derived so that there are enough files + * in each group to give a good chance of finding an eligible file + * for each operation, but so that there are enough groups (each + * representing a point on the distribution curve) to generate a + * fairly smooth access distribution curve. + */ + + /* + * group_cnt = 8 + ((Num_working_io_files/500) * 4); + * + * The function is chosen to guarentee that each group contains + * at least 1 file, and, beginning with a base of 8 groups, the + * number of groups increases by 4 for each 500 files in the working + * set. It was arrived at heuristically. The goal is to put enough + * files into each group to ensure that a file with the right + * attributes can be found once the group is selected (which can be + * difficult for small working sets), while at the same time creating + * enough groups to provide enough points on the distribution curve + * to yield an interesting access distribution. + * + * Since this function is being computed per child, the interesting range + * of working set sizes is computed based on a range of per child load + * values from 1 op/sec to 100 op/sec. Note that this assumes an + * average server response time of at least 10 msec, which seems to be + * a good minimum value for a wide range of servers given the default + * mix of NFS operations. + * Based on these load values, the total file set, based on the default + * values of 10 MB/op and 38 files/MB, works out to 380 - 38000 files. + * The default working set of 10% of these files yields a working + * set size of 38 - 3800 files. + */ + + files_per_generation = (_GROUP_DIVISOR * generations) / _FILES_PER_GROUP; + Io_working_set.access_group_cnt = generations + + ((Num_working_io_files/files_per_generation) * generations); + /* + * if the number of files in the working set is not a multiple of + * the group size, then some groups will contain (group_size+1) files. + * Thus, this is the base group size. + */ + Io_working_set.access_group_size = Num_working_io_files / + Io_working_set.access_group_cnt; + + if (init_rand_range(Num_io_files)) { + (void) fprintf(stderr, "%s: init_fileinfo io init_rand_range failed", + sfs_Myname); + (void) generic_kill(0, SIGINT); + exit(185); + } + + /* randomly set up working set of indices into Io_files */ + for (i = 0; i < Num_working_io_files; i++) { + if (Num_working_io_files != Num_io_files) { + /* generate a random subset */ + index = rand_range(i); + } else { + /* match the working set one-to-one with the files */ + index = i; + } + Io_files[index].working_set = 1; + Io_working_set.entries[i].index = index; + + if (DEBUG_CHILD_FILES) { + (void) fprintf(stderr, "%d,", index); + (void) fflush(stderr); + } + } + + /* initialization for distribution function */ + range = 0; + lambda = (double) (generations / 2); + if (lambda <= 0) lambda = 1; + e_to_the_lambda = exp(lambda); + cumulative_ratio = 1.0; + + if (DEBUG_CHILD_FILES) { + (void) fprintf(stderr, + "\ngrp_cnt %d lambda %6.0f e_to_the_lambda %6.2f\n", + Io_working_set.access_group_cnt, lambda, + e_to_the_lambda); + (void) fflush(stderr); + } + + /* assign a range to each group */ + for (i = 0; i < Io_working_set.access_group_cnt; i++) { + /* + * get next value in poisson distribution sequence, using + * lambda^x / (e^(lambda) * x!) , for x=1,2,3,...,group_cnt + */ + double probability; + + if( i % generations == 0) + { + lambda = (double) (generations / 2); + if (lambda <= 0) lambda = 1; + e_to_the_lambda = exp(lambda); + cumulative_ratio = 1.0; + } + probability = cumulative_ratio/e_to_the_lambda; + if (probability <= 0.0 || probability > 1.0) { + (void) fprintf(stderr, "%s: access probability = %g while setting up Io_working_set, i=%d of %d\n", + sfs_Myname, probability, + i, Io_working_set.access_group_cnt); + (void) generic_kill(0, SIGINT); + exit(154); + } + + /* convert probability to scaled integer */ + next_value = (int) (PROB_SCALE * probability); + + /* check for negative numbers */ + if (next_value <= 0) { + (void) fprintf(stderr, "%s: next_value = %d while setting up Io_working_set, i=%d of %d\n", + sfs_Myname, next_value, + i, Io_working_set.access_group_cnt); + (void) generic_kill(0, SIGINT); + exit(154); + } + + previous_range = range; + range = previous_range + next_value; + if (range <= previous_range || range < 0) { + (void) fprintf(stderr, "%s: range = %d previous_range = %d while setting up Io_working_set, i=%d of %d\n", + sfs_Myname, range, previous_range, + i, Io_working_set.access_group_cnt); + (void) generic_kill(0, SIGINT); + exit(154); + } + + /* assign range value to each file in this group */ + group_size = Io_working_set.access_group_size; + group_cnt = Io_working_set.access_group_cnt; + if (i < (Num_working_io_files - + ((Num_working_io_files / group_cnt) * group_cnt))) + group_size += 1; + for (j = 0; j < group_size; j++) { + index = i + (j * Io_working_set.access_group_cnt); + Io_working_set.entries[index].range = range; + } + + cumulative_ratio *= lambda / (double) ((i%generations)+1); + + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "group %d next %d range %d\n", + i, next_value, range); + (void) fflush(stderr); + } + } + Io_working_set.max_range = range; + + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "\nIo size=%d cnt=%d max=%d\n", + Io_working_set.access_group_size, + Io_working_set.access_group_cnt, + Io_working_set.max_range); + (void) fflush(stderr); + } +#endif /* POISSON_ACCESS */ + + + /* figure out how many files to allocate and initialize */ + + /* initialize half the non-I/O files */ + /* NOTE: initializing half the non-i/o files works ok with the + default op mix. If the mix is changed affecting the + ratio of creations to removes, there may not be enough + empty slots for file creation (or there may not be + enough created during initialization to handle a lot of + removes that occur early in the test run), and this would + cause do_op() to fail to find a file appropriate for the + chosen op. This will result in the global variable + Ops[op].no_calls being incremented (turn on child level + debugging to check this count), and the do_op() local + variable aborted_ops to be incremented and checked during + runtime for too many failures. + */ + num_non_io_to_init = Num_non_io_files * RATIO_NON_IO_INIT; + + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "%s: allocate %d non-i/o files\n", + sfs_Myname, Num_non_io_files); + (void) fflush(stderr); + } + Non_io_files = (sfs_fh_type *) + calloc(Num_non_io_files, sizeof(sfs_fh_type)); + if (Non_io_files == (sfs_fh_type *) 0) { + (void) fprintf(stderr,"%s: init_fileinfo nio calloc %d bytes failed", + sfs_Myname, Num_non_io_files * sizeof(sfs_fh_type)); + (void) generic_kill(0, SIGINT); + exit(154); + } + for (i = 0; i < Num_non_io_files; i++) { + Non_io_files[i].working_set = 0; + Non_io_files[i].state = Nonexistent; + if (i <= num_non_io_to_init) + Non_io_files[i].initialize = 1; + Non_io_files[i].size = get_file_size(io_file_num % 100); + Non_io_files[i].unique_num = Files_created++; + /* Allocation of fh_data will happen in init_testdir */ + Non_io_files[i].fh_data = (sfs_fh_data *)0; + io_file_num++; + } + + /* Working Set Non i/o Files - Initialize the working files array. */ + Num_working_non_io_files = Num_non_io_files; + Non_io_working_set.entries = (sfs_work_fh_type *) + calloc(Num_working_non_io_files, + sizeof(sfs_work_fh_type)); + if (Non_io_working_set.entries == (sfs_work_fh_type *) 0) { + (void) fprintf(stderr,"%s: init_fileinfo nwio calloc %d bytes failed", + sfs_Myname, Num_working_io_files * sizeof(sfs_work_fh_type)); + (void) generic_kill(0, SIGINT); + exit(155); + } + + /* + * Non_io_files are accessed uniformly. Each entry has a + * 1/Num_working_non_io_files change of being accessed. + * The choice is made by generating a random number in the range + * 0 through (Num_working_non_io_files - 1) and finding the entry + * with the greatest range value less than the random number. + * If the file at Non_io_files[index] is eligible for the operation, + * it is accessed, otherwise, the access group that the entry belongs + * to is searched sequentially until an eligible file is found. + * For non i/o files, all of the working set files are in the same + * access group (since they access is uniform, this is ok, and + * maximizes the chances of finding an eligible file). + */ + if (init_rand_range(Num_non_io_files)) { + (void) fprintf(stderr, "%s: init_fileinfo non_io init_rand_range failed", + sfs_Myname); + (void) generic_kill(0, SIGINT); + exit(186); + } + + for (i = 0; i < Num_working_non_io_files; i++) { + if (Num_working_non_io_files != Num_non_io_files) { + /* generate a random subset */ + index = rand_range(i); + } else { + /* match the working set one-to-one with the files */ + index = i; + } + Non_io_files[index].working_set = 1; + Non_io_working_set.entries[i].index = index; + Non_io_working_set.entries[i].range = i + 1; + } + Non_io_working_set.access_group_size = Num_working_non_io_files; + Non_io_working_set.access_group_cnt = 1; + Non_io_working_set.max_range = Num_working_non_io_files; + + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "\nNon_io size=%d cnt=%d max=%d\n", + Non_io_working_set.access_group_size, + Non_io_working_set.access_group_cnt, + Non_io_working_set.max_range); + (void) fflush(stderr); + } + + + /* Symlinks - Initialize the files info structure. */ + Num_symlink_files = + Num_symlinks + /* exist: readlink */ + Ops[SYMLINK].target_calls; /* non-exist: symlink */ + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "%s: allocate %d symlinks\n", + sfs_Myname, Num_symlink_files); + (void) fflush(stderr); + } + Symlinks = (sfs_fh_type *) + calloc(Num_symlink_files, sizeof(sfs_fh_type)); + if (Symlinks == (sfs_fh_type *) 0) { + (void) fprintf(stderr,"%s: init_fileinfo sym calloc %d bytes failed", + sfs_Myname, (Num_symlink_files * sizeof(sfs_fh_type))); + (void) generic_kill(0, SIGINT); + exit(156); + } + for (i = 0; i < Num_symlink_files; i++) { + Symlinks[i].working_set = 0; + Symlinks[i].state = Nonexistent; + if (i <= Num_symlinks) + Symlinks[i].initialize = 1; + Symlinks[i].fh_data = (sfs_fh_data *)0; + Symlinks[i].unique_num = i; + } + + /* Working Set Symlinks - Initialize the working files array. */ + /* This appears to cause the following loop to be mostly dead */ + /* code. It is unclear why this line is here. One */ + /* possibility is that Num_symlink_files should be */ + /* Num_symlinks. XXX */ + Num_working_symlinks = Num_symlink_files; + Symlink_working_set.entries = (sfs_work_fh_type *) + calloc(Num_working_symlinks, + sizeof(sfs_work_fh_type)); + if (Symlink_working_set.entries == (sfs_work_fh_type *) 0) { + (void) fprintf(stderr,"%s: init_fileinfo wsym calloc %d bytes failed", + sfs_Myname, Num_working_symlinks * sizeof(sfs_work_fh_type)); + (void) generic_kill(0, SIGINT); + exit(157); + } + + /* + * Symlinks are accessed uniformly. See Non_io_files for a description. + */ + if (init_rand_range(Num_symlink_files)) { + (void) fprintf(stderr, "%s: init_fileinfo sym init_rand_range failed", + sfs_Myname); + (void) generic_kill(0, SIGINT); + exit(187); + } + + for (i = 0; i < Num_working_symlinks; i++) { + if (Num_working_symlinks != Num_symlink_files) { + /* generate a random subset */ + index = rand_range(i); + } else { + /* match the working set one-to-one with the files */ + index = i; + } + + Symlinks[index].working_set = 1; + Symlink_working_set.entries[i].index = index; + Symlink_working_set.entries[i].range = i + 1; + } + Symlink_working_set.access_group_size = Num_working_symlinks; + Symlink_working_set.access_group_cnt = 1; + Symlink_working_set.max_range = Num_working_symlinks; + + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "\nSymlink size=%d cnt=%d max=%d\n", + Symlink_working_set.access_group_size, + Symlink_working_set.access_group_cnt, + Symlink_working_set.max_range); + (void) fflush(stderr); + } + + /* + * Free up random number range + */ + (void)init_rand_range(0); + + +} /* init_fileinfo */ + +/* + * allocate and initialize the directory layout of the files + * + * We can only place files in directories that can't be removed + */ +static void +init_dirlayout(void) +{ + int i,j; + + /* + * Initially create directories only one level deep so all directories + * must be in the parent directory. + */ + for (i = 0; i < Num_dir_files; i++) { + Dirs[i].dir = &Export_dir; + } + + /* + * Files must only be placed in the first Num_dirs entries leaving + * a set for directory create and remove. + */ + j = 0; + for (i = 0; i < Num_io_files; i++) { + if (i != 0 && (i % Files_per_dir) == 0) + j++; + Io_files[i].dir = &Dirs[j]; + } + + /* + * All non-io and symlink files are placed in the parent directory + */ + for (i = 0; i < Num_non_io_files; i++) { + Non_io_files[i].dir = &Export_dir; + } + + for (i = 0; i < Num_symlink_files; i++) { + Symlinks[i].dir = &Export_dir; + } +} + +/* + * allocate and initialize client handles + */ +static int +init_rpc(void) +{ + /* + * Set up the client handles. We get them all before trying one + * out to insure that the client handle for LOOKUP class is allocated + * before calling op_getattr(). + */ + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname); + } + + NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent, + (uint32_t) NFS_PROGRAM, + (uint32_t) nfs_version, + RPC_ANYSOCK, &Nfs_timers[0]); + + if (NFS_client == ((CLIENT *) NULL)) { + return(-1); + } + + /* + * create credentials using the REAL uid + */ + NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid, + (int)Cur_gid, 0, NULL); + + /* Initialize biod simulation mechanism if desired. */ + if (Biod_max_outstanding_reads > 0 || Biod_max_outstanding_writes > 0) { + if (biod_init(Biod_max_outstanding_writes, + Biod_max_outstanding_reads) == -1) { + return(-1); + } + } + + return(0); +} /* init_rpc */ + +/* + * Initialize the test directory 'parentdir'/testdir'dirnum'. + * + * If the directory already exists, check to see that all of the + * files exist and can be written. If the directory doesn't exist + * create it and fill it with the proper files. The caller is + * left with his cwd being the test directory. + * + * Each child pseudo-mount's his own test directory to get its filehandle. + * + * Files, directories, and symlinks all have the same name structure + * but they are strictly ordered, files first, directories next, then symlinks. + * While initializing after a previous run we may have to delete existing + * files of the wrong type and then create them later. + * + * XXX In the future it is probably wiser to have seperate namespaces for + * each type of file. + */ +static void +init_testdir(void) +{ + int filenum; + int max_filenum; + int init_size; + int append_size; + int ret; + int non = 0; + int dealloc; + int alloc_count, dealloc_count; + /* + * Create directories first so operations that + * require them will have a file to work with. + */ + alloc_count=dealloc_count=0; + for (filenum = 0; filenum < Num_dir_files; filenum++) { + sfs_gettime(&Cur_time); + + Cur_file_ptr = &Dirs[filenum]; + dealloc=0; + if(Cur_file_ptr->fh_data == (sfs_fh_data *)0) + { + alloc_count++; + Cur_file_ptr->fh_data = calloc(1,sizeof(sfs_fh_data)); + Cur_file_ptr->attributes2.type = NFNON; + Cur_file_ptr->attributes3.type = NF3NON; + if(Cur_file_ptr->working_set == 1) + dealloc=0; + else + dealloc=1; + } + + (void) sprintf(Cur_filename, Dirspec, Cur_file_ptr->unique_num); + + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "%s: initialize %s (DIR)\n", + sfs_Myname, Cur_filename); + (void) fflush(stderr); + } + + if ((ret = lad_lookup(Cur_file_ptr, Cur_filename)) == -1) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(159); + } + + if (ret == 0) { + /* file exists */ + if (fh_isdir(Cur_file_ptr) && Cur_file_ptr->initialize) + { + if(dealloc == 1) + { + dealloc_count++; + free(Cur_file_ptr->fh_data); + Cur_file_ptr->fh_data=(sfs_fh_data *)0; + } + continue; + } + + if (lad_remove(Cur_file_ptr, Cur_filename) != 0) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(160); + } + } + + if (!Cur_file_ptr->initialize) { + /* dir shouldn't exist */ + if(dealloc == 1) + { + dealloc_count++; + free(Cur_file_ptr->fh_data); + Cur_file_ptr->fh_data=(sfs_fh_data *)0; + } + continue; + } + + /* make the directory */ + if (lad_mkdir(Cur_file_ptr, Cur_filename) == -1) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(161); + } + if(dealloc == 1) + { + dealloc_count++; + free(Cur_file_ptr->fh_data); + Cur_file_ptr->fh_data=(sfs_fh_data *)0; + } + } /* end for each directory */ + + /* + * Setup for file i/o operations. + * Verify that we can read and write all the files. + * Make sure we have the attributes && fh for all regular files. + * Create any missing files. + */ + max_filenum = Num_io_files + Num_non_io_files; + alloc_count=dealloc_count=0; + for (filenum = 0; filenum < max_filenum; filenum++) { + sfs_gettime(&Cur_time); + + if (filenum < Num_io_files) { + Cur_file_ptr = &Io_files[filenum]; + } else { + Cur_file_ptr = &Non_io_files[filenum - Num_io_files]; + non = 1; + } + (void) sprintf(Cur_filename, Filespec, Cur_file_ptr->unique_num); + dealloc=0; + if(Cur_file_ptr->fh_data == (sfs_fh_data *)0) + { + alloc_count++; + Cur_file_ptr->fh_data = calloc(1,sizeof(sfs_fh_data)); + Cur_file_ptr->attributes2.type = NFNON; + Cur_file_ptr->attributes3.type = NF3NON; + if(Cur_file_ptr->working_set == 1) + dealloc=0; + else + dealloc=1; + } + + /* + * Get the size this file should be initialized to, then reset + * so we don't get confused. + */ + init_size = Cur_file_ptr->size; + Cur_file_ptr->size = 0; + + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "%s: initialize %s (REG for %sIO)\n", + sfs_Myname, Cur_filename, + (non ? "non-": "")); + (void) fflush(stderr); + } + + if ((ret = lad_lookup(Cur_file_ptr, Cur_filename)) == -1) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(162); + } + + if (ret == 0) { + /* + * If file exists and it shouldn't, remove it + */ + if (!Cur_file_ptr->initialize) { + if (lad_remove(Cur_file_ptr, Cur_filename) != 0) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(163); + } + if(dealloc == 1) + { + dealloc_count++; + free(Cur_file_ptr->fh_data); + Cur_file_ptr->fh_data=(sfs_fh_data *)0; + } + continue; + } + + /* + * file exists: make sure it is + * - a regular file + * - accessible (permissions ok) + * if not, remove it (if necessary) and recreate it + * or extend or truncate it to the standard length. + */ + if (fh_isfile(Cur_file_ptr) && + check_fh_access(Cur_file_ptr) == 0) { + goto adjust_size; + } + if (lad_remove(Cur_file_ptr, Cur_filename) != 0) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(164); + } + + } /* end if the file exists */ + + /* the file doesn't exist */ + if (!Cur_file_ptr->initialize) { + /* file doesn't exist and it shouldn't */ + if(dealloc == 1) + { + dealloc_count++; + free(Cur_file_ptr->fh_data); + Cur_file_ptr->fh_data=(sfs_fh_data *)0; + } + continue; + } + + /* if the file doesn't exist (or was removed), create it */ + if (lad_create(Cur_file_ptr, Cur_filename) == -1) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(165); + } + +adjust_size: + /* the non-i/o regular files can be left empty */ + if (filenum >= Num_io_files) { + /* Truncate if it has grown */ + if (fh_size(Cur_file_ptr) != 0) { + if (lad_truncate(Cur_file_ptr, 0)) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(166); + } + } + if(dealloc == 1) + { + dealloc_count++; + free(Cur_file_ptr->fh_data); + Cur_file_ptr->fh_data=(sfs_fh_data *)0; + } + continue; + } + + /* the i/o file must be prefilled, check if file too big */ + if (fh_size(Cur_file_ptr) > init_size) { + /* Truncate if it has grown */ + if (fh_size(Cur_file_ptr) != 0) { + if (lad_truncate(Cur_file_ptr, init_size)) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(167); + } + } + if(dealloc == 1) + { + dealloc_count++; + free(Cur_file_ptr->fh_data); + Cur_file_ptr->fh_data=(sfs_fh_data *)0; + } + continue; + } + + /* the i/o file must be prefilled, set up the write arguments. */ + if (fh_size(Cur_file_ptr) < init_size) { + append_size = init_size - fh_size(Cur_file_ptr); + + if (lad_write(Cur_file_ptr, fh_size(Cur_file_ptr), append_size)) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(168); + } + } + if(dealloc == 1) + { + dealloc_count++; + free(Cur_file_ptr->fh_data); + Cur_file_ptr->fh_data=(sfs_fh_data *)0; + } + } /* end for each regular file */ + + /* + * Create symlinks so operations that + * require them will have a file to work with. + */ + alloc_count=dealloc_count=0; + for (filenum = 0; filenum < Num_symlink_files; filenum++) { + char symlink_target[SFS_MAXNAMLEN]; + + sfs_gettime(&Cur_time); + + Cur_file_ptr = &Symlinks[filenum]; + (void) sprintf(Cur_filename, Symspec, Cur_file_ptr->unique_num); + + dealloc=0; + if(Cur_file_ptr->fh_data == (sfs_fh_data *)0) + { + alloc_count++; + Cur_file_ptr->fh_data = calloc(1,sizeof(sfs_fh_data)); + Cur_file_ptr->attributes2.type = NFNON; + Cur_file_ptr->attributes3.type = NF3NON; + if(Cur_file_ptr->working_set == 1) + dealloc=0; + else + dealloc=1; + } + if (DEBUG_CHILD_SETUP) { + (void) fprintf(stderr, "%s: initialize %s (SYMLINK)\n", + sfs_Myname, Cur_filename); + (void) fflush(stderr); + } + + if ((ret = lad_lookup(Cur_file_ptr, Cur_filename)) == -1) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(169); + } + + if (ret == 0) { + /* file exists */ + if (lad_remove(Cur_file_ptr, Cur_filename) != 0) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(170); + } + } + + /* File doesn't exist */ + if (Cur_file_ptr->initialize) { + /* make the symlink */ + (void) sprintf(symlink_target, Filespec, filenum); + if (lad_symlink(Cur_file_ptr, symlink_target, Cur_filename) != 0) { + /* some error that I don't know what to do with, quit. */ + (void) generic_kill(0, SIGINT); + exit(171); + } + } + if(dealloc == 1) + { + dealloc_count++; + free(Cur_file_ptr->fh_data); + Cur_file_ptr->fh_data=(sfs_fh_data *)0; + } + } /* end for each symlink */ +} /* init_testdir */ + +/* + * Initialize the test results counters. + */ +void +init_counters(void) +{ + uint_t i; + uint_t start_msec; + + /* Ready to go - initialize operation counters */ + for (i = 0; i < NOPS + 1; i++) { + Ops[i].req_cnt = 0; + Ops[i].results.good_calls = 0; + Ops[i].results.bad_calls = 0; + Ops[i].results.fast_calls = 0; + Ops[i].results.time.sec = 0; + Ops[i].results.time.usec = 0; + Ops[i].results.msec2 = 0; + } + + /* initialize use count for each file */ + for (i = 0; i < Num_io_files; i++) { + Io_files[i].use_cnt = 0; + Io_files[i].xfer_cnt = 0; + } + for (i = 0; i < Num_non_io_files; i++) + Non_io_files[i].use_cnt = 0; + for (i = 0; i < Num_dir_files; i++) + Dirs[i].use_cnt = 0; + for (i = 0; i < Num_symlink_files; i++) + Symlinks[i].use_cnt = 0; + + /* initialize timers and period variables */ + sfs_gettime(&Starttime); + Cur_time = Starttime; + start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000); + Previous_chkpnt_msec = start_msec; + Calls_this_period = 0; + Reqs_this_period = 0; + Sleep_msec_this_period = 0; + Calls_this_test = 0; + Reqs_this_test = 0; + Sleep_msec_this_test = 0; +} + + + +/* + * ------------------------- Load Generation ------------------------- + */ + +/* + * The routines below attempt to do over-the-wire operations. + * Each op tries to cause one or more of a particular + * NFS operation to go over the wire. Each individual op routine + * returns how many OTW calls were made. + * + * An array of file information is kept for files existing in + * the test directory. File handles, attributes, names, etc + * are stored in this array. + * + */ + + +#define OP_ABORTED (-1) +#define OP_BORROWED (-2) +#define OP_SKIPPED (-3) +/* + * Randomly perform an operation according to the req mix weightings. + */ +static int +do_op(void) +{ + double ratio; + int op_count; + int opnum; + int start_opnum; + static int failed_ops = 0; + static int aborted_ops = 0; + static int borrowed_ops = 0; + + if (Testop != -1) { + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "testop start op=%s\n", Ops[Testop].name); + } + op_count = op(Testop); + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "end op=%s\n", Ops[Testop].name); + } + return(op_count); + } + + /* get a random number and search the Ops tables for the proper entry */ + ratio = sfs_random() % 10000; + for (opnum = 0; Ops[opnum].req_pcnt <= ratio / 100.0 ; opnum++) { + ; + } + + /* + * If test targeted a a specific number of ops, + * and the call would put us over the call target for this op, + * search Ops table sequentially for an op that hasn't + * reached its target yet + */ + if (!Timed_run) { + start_opnum = opnum; + for (; Ops[opnum].results.good_calls >= Ops[opnum].target_calls;) { + opnum = (opnum + 1) % NOPS; + if (opnum == start_opnum) + break; + } + } + + if (DEBUG_CHILD_RPC) { + (void) fprintf(stderr, "(%d,%d,%d) ", + Child_num, Ops[TOTAL].results.good_calls, opnum); + (void) fflush(stderr); + } + + /* attempt the op */ + op_count = op(opnum); + + /* count the operations as completed or check for too many errors */ + if (op_count > 0) { + Ops[opnum].req_cnt++; + Ops[TOTAL].req_cnt++; + } else if (op_count == 0) { + failed_ops++; + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "Child %d - %d failed %d op\n", + Child_num, failed_ops, opnum); + (void) fflush(stderr); + } + if ((failed_ops % 50) == 0) { + (void) fprintf(stderr, "Child %d - %d failed ops\n", + Child_num, failed_ops); + (void) fflush(stderr); + } + } else if (op_count == OP_ABORTED) { + aborted_ops++; + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "Child %d - %d aborted %d op\n", + Child_num, aborted_ops, opnum); + (void) fflush(stderr); + } + if ((aborted_ops % 50) == 0) { + (void) fprintf(stderr, "Child %d - %d aborted ops\n", + Child_num, aborted_ops); + (void) fflush(stderr); + } + } else if (op_count == OP_BORROWED) { + borrowed_ops++; + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "Child %d - %d borrowed %d op\n", + Child_num, borrowed_ops, opnum); + (void) fflush(stderr); + } + } else if (op_count == OP_SKIPPED) { + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "Child %d - skipped %d op\n", + Child_num, opnum); + (void) fflush(stderr); + } + } + + return(op_count); + +} /* do_op */ + + +/* + * Because file sizes are variable in length, it is possible that + * a group chosen for a large transfer size may not contain a file + * that large. Loop calling randfh to try and find another group + * with a large enough file, but only up to IO_LOOP_MAX times. + */ +#define IO_LOOP_MAX 5 + +/* + * Call the RPC operation generator for op 'opnum'. + * The return values of the op generator routines is the count + * of operations performed. This routine also returns that count. + * A return of 0 means no operation was attempted, + * OP_ABORTED (-1) means that the operation failed. + * OP_BORROWED (-2) means that the operation was borrowed. + * OP_SKIPPED (-3) means that the operation was not done on purpose. + */ +static int +op( + int opnum) +{ + int op_count; + int trunc_count; + sfs_io_op_dist_type *dist; /* io size distribution */ + int i; + int ratio; + int buf_size; + int frag_size; + int xfer_size; + int file_size; + int trunc_op; + uint_t append_flag = 0; + uint_t randfh_flags = 0; + char *spec; + int io_loop = 0; + + spec = Filespec; + + /* pick a file that make sense for the operation */ + switch (opnum) { + + case NULLCALL: + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file); + break; + + case GETATTR: + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file); + break; + + case SETATTR: + if (Setattr_borrowed != 0) { + Setattr_borrowed--; + return(OP_BORROWED); + } + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file); + break; + + case ROOT: + Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file); + break; + + case LOOKUP: + ratio = (int) (sfs_random() % 100); + if (ratio < Num_failed_lookup) + Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file); + else + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file); + break; + + case READLINK: + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_symlink); + spec = Symspec; + break; + + case READ: + /* special handling for i/o operations */ + dist = Io_dist_ptr->read; + + /* determine number of full buffers and their total size */ + ratio = (sfs_random() % 100); + for (i = 0; dist[i].pcnt <= ratio; i++) + ; + buf_size = dist[i].bufs * Bytes_per_block; + + /* determine size of fragment */ + /* 1KB - (Kb_per_block - 1) KB fragment */ + ratio = sfs_random(); + if (Kb_per_block > 1) + ratio = ratio % (Kb_per_block-1); + else + ratio = 0; + ratio = (ratio + 1) * 1024; + frag_size = dist[i].frags * ratio; + + xfer_size = buf_size + frag_size; + + do { + Cur_file_ptr = randfh(opnum, xfer_size, 0, Exists, + Sfs_io_file); + } while (Cur_file_ptr == (sfs_fh_type *) -1 && + io_loop++ < IO_LOOP_MAX); + break; + + case WRCACHE: + Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file); + break; + + case WRITE: + /* special handling for i/o operations */ + dist = Io_dist_ptr->write; + + /* determine number of full buffers and their total size */ + ratio = (sfs_random() % 100); + for (i = 0; dist[i].pcnt <= ratio; i++) + ; + buf_size = dist[i].bufs * Bytes_per_block; + + /* determine size of fragment */ + /* 1KB - (Kb_per_block - 1) KB fragment */ + ratio = sfs_random(); + if (Kb_per_block > 1) + ratio = ratio % (Kb_per_block-1); + else + ratio = 0; + ratio = (ratio + 1) * 1024; + frag_size = dist[i].frags * ratio; + + xfer_size = buf_size + frag_size; + + /* decide if it should append or overwrite. */ + ratio = (sfs_random() % 100); + if (ratio < Append_percent) { + append_flag = 1; + randfh_flags &= RANDFH_APPEND; + } + + /* decide if a truncation will be needed */ + if (append_flag && + ((Cur_fss_bytes + (xfer_size / 1024)) > Limit_fss_bytes)) { + randfh_flags &= RANDFH_TRUNC; + } + + do { + Cur_file_ptr = randfh(opnum, xfer_size, + randfh_flags, + Exists, Sfs_io_file); + } while (Cur_file_ptr == (sfs_fh_type *) -1 && + io_loop++ < IO_LOOP_MAX); + break; + + case CREATE: + if (Create_borrowed != 0) { + Create_borrowed--; + return(OP_BORROWED); + } + if ((Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, + Sfs_non_io_file)) != (sfs_fh_type *) NULL) + break; + + /* if there are no Nonexistent files, use one that exists */ + Cur_file_ptr = randfh(opnum, 0, 0, Exists, + Sfs_non_io_file); + /* flag create of existing file for data dump interface */ + dump_create_existing_file = TRUE; + break; + + case REMOVE: + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file); + break; + + case RENAME: + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file); + break; + + case LINK: + Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, + Sfs_non_io_file); + break; + + case SYMLINK: + Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_symlink); + spec = Symspec; + break; + + case MKDIR: + Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_dir); + spec = Dirspec; + break; + + case RMDIR: + Cur_file_ptr = randfh(opnum, 0, 0, Empty_dir, Sfs_dir); + spec = Dirspec; + break; + + case READDIR: + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_dir); + spec = Dirspec; + break; + + case FSSTAT: + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file); + break; + + case ACCESS: + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file); + break; + + case COMMIT: + return(OP_SKIPPED); + + case FSINFO: + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file); + break; + + case MKNOD: + Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file); + break; + + case PATHCONF: + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file); + break; + + case READDIRPLUS: + Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_dir); + spec = Dirspec; + break; + + default: + (void) fprintf(stderr, "%s: invalid operation %d\n", sfs_Myname, opnum); + (void) generic_kill(0, SIGINT); + exit(172); + } /* switch on opnum */ + + if (Cur_file_ptr == (sfs_fh_type *) NULL || + Cur_file_ptr == (sfs_fh_type *) -1) { + Ops[opnum].no_calls++; + return(OP_ABORTED); + } + + (void) sprintf(Cur_filename, spec, Cur_file_ptr->unique_num); + + /* Call the op routine. For io operations, maintain file set size info. */ + switch (opnum) { + + case SETATTR: + op_count = (*Ops[opnum].funct)(-1); + break; + + case READ: + op_count = (*Ops[opnum].funct)(xfer_size); + if (op_count > 0) + Cur_file_ptr->xfer_cnt += (xfer_size + 1023) / 1024; + else if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: READ failed\n", sfs_Myname); + (void) fflush(stderr); + } + break; + + case WRITE: + trunc_count = 0; + + /* if appending, we may need to truncate the file first */ + if (append_flag && + ((Cur_fss_bytes + (xfer_size / 1024)) > Limit_fss_bytes)) { + + /* use either SETATTR or CREATE for truncation */ + file_size = fh_size(Cur_file_ptr); + trunc_op = -1; /* assume there are no ops to borrow */ + + if (Ops[SETATTR].mix_pcnt == 0 && Ops[CREATE].mix_pcnt == 0) + trunc_op = -1; /* no ops to borrow */ + + else if (Ops[SETATTR].mix_pcnt > 0 && Ops[CREATE].mix_pcnt > 0){ + /* only borrow if the target hasn't been met yet */ + if (Ops[SETATTR].results.good_calls + >= Ops[SETATTR].target_calls) { + if (Ops[CREATE].results.good_calls + < Ops[CREATE].target_calls) { + trunc_op = CREATE; /* borrow a CREATE */ + } + } else if (Ops[CREATE].results.good_calls + >= Ops[CREATE].target_calls) { + trunc_op = SETATTR; /* borrow a SETATTR */ + } else { + /* borrow weighted by mix percentage */ + if ((Ops[SETATTR].mix_pcnt * Create_borrowed) > + (Ops[CREATE].mix_pcnt * Setattr_borrowed)) + trunc_op = SETATTR; + else + trunc_op = CREATE; + } + + } else if (Ops[SETATTR].results.good_calls < + Ops[SETATTR].target_calls) { + /* only borrow if the target hasn't been met yet */ + trunc_op = SETATTR; /* borrow a SETATTR */ + + } else if (Ops[CREATE].results.good_calls < + Ops[CREATE].target_calls) { + /* only borrow if the target hasn't been met yet */ + trunc_op = CREATE; /* borrow a CREATE */ + } + + /* perform the truncation and update the file set size */ + if (trunc_op != -1) { + dump_truncate_op = TRUE; + if (trunc_op == SETATTR) { + trunc_count = (*Ops[SETATTR].funct)(0); + if (trunc_count > 0) { + Setattr_borrowed++; + if (DEBUG_CHILD_FILES) { + (void) fprintf(stderr, "%s: SETATTR TRUNCATE\n", + sfs_Myname); + (void) fflush(stderr); + } + } + } else if (trunc_op == CREATE) { + trunc_count = (*Ops[CREATE].funct)(); + if (trunc_count > 0) { + Create_borrowed++; + if (DEBUG_CHILD_FILES) { + (void) fprintf(stderr, "%s: CREATE TRUNCATE\n", + sfs_Myname); + (void) fflush(stderr); + } + } + } + + Cur_fss_bytes -= (file_size / 1024); + if (Cur_fss_bytes < Least_fss_bytes) + Least_fss_bytes = Cur_fss_bytes; + } + } /* end of if an append is needed */ + + /* + * do the write request + * specify the stable flag to always be off, it is not used + * with V2 servers. + */ + op_count = (*Ops[opnum].funct)(xfer_size, append_flag, 0); + if (op_count > 0) + Cur_file_ptr->xfer_cnt += (xfer_size + 1023) / 1024; + else if (DEBUG_CHILD_ERROR) { + (void) fprintf(stderr, "%s: WRITE failed\n", sfs_Myname); + (void) fflush(stderr); + } + if (append_flag) { + Cur_fss_bytes += (xfer_size / 1024); + if (Cur_fss_bytes > Most_fss_bytes) + Most_fss_bytes = Cur_fss_bytes; + } + op_count += trunc_count; + break; + + default: + op_count = (*Ops[opnum].funct)(); + break; + + } /* send switch on opnum */ + + if ((DEBUG_CHILD_ERROR) && (op_count <= 0)) { + (void) fprintf(stderr, "%s: OP %d failed\n", sfs_Myname, opnum); + (void) fflush(stderr); + } + + return(op_count); + +} /* op */ + + +/* + * Return an entry into the fh array for a file of type 'file_type' + * with existence state 'file_state'. When 'opnum' specifies an I/O + * operation, the file must be atleast 'xfer_size' bytes long + * (except when 'append_flag' is true). If 'trunc_flag', spare the + * first file found that is longer than the base file size (to support + * the READ operation). If only one file is longer than the base file + * size, return the the next longest file. + */ +sfs_fh_type * +randfh( + int opnum, + int xfer_size, + uint_t flags, + sfs_state_type file_state, + sfs_file_type file_type) +{ + sfs_fh_type * files; /* file array */ + int fh; /* index into file array */ + int found_fh = -1; /* index into file array */ + uint_t append_flag = flags & RANDFH_APPEND; + uint_t trunc_flag = flags & RANDFH_TRUNC; + + sfs_work_set_type * work_set; /* work_set array */ + int start_file; /* index into work_set array */ + int file; /* index into work_set array */ + + int nworkfiles; /* # files in work_set */ + int group_cnt; /* # file groups in work_set */ + int group_size; /* size of each group in work_set */ + int group; /* group index with work_set */ + int offset; /* file index within group */ + + int value; /* distribution function value */ + int previous; /* binary search for value */ + int low; /* binary search for value */ + int high; /* binary search for value */ + + int found_file = 0; /* count */ + int best_delta = 0; + static int op_num = 0; + long rand_int; + int max_range; + + op_num++; + + /* + * if the more than one type of file will do, choose one. + * Note: this code assumes specific values and order for + * the entries in sfs_file_enum_type. + */ + switch (file_type) { + + case Sfs_regular: + file_type = (int) (sfs_random() % 2); + break; + + case Sfs_non_dir: + file_type = (int) (sfs_random() % 3); + break; + + case Sfs_any_file: + file_type = (int) (sfs_random() % 4); + break; + + default: + break; + + } /* end switch on file type */ + + /* get the file type arrays */ + switch (file_type) { + + case Sfs_io_file: + files = Io_files; + work_set = &Io_working_set; + nworkfiles = Num_working_io_files; + break; + + case Sfs_non_io_file: + files = Non_io_files; + work_set = &Non_io_working_set; + nworkfiles = Num_working_non_io_files; + break; + + case Sfs_symlink: + files = Symlinks; + work_set = &Symlink_working_set; + nworkfiles = Num_working_symlinks; + break; + + case Sfs_dir: + files = Dirs; + work_set = &Dir_working_set; + nworkfiles = Num_working_dirs; + break; + + default: + (void) fprintf(stderr, "%s: invalid file type\n", sfs_Myname); + (void) kill(0, SIGINT); + exit(174); + } /* end switch on file type */ + + /* + * Pick the access group. + * + * Each access group consists of those files in the working set + * (numbered according to the file's index in the array) that + * have the same value modulo the number of groups. For example, + * a working set of 13 files with 3 groups is organized as + * group files + * ----- ----------------- + * 0 0, 3, 6, 9, 12 ie, == 0 mod 3 + * 1 1, 4, 7, 10 ie, == 1 mod 3 + * 2 2, 5, 8, 11 ie, == 2 mod 3 + * + * Generate a random number mod the maximum range value of the working set. + * and then binary search the first group_cnt entries in the working set + * to find the group whose range contains the random number. + * (this implements the file access distribution function) + */ + max_range = work_set->max_range; + rand_int = (long) sfs_random(); + + while ((rand_int / max_range) >= (_M_MODULUS / max_range)) { + /* + * for large values of max_range, modulo doesn't provide a uniform + * distribution unless we exclude these values ... + */ + rand_int = (long) sfs_random(); + } + value = rand_int % max_range; + + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "randfh: size=%d cnt=%d max=%d val=%3d\n", + work_set->access_group_size, + work_set->access_group_cnt, + work_set->max_range, value); + (void) fflush(stderr); + } + + previous = -1; + for (low = 0, high = work_set->access_group_cnt-1, group = (low + high)/2;; + previous = group, group = (low + high)/2) { + + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, + "PICK GROUP low=%d hi=%d grp=%d range=%d val=%d\n", + low, high, group, work_set->entries[group].range, + value); + (void) fflush(stderr); + } + + if (previous == group) + break; + if (work_set->entries[group].range == value) + break; + if (work_set->entries[group].range > value) { + if (group == 0) + break; + if (work_set->entries[group-1].range < value) + break; + else + high = group - 1; + } else if (work_set->entries[group].range < value) { + if (work_set->entries[group+1].range > value) { + group++; + break; + } else + low = group + 1; + } + } + + /* + * Pick a file within the group to operate on. + * Since (working_set_size / group_size) may have a remainder, + * groups may have either group_size or (group_size+1) files. + */ + group_cnt = work_set->access_group_cnt; + group_size = work_set->access_group_size; + if (group < (nworkfiles - ((nworkfiles / group_cnt) * group_cnt))) + group_size += 1; + + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "Selected group = %d\n", group); + (void) fflush(stderr); + } + /* + * Beginning with a random starting point in the group, + * search for a file that is eligible for this operation. + * index is an index into the files in the group. + * file and start_file are indices into the working set array. + */ + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "group_size = %d\n", group_size); + (void) fflush(stderr); + } + + offset = (int) (sfs_random() % group_size); + start_file = group + (offset * group_cnt); + file = start_file; + do { + int f_size; + int delta; + + fh = work_set->entries[file].index; + + if (DEBUG_CHILD_OPS) { + (void) fprintf(stderr, "PICK FILE op= %d file=%d fh=%d\n", + opnum, file, fh); + (void) fprintf(stderr, "fh_state = %d file_state= %d\n", + files[fh].state, file_state); + (void) fflush(stderr); + } + + /* look for a file that has the right state attribute */ + if (files[fh].state == file_state) { + f_size = fh_size(&files[fh]); + + /* + * for read and write ops and setattr truncates, + * the file must be large enough to do the xfer or truncate. + */ + if ((opnum == READ) || (opnum == WRITE && !append_flag) || + trunc_flag) { + + /* + * If the request is a read and the transfer size is + * less than or equal to be block size, grab the first + * file that is less than or equal in size. Should never + * see a transfer size less than block size as it will + * be rounded up for the request. This allows small files + * to be read. + */ + if (opnum == READ && xfer_size <= Bytes_per_block) { + if (f_size <= Bytes_per_block) { + found_fh = fh; + break; + } + } +/* #define FIRST_FIT */ +#define BEST_FIT +#ifdef FIRST_FIT + if (f_size >= xfer_size) { + found_fh = fh; + break; + } +#endif +#ifdef BEST_FIT + if (DEBUG_CHILD_FIT) { + (void) fprintf(stderr, +"%s: %8d: xfer_size %d f_size %d best_delta %d found %d\n", +sfs_Myname, op_num, xfer_size, f_size, best_delta, found_file); + (void) fflush(stderr); + } + + /* + * If we find an good enough match we should use it. + * Define good enough to be xfer_size <= X < xfer_size + 8K + * If not we continue to search for the best fit within + * a fixed distance 8. + */ + if (f_size >= xfer_size) { + if (f_size < (xfer_size + 8 * 1024)) { + found_fh = fh; + break; + } + + found_file++; + delta = f_size - xfer_size; + + if (found_fh == -1) { + best_delta = delta; + found_fh = fh; + /* break; Removed as per Robinson */ + } + + if (delta < best_delta) { + found_fh = fh; + best_delta = delta; + } + + if (found_file >= 8) { + break; + } + } +#endif + } else { + /* for non-i/o ops, only requirement is proper file state */ + found_fh = fh; + break; + } + } + offset = (offset + 1) % group_size; + file = group + (offset * group_cnt); + } while (file != start_file); + + if (found_fh == -1) { + /* didn't find a file for this operation */ + if (DEBUG_CHILD_FIT) { + if (opnum == READ || (opnum == WRITE && !append_flag) || + opnum == SETATTR) { + (void) fprintf(stderr, "%s: no file for %d byte %d op\n", + sfs_Myname, xfer_size, opnum); + } else { + (void) fprintf(stderr, "%s: no file for %d op\n", + sfs_Myname, opnum); + } + (void) fflush(stderr); + return((sfs_fh_type *) -1); + } + return((sfs_fh_type *) NULL); + } + /* found it */ + files[found_fh].use_cnt++; + return(&files[found_fh]); +} /* randfh */ + +/* + * ------------------------ Miscellaneous Subroutines ----------------------- + */ + +/* + * check to make sure that we have both read and write permissions + * for this file or directory given in 'statb'. + * return: 0 == ok, -1 == bad + */ +int +check_access( + struct stat *statb) +{ + /* check user */ + if (statb->st_uid == Real_uid) { + if ((statb->st_mode & 0400) && (statb->st_mode & 0200)) { + return(0); + } else { + return(-1); + } + } + + /* check group */ + if (statb->st_gid == Cur_gid) { + if ((statb->st_mode & 040) && (statb->st_mode & 020)) { + return(0); + } else { + return(-1); + } + } + + /* check other */ + if ((statb->st_mode & 04) && (statb->st_mode & 02)) { + return(0); + } else { + return(-1); + } + +} /* check_access */ + +/* + * check to make sure that we have both read and write permissions + * for this file or directory given in the file attributes. + * return: 0 == ok, -1 == bad + */ +int +check_fh_access(sfs_fh_type *file_ptr) +{ + /* check user */ + if (fh_uid(file_ptr) == Real_uid) { + if ((fh_mode(file_ptr) & 0400) && (fh_mode(file_ptr) & 0200)) { + return(0); + } else { + return(-1); + } + } + + /* check group */ + if (fh_gid(file_ptr) == Cur_gid) { + if ((fh_mode(file_ptr) & 040) && (fh_mode(file_ptr) & 020)) { + return(0); + } else { + return(-1); + } + } + + /* check other */ + if ((fh_mode(file_ptr) & 04) && (fh_mode(file_ptr) & 02)) { + return(0); + } else { + return(-1); + } +} + +static int last_bad_calls = 0; + +/* + * Adjust the sleep time per call based on a number of global variables, + */ +static void +check_call_rate() +{ + int call_target_per_period; /* target calls for each period */ + int req_target_per_period; /* target reqs for each period */ + int call_target_this_test; /* target calls for test so far */ + int req_target_this_test; /* target reqs for test so far */ + int msec_this_period; /* actual length of this period */ + int msec_this_test; /* actual length of test so far */ + uint_t current_msec; /* current time in msecs */ + int old_target_sleep_mspc; + struct ladtime elapsed_time; /* Current_time - Start_time */ + + int reqs_needed_next_period;/* req target for the next period */ + float mspc; /* target msec per call, with/sleep */ + float work_mspc; /* actual msec worked / call */ + + + if (Child_num == -1) + /* I'm the parent, ignore the signal */ + return; + + /* update the test so far totals */ + Calls_this_test += Calls_this_period; + Reqs_this_test += Reqs_this_period; + Sleep_msec_this_test += Sleep_msec_this_period; + + /* compute per period targets */ + call_target_per_period = (int) (Child_call_load * + ((float) Msec_per_period / (float) 1000)); + req_target_per_period = (int) (Child_req_load * + ((float) Msec_per_period / (float) 1000)); + + /* + * The child() routine retrieved the Cur_time when deciding to call us. + * Use Cur_time to compute the elapsed time since the last checkpoint + * and the current checkpoint time (ie, elapsed time since test began) + */ + /* sfs_gettime(&Cur_time); */ + elapsed_time.sec = Cur_time.sec; + elapsed_time.usec = Cur_time.usec; + SUBTIME(elapsed_time, Starttime); + + msec_this_test = (elapsed_time.sec * 1000) + (elapsed_time.usec / 1000); + current_msec = (Cur_time.sec * 1000) + (Cur_time.usec / 1000); + msec_this_period = current_msec - Previous_chkpnt_msec; + + if (msec_this_test < Sleep_msec_this_test) { + if (DEBUG_CHILD_XPOINT) { + (void) fprintf(stderr, + "Accum. sleep time %d is msecs ahead of wall clock\n", + Sleep_msec_this_test - msec_this_test); + (void) fflush(stderr); + } + Sleep_msec_this_test = msec_this_test; + } + + /* compute targets for test so far */ + call_target_this_test = (int) ((Child_call_load * (float) msec_this_test) + / (float) 1000); + req_target_this_test = (int) ((Child_req_load * (float) msec_this_test) + / (float) 1000); + + /* save the old sleep rate */ + old_target_sleep_mspc = Target_sleep_mspc; + + /* Compute how long each request has taken on average. */ + if (Reqs_this_test != 0) + work_mspc = ((float) (msec_this_test - Sleep_msec_this_test)) + / (float) Reqs_this_test; + else + work_mspc = (1000.0 / (float) Child_req_load) / 2.0; + + /* + * Compute the number of reqs needed in the next period + * in order to just meet the reqstarget for the test when that period ends. + * (Try to make up the entire shortage or overage in the next period.) + * Beware that we might not need to make any reqs next period. + */ + reqs_needed_next_period = (req_target_this_test - Reqs_this_test) + + req_target_per_period; + + if (reqs_needed_next_period <= 0) { + /* if no reqs are needed, set the sleep time to the whole period */ + mspc = 0.0; + Target_sleep_mspc = Msec_per_period; + } else { + /* decide how much time is available for each request */ + mspc = (float) (Msec_per_period) / (float) (reqs_needed_next_period); + Target_sleep_mspc = (int) (mspc - work_mspc); + } + + /* Don't increase the target_sleep_mspc by much more than a factor of two, + because doing so can lead to violent oscillations. */ + if (Target_sleep_mspc > 2*(old_target_sleep_mspc + 5)) { + Target_sleep_mspc = 2*(old_target_sleep_mspc + 5); + } + + if (Target_sleep_mspc >= Msec_per_period) { + Target_sleep_mspc = Msec_per_period; + if (DEBUG_CHILD_XPOINT) { + (void) fprintf(stderr, + "Child %d: 0 call, rqnd %d mspc %3.2f wmspc %3.2f time %d slp %d reqs %d\n", + Child_num, reqs_needed_next_period, mspc, work_mspc, + msec_this_test, Sleep_msec_this_test, Reqs_this_test); + (void) fflush(stderr); + } + if (Measurement_in_progress) { + (void) fprintf(stderr, + "Child %d: 0 calls during measurement interval\n",Child_num); + (void) fprintf(stderr, + "Child %d: probably unstable, try more processes.\n",Child_num); + (void) generic_kill(0, SIGINT); + (void) fflush(stderr); + exit(188); + } + } + if (Target_sleep_mspc <= 0) { + Target_sleep_mspc = 0; + if (DEBUG_CHILD_XPOINT) { + (void) fprintf(stderr, + "Child %d: 0 slp, rqnd %d mspc %3.2f wmspc %3.2f time %d slp %d reqs %d\n", + Child_num, reqs_needed_next_period, mspc, work_mspc, + msec_this_test, Sleep_msec_this_test, Reqs_this_test); + (void) fflush(stderr); + } + } + + if (DEBUG_CHILD_XPOINT) { + (void) fprintf(stderr, "Child %d\n%s", Child_num, + " msec_prd calls_prd reqs_prd calls_tot req_tot mspc_req\n"); + (void) fprintf(stderr, "target: %8d %9d %8d %9d %8d %6.2f\n", + Msec_per_period, + call_target_per_period, req_target_per_period, + call_target_this_test, req_target_this_test, + 1000.0 / (float) req_target_per_period); + (void) fprintf(stderr, "actual: %8d %9d %8d %9d %8d ->%6.2f\n", + msec_this_period, + Calls_this_period, Reqs_this_period, + Calls_this_test, Reqs_this_test, + mspc); + (void) fprintf(stderr, + " old_sleep_mspc %5d new_sleep_mspc %5d\n\n", + old_target_sleep_mspc, Target_sleep_mspc); + } + + /* + * check for too many failed RPC calls + * and print a warning if there are too many. + */ + if (((Ops[TOTAL].results.bad_calls - last_bad_calls) > 100) || + ((Ops[TOTAL].results.good_calls > 300) && + ((Ops[TOTAL].results.bad_calls - last_bad_calls) > + Ops[TOTAL].results.good_calls/50))) { + (void) fprintf(stderr, + "%s: too many failed RPC calls - %d good %d bad\n", + sfs_Myname, Ops[TOTAL].results.good_calls, + Ops[TOTAL].results.bad_calls); + last_bad_calls = Ops[TOTAL].results.bad_calls; + } + + /* reset the period counters */ + Calls_this_period = 0; + Reqs_this_period = 0; + Sleep_msec_this_period = 0; + Previous_chkpnt_msec = current_msec; + +} /* check_call_rate */ + +/* sfs_c_chd.c */ diff --git a/TBBT/trace_play/sfs_c_chd.work b/TBBT/trace_play/sfs_c_chd.work new file mode 100644 index 0000000..195beb0 --- /dev/null +++ b/TBBT/trace_play/sfs_c_chd.work @@ -0,0 +1,3815 @@ +/* Try to change thread scheduling and uses three threads */ +#ifndef lint +static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * -------------------------- sfs_c_chd.c ------------------------- + * + * The sfs child. Routines to initialize child parameters, + * initialize test directories, and generate load. + * + *.Exported_Routines + * void child(int, float, int, char *); + * void init_fileinfo(void); + * void init_counters(void); + * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type, + * sfs_file_type); + * int check_access(struct *stat) + * int check_fh_access(); + * + *.Local_Routines + * void check_call_rate(void); + * void init_targets(void); + * void init_dirlayout(void); + * void init_rpc(void); + * void init_testdir(void); + * int do_op(void); + * int op(int); + * + *.Revision_History + * 21-Aug-92 Wittle randfh() uses working set files array. + * init_fileinfo() sets up working set. + * 02-Jul-92 Teelucksingh Target file size now based on peak load + * instead of BTDT. + * 04-Jan-92 Pawlowski Added raw data dump hooks. + * 16-Dec-91 Wittle Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" +#include "rfs_c_def.h" +#include "generic_hash.h" +#include "nfsd_nfsfh_cust.h" + +extern struct hostent *Server_hostent; + +#define PROB_SCALE 1000L +#define _M_MODULUS 2147483647L /* (2**31)-1 */ + +#define _GROUP_DIVISOR 500 +#define _FILES_PER_GROUP 4 +#define _MIN_GROUPS 12 +#define _WORKING_SET_AT_25_OPS_PER_SEC 975 + + +/* + * ----------------------- External Definitions ----------------------- + */ +extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *); +extern enum clnt_stat proc_header(CLIENT *cl, xdrproc_t xdr_results, void *results_ptr); +extern int biod_poll_wait(CLIENT *, uint32_t); +extern enum clnt_stat get_areply_udp (CLIENT * cl, uint32_t *xid, struct timeval *timeout); +extern char * parse_name (char * t, char * buf); + +/* forward definitions for local functions */ +static int init_rpc(void); + +/* RFS: forward definitions for local functions */ +void init_ops(void); +static void init_signal(); +extern void init_file_system (void); +extern void init_dep_tab (void); +static int read_trace(void); +static void read_fh_map(); +static void init_play (char * mount_point); +static void trace_play(void); +void print_result(void); +static int get_nextop(void); +static int check_timeout(void); +static struct biod_req * get_biod_req(int dep_tab_index); +static int lookup_biod_req (int xid); +static void init_time_offset(void); +void adjust_play_window (int flag, int * poll_timeout_arg); +static int poll_and_get_reply (int usecs); +static char * nfs3_strerror(int status); +static void check_clock(void); +static double time_so_far1(void); +static double get_resolution(void); +static void usage(void); +void init_dep_tab_entry (int dep_index); +extern inline fh_map_t * lookup_fh (char * trace_fh ); +static void finish_request (int biod_index, int dep_index, int status, int dep_flag); +static inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line); +static inline char * find_lead_trace_fh(int proc, char * line); +static inline char * find_reply_trace_fh (char * line); + +/* + * ------------- Per Child Load Generation Rate Variables ----------- + */ +static uint_t Calls_this_period; /* calls made during the current run period */ +static uint_t Calls_this_test; /* calls made during the test so far */ +static uint_t Reqs_this_period; /* reqs made during the current run period */ +static uint_t Reqs_this_test; /* reqs made during the test so far */ +static uint_t Sleep_msec_this_test; /* msec slept during the test so far */ +static uint_t Sleep_msec_this_period; +static uint_t Previous_chkpnt_msec; /* beginning time of current run period */ +static int Target_sleep_mspc; /* targeted sleep time per call */ + +static char io_buf[BUFSIZ]; /* io buffer for print out messages */ + +char * sfs_Myname; +int Log_fd; /* log fd */ +char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */ +int Validate = 0; /* fake variable */ +int Child_num = 0; /* fake: child index */ +int Tcp = 0; /* We implement UDP first */ +int Client_num = 1; /* fake: number of client */ +uid_t Real_uid; +gid_t Cur_gid; +uid_t Cur_uid; +/* as long as the inital value is different, then it's OK */ +int recv_num = 0; +int timeout_num = 0; +int send_num = 0; +int exit_flag = 0; +int async_rpc_sem; +int no_progress_flag = 0; +int num_out_reqs_statistics[MAX_OUTSTANDING_REQ+1]; +int num_out_reqs_statistics_at_timeout[MAX_OUTSTANDING_REQ+1]; + +/* + * ------------------------- SFS Child ------------------------- + */ + +static int nfs2proc_to_rfsproc[18] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17}; +static int nfs3proc_to_rfsproc[NFS3_PROCEDURE_COUNT] = {0, 1, 2, 4, 18, 5, 6, 8, 9, 14, + 13, 21, 10, 15, 11, 12, 16, 23, 17, 20, + 22, 19}; +void print_usage(int pos, int argc, char ** argv) +{ + int i; + printf("sfs3 hostname:mount_dir trace_file|stdin fh_map_file play_scale warmup_time(in seconds) \n"); + printf("sfs3 -pair_trace trace_file\n"); + printf("sfs3 -pair_write trace_file\n"); + printf("sfs3 -help\n"); + printf ("pos %d argc %d", pos, argc); + for (i=0; i<argc; i++) { + printf(" %s", argv[i]); + } + printf ("\n"); + exit; +} + +void print_cyclic_buffers () +{ + CYCLIC_PRINT(memory_trace_index); + CYCLIC_PRINT(dep_tab_index); + CYCLIC_PRINT(dep_window_index); +} + +void add_to_dep_tab(int i) +{ + char * trace_fh; + fh_map_t * fh_map_entry; + dep_tab_t * ent; + + trace_fh = strstr (memory_trace[i].line, "fh"); + if (!trace_fh) + printf ("memory_trace[%d].line %s\n", i, memory_trace[i].line); + RFS_ASSERT (trace_fh); + trace_fh += 3; + fh_map_entry = lookup_fh (trace_fh); + if (fh_map_entry && (fh_map_entry->flag==FH_MAP_FLAG_DISCARD) ) { + req_num_with_discard_fh ++; + return; + } + if (fh_map_entry) + req_num_with_init_fh ++; + else + req_num_with_new_fh ++; + + RFS_ASSERT (!CYCLIC_FULL(dep_tab_index)); + ent = &(dep_tab[dep_tab_index.head]); + + ent->disk_index = memory_trace[i].disk_index; + ent->memory_index = i; +#ifdef REDUCE_MEMORY_TRACE_SIZE + ent->trace_status = memory_trace[i].trace_status; + ent->reply_trace_fh = memory_trace[i].reply_trace_fh; +#endif + ent->line = memory_trace[i].line; + init_dep_tab_entry(dep_tab_index.head); + + if (rfs_debug && (i%100000)==0) + printf ("dep_tab[%d].disk_index %d = memory_trace[%d].disk_index %d\n", dep_tab_index.head, ent->disk_index, i, memory_trace[i].disk_index); + CYCLIC_MOVE_HEAD(memory_trace_index); + CYCLIC_MOVE_HEAD(dep_tab_index); +} + +void init_profile_variables () +{ + init_profile ("total_profile", &total_profile); + init_profile ("execute_next_request_profile", &execute_next_request_profile); + init_profile ("valid_get_nextop_profile", &valid_get_nextop_profile); + init_profile ("invalid_get_nextop_profile",&invalid_get_nextop_profile); + init_profile ("prepare_argument_profile", &prepare_argument_profile); + init_profile ("biod_clnt_call_profile", &biod_clnt_call_profile); + init_profile ("receive_next_reply_profile", &receive_next_reply_profile); + init_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile); + init_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile); + init_profile ("decode_reply_profile", &decode_reply_profile); + init_profile ("check_reply_profile", &check_reply_profile); + init_profile ("add_create_object_profile", &add_create_object_profile); + init_profile ("check_timeout_profile", &check_timeout_profile); + init_profile ("adjust_play_window_profile",&adjust_play_window_profile); + init_profile ("fgets_profile",&fgets_profile); + init_profile ("read_line_profile",&read_line_profile); + init_profile ("read_trace_profile",&read_trace_profile); +} + +static char trace_file[256]="anon-lair62-011130-1200.txt"; +int print_memory_usage() +{ + printf("size of fh_map_t %d size of fh_map %d\n", sizeof(fh_map_t), sizeof(fh_map)); + printf("sizeof dep_tab_t %d sizeof dep_tab %d\n", sizeof(dep_tab_t), sizeof(dep_tab)); + printf("size of memory_trace_ent_t %d sizeof memory_trace %d\n", sizeof(memory_trace_ent_t), sizeof(memory_trace)); + printf("size of CREATE3args %d\n", sizeof( CREATE3args)); + printf("size of MKDIR3args %d\n", sizeof( MKDIR3args)); + printf("size of READ3args %d\n", sizeof( READ3args)); + printf("size of WRITE3args %d\n", sizeof( WRITE3args)); + printf("size of RENAME3args %d\n", sizeof( RENAME3args)); + printf("size of GETATTR3args %d\n", sizeof( GETATTR3args)); + printf("size of SETATTR3args %d\n", sizeof( SETATTR3args)); + printf("size of LINK3args %d\n", sizeof( LINK3args)); + printf("size of SYMLINK3args %d\n", sizeof( SYMLINK3args)); + printf("size of MKNOD3args %d\n", sizeof( MKNOD3args)); + printf("size of RMDIR3args %d\n", sizeof( RMDIR3args)); + printf("size of REMOVE3args %d\n", sizeof( REMOVE3args)); + printf("size of LOOKUP3args %d\n", sizeof( LOOKUP3args)); + printf("size of READDIR3args %d\n", sizeof( READDIR3args)); + printf("size of READDIRPLUS3args %d\n", sizeof( READDIRPLUS3args)); + printf("size of FSSTAT3args %d\n", sizeof( FSSTAT3args)); + printf("size of FSINFO3args %d\n", sizeof( FSINFO3args)); + printf("size of COMMIT3args %d\n", sizeof( COMMIT3args)); + printf("size of ACCESS3args %d\n", sizeof( ACCESS3args)); + printf("size of READLINK3args %d\n", sizeof( READLINK3args)); + + +} + +void recv_thread() +{ + int last_print_time = -1; + int busy_flag; + + while (send_num ==0) { + usleep(1000); + } + + //while (!CYCLIC_EMPTY(dep_tab_index)) { + while (!exit_flag) { + if ((total_profile.in.tv_sec - last_print_time >= 10)) { + static int recv_num_before_10_seconds = 0; + last_print_time = total_profile.in.tv_sec; + fprintf (stdout, "<<<<< recv_thread recv_num %d time %d num_out_reqs %d \n", recv_num, total_profile.in.tv_sec, num_out_reqs); + if (recv_num == recv_num_before_10_seconds) { + no_progress_flag = 1; + RFS_ASSERT (0); + } else + recv_num_before_10_seconds = recv_num; + } + //start_profile (&check_timeout_profile); + check_timeout(); + //end_profile (&check_timeout_profile); + + pthread_yield(); + //usleep(1000); + start_profile (&receive_next_reply_profile); + /* actually the performance of two policy seems to be same */ +//#define SEND_HIGHER_PRIORITY_POLICY +#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY +#ifdef SEND_HIGHER_PRIORITY_POLICY + receive_next_reply(IDLE); +#endif +#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY + busy_flag = IDLE; + while (receive_next_reply(busy_flag)!=-1) { + busy_flag = BUSY; + } +#endif + end_profile (&receive_next_reply_profile); + } + printf ("<<<< recv thread EXIT\n"); + exit_flag = 2; +} + +int io_thread () +{ +/* number of seconds the I/O thread pauses after each time trying to read the requests */ +#define IO_THREAD_PAUSE_TIME 1 + + int i; + int j = 0; + + disk_io_status = read_trace (); + while (disk_io_status == TRACE_BUF_FULL) { + + usleep (10000); + if ((j++%1000)==0) { + printf("&&&&&&&&&& io thread, sleep %d seconds\n", j/100); + } + + disk_io_status = read_trace (); + //printf ("io_thread, after read_trace, disk_index %d\n", disk_index); + +#ifdef SEQUEN_READ + for (i=0; i<SEQUEN_READ_NUM; i++) { + add_to_dep_tab(CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size)); + } +#endif + //printf ("***************** IO THREAD SLEEP 1 s\n"); + //print_cyclic_buffers(); + //pthread_yield(); + } + RFS_ASSERT (disk_io_status == TRACE_FILE_END); + printf("io_thread EXIT\n"); +} + +int execute_thread() +{ + int i; + struct timeval playstart_time, playend_time; + + sleep (10); /* first let io_thread to run for a while */ + printf ("trace_play\n"); + + gettimeofday(&playstart_time, NULL); + + init_time_offset(); + trace_play (); + + gettimeofday(&playend_time, NULL); + + { + int usec, sec; + sec = playend_time.tv_sec - playstart_time.tv_sec; + usec = sec * 1000000 + (playend_time.tv_usec - playstart_time.tv_usec); + sec = usec / 1000000; + usec = usec % 1000000; + printf("Total play time: %d sec %d usec\n", sec, usec); + if (sec > WARMUP_TIME) + print_result(); + else + printf ("the trace play time %d is less than WARMUP_TIME %d, no statistical results\n"); + } +#ifdef RECV_THREAD + exit_flag = 1; + while (exit_flag == 1) { + usleep (1000); + } +#endif + + clnt_destroy(NFS_client); + biod_term(); +} + +int init_thread () +{ + pthread_attr_t attr; + int arg; + int ret = 0; + pthread_t io_thread_var; +#ifdef RECV_THREAD + pthread_t recv_thread_var; +#endif + pthread_t execute_thread_var; + + ret = pthread_attr_init (&attr); + if (ret!=0) { + perror("pthread_attr_init attr"); + return ret; + } +#ifdef IO_THREAD + ret = pthread_create (&(io_thread_var), &attr, &io_thread, (void *)&arg ); + if (ret!=0) { + perror("io_pthread_attr_init"); + return ret; + } +#endif + +#ifdef RECV_THREAD + ret = pthread_create (&(recv_thread_var), &attr, &recv_thread, (void *)&arg ); + if (ret!=0) { + perror("recv_pthread_attr_init"); + return ret; + } +#endif + +/* + ret = pthread_create (&(execute_thread_var), &attr, &execute_thread, (void *)&arg ); + if (ret!=0) { + perror("io_pthread_attr_init"); + return ret; + } +*/ +} + +void init_buffers() +{ + CYCLIC_INIT("memory_trace_index",memory_trace_index,MAX_MEMORY_TRACE_LINES); + CYCLIC_INIT("dep_tab_index ",dep_tab_index,DEP_TAB_SIZE); + CYCLIC_INIT("dep_window_index ",dep_window_index,DEP_TAB_SIZE); +} + +int main(int argc, char ** argv) +{ + extern char * optarg; + int i; + struct timeval in={1000000, 100}; + + init_profile_variables(); + if ((argc==1) || (argc==2 && !strcmp(argv[1],"-help"))) { + print_usage(0, argc, argv); + exit(0); + } + if (!strcmp(argv[1], "-pair_write")) { + if (argc==3) + strcpy(trace_file, argv[2]); + else + strcpy(trace_file, "stdin"); + pair_write(trace_file); + exit(0); + } + if (!strcmp(argv[1], "-pair_trace")) { + if (argc==3) + strcpy(trace_file, argv[2]); + else + strcpy(trace_file, "stdin"); + pair_trace(trace_file); + exit(0); + } + if (!strcmp(argv[1], "-check_aging")) { + if (argc!=3) { + print_usage(3, argc, argv); + exit(0); + } + strcpy(trace_file, argv[2]); + check_aging (trace_file); + exit(0); + } + if (!strcmp(argv[1], "-check_statistics")) { + if (argc!=3) { + print_usage(1, argc, argv); + exit(0); + } + strcpy(trace_file, argv[2]); + memset(fh_htable, 0, sizeof (fh_htable)); + check_statistics (trace_file); + exit(0); + } + + if (argc!=6) { + print_usage(2, argc, argv); + exit(0); + } + + PLAY_SCALE = atoi (argv[4]); + RFS_ASSERT (PLAY_SCALE >=1 && PLAY_SCALE <=10000); + + WARMUP_TIME = atoi (argv[5]); + RFS_ASSERT (WARMUP_TIME >=0 && WARMUP_TIME <=1000); + + print_memory_usage(); + check_clock(); + getmyhostname(lad_hostname, HOSTNAME_LEN); + + init_ops(); + /* + * Get the uid and gid information. + */ + Real_uid = getuid(); + Cur_gid = getgid(); + //Real_uid = 513; + //Cur_gid = 513; + + Nfs_timers = Nfs_udp_timers; + + init_semaphores(); + init_file_system (); + init_signal(); + init_play (argv[1]); + //init_play ("capella:/p5/RFSFS"); + init_fh_map(); + read_fh_map (argv[3]); + //read_fh_map ("fh-path-map-play"); + strcpy(trace_file, argv[2]); + +/* If ordered by TIMESTAMP, + * memory_trace_index.tail <= dep_tab_index.tail < dep_window_max <= + * dep_tab_index.head <= memory_trace_index.head + */ + + init_global_variables(); + init_buffers(); + init_thread(); + pthread_yield(); + execute_thread(); +} + +int init_global_variables() +{ + memset (num_out_reqs_statistics, 0, sizeof(num_out_reqs_statistics)); + memset (num_out_reqs_statistics_at_timeout, 0, sizeof(num_out_reqs_statistics_at_timeout)); +} + +int init_semaphores() +{ + async_rpc_sem = dest_and_init_sem (ASYNC_RPC_SEM_KEY, 1, "async_rpc_sem"); +} + +void init_ops (void) +{ + Ops = nfsv3_Ops; + nfs_version = NFS_V3; +} + +/* Set up the signal handlers for all signals */ +void init_signal() +{ +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + struct sigaction sig_act, old_sig_act; + + /* use XOPEN signal handling */ + + sig_act.sa_handler = generic_catcher; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + + /* signals handlers for signals used by sfs */ + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGINT"); + exit(135); + } + + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) { + perror("sigaction failed: SIGTERM"); + exit(137); + } +#else + /* signals handlers for signals used by sfs */ + (void) signal(SIGINT, sfs_cleanup); + // RFS (void) signal(SIGALRM, sfs_alarm); + (void) signal(SIGTERM, sfs_cleanup); +#endif +} + +void +init_play ( + char * mount_point) /* Mount point for remote FS */ +{ + char namebuf[NFS_MAXNAMLEN] = "trace_play"; /* unique name for this program */ + CLIENT * mount_client_ptr; /* Mount client handle */ + + if (!rfs_debug); + (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ); + + sfs_Myname = namebuf; + + /* + * May require root priv to perform bindresvport operation + */ + mount_client_ptr = lad_getmnt_hand(mount_point); + if (mount_client_ptr == NULL) { + exit(145); + } + + /* + * should be all done doing priv port stuff + */ + + if (init_rpc() == -1) { + (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname); + (void) generic_kill(0, SIGINT); + exit(146); + } + + + /* + * finish all priv bindresvport calls + * reset uid + */ + if (setuid(Real_uid) != (uid_t)0) { + (void) fprintf(stderr,"%s: %s%s", sfs_Myname, + "cannot perform setuid operation.\n", + "Do `make install` as root.\n"); + } + + init_mount_point(0, mount_point, mount_client_ptr); + + + /* + * Cleanup client handle for mount point + */ + clnt_destroy(mount_client_ptr); + + init_counters(); +} + +#ifdef REDUCE_MEMORY_TRACE_SIZE +void inline format_line (char * line_before, char * line) +{ + char * pv = line_before; + char * pl = line; + char * p; + int i; + + //printf("format_line before %s\n", line_before); + p = strstr (pv, "fh"); + while (p!=NULL) { + while (pv<=p) + *pl++ = *pv++; + *pl++ = *pv++; + if (*pv==2) { + *pl++ = *pv++; + } + *pl++ = *pv++; + i = 0; + while (*pv !=' ') { + RFS_ASSERT ((*pv >='0' && *pv <='9') || (*pv >='a' && *pv<='f')); + *pl++ = *pv++; + i++; + } + RFS_ASSERT ((i==48) || (i==40) || (i==24)); + while (i<48) { + *pl++ = '0'; + i++; + } + p = strstr (pv, "fh"); + } + while ((*pv)!=NULL) { + *pl++ = *pv++; + } + //printf("format_line after %s\n", line); +} + +char * read_line (int disk_index) +{ + static FILE * fp=NULL; + static int start=0; + static int start_disk_index=0; + int i; + static int finish_flag = 0; + static int varfh_flag = 0; +#define READ_LINE_BUF_SIZE (MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR+2) +#define SAFE_BYTES 1000 +#define READ_LINE_LENGTH (MAX_TRACE_LINE_LENGTH+SAFE_BYTES) + + static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH]; + char varfh_line_buf[READ_LINE_LENGTH]; + + start_profile (&read_line_profile); + + if (fp==NULL) { + if (strcmp(trace_file, "stdin")) { + fp = fopen(trace_file, "r"); + + if (strstr(trace_file, ".varfh")) { + varfh_flag = 1; + }; + if (strstr(trace_file, ".fmt1")) { + TRACE_COMMAND_REPLY_FLAG_POS += 12; + TRACE_VERSION_POS +=12; + TRACE_MSGID_POS +=12; + TRACE_FH_SIZE =48; + } + if (!fp) { + printf("can not open files %s\n", fp); + perror("open"); + } + } else { + fp = stdin; + } + RFS_ASSERT (fp!=NULL); + for (i=0; i<READ_LINE_BUF_SIZE; i++) { + start_profile(&fgets_profile); + if (varfh_flag) { + if (!fgets(varfh_line_buf, READ_LINE_LENGTH, fp)) { + RFS_ASSERT (0); + } + format_line (varfh_line_buf, line_buf[i]); + } else { + if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) { + RFS_ASSERT (0); + } + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]); + } + } + + if (disk_index > start_disk_index+READ_LINE_BUF_SIZE) { + printf ("disk_index %d start_disk_index %d READ_LINE_BUF_SIZE %d\n", + disk_index, start_disk_index, READ_LINE_BUF_SIZE); + } + RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE) + if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) { + if (finish_flag) { + return NULL; + } + start_profile(&fgets_profile); + if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) { + end_profile(&fgets_profile); + finish_flag = 1; + return NULL; + } + end_profile(&fgets_profile); + //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]); + start = (start+1) % READ_LINE_BUF_SIZE; + start_disk_index ++; + } + RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE) + i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE; + +/* + if (!(strlen(line_buf[i])>80)) { + printf ("start %d start_disk_index %d disk_index %d strlen %d line_buf[%d] %s\n", start, start_disk_index, disk_index, strlen(line_buf[i]), i, line_buf[i]); + RFS_ASSERT (strlen(line_buf[i])>80); + } + if (!((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH))) + printf ("disk_index %d strlen %d line_buf[%d] %s\n", disk_index, strlen(line_buf[i]), i, line_buf[i]); + RFS_ASSERT ((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH)); +*/ + + end_profile (&read_line_profile); + return (line_buf[i]); +} + +#define OPS_FLAG_INC 0 +#define OPS_FLAG_PRINT 1 +int read_write_fh_statistics (int flag, char * buf, int timestamp) +{ + static FILE * fp = NULL; + char * p; + char c; + if (!fp) { + fp = fopen ("read_write_fh.output", "w"); + RFS_ASSERT (fp); + } + if (flag == OPS_FLAG_INC) { + p = strstr (buf, "fh"); + RFS_ASSERT (p); + c = p[40+3]; + p[40+3]=0; + fprintf(fp, "%s\n", p+3+24); + p[40+3]=c; + }; + if (flag == OPS_FLAG_PRINT) { + int disk_index = (int) buf; + fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + } +} + +int write_statistics(int flag, char * buf, char * reply_buf, int trace_status) +{ + static FILE * fp = NULL; + int pre_size, size, count; + char * p = reply_buf; + if (!fp) { + fp = fopen ("write.output", "w"); + RFS_ASSERT (fp); + } + if (flag == OPS_FLAG_INC) { + p = strstr (p, "pre-size"); + RFS_ASSERT (p); + sscanf (p, "pre-size %x", &pre_size); + p += strlen("pre-size"); + p = strstr (p, "size"); + RFS_ASSERT (p); + sscanf (p, "size %x", &size); + p = strstr (p, "count"); + if (!p) + fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + RFS_ASSERT (p); + sscanf (p, "count %x", &count); + fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + } + if (flag == OPS_FLAG_PRINT) { + int disk_index = (int) buf; + int timestamp = (int) reply_buf; + fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + } +} + +void ops_statistics (int ops_flag, int proc, int timestamp) +{ + static FILE * fp = NULL; + static struct { + int count; + int count_K; + int update_flag; + char name[32]; + } ops[NOPS]={{0, 0, 0, "NULL"},{0, 0, 0, "GETATTR"},{0, 0, 1, "SETATTR"},{0, 0, 0, "ROOT"}, + {0, 0, 0, "LOOKUP"},{0, 0, 0, "READLINK"},{0, 0, 0, "READ"},{0, 0, 1, "WRCACHE"}, + {0, 0, 1, "WRITE"}, {0, 0, 1, "CREATE"},{0, 0, 1, "REMOVE"},{0, 0, 1, "RENAME"}, + {0, 0, 1, "LINK"}, {0, 0, 1, "SYMLINK"},{0, 0, 1, "MKDIR"},{0, 0, 1, "RMDIR"}, + {0, 0, 0, "READDIR"},{0, 0, 0, "FSSTAT"},{0, 0, 0, "ACCESS"},{0, 0, 0, "COMMIT"}, + {0, 0, 0, "FSINFO"},{0, 0, 1, "MKNOD"}, {0, 0, 0, "PATHCONF"}, {0, 0, 0, "READDIRPLUS"}}; + + if (ops_flag == OPS_FLAG_INC) { + RFS_ASSERT (proc>=0 && proc<NOPS); + ops[proc].count ++; + if (ops[proc].count == 1000) { + ops[proc].count_K ++; + ops[proc].count = 0; + } + } + if (ops_flag == OPS_FLAG_PRINT) { + int i; + int update_K=0, update=0, total_K=0, total=0; + float f1, f2; + int disk_index = proc; + + if (!fp) { + fp = fopen ("mix.output", "w"); + RFS_ASSERT (fp); + } + for (i=0; i<NOPS; i++) { + total_K += ops[i].count_K; + total += ops[i].count; + if (ops[i].update_flag) { + update_K += ops[i].count_K; + update += ops[i].count; + } + } + update_K += update/1000; + update = update%1000; + total_K += total/1000; + total = total%1000; + + f1 = total_K; + f1 = f1*1000+total; + for (i=0; i<NOPS; i++) { + f2 = ops[i].count_K; + f2 = f2*1000+ops[i].count; + fprintf (fp, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1); + fprintf (stderr, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1); + } + f2 = update_K; + f2 = f2*1000+update; + fprintf (fp, " total %8d,%03d\n", total_K, total); + fprintf (stderr, " total %8d,%03d\n", total_K, total); + fprintf (fp, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1); + fprintf (stderr, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1); + fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + fprintf(stderr, "###### disk_index %d timestamp %d\n", disk_index, timestamp); + } +} + + +void truncate_statistics (int flag, int proc, char * buf, char * reply_buf) +{ +#define TRACE_FH_SIZE2 16 +#define BLOCK_SIZE 4096 + static int no_size_num = 0; + static int have_size_num = 0; + static int equal_size_num = 0; + static int first_size_num = 0; + static int truncate_num = 0; + static int truncate_size = 0; + static int truncate_KB = 0; + static int truncate_block_num = 0; + static int padding_num = 0; + static int padding_KB = 0; + static int padding_size = 0; + static FILE * fp = NULL; + char * p; + char * trace_fh; + int pre_size, size, count; + struct generic_entry * ent; + + if (flag == OPS_FLAG_PRINT) { + int disk_index = proc; + int timestamp = (int)buf; + if (!fp) { + fp = fopen ("truncate.output", "w"); + RFS_ASSERT (fp); + } + truncate_KB += truncate_size/1000; + truncate_size %= 1000; + padding_KB += padding_size/1000; + padding_size %= 1000; + fprintf(fp, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB); + fprintf(stderr, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB); + return; + } + + p = strstr (&buf[TRACE_MSGID_POS], "fh"); + RFS_ASSERT (p); + p+=3; + trace_fh = p; + + if (proc==SETATTR) { + p = strstr (buf, " size "); + } else { + RFS_ASSERT (proc==WRITE); + p = strstr (reply_buf, " ftype 1 "); + RFS_ASSERT (p); + p = strstr (p, " size "); + RFS_ASSERT (p); + if (strstr(p, " ftype 1 ")) { + fprintf (fp, "#### %s%s\n", buf, reply_buf); + fprintf (stderr, "#### %s%s\n", buf, reply_buf); + } + RFS_ASSERT (!strstr(p, " ftype 1 ")); + } + if (!p) { + no_size_num ++; + return; + } + have_size_num ++; + + sscanf (p, " size %x", &size); + if (size <0 || size > 2000000000) { + fprintf (fp, "#### size too big %x %s %s\n", size, buf, reply_buf); + fprintf (stderr, "#### size too big %x %s %s\n", size, buf, reply_buf); + } + + RFS_ASSERT (size >=0 && size <2000000000); + ent = generic_lookup (trace_fh+24, TRACE_FH_SIZE2, 0, fh_htable, FH_HTABLE_SIZE); + if (ent) { + if (ent->key3 != size) { + if (proc==SETATTR) { + //printf ("%s\n", buf); + //printf ("size change fh %s pre-size %x size %x\n", trace_fh, ent->key3, size); + if (ent->key3 > size) { + truncate_num ++; + truncate_size += ent->key3 - size; + truncate_block_num += (ent->key3+BLOCK_SIZE-1)/BLOCK_SIZE; + if (size!=0) { + //fprintf (stderr, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf); + //fprintf (fp, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf); + truncate_block_num -= (size + BLOCK_SIZE-1)/BLOCK_SIZE; + } + if (truncate_size > 1000000000) { + truncate_KB += truncate_size/1000; + truncate_size %= 1000; + } + } else { + padding_num ++; + //printf ("%s\n", buf); + //printf ("padding fh %s pre-size %x size %x\n", trace_fh, ent->key3, size); + padding_size += size - ent->key3; + if (padding_size > 1000000000) { + padding_KB += padding_size/1000; + padding_size %= 1000; + } + } + } + ent->key3 = size; + }else + equal_size_num++; + } else { + generic_insert(trace_fh+24, TRACE_FH_SIZE2, size, fh_htable, FH_HTABLE_SIZE); + first_size_num ++; + } +}; + +int get_timestamp_usec (char * buf) +{ + char str[128]; + int ret; + strncpy(str, buf, 100); + RFS_ASSERT (str[10]=='.'); + RFS_ASSERT (str[17]==' '); + str[17]=0; + ret = atoi(&str[11]); + RFS_ASSERT (ret >=0 && ret <=999999); + return ret; +} + +int get_timestamp_sec (char * buf) +{ + char str[128]; + int ret; + strncpy(str, buf, 100); + RFS_ASSERT (str[10]=='.'); + str[10]=0; + ret = atoi(str); + RFS_ASSERT (ret >1000000000 && ret <2000000000); + return ret; +} + +int check_aging (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + int trace_status; + int debug = 0; + int nfs3proc, msgid, proc; + + while ((buf=read_line(++disk_index))!=NULL) { + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + + proc = nfs3proc_to_rfsproc[nfs3proc]; + ops_statistics (OPS_FLAG_INC, proc, -1); + + switch (proc) { + int off, count, size; + char * t; + case CREATE: printf("%s\n", "create"); break; + case MKDIR: printf("%s\n", "mkdir"); break; + case REMOVE: printf("%s\n", "remove"); break; + case RMDIR: printf("%s\n", "rmdir"); break; + case WRITE: + t = buf; + t = strstr (t, "off"); + RFS_ASSERT (t); + t+=4; + sscanf (t, "%x", &off); + RFS_ASSERT (off>=0 && off<0x7FFFFFFF) + t = strstr (t, "count"); + RFS_ASSERT (t); + t+=6; + sscanf (t, "%x", &count); + RFS_ASSERT (count <= 32768); + printf("%s off %x count %x\n", "write", off, count); + //printf("%s count %x\n", "write", count); + break; + case SETATTR: + t = strstr (buf, " size "); + if (t) { + sscanf (t, " size %x", &size); + printf ("%s size %x\n", "setattr", size); + } + } + if ((disk_index%10000)==0) { + fprintf(stderr, "%d disk trace passed\n", disk_index); + } + }; + + fprintf(stderr, "%d disk trace parsed\n", disk_index); + ops_statistics (OPS_FLAG_PRINT, disk_index, -1); +} + + +int check_statistics (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + char * p; + int trace_status; + int debug = 0; + int nfs3proc, msgid, proc; + static int last_timestamp_sec = -1; + int timestamp_sec; + int memory_trace_size = 0; + + while ((buf=read_line(++disk_index))!=NULL) { + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + timestamp_sec = get_timestamp_sec(buf); + + proc = nfs3proc_to_rfsproc[nfs3proc]; + ops_statistics (OPS_FLAG_INC, proc, -1); + + if (proc!= WRITE && proc!=SETATTR && proc!=READ) { + continue; + } + RFS_ASSERT (buf[strlen(buf)-1]=='\n'); + buf [strlen(buf)-1] = 0; + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + if (debug) + printf("read line disk_index %d %s\n", disk_index, buf); + + trace_status = NFS3ERR_RFS_MISS; + for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE; i++) { + reply_buf = read_line(i); + if (debug) + printf("searching for reply: read line %s\n", reply_buf); + if (!reply_buf) + break; + if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&reply_buf[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) { + trace_status = find_reply_status(reply_buf); + if (trace_status == NFS3_OK) { + if (proc==READ || proc==WRITE) + read_write_fh_statistics (OPS_FLAG_INC, buf, 0); + if (proc == WRITE) + write_statistics (OPS_FLAG_INC, buf, reply_buf, trace_status); + if (proc==WRITE || proc==SETATTR) + truncate_statistics (OPS_FLAG_INC, proc, buf, reply_buf); + } + }; + } + } + //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS) + if (trace_status == NFS3ERR_RFS_MISS) { + //printf ("%s no reply\n", buf); + missing_reply_num ++; + } + +#if 0 /* commented out by G. Jason Peng */ + if * + if ((missing_reply_num > memory_trace_size/10) && (missing_reply_num > 100)) { + printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size); + exit (0); + } +#endif + + memory_trace_size ++; + + if (last_timestamp_sec == -1) { + last_timestamp_sec = timestamp_sec; + } else if (timestamp_sec - last_timestamp_sec >=3600) { + ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec); + truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL); + read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec); + write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1); + last_timestamp_sec = timestamp_sec; + } +/* + if ((memory_trace_size%10000)==0) { + fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num); + ops_statistics (OPS_FLAG_PRINT, -1); + truncate_statistics (OPS_FLAG_PRINT, -1, NULL, NULL); + } +*/ + }; + + fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num); + ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec); + truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL); + read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec); + write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1); +} + + +/* This routine output all the requests, together with their replies */ +int pair_trace (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + char * p; + int trace_status; + int debug = 0; + int nfs3proc, msgid; + int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + int memory_trace_size = 0; + FILE * outputfp; + char output_file[1024]; + + strcpy (output_file, tracefile); + strcat (output_file, ".pair"); + outputfp = fopen (output_file, "w"); + RFS_ASSERT (outputfp); + + while ((buf=read_line(++disk_index))!=NULL) { + if (disk_index == 258) + f(); + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + ops[nfs3proc]++; + + buf [strlen(buf)-1] = 0; + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + if (debug) + printf("read line disk_index %d %s\n", disk_index, buf); + fprintf (outputfp, "%s\n", buf); + + trace_status = NFS3ERR_RFS_MISS; + for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) { + reply_buf = read_line(i); + if (debug) + printf("searching for reply: read line %s\n", reply_buf); + if (!reply_buf) + break; + if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&reply_buf[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) { + fprintf(outputfp, "%s", reply_buf); + trace_status = find_reply_status(reply_buf); + if (debug) + fprintf(stderr, "reply found trace_status %d\n", find_reply_status(reply_buf)); + break; + }; + } + } + + if (trace_status == NFS3ERR_RFS_MISS) { + fprintf (stderr, "%s no reply\n", buf); + fprintf(outputfp, "missing_reply\n"); + missing_reply_num ++; + } + + if (missing_reply_num > memory_trace_size/10 && missing_reply_num >100) { + fprintf (stderr, "missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size); + exit (0); + } + + memory_trace_size ++; + + if ((memory_trace_size%10000)==0) + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + }; + + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); + +} +/* This routine output all the write requests, together with their replies. It is used for + * analysis of write requests: appended bytes, overwrite bytes etc + */ +int pair_write (char * tracefile) +{ + int disk_index=-1; + char *buf; + char *reply_buf; + int i; + char * p; + int trace_status; + int pair_write_debug = 0; + int nfs3proc, msgid; + int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + int memory_trace_size = 0; + + while ((buf=read_line(++disk_index))!=NULL) { + if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C') + continue; + if (buf[TRACE_VERSION_POS]!='3') + continue; + sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc); + + RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT); + ops[nfs3proc]++; + + if (!strstr(buf, "write")) + continue; + + buf [strlen(buf)-1] = 0; + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + if (pair_write_debug) + printf("read line disk_index %d %s\n", disk_index, buf); + + /* store the request to memory */ + //strcpy (memory_trace[memory_trace_size].line, buf); + //memory_trace[memory_trace_size].disk_index = disk_index; + + /* find and store the reply status and reply fhandle to memory */ + //memory_trace[memory_trace_size].trace_status = NFS3ERR_RFS_MISS; + trace_status = NFS3ERR_RFS_MISS; + for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) { + reply_buf = read_line(i); + if (pair_write_debug) + printf("searching for reply: read line %s\n", reply_buf); + if (!reply_buf) + break; + if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&reply_buf[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) { + int pre_size, size, count; + //memory_trace[memory_trace_size].trace_status = find_reply_status(reply_buf); + if (pair_write_debug) + printf("reply found trace_status %d\n", find_reply_status(reply_buf)); + //break; + trace_status = find_reply_status(reply_buf); + if (trace_status == NFS3_OK) { + p = strstr (p, "pre-size"); + RFS_ASSERT (p); + sscanf (p, "pre-size %x", &pre_size); + p += strlen("pre-size"); + p = strstr (p, "size"); + RFS_ASSERT (p); + sscanf (p, "size %x", &size); + p = strstr (p, "count"); + if (!p) + printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + RFS_ASSERT (p); + sscanf (p, "count %x", &count); + printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count); + break; + } + }; + } + } + //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS) + if (trace_status == NFS3ERR_RFS_MISS) { + printf ("%s no reply\n", buf); + missing_reply_num ++; + } + +#if 0 /* commented out by G. Jason Peng */ + if (missing_reply_num > memory_trace_size/10) { + printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size); + exit (0); + } +#endif + + memory_trace_size ++; + + /* + if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) { + fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES); + break; + } + */ + if ((memory_trace_size%10000)==0) + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + }; + + fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num ); + //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); + +} + +#ifdef notdef +/* This function is not finished writing */ +int calculate_performance() +{ + char *buf; + char *reply_buf; + int i; + char * p; + int debug = 0; + +typedef struct { + struct timeval start; + struct timeval stop; + int trace_status; + int op; +} trace_performance_ent_t; + + struct timeval req_time; + struct timeval reply_time; + + trace_performance_ent_t * ent = NULL; + + while (!CYCLIC_FULL(memory_trace_index)) { + + if (ent!=NULL && (ent->trace_status == NFS3ERR_RFS_MISS)) + buf = reply_buf; + if ((buf=read_line(++disk_index))==NULL) { +END: fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, CYCLIC_NUM(memory_trace_index), missing_reply_num ); + fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); + end_profile (&read_trace_profile); + return TRACE_FILE_END; + } + + get_timestamp (&ent->req_time, buf); + if (MAX_COMMAND_REPLY_DISTANCE ==1) { + ent->trace_status == NFS3ERR_RFS_MISS; + } else { + reply_buf=read_line(++disk_index); + RFS_ASSERT (reply_buf); + if (!strcmp(reply_buf, "missing_reply\n")) { + ent->trace_status == NFS3ERR_RFS_MISS; + } else { + get_timestamp (&ent->reply_time, buf); + ent->trace_status = find_reply_status(reply_buf); + } + } + } +} +#endif + +int read_trace () +{ + char *buf; + char *reply_buf; + int i; + char * p; + int debug = 0; + memory_trace_ent_t * ent=NULL; + + start_profile (&read_trace_profile); + + while (!CYCLIC_FULL(memory_trace_index)) { + if (ent!=NULL && (ent->trace_status == NFS3ERR_RFS_MISS)) + buf = reply_buf; + if ((buf=read_line(++disk_index))==NULL) { +END: fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, CYCLIC_NUM(memory_trace_index), missing_reply_num ); + fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh); + end_profile (&read_trace_profile); + return TRACE_FILE_END; + } + + if (rfs_debug) + printf ("disk_index %d %s\n", disk_index, buf); + + if (disk_index==0) { + trace_timestamp1 = get_timestamp_sec (buf); + trace_starttime.sec = get_timestamp_sec (buf); + trace_starttime.usec = get_timestamp_usec (buf); + trace_starttime.esec = 0; + printf ("trace starttime %d %d %d\n", trace_starttime.sec, trace_starttime.usec, trace_starttime.esec); + } else + trace_timestamp2 = get_timestamp_sec (buf); + + /* store the request to memory */ + ent = &(memory_trace[memory_trace_index.head]); + strcpy (ent->line, buf); + ent->disk_index = disk_index; + + if (MAX_COMMAND_REPLY_DISTANCE ==1) { + ent->trace_status == NFS3ERR_RFS_MISS; + } else { + reply_buf=read_line(++disk_index); + RFS_ASSERT (reply_buf); + if (!strcmp(reply_buf, "missing_reply\n")) { + ent->trace_status == NFS3ERR_RFS_MISS; + } else { + ent->trace_status = find_reply_status(reply_buf); + } + }; + + if (ent->trace_status == NFS3ERR_RFS_MISS) + missing_reply_num ++; + + if (MAX_COMMAND_REPLY_DISTANCE > 1) { + if ((missing_reply_num > disk_index/5) && (missing_reply_num > 100)) { + printf ("missing_reply_num %d too high for disk_index %d\n", missing_reply_num, disk_index); + exit (0); + } + } + + /* find and store the reply trace fhandle for create-class requests */ + if (ent->trace_status==NFS3_OK) { + if (strstr(buf, "create") || strstr(buf, "mkdir") + || (strstr(buf, "symlink") && (buf[TRACE_VERSION_POS]!='2')) + || strstr(buf, "mknod") ) { + p = find_reply_trace_fh(reply_buf); + if (p==NULL) { + printf("skip line disk_index %d %s \n", disk_index, buf); + continue; + } + memcpy(ent->reply_trace_fh, p, TRACE_FH_SIZE); + } else + memset(ent->reply_trace_fh, 0, TRACE_FH_SIZE); + } + + add_to_dep_tab(memory_trace_index.head); + + if (((disk_index+1)%20000)==0) { + fprintf(stderr, "%d disk trace parsed \n", disk_index+1); + }; + }; + + end_profile (&read_trace_profile); + return TRACE_BUF_FULL; +} +#else /* not defined REDUCE_MEMORY_TRACE_SIZE */ +int read_trace () +{ + FILE * fp; + char buf[1024]; + // char * t=buf; + int disk_index=0; + + fp = fopen(trace_file, "r"); + RFS_ASSERT (fp!=NULL); + while (fgets(buf, MAX_TRACE_LINE_LENGTH, fp)) { + if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH))) + printf("strlen(buf) %d buf %s \n", strlen(buf), buf); + RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)); + + /* store the request to memory */ + strcpy (memory_trace[memory_trace_size].line, buf); + memory_trace[memory_trace_size].disk_index = disk_index; + memory_trace_size ++; + + if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) { + fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES); + break; + } + if ((disk_index%100000)==0) + fprintf(stderr, "%d disk trace parsed, store %d trace lines to memory\n", disk_index, memory_trace_size); + disk_index ++; + } + + fprintf(stderr, "total %d disk lines %d memory lines \n", disk_index, memory_trace_size ); +} +#endif + + +#ifdef REDUCE_MEMORY_TRACE_SIZE +inline int disk_index_to_memory_index (int disk_index) +{ + static int memory_index = 0; + + RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index)); + RFS_ASSERT (memory_trace[memory_trace_index.tail].disk_index <= disk_index); + RFS_ASSERT (memory_trace[CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size)].disk_index >=disk_index); + if (disk_index > memory_trace[memory_index].disk_index) { + while (memory_trace[memory_index].disk_index < disk_index) { + memory_index = CYCLIC_ADD(memory_index,1,memory_trace_index.size); + } + }; + if (disk_index < memory_trace[memory_index].disk_index) { + while (memory_trace[memory_index].disk_index > disk_index) { + memory_index = CYCLIC_MINUS(memory_index,1,memory_trace_index.size); + } + }; + + RFS_ASSERT (disk_index == memory_trace[memory_index].disk_index); + return memory_index; +} +#else +#define disk_index_to_memory_index(disk_index) disk_index +#endif + +#define get_line_by_disk_index(disk_index) \ + memory_trace[disk_index_to_memory_index(disk_index)].line + +inline char * find_reply_line (char * command_line, int cur_disk_index) +{ + int i; + char * line; + char * p; + int request_memory_index = disk_index_to_memory_index (cur_disk_index); + for (i=request_memory_index+1; i<request_memory_index+MAX_COMMAND_REPLY_DISTANCE && i<MAX_MEMORY_TRACE_LINES; i++) { + line = memory_trace[i].line; + if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R') { + p = strchr (&line[TRACE_MSGID_POS], ' '); + RFS_ASSERT (p); + if (!strncmp(&line[TRACE_MSGID_POS], &command_line[TRACE_MSGID_POS], p-&(line[TRACE_MSGID_POS]))) + return line; + } + } + return NULL; +} + +inline int find_reply_status (char * line) +{ + char * p; + int i=0; + + if (rfs_debug) + printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + if (!(line[TRACE_COMMAND_REPLY_FLAG_POS]=='R')) { + printf ("disk_index %d %s\n", disk_index, line); + }; + RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R'); + p = line+TRACE_MSGID_POS+2; /* at least one letter for msgid and one letter for space */ + if (strstr(p, "OK")) + return NFS3_OK; + if (strstr(p, "lookup 2") || strstr(p, "lookup 2")) + return 0x2; + if (strstr(p, "create d")) + return 0xd; + if (strstr(p, "setattr 1")) + return 0x1; + if (strstr(p, "setattr 2712")) /* 10002 NFS3ERR_NOT_SYNC */ + return 0x2712; + if (strstr(p, "lookup d")) + return 0xd; + if (strstr(p, "read d")) + return 0xd; + if (strstr(p, "write d")) + return 0xd; + if (strstr(p, "write 46")) + return 0x46; + if (strstr(p, "getattr 46")) + return 0x46; + if (strstr(p, "mkdir d")) + return 0xd; + printf ("line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + printf ("!!!!!!!!!!!!!!!!!!!!!!!!\n"); + fprintf (stderr, "line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + fprintf (stderr, "!!!!!!!!!!!!!!!!!!!!!!!!\n"); +} + +inline int find_reply_status_old (char * line) +{ + char * p; + int i=0; + + //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]); + RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R'); + if (!strstr(line, "OK")) { + p=strstr(line, " 6 read "); + if (p) { + p+=strlen(" 6 read "); + } else { + p = strstr (line, "status=XXX"); + RFS_ASSERT (p); + p--; + RFS_ASSERT (*p==' '); + while (*p==' ') + p--; + while (*p!=' ') { + p--; + } + p++; + } + sscanf (p, "%x", &i); + if ((i<=0) || (i>10000)) + printf("line %s\n", line); + RFS_ASSERT (i>0 && i<10009); + } + return i; +} + +inline char * find_reply_trace_fh (char * line) +{ + char * p; + p = strstr (line, "OK fh"); + if (!p) { + printf ("disk_index %d find_reply_trace_fh line %s\n", disk_index, line); + return NULL; + } else + return p+6; +} + +#ifndef NO_DEPENDENCY_TABLE +inline int disk_index_to_dep_index(int cur_dep_index, int disk_index) +{ + int i; + for (i=cur_dep_index; i>min_dep_index; i--) { + if (dep_tab[i].disk_index == disk_index) + return i; + } + RFS_ASSERT (0); +} +#endif + +inline int is_play_candidate (int dep_index) +{ + int proc = dep_tab[dep_index].proc; + int status = dep_tab[dep_index].status; + int trace_status = dep_tab[dep_index].trace_status; + +#ifndef TAKE_CARE_CREATE_MODE_BY_DAN + /* for a failed create in trace, trace_replay just ignore many time the trace create fail + * due to access control, but trace_play will success because our access control + * may be loose (all uid/gid is mapped to single one 513:513, so we just skip these requests + */ + if ((proc==CREATE || proc==MKDIR) && (trace_status!=NFS3_OK) && (status!=NFS3ERR_RFS_MISS)) { + if (dependency_debug) + printf ("disk[%d] ignore failed create/mkdir in trace, trace_status %d line %s", + dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line); + failed_create_command_num ++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_OTHER_FAILED_COMMAND + if (((trace_status == NFS3ERR_ACCES) && (proc==READ || proc==WRITE || proc==LOOKUP)) || + ((trace_status == NFS3ERR_PERM) && (proc==SETATTR)) ){ + if (dependency_debug) + printf ("disk[%d] ignore other failed command in trace, trace_status %d line %s", + dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line); + + failed_other_command_num ++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_SYMBOLIC_LINK + if ((dep_tab[dep_index].proc==READLINK) ) { /* send request */ + skipped_readlink_command_num ++; + return FALSE; + } +#endif +/* This is actually take care in get_nextop by checking fh_map error when dep_index==min_dep_index */ +#ifndef TAKE_CARE_CUSTOM_COMMAND + /* this line has a file handle which should belong to discard but it is not + * the file handle directly appears as parent directory in a lookup request + * the return value is NOENT, the parent directory should have been initialized + * but the initialization code just ignored all lookup request which didn't success + * including NOENT even though the parent directory is still valid. + */ +/* + if (( ((dep_tab[dep_index].disk_index==262213) || (dep_tab[dep_index].disk_index==214402)) + && !(strcmp(trace_file, "anon-lair62-011130-1100.txt")) + ) || + ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470)) + && !(strcmp(trace_file, "anon-lair62-011130-1000.txt")) + )) { + skipped_custom_command_num++; + return FALSE; + } +*/ + if (( ((dep_tab[dep_index].disk_index==423727) || (0)) + && !(strncmp(trace_file, "anon-lair62-011130-1500.txt", strlen("anon-lair62-011130-1500.txt"))) + ) || + ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470)) + && !(strcmp(trace_file, "anon-lair62-011130-1000.txt")) + )) { + skipped_custom_command_num++; + return FALSE; + } + /* this line is about the mkdir 116d9d originally in anon-lair62-011130-1400.txt */ + if (!strncmp(dep_tab[dep_index].line, "1007147245.194201", strlen("1007147245.194201"))) { + skipped_custom_command_num++; + return FALSE; + } +#endif +#ifndef TAKE_CARE_FSSTAT_COMMAND + /* the file handle used in this command is not processed properly by pre-processing */ + if (proc==FSSTAT) { + char * trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line); + fh_map_t * fh = lookup_fh (trace_fh); + if (!fh) { + skipped_fsstat_command_num++; + return FALSE; + } + } +#endif + return TRUE; +} + +inline int is_dir_op (int proc) +{ + switch (proc) { + case MKDIR: + case CREATE: + case LINK: + case SYMLINK: + case MKNOD: + case REMOVE: + case RMDIR: + case RENAME: + return 1; + default: + return 0; + } +} + +inline int is_create_op (int proc) +{ + if (proc==CREATE || proc==MKDIR || proc==LINK || proc==SYMLINK || proc==MKNOD || proc==RENAME) + return 1; + return 0; +} + +inline int is_delete_op (int proc) +{ + if (proc==REMOVE || proc==RMDIR || proc==RENAME) + return 1; + return 0; +} + +static inline char * find_lead_trace_fh(int proc, char * line) +{ + char * p; + /* check the file handle availability */ + p = strstr (line, "fh"); + RFS_ASSERT (p); + p+=3; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line); + return p; +} + +inline char * find_another_trace_fh(int proc, char * line) +{ + char * p; + /* check the file handle availability */ + p = strstr (line, "fh2"); + RFS_ASSERT (p); + p+=4; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line); + return p; +} + +/* return the index of next request in dep_tab. + * Return -1 if there is no suitable request to send + */ +inline int get_nextop(void) +{ + int i,j, k; + int * t; + static int dep_index = -2; + char * line; + char * p; +#define INIT_MIN_WAIT_VALUE -999 + static int min_wait_fhandle_dep_index = INIT_MIN_WAIT_VALUE; + int proc; + int flag; + + if (min_wait_fhandle_dep_index == -999) + min_wait_fhandle_dep_index = dep_window_index.head; + + for (i=0; i<CYCLIC_NUM(dep_window_index); i++) { + dep_index = (dep_window_index.tail+i) % dep_window_index.size; + + proc = dep_tab[dep_index].proc; + flag = dep_tab[dep_index].flag; + + if (dependency_debug) + printf ("get_nextop check dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); +#ifdef NO_DEPENDENCY_TABLE + if (dep_tab[dep_index].flag == DEP_FLAG_INIT) { + if (is_play_candidate(dep_index)==TRUE) { + /* the trace_fh is the file handle for the operation directory, trace_fh_2 is other file handle + * used in the request */ + if (proc==LINK || proc==RENAME) { + dep_tab[dep_index].trace_fh = find_another_trace_fh (proc, dep_tab[dep_index].line); + dep_tab[dep_index].trace_fh_2 = find_lead_trace_fh(proc, dep_tab[dep_index].line); + dep_tab[dep_index].fh = 0; + dep_tab[dep_index].fh_2 = 0; + } else { + dep_tab[dep_index].trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line); + dep_tab[dep_index].fh = 0; + dep_tab[dep_index].fh_2 = (fh_map_t *)1; + }; + dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE; +#ifdef TIME_PLAY + dep_tab[dep_index].skip_sec = skip_sec; +#endif + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_CANDIDATE\n", dep_tab[dep_index].disk_index); + } else { + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_DONE; + continue; + } + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE) ) { + + if (!dep_tab[dep_index].fh) + dep_tab[dep_index].fh = lookup_fh (dep_tab[dep_index].trace_fh); + if (!dep_tab[dep_index].fh_2) + dep_tab[dep_index].fh_2 = lookup_fh (dep_tab[dep_index].trace_fh_2); + + /* need to wait for file handle */ + if ((!dep_tab[dep_index].fh) || (!dep_tab[dep_index].fh_2)) { + if (dependency_debug) + printf("disk[%d] can not lookup file handle\n", dep_tab[dep_index].disk_index); + if (dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) { + if (dependency_debug) + printf ("disk[%d] state DEP_FLAG_CANDIDATE to DEP_FLAG_WAIT_FHANDLE\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_FHANDLE; + sfs_gettime (&dep_tab[dep_index].start); + if (CYCLIC_LESS(dep_tab_index,dep_index,min_wait_fhandle_dep_index)) + min_wait_fhandle_dep_index = dep_index; + } else { + struct ladtime tmp; + if (dep_index==dep_window_index.tail) { + if (!profile_debug) + printf ("fh_path_map error disk[%d] state DEP_FLAG_WAIT_FHANDLE to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index); + fh_path_map_err_num ++; + dep_tab[dep_index].flag = DEP_FLAG_DONE; + continue; + } + sfs_gettime (&tmp); + SUBTIME (tmp, dep_tab[dep_index].start); +#define DEPENDENCY_TIMEOUT 50 +#ifdef TIME_PLAY + RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT + (skip_sec - dep_tab[dep_index].skip_sec)); +#else + if (tmp.sec >= DEPENDENCY_TIMEOUT) { + printf("dep_tab[%d].flag %d disk_index %d line %s\n", dep_index, + dep_tab[dep_index].flag, dep_tab[dep_index].disk_index, + dep_tab[dep_index].line); + } + RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT ); +#endif + } + continue; + } + + /* file handle ready, adjust_min_wait_fhandle_dep_index */ + if ((dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE)) { + if (dep_index == min_wait_fhandle_dep_index) { + min_wait_fhandle_dep_index = dep_window_index.head; + for (j=CYCLIC_ADD(dep_index,1,dep_window_index.size); CYCLIC_LESS(dep_window_index,j,dep_window_index.head); j++) { + if (dep_tab[j].flag ==DEP_FLAG_WAIT_FHANDLE) { + min_wait_fhandle_dep_index = j; + break; + } + } + } + } + if (dependency_debug) + printf("disk[%d] found file handle\n", dep_tab[dep_index].disk_index); + dep_tab[dep_index].flag = DEP_FLAG_FHANDLE_READY; + + /* the normal file operation can be executed now */ + if (!is_dir_op (dep_tab[dep_index].proc)) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + + if (dependency_debug) + printf("disk[%d] directory operation \n", dep_tab[dep_index].disk_index); + /* the directory operation need to lock the directory first */ + if (dep_tab[dep_index].fh->lock) { + if (dependency_debug) + printf ("disk[%d] state %d to DEP_FLAG_WAIT_DIRECTORY\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_DIRECTORY; + continue; + } + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_FHANDLE_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DIRECTORY)) { + int j = dep_tab[dep_index].fh - fh_map; + if (dependency_debug) { + printf ("dep_tab[%d].disk_index %d, fh_map[%d] lock=%d\n",dep_index, dep_tab[dep_index].disk_index, j, dep_tab[dep_index].fh->lock); + printf ("trace_fh %s path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path); + printf ("trace_fh %s path %s\n", fh_map[j].trace_fh, fh_map[j].path); + } + if ((dep_tab[dep_index].fh->lock) || ((proc==RENAME) && (dep_tab[dep_index].fh_2->lock)) ) { + if (dependency_debug) + printf ("continue to wait for directory lock\n"); + continue; + } + if (dependency_debug) + printf ("dep_tab[%d] disk index %d LOCK fh_map[%d] \n", dep_index, dep_tab[dep_index].disk_index, j); + dep_tab[dep_index].fh->lock = 1; + if (proc==RENAME) + dep_tab[dep_index].fh_2->lock = 1; + + /* the non-delete directory operation can proceed now */ + if (!is_delete_op (dep_tab[dep_index].proc)) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + + /* the delete operation can proceed if nobody ahead is waiting for fhandle */ + /* probably this condition is not strong enough */ +// if ((min_wait_fhandle_dep_index<dep_index) ) { + if (dep_index!=dep_window_index.tail) { + if (dependency_debug) + printf ("disk[%d] state %d to DEP_FLAG_WAIT_DELETE\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag); + dep_tab[dep_index].flag = DEP_FLAG_WAIT_DELETE; + continue; + } + dep_tab[dep_index].flag = DEP_FLAG_DIRECTORY_READY; + } + + if ((dep_tab[dep_index].flag == DEP_FLAG_DIRECTORY_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DELETE)) { +// if (min_wait_fhandle_dep_index > dep_index) { + if (dep_index==dep_window_index.tail) { + if (dependency_debug) + printf ("return disk[%d]\n", dep_tab[dep_index].disk_index); + return dep_index; + } + } +#else /*NO_DEPENDENCY_TABLE undefined */ + /* this part of code will be invalid after CYCLIC buffer design */ + if (dep_tab[dep_index].flag == DEP_FLAG_INIT){ + for (j=0, t=&(dep_tab[dep_index].dep_ops[0]); + (j<dep_tab[dep_index].init_dep_num) && (dep_tab[dep_index].cur_dep_num>0); + j++, t++) { + if (*t !=-1) { + if (dep_tab[disk_index_to_dep_index(dep_index, *t)].flag == DEP_FLAG_DONE) { + /* The depended request has been finished */ + *t = -1; + dep_tab[dep_index].cur_dep_num --; + } + } + } + + if (dep_tab[dep_index].cur_dep_num == 0) { + return dep_index; + } + } +#endif + } + + if (dependency_debug) + printf ("get_nexop return -1\n"); + return -1; +} + +int check_timeout(void) +{ + static int biod_index = 0; + int i; + int dep_index; /* index into dep_tab */ + int proc; + sfs_op_type *op_ptr; /* per operation info */ + struct ladtime timeout; + struct ladtime current_time; + + sfs_gettime (¤t_time); + + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + if (biod_reqp[biod_index].in_use==TRUE) { + timeout = biod_reqp[biod_index].timeout; + if ((current_time.sec>timeout.sec) || + ((current_time.sec==timeout.sec) && (current_time.usec>timeout.usec))) { + + dep_index = biod_reqp[biod_index].dep_tab_index; + proc = dep_tab[dep_index].proc; + op_ptr = &Ops[proc]; + op_ptr->results.timeout_calls++; + Ops[TOTAL].results.timeout_calls++; + + + if (is_create_op(proc)) { + dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE; + printf ("resend dep_tab[%d], disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); + finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT, DEP_FLAG_CANDIDATE); + } else { + finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT, DEP_FLAG_DONE); + } + timeout_num ++; + num_out_reqs_statistics_at_timeout[num_out_reqs]++; + + //RFS_ASSERT (!is_create_op(proc)); + + if (per_packet_debug) + printf ("timeout request: disk_index %d, dep_index %d biod_reqp[%d].start %d:%d timeout %d:%d current %d:%d\n", dep_tab[dep_index].disk_index, dep_index, biod_index, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, timeout.sec, timeout.usec, current.sec, current.usec); + } + } + } +} + +/* Allocate a biod_req entry to send and receive request dep_tab[dep_index] + * build the cross reference between dep_tab entry and biod_req entry + */ +struct biod_req * get_biod_req(int dep_index) /* index into dep_tab */ +{ + static int biod_index = 0; + int i; + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + if (!biod_reqp[biod_index].in_use) { + biod_reqp[biod_index].in_use = 1; + biod_reqp[biod_index].dep_tab_index = dep_index; + dep_tab[dep_index].biod_req_index = biod_index; + num_out_reqs++; + return &(biod_reqp[biod_index]); + } + } + return NULL; +} + +/* Return index into biod_reqp + * return -1 upon failure + */ +int lookup_biod_req (int xid) +{ + static int biod_index = 0; + int i; + for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) { + /* give a NULL as timeout pointer may cause indefinitely block */ + if ((biod_reqp[biod_index].in_use == TRUE) &&( biod_reqp[biod_index].xid == xid)) { + return biod_index; + } + } + return -1; +} + +extern struct ladtime test_start; +void init_time_offset(void) +{ + struct ladtime tmp1; + struct ladtime tmp2; + + test_start.sec = 0; + test_start.usec = 0; + sfs_gettime (&tmp1); /* called at initial time: tmp1 = play_starttime */ +#ifdef SPEED_UP + DIVTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime / SCALE */ +#endif +#ifdef SLOW_DOWN + MULTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime * SCALE */ +#endif + + tmp2 = trace_starttime; /* tmp2 = trace_starttime */ + SUBTIME (tmp2, tmp1); /* tmp2 = trace_starttime - play_starttime *|/ SCALE */ + time_offset = tmp2; /* time_offset = trace_starttime - play_starttime *|/ SCALE */ +} + +/* initialize timestamp and proc field of dep_tab entry */ +void init_dep_tab_entry (int dep_index) +{ + char * line; + int version; + int nfsproc; + int msgid; + + //line = get_line_by_disk_index (dep_tab[dep_index].disk_index); + line = dep_tab[dep_index].line; + sscanf (line, "%d.%d", &(dep_tab[dep_index].timestamp.tv_sec), &(dep_tab[dep_index].timestamp.tv_usec)); + sscanf (&line[TRACE_MSGID_POS], "%x %x", &msgid, &nfsproc); + //printf ("msgid %x nfsproc %x\n", msgid, nfsproc); + if (line[TRACE_VERSION_POS]=='2') { + dep_tab[dep_index].proc = nfs2proc_to_rfsproc[nfsproc]; + RFS_ASSERT (nfsproc <18); + } else { + /* This is for debug purpose */ + if (line[TRACE_VERSION_POS] !='3') { + fprintf(stderr, "line[TRACE_VERSION_POS] %c line %s\n", line[TRACE_VERSION_POS], line); + line = get_line_by_disk_index (dep_tab[dep_index].disk_index-1); + if (!line) + line = get_line_by_disk_index (dep_tab[dep_index].disk_index-2); + RFS_ASSERT (line); + fprintf(stderr, "previousline %s\n", line); + } + RFS_ASSERT (line[TRACE_VERSION_POS] =='3'); + if (nfsproc >= NFS3_PROCEDURE_COUNT) { + fprintf(stderr, "proc %d line %s\n", nfsproc, line); + + } + RFS_ASSERT (nfsproc <NFS3_PROCEDURE_COUNT); + dep_tab[dep_index].proc = nfs3proc_to_rfsproc[nfsproc]; + } + RFS_ASSERT (dep_tab[dep_index].proc >= 0 && dep_tab[dep_index].proc < NOPS); + dep_tab[dep_index].flag = DEP_FLAG_INIT; +} + +void adjust_play_window (int flag, int * poll_timeout_arg) +{ + struct ladtime max_window_time; + static struct ladtime max_poll_time = {0, 2000, 0}; + struct ladtime t; + int i; + char * line; + cyclic_index_t old_dep_window_index = dep_window_index; + +#ifdef notdef + printf ("^^^^^^^^^^^^^^^ adjust_play_window, begin\n"); + CYCLIC_PRINT (dep_tab_index); + printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index); + CYCLIC_PRINT (dep_window_index); + CYCLIC_PRINT (memory_trace_index); + printf (" adjust_play_window, begin\n"); +#endif + + while ((!CYCLIC_EMPTY(dep_window_index)) && (dep_tab[dep_window_index.tail].flag == DEP_FLAG_DONE)) { +#ifdef notdef + //CYCLIC_PRINT (memory_trace_index); + //printf("MOVE_TAIL_TO memory_index %d\n", dep_tab[dep_tab_index.tail].memory_index); + RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index)); + RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head)); + printf("%d is done\n", dep_window_index.tail); +#endif + CYCLIC_MOVE_TAIL(dep_tab_index); + CYCLIC_MOVE_TAIL(dep_window_index); + +#ifdef notdef + CYCLIC_PRINT (dep_tab_index); + CYCLIC_PRINT (dep_window_index); + + if (! (dep_tab_index.tail == dep_window_index.tail)) { + CYCLIC_PRINT(dep_tab_index); + CYCLIC_PRINT(dep_window_index); + }; + RFS_ASSERT ( dep_tab_index.tail == dep_window_index.tail); +#endif + + if (!CYCLIC_EMPTY(dep_tab_index)) { +#ifdef notdef + RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index)); + if (!(CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head))) { + CYCLIC_PRINT(memory_trace_index); + CYCLIC_PRINT(dep_tab_index); + printf("dep_tab[head-1].memory_index, %d [tail].memory_index %d\n", + dep_tab[CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size)].memory_index, + dep_tab[dep_tab_index.tail].memory_index); + } + RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head)); +#endif + CYCLIC_SET_TAIL_TO(&memory_trace_index, dep_tab[dep_tab_index.tail].memory_index); + //printf ("set memory_trace_index to %d=%d, dep_tab_index.tail %d\n", memory_trace_index.tail, dep_tab[dep_tab_index.tail].memory_index, dep_tab_index.tail); + } else { + // CYCLIC_MOVE_TAIL (memory_trace_index); + } + } + + while (CYCLIC_EMPTY(dep_tab_index)) { + + if (disk_io_status == TRACE_FILE_END) + return; + else { + //printf ("************** ADJUST_PLAY_WINDOW sleep 1 s\n"); + //print_cyclic_buffers(); + //pthread_yield(); + //usleep (1000); + } + } + + /* max_trace_window_time = current *|/ SCALE + trace_starttime */ + sfs_gettime (¤t); + +#ifdef TIME_PLAY +#ifdef SPEED_UP + MULTIME (current, PLAY_SCALE); +#endif +#ifdef SLOW_DOWN + DIVTIME (current, PLAY_SCALE); +#endif + ADDTIME (current, trace_starttime); + max_window_time = current; + + /* Right now it is not clear how to deal with the situation where MAX_PLAY_WINDOW is reached */ + if (CYCLIC_NUM(dep_window_index) == MAX_PLAY_WINDOW) { + //printf ("can not catch up the speed, dep_tab_size %d dep_window_max %d reach min_dep_index %d+MAX_PLAY_WINDOW\n", dep_tab_size, dep_window_max, min_dep_index); + //printf ("."); + can_not_catch_speed_num ++; + } + while ((CYCLIC_NUM(dep_window_index) < MAX_PLAY_WINDOW) && + (CYCLIC_NUM(dep_window_index) < CYCLIC_NUM(dep_tab_index)) ) { + struct ladtime t; + int dep_index = (dep_window_index.tail+i) % dep_window_index.size; + t.sec = dep_tab[dep_index].timestamp.tv_sec; + t.usec = dep_tab[dep_index].timestamp.tv_usec; + if ((t.sec>max_window_time.sec)||(t.sec==max_window_time.sec && t.usec>max_window_time.usec)) + break; + CYCLIC_MOVE_HEAD(dep_window_index); + } +#else + ADDTIME (current, trace_starttime); + max_window_time = current; + while ((CYCLIC_NUM(dep_window_index) < MAX_PLAY_WINDOW) && + (CYCLIC_NUM(dep_window_index) < CYCLIC_NUM(dep_tab_index)) ) { + CYCLIC_MOVE_HEAD(dep_window_index); + } +#endif + + if (flag == BUSY) + *poll_timeout_arg = 0; + else if (CYCLIC_NUM(dep_window_index)==CYCLIC_NUM(dep_tab_index)) { + *poll_timeout_arg = 1000; /* poll_timeout set to 1 second for the last request */ + } else { +#ifdef TIME_PLAY + struct ladtime tmp; + struct ladtime tmp1; + tmp.sec = dep_tab[dep_window_index.head].timestamp.tv_sec; + tmp.usec = dep_tab[dep_window_index.head].timestamp.tv_usec; + if (adjust_play_window_debug>=2) + printf ("dep_tab[dep_window_index.head %d].timestamp %d:%d, max_window_time %d:%d\n", + dep_window_index.head, tmp.sec, tmp.usec, max_window_time.sec, max_window_time.usec); + + SUBTIME (tmp, max_window_time); +#ifdef SPEED_UP + DIVTIME (tmp, PLAY_SCALE); +#endif +#ifdef SLOW_DOWN + MULTIME (tmp, PLAY_SCALE); +#endif +/* + tmp1 = tmp; + + if (tmp.sec > max_poll_time.sec) { + + if (rfs_debug) + printf ("dep_tab[%d].timestamp %d:%d, max_window_time %d:%d\n", + dep_window_max, dep_tab[dep_window_max].timestamp.tv_sec, dep_tab[dep_window_max].timestamp.tv_usec, max_window_time.sec, max_window_time.usec); + printf ("skip %d seconds\n", tmp.sec-max_poll_time.sec); + SUBTIME (tmp, max_poll_time); + tmp.usec = 0; + skip_sec += tmp.sec; + SUBTIME (test_start, tmp); + tmp = max_poll_time; + } +*/ + + //RFS_ASSERT ((tmp.sec < 1000)); + if (tmp.sec > 1000) + tmp.sec = 1000; + if ((tmp.sec ==0) && (tmp.usec==0)) { + *poll_timeout_arg = 0; + } else + *poll_timeout_arg = tmp.sec*1000000+tmp.usec; +#else + /* + struct ladtime tmp; + struct ladtime tmp1; + tmp.sec = dep_tab[dep_window_max].timestamp.tv_sec; + tmp.usec = dep_tab[dep_window_max].timestamp.tv_usec; + tmp1.sec = dep_tab[dep_window_max-1].timestamp.tv_sec; + tmp1.usec = dep_tab[dep_window_max-1].timestamp.tv_usec; + SUBTIME (tmp, tmp1); + RFS_ASSERT ((tmp.sec < 1000)); + RFS_ASSERT ((tmp.sec>0) || ((tmp.sec==0) && (tmp.usec>0))); + *poll_timeout = tmp.sec*1000000+tmp.usec; + */ + + *poll_timeout_arg = 100000; +#endif + } + if (rfs_debug) + printf ("adjust_play_window: flag %d min %d -> %d, max %d -> %d poll_timeout_arg %d \n", + flag, old_dep_window_index.tail, dep_window_index.tail, old_dep_window_index.head, + dep_window_index.head, *poll_timeout_arg); + +#ifdef notdef + printf ("^^^^^^^^^^^^^^^ adjust_play_window, end\n"); + CYCLIC_PRINT (dep_tab_index); + printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index); + CYCLIC_PRINT (dep_window_index); + CYCLIC_PRINT (memory_trace_index); + printf (" adjust_play_window, end\n\n"); +#endif + //CYCLIC_ASSERT(4); +} + +/* poll for usecs and receive, after receive one reply, + * return index in biod_reqp of the corresponding request + */ +int poll_and_get_reply (int usecs) +{ + int biod_index = -1; + int xid; + int error; + struct timeval zero_time = {0, 0}; /* Immediately return */ + +#ifdef RECV_THREAD + //printf("recv thread waitsem 1\n"); + waitsem (async_rpc_sem); + //printf("recv thread got sem 1\n"); +#endif + do { + error = biod_poll_wait (NFS_client, usecs); + switch (error) { + case -1: + if (errno == EINTR) { + error = 1; + continue; + } + if (rfs_debug) { + (void) fprintf(stderr, "biod_poll_wait error\n"); + perror (""); + (void) fflush(stderr); + } + break; + case 0: + break; + default: +#ifdef UDP + //printf("recv thread waitsem 2\n"); + //waitsem (async_rpc_sem); + //printf("recv thread got sem 2\n"); + error = get_areply_udp (NFS_client, &xid, &zero_time); + //postsem (async_rpc_sem); + //printf("recv thread postsem 2\n"); + // RFS_ASSERT (error!= RPC_TIMEOUT); /* we have polled and know there is data */ + // RFS_ASSERT (error!= RPC_CANTRECV); + RFS_ASSERT (error == RPC_SUCCESS); + + biod_index = lookup_biod_req (xid); + sfs_gettime (&(biod_reqp[biod_index].stop)); +#else + RFS_ASSERT (0); +#endif + } + } while (0); +#ifdef RECV_THREAD + postsem (async_rpc_sem); + //printf("recv thread postsem 1\n"); +#endif + return biod_index; +} + +void print_result(void) +{ + int i, j; + struct ladtime t; + int dep_index; + int avg_msecs; + unsigned long long tmp; + int avg_usecs; + + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stdout, "trace play result:\n"); + (void) fprintf(stdout, "\t percentage good_cnt bad_cnt timeout_cnt\telapsed time\t\t\taverage time\n"); + for (i=0; i<NOPS+1; i++) { + if (Ops[i].results.good_calls==0) { + avg_msecs = 0; + avg_usecs = 0; + } else { + tmp = Ops[i].results.time.sec*1000000 + Ops[i].results.time.usec; + avg_msecs = 0; + avg_usecs = tmp/Ops[i].results.good_calls; +/* + avg_msecs = (Ops[i].results.time.sec*1000 + Ops[i].results.time.usec/1000)/Ops[i].results.good_calls; + avg_usecs = (Ops[i].results.time.usec%1000)/Ops[i].results.good_calls; +*/ + } + (void) fprintf(stdout, "%11s\t%4.1f\t%4d\t%4d\t%4d\t\tsec %8d usec %8d \tusec %8d\n", + Ops[i].name, + (float)(100*Ops[i].results.good_calls)/(float)Ops[TOTAL].results.good_calls, + Ops[i].results.good_calls, Ops[i].results.bad_calls, Ops[i].results.timeout_calls, + Ops[i].results.time.sec, Ops[i].results.time.usec, avg_msecs*1000+avg_usecs); + } + (void) fflush (stdout); + } + +#if 0 /* commented out by G. Jason Peng */ + RFS_ASSERT (read_data_owe_GB==0); + printf("read_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",read_data_total_GB, read_data_total, read_data_owe_GB, read_data_owe, (read_data_owe)/(read_data_total/100), read_data_adjust_times); + printf("write_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",write_data_total_GB, write_data_total, write_data_owe_GB, write_data_owe, (write_data_owe)/(write_data_total/100), write_data_adjust_times); + printf("poll_timeout_0_num %d poll_timeout_pos_num %d\n", poll_timeout_0_num, poll_timeout_pos_num); + printf("failed_create_command_num_in_original_trace %d\nfailed_other_command_num_in_original_trace %d\nskipped_readlink_command_num %d\nskipped_custom_command_num %d\nfh_path_map_err_num %d\nskipped_fsstat_command_num %d\nmissing_reply_num %d\nrename_rmdir_noent_reply_num %d\nrmdir_not_empty_reply_num %d\nloose_access_control_reply_num %d\nlookup_err_due_to_rename %d\nlookup_err_due_to_parallel_remove %d\nlookup_eaccess_enoent_mismatch %d\nread_io_err_num %d\nstale_fhandle_err_num %d abnormal_EEXIST_num %d abnormal_ENOENT_num %d proper_reply_num %d run_stage_proper_reply_num %d\n", + failed_create_command_num, + failed_other_command_num, + skipped_readlink_command_num, + skipped_custom_command_num, + fh_path_map_err_num, + skipped_fsstat_command_num, + missing_reply_num, + rename_rmdir_noent_reply_num, + rmdir_not_empty_reply_num, + loose_access_control_reply_num, + lookup_err_due_to_rename_num, + lookup_err_due_to_parallel_remove_num, + lookup_eaccess_enoent_mismatch_num, + read_io_err_num, + stale_fhandle_err_num, + abnormal_EEXIST_num, + abnormal_ENOENT_num, + proper_reply_num, run_stage_proper_reply_num); +#endif + +// print_dump(Client_num, Child_num); +} + +/* + * allocate and initialize client handles + */ +static int +init_rpc(void) +{ + int dummy = 0; + + /* + * Set up the client handles. We get them all before trying one + * out to insure that the client handle for LOOKUP class is allocated + * before calling op_getattr(). + */ + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname); + } + + NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent, + (uint32_t) NFS_PROGRAM, + (uint32_t) nfs_version, + RPC_ANYSOCK, &Nfs_timers[0]); + + if (NFS_client == ((CLIENT *) NULL)) { + return(-1); + } + + /* + * create credentials using the REAL uid + */ + NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid, + (int)Cur_gid, 0, NULL); + + if (biod_init(dummy, dummy) == -1) { + return(-1); + } + + return(0); +} /* init_rpc */ + +void +init_counters(void) +{ + uint_t i; + uint_t start_msec; + + /* Ready to go - initialize operation counters */ + for (i = 0; i < NOPS + 1; i++) { + Ops[i].req_cnt = 0; + Ops[i].results.good_calls = 0; + Ops[i].results.bad_calls = 0; + Ops[i].results.timeout_calls = 0; // RFS + Ops[i].results.fast_calls = 0; + Ops[i].results.time.sec = 0; + Ops[i].results.time.usec = 0; + Ops[i].results.msec2 = 0; + } + + /* initialize timers and period variables */ + sfs_gettime(&Starttime); + Cur_time = Starttime; + start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000); + Previous_chkpnt_msec = start_msec; + Calls_this_period = 0; + Reqs_this_period = 0; + Sleep_msec_this_period = 0; + Calls_this_test = 0; + Reqs_this_test = 0; + Sleep_msec_this_test = 0; +} + +static char * +nfs3_strerror(int status) +{ + static char str[40]; + switch (status) { + case NFS3_OK: + (void) strcpy(str, "no error"); + break; + case NFS3ERR_PERM: + (void) strcpy(str, "Not owner"); + break; + case NFS3ERR_NOENT: + (void) strcpy(str, "No such file or directory"); + break; + case NFS3ERR_IO: + (void) strcpy(str, "I/O error"); + break; + case NFS3ERR_NXIO: + (void) strcpy(str, "No such device or address"); + break; + case NFS3ERR_ACCES: + (void) strcpy(str, "Permission denied"); + break; + case NFS3ERR_EXIST: + (void) strcpy(str, "File exists"); + break; + case NFS3ERR_XDEV: + (void) strcpy(str, "Cross-device link"); + break; + case NFS3ERR_NODEV: + (void) strcpy(str, "No such device"); + break; + case NFS3ERR_NOTDIR: + (void) strcpy(str, "Not a directory"); + break; + case NFS3ERR_ISDIR: + (void) strcpy(str, "Is a directory"); + break; + case NFS3ERR_INVAL: + (void) strcpy(str, "Invalid argument"); + break; + case NFS3ERR_FBIG: + (void) strcpy(str, "File too large"); + break; + case NFS3ERR_NOSPC: + (void) strcpy(str, "No space left on device"); + break; + case NFS3ERR_ROFS: + (void) strcpy(str, "Read-only file system"); + break; + case NFS3ERR_MLINK: + (void) strcpy(str, "Too many links"); + break; + case NFS3ERR_NAMETOOLONG: + (void) strcpy(str, "File name too long"); + break; + case NFS3ERR_NOTEMPTY: + (void) strcpy(str, "Directory not empty"); + break; + case NFS3ERR_DQUOT: + (void) strcpy(str, "Disc quota exceeded"); + break; + case NFS3ERR_STALE: + (void) strcpy(str, "Stale NFS file handle"); + break; + case NFS3ERR_REMOTE: + (void) strcpy(str, "Object is remote"); + break; + case NFS3ERR_BADHANDLE: + (void) strcpy(str, "Bad file handle"); + break; + case NFS3ERR_NOT_SYNC: + (void) strcpy(str, "Not sync write"); + break; + case NFS3ERR_BAD_COOKIE: + (void) strcpy(str, "Bad cookie"); + break; + case NFS3ERR_NOTSUPP: + (void) strcpy(str, "Operation not supported"); + break; + case NFS3ERR_TOOSMALL: + (void) strcpy(str, "Value too small"); + break; + case NFS3ERR_SERVERFAULT: + (void) strcpy(str, "Server fault"); + break; + case NFS3ERR_BADTYPE: + (void) strcpy(str, "Bad type"); + break; + case NFS3ERR_JUKEBOX: + (void) strcpy(str, "Jukebox"); + break; + case NFS3ERR_RFS_TIMEOUT: + (void) strcpy(str, "Timeout"); + break; + default: + (void) sprintf(str, "Unknown status %d", status); + break; + } + return (str); +} + +/* + * Check the gettimeofday() resolution. If the resolution + * is in chunks bigger than SFS_MIN_RES then the client + * does not have a usable resolution for running the + * benchmark. + */ +static void +check_clock(void) +{ + double time_res; + char tmp_hostname[HOSTNAME_LEN]; + + time_res = get_resolution(); + getmyhostname(tmp_hostname, HOSTNAME_LEN); + if( time_res > (double)SFS_MIN_RES ) + { + (void) fprintf(stderr, + "\n%s: Clock resolution too poor to obtain valid results.\n", + tmp_hostname); + (void) fprintf(stderr, + "%s: Clock resolution %f Micro seconds.\n", tmp_hostname, + time_res); + exit(175); + } + else + { + (void) fprintf(stderr, + "\n%s: Good clock resolution [ %f ] Micro seconds.\n", + tmp_hostname, time_res); + } +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns the resolution of the gettimeofday() function + * in microseconds. + */ +static double +get_resolution(void) +{ + double starttime, finishtime, besttime; + long j,delay; + int k; + + finishtime=time_so_far1(); /* Warm up the instruction cache */ + starttime=time_so_far1(); /* Warm up the instruction cache */ + delay=j=0; /* Warm up the data cache */ + for(k=0;k<10;k++) + { + while(1) + { + starttime=time_so_far1(); + for(j=0;j< delay;j++) + ; + finishtime=time_so_far1(); + if(starttime==finishtime) + delay++; + else + { + if(k==0) + besttime=(finishtime-starttime); + if((finishtime-starttime) < besttime) + besttime=(finishtime-starttime); + break; + } + } + } + return(besttime); +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns current result of gettimeofday() in microseconds. + */ +/************************************************************************/ +/* Time measurement routines. */ +/* Return time in microseconds */ +/************************************************************************/ + +static double +time_so_far1(void) +{ + /* For Windows the time_of_day() is useless. It increments in 55 */ + /* milli second increments. By using the Win32api one can get */ + /* access to the high performance measurement interfaces. */ + /* With this one can get back into the 8 to 9 microsecond */ + /* resolution. */ +#ifdef Windows + LARGE_INTEGER freq,counter; + double wintime; + double bigcounter; + + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&counter); + bigcounter=(double)counter.HighPart *(double)0xffffffff + + (double)counter.LowPart; + wintime = (double)(bigcounter/(double)freq.LowPart); + return((double)wintime*1000000.0); +#else +#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5) + struct timespec gp; + + if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1) + perror("getclock"); + return (( (double) (gp.tv_sec)*1000000.0) + + ( ((float)(gp.tv_nsec)) * 0.001 )); +#else + struct timeval tp; + + if (gettimeofday(&tp, (struct timezone *) NULL) == -1) + perror("gettimeofday"); + return ((double) (tp.tv_sec)*1000000.0) + + (((double) tp.tv_usec) ); +#endif +#endif +} + +static void +usage(void) +{ + fprintf(stderr, "trace play usage"); +} +extern void init_file_system (void) +{ + return; +} + +void show_fhandle (nfs_fh3 * fhp) +{ + struct knfs_fh * kfhp = (struct knfs_fh *)fhp; + + int dev; + + if (quiet_flag) + return; + + RFS_ASSERT (kfhp->fh_version == 1); + RFS_ASSERT (kfhp->fh_fsid_type == 0); + RFS_ASSERT (kfhp->fh_auth_type == 0); + + dev = ntohs(kfhp->fh_dev_major); + dev = dev<<8; + dev = dev + ntohs(kfhp->fh_dev_minor); + + /* kfhp->fh_dev_ino hold the inode number of export point of the mounted + * file system. For example, if /tmp/t1 is exported, /tmp/t1/t2 is mounted, + * then fh_dev_ino hold the inode number of t1, not t2 + */ + + switch (kfhp->fh_fileid_type) { + case 0: + printf("fh:type 0 root dev 0x%x dev_ino %d\n", dev, kfhp->fh_dev_ino); + break; + case 1: + printf("fh:type 1 %d %x dev %x dev_ino %x\n", + kfhp->fh_ino, kfhp->fh_generation, dev, kfhp->fh_dev_ino); + break; + case 2: + printf("fh:type2 %d %x dirino %d dev 0x%x dev_ino %x\n", + kfhp->fh_ino, kfhp->fh_generation, kfhp->fh_dirino, dev, kfhp->fh_dev_ino); + break; + default: + RFS_ASSERT (0); + } +} + +nfs_fh3 zero_fhandle; +int init_fh_map () +{ + memset (fh_map, 0, sizeof (fh_map)); + memset(fh_htable, 0, sizeof (fh_htable)); + memset (&zero_fhandle, 0, sizeof(nfs_fh3)); + printf ("SIZE of fh map %d KB\n", sizeof (fh_map)/1000); + fh_i = 0; +} + +int add_fh (int map_flag, char * trace_fh, char * path, nfs_fh3 * play_fh) +{ + char * old_trace_fh; + + /* first lookup if the entry for fh is already in the table */ + struct generic_entry * p; + + p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE); + if (p) { + RFS_ASSERT (fh_map[p->key3].flag = FH_MAP_FLAG_PARTIAL); + RFS_ASSERT (map_flag ==FH_MAP_FLAG_COMPLETE); + fh_map[p->key3].flag = map_flag; + //RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)); + if (memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)) { + int i; + printf ("fh_map[%d].trace_fh %s trace_fh %s", p->key3, fh_map[p->key3].trace_fh, trace_fh); + for (i=0; i<fh_i; i++) { + int * p1 = (int *)&(fh_map[i].play_fh); +#ifdef COMPRESS_TRACE_FH + int * p = (int *)fh_map[i].trace_fh; + printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); +#else + printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, fh_map[i].trace_fh, fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); + } +#endif + RFS_ASSERT (0); + } + RFS_ASSERT (!strcmp(fh_map[p->key3].path, path)); + /* It's possible that in fh-path-map, many trace_fh are corresponding to one path + * some of it may be the result of lookup after symlink, which is not handled + * properly as new created objects + */ +#ifdef TAKE_CARE_SYMBOLIC_LINK + RFS_ASSERT (!memcmp(&fh_map[p->key3].play_fh, &zero_fhandle, sizeof(nfs_fh3))); +#endif + memcpy (&fh_map[p->key3].play_fh, play_fh, sizeof (nfs_fh3)); + if ((fh_map_debug==1)) // || (stage ==TRACE_PLAY_STAGE)) + printf ("update the play_fh for trace_fh %s path %s \n", trace_fh, path); + return 0; + } + + fh_map[fh_i].flag = map_flag; + fh_map[fh_i].lock = 0; + strncpy(fh_map[fh_i].trace_fh, trace_fh, TRACE_FH_SIZE); + + RFS_ASSERT (strlen(path) < MAX_PLAY_PATH_SIZE); + strcpy (fh_map [fh_i].path, path); + if (map_flag==FH_MAP_FLAG_COMPLETE) + memcpy (&fh_map[fh_i].play_fh, play_fh, sizeof(nfs_fh3)); + else + memset (&fh_map[fh_i].play_fh, 0, sizeof(nfs_fh3)); + + if ((fh_map_debug==1)) { // || (stage ==TRACE_PLAY_STAGE)) { + printf ("insert trace_fh %s path %s play_fh:\n", trace_fh, path); + if (map_flag == FH_MAP_FLAG_COMPLETE) { + //show_fhandle(play_fh); + } else + printf("null\n"); + } + +/* + if (map_flag == FH_MAP_FLAG_DISCARD) + printf ("insert flag %d trace_fh %s path %s play_fh:\n", map_flag, trace_fh, path); +*/ + + generic_insert(trace_fh, TRACE_FH_SIZE, fh_i, fh_htable, FH_HTABLE_SIZE); + + fh_i = (fh_i+1); + RFS_ASSERT (fh_i < FH_MAP_SIZE); + + return 0; +}; + +inline fh_map_t * lookup_fh (char * trace_fh ) +{ + struct generic_entry * p; + p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE); + if (fh_map_debug==1) + printf ("lookup trace_fh %s\n", trace_fh); + + if (p) { + if (fh_map_debug==1) { + printf ("found: fh_i[%d] trace_fh %s path %s play_fh:\n", p->key3, fh_map[p->key3].trace_fh, fh_map[p->key3].path); + //show_fhandle(&fh_map[p->key3].play_fh); + } + RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)); + return (&(fh_map[p->key3])); + } else { + //printf ("lookup_fh %s not found\n", trace_fh); + if (stage != READ_DEP_TAB_STAGE && (fh_map_debug==1)) { + printf ("lookup not found trace_fh %s\n", trace_fh); + } + return NULL; + } + RFS_ASSERT (0); +} + +int delete_fh (char * trace_fh, int fh_map_index) +{ + generic_delete (trace_fh, TRACE_FH_SIZE, fh_map_index, fh_htable, FH_HTABLE_SIZE); + return 0; +}; + +int lookup_init_filesystem (nfs_fh3 * parent, char * name, nfs_fh3 * result) +{ + LOOKUP3args args; + LOOKUP3res reply; /* the reply */ + enum clnt_stat rpc_stat; /* result from RPC call */ + struct ladtime start; + struct ladtime stop; + static int i=0; + + /* set up the arguments */ + (void) memcpy((char *) &args.what.dir, (char *) parent, + sizeof (nfs_fh3)); + args.what.name = name; + (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3)); + + /* make the call */ + sfs_gettime(&start); + rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP, + xdr_LOOKUP3args, (char *) &args, + xdr_LOOKUP3res, (char *) &reply, + Nfs_timers[Init]); + sfs_gettime(&stop); + + if (rpc_stat !=RPC_SUCCESS) { + printf("rpc_stat %d\n", rpc_stat); + perror(""); + } + RFS_ASSERT (rpc_stat == RPC_SUCCESS); + (void) memcpy((char *) result, (char *) &reply.resok.object, sizeof (nfs_fh3)); + return (reply.status); +} + +void read_fh_map(char * fh_map_file) +{ + FILE * fp; + int i = 0; + char buf[1024]; + char trace_fh[MAX_TRACE_FH_SIZE]; + char intbuf[9]; + char * trace_path; + char * p; + int map_flag; +#define MAX_PATH_DEPTH 20 + nfs_fh3 parents[MAX_PATH_DEPTH]; + char * lookup_path_ptr[MAX_PATH_DEPTH]; + char lookup_path [MAX_PLAY_PATH_SIZE]; + int depth; + int new_dir_flag = 0; + int lineno = 0; + + depth = 0; + memset(lookup_path_ptr, 0, sizeof(lookup_path_ptr)); + memcpy(&parents[depth], &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3)); + strcpy(lookup_path, "/"); + lookup_path_ptr[depth]=&lookup_path[0]; + + fp = fopen(fh_map_file, "r"); + if (!fp) { + printf ("can not opern %s\n", fh_map_file); + perror("open"); + exit (0); + } + RFS_ASSERT (fp!=NULL); + if (strstr(fh_map_file, "fmt1")) { + TRACE_FH_SIZE = 48; + } + + intbuf[8]=0; + + memset(buf, 0, sizeof(buf)); + while (fgets(buf, 1024, fp)) { + lineno ++; + if (fh_i % 10000==0) + printf("%d fh_map entry read\n", fh_i); + + RFS_ASSERT (buf[strlen(buf)-1]=='\n'); + buf[strlen(buf)-1]=0; + if (fh_map_debug) { + printf("%d fgets return %s\n", fh_i, buf); + printf("depth %d lookup_path %s\n", depth, lookup_path); + } + //for (i=0; i<=depth; i++) + //printf("lookup_path_ptr[%d] %s ", i, lookup_path_ptr[i]); + //printf("\n"); +#ifdef COMPRESS_TRACE_FH + for (i=0; i<TRACE_FH_SIZE/8; i++) { + strncpy(intbuf, buf+i*8, 8); + sscanf(intbuf, "%x", trace_fh+i*8); // maybe it should be 4, anyway we don't compress for now + } + trace_path = buf+TRACE_FH_SIZE*2+1; /* +1 the trace contains only initial file handle */ +#else + memcpy(trace_fh, buf, TRACE_FH_SIZE); + trace_path = buf + TRACE_FH_SIZE +1; +#endif +#ifdef TRACE_CONTAIN_LATER_FHANDLE + trace_path = +=2; /* +3 if the trace contains both initial and later created file handle */ +#endif + +#ifdef NO_DEPENDENCY_TABLE + if (!strncmp (trace_path, "DISCARD", 7) || + !strncmp (trace_path, "LN", 2) ) { + map_flag = FH_MAP_FLAG_DISCARD; + add_fh (map_flag, buf, trace_path, 0); + continue; + } +#endif + + p = trace_path+strlen(trace_path)-2; + while (*p!='/') + p--; + p++; + //RFS_ASSERT (p-trace_path<=strlen(lookup_path)+1); + //RFS_ASSERT (p>trace_path); + + if (strncmp(lookup_path, trace_path, p-trace_path)) { + printf("strncmp lookup_path %s trace_path %s for length %d\n", lookup_path, trace_path, p-trace_path); + } + RFS_ASSERT (!strncmp(lookup_path, trace_path, p-trace_path)); + //while (strncmp(lookup_path, trace_path, p-trace_path)) { /* one step deeper */ + while (strlen(lookup_path)>p-trace_path && depth>0) { + //printf("depth--\n"); + if (depth<=0) + printf ("lookup_path %s trace_path %s p-trace_path %d depth %d\n", lookup_path, trace_path, p-trace_path, depth); + RFS_ASSERT (depth>0); + *lookup_path_ptr[depth]=0; + lookup_path_ptr[depth]=0; + depth--; + } + RFS_ASSERT (strlen(lookup_path)==(p-trace_path) || (depth==0)); + + +#ifdef TRACE_CONTAIN_LATER_FHANDLE + if (buf[TRACE_FH_SIZE*2+1]=='Y') { + map_flag = FH_MAP_FLAG_COMPLETE; + } else { + map_flag = FH_MAP_FLAG_PARTIAL; + RFS_ASSERT (buf[TRACE_FH_SIZE*2+1]=='N'); + } +#else + map_flag = FH_MAP_FLAG_COMPLETE; +#endif + if ((*(p+strlen(p)-1))=='/') { + *(p+strlen(p)-1)=0; + new_dir_flag = 1; + } else + new_dir_flag = 0; + + if (map_flag == FH_MAP_FLAG_COMPLETE) { + int ret = lookup_init_filesystem (&parents[depth], p, &parents[depth+1]); + if (ret!=NFS3_OK) { + printf ("lineno %d %s\n", lineno, buf); + } + RFS_ASSERT (ret == NFS3_OK); + add_fh (map_flag, buf, trace_path, &parents[depth+1]); + } else + add_fh (map_flag, buf, trace_path, 0); + + if (new_dir_flag) { + /* the new fhandle is of a directory */ + lookup_path_ptr[depth+1] = lookup_path+strlen(lookup_path); + strcat (lookup_path, p); + strcat (lookup_path, "/"); + + //printf("depth++\n"); + depth++; + } + + memset(buf, 0, sizeof(buf)); + } + + if (fh_map_debug) { + for (i=0; i<fh_i; i++) { + int * p1 = (int *)&(fh_map[i].play_fh); +#ifdef COMPRESS_TRACE_FH + int * p = (int *)fh_map[i].trace_fh; + printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); +#else + printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n", + i, fh_map[i].trace_fh, fh_map[i].path, + *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7)); + } +#endif + + fprintf(stderr, "total %d requests \n", i); + } +} + +int f() +{ + return 1; +} + +inline free_biod_req (int biod_index) +{ + RFS_ASSERT (biod_reqp[biod_index].in_use == TRUE); + biod_reqp[biod_index].in_use = FALSE; + num_out_reqs --; +} + +void finish_request (int biod_index, int dep_index, int status, int dep_flag) +{ + static int i = 0; + /* the ending operation, same as when a request time out */ + + dep_tab[dep_index].stop = biod_reqp[biod_index].stop; /* RFS: to dump data */ + free_biod_req (biod_index); + + dep_tab[dep_index].status = status; + + if (event_order_index < EVENT_ORDER_SIZE) + event_order[event_order_index++] = -dep_tab[dep_index].disk_index; + + dep_tab[dep_index].flag = dep_flag; + if (is_dir_op(dep_tab[dep_index].proc)) { + int j; + RFS_ASSERT (dep_tab[dep_index].fh->lock = 1); + dep_tab[dep_index].fh->lock = 0; + if (dep_tab[dep_index].proc==RENAME) + dep_tab[dep_index].fh_2->lock = 0; + j = dep_tab[dep_index].fh-fh_map; + if (dependency_debug) { + printf ("fh_map[%d] is UNlocked\n",j); + printf ("trace_fh %d path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path); + printf ("trace_fh %d path %s\n", fh_map[j].trace_fh, fh_map[j].path); + } + } +} + +/* the request argument may have pointers pointing to buffers, e.g. the name in lookup, + * the target of symlink, the write data */ +char arg_res[MAX_ARG_RES_SIZE]; +char buf1 [MAX_BUF1_SIZE]; +char buf2 [MAX_BUF2_SIZE]; + +int execute_next_request () +{ + int dep_index; + int proc; + char * line; + struct biod_req * reqp; + sfs_op_type *op_ptr; /* per operation info */ + struct ladtime call_timeout; + static int last_print_time = -1; + + if (num_out_reqs == max_biod_reqs) { + return -1; + } + + start_profile (&valid_get_nextop_profile); + start_profile (&invalid_get_nextop_profile); + dep_index = get_nextop(); + if (dep_index == -1) { + end_profile (&invalid_get_nextop_profile); + return dep_index; + }; + end_profile (&valid_get_nextop_profile); + + start_profile (&prepare_argument_profile); + line = dep_tab[dep_index].line; + + if (per_packet_debug) + fprintf (stdout, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE); + + end_profile(&total_profile); + if ((total_profile.in.tv_sec - last_print_time >= 10)) { + last_print_time = total_profile.in.tv_sec; + //fprintf (stdout, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE); +/* + CYCLIC_PRINT (dep_tab_index); + { + int tmp = CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size); + printf("dep_tab_index.head-1 %d disk_index %d tail %d disk_index %d\n", tmp, dep_tab[tmp].disk_index, + dep_tab_index.tail, dep_tab[dep_tab_index.tail].disk_index); + } +*/ +#ifdef TIME_PLAY +#ifdef SPEED_UP +/* + if (can_not_catch_speed_num < 2000) { + PLAY_SCALE ++; + printf ("set PLAY_SCALE to %d\n", PLAY_SCALE); + }; + if (can_not_catch_speed_num > 50000) { + PLAY_SCALE /= 2; + } else { + if (can_not_catch_speed_num > 5000) { + PLAY_SCALE -= 2; + if (PLAY_SCALE < 1) + PLAY_SCALE = 1; + } + } +*/ +#endif + if ((total_profile.in.tv_sec > 100)) { + can_not_catch_speed_num_total += can_not_catch_speed_num; + } + can_not_catch_speed_num = 0; +#endif + } + if (rfs_debug) + printf ("processing dep_tab[%d] disk_index %d %s\n", dep_index, dep_tab[dep_index].disk_index, line); + + proc = dep_tab[dep_index].proc; + rfs_Ops[proc].setarg (dep_index, line, arg_res, buf1, buf2); + + op_ptr = &Ops[proc]; + reqp = get_biod_req (dep_index); + RFS_ASSERT (reqp); + +#ifdef notdef /* place to set request timeout. G. Jason Peng */ + call_timeout.sec = 2; //Nfs_timers[op_ptr->call_class].tv_sec; + call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; +#else + call_timeout.sec = 0; + call_timeout.usec = 300000; + //call_timeout.usec = 14000; + //call_timeout.usec = 13000; + //call_timeout.usec = 6000; + //call_timeout.usec = 8000; + //call_timeout.usec = 10000; +#endif + + /* make the call */ + sfs_gettime(&(reqp->start)); + end_profile (&prepare_argument_profile); + start_profile (&biod_clnt_call_profile); +#define REAL_PLAY +#ifdef REAL_PLAY + +#ifdef RECV_THREAD + //printf ("send thread waitsem\n"); + waitsem(async_rpc_sem); + //printf ("send thread got sem\n"); +#endif + reqp->xid = biod_clnt_call(NFS_client, rfs_Ops[proc].nfsv3_proc, + rfs_Ops[proc].xdr_arg, arg_res); +#ifdef RECV_THREAD + postsem(async_rpc_sem); + //printf ("send thread postsem\n"); +#endif + +#else // REAL_PLAY + reqp->xid = dep_index+1; /* just fake a message id and let it expire */ +#endif + RFS_ASSERT (reqp->xid != 0); + reqp->timeout = reqp->start; + ADDTIME (reqp->timeout, call_timeout); + dep_tab[dep_index].flag = DEP_FLAG_SENT; + if (event_order_index < EVENT_ORDER_SIZE) + event_order[event_order_index++] = dep_tab[dep_index].disk_index; + + dep_tab[dep_index].start = reqp->start; /* RFS: to dump data */ + end_profile (&biod_clnt_call_profile); + + send_num ++; +} + +void check_reply (int proc, int biod_index, int dep_index, int status, char * errmsg, int trace_status) +{ + if (((status!=trace_status)) && (status!=NFS3_OK) && (trace_status!=NFS3ERR_RFS_MISS)) { + if (!profile_debug) + printf ("receive problem reply, xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d command disk index %d\n", biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec, dep_tab[dep_index].disk_index); +#ifndef TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES + /* these files is not looked up and is not create/mkdir/symlink/link/mknod ed before they + * are refered by name through rename, remove + */ + if ((proc==RENAME || proc==REMOVE) && (status==NFS3ERR_NOENT) && (trace_status ==0)) { + /* current initialization doesnot take care of rename source, if there is no + * create or lookup before that source, the source object will not exist when + * rename occurs + */ + rename_rmdir_noent_reply_num++; + } else +#endif +#ifndef TAKE_CARE_SYMBOLIC_LINK + if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status==NFS3ERR_NOENT)) { + /* in the original trace, first lookup return NOENT, then symlink is executed, then lookup return OK + * the initialization considers only the lookup return OK and created the file in the initialization + * so in trace play the first lookup return OK + */ + RFS_ASSERT (1); + } else // if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) && (trace_status == 0)) { + /* trace_status could be EAGAIN */ + if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) ) { + /* due to similar reason as above, the initialization code initializes the symbolic link as a normal + * file already + */ + RFS_ASSERT (1); + } else +#endif +#ifndef TAKE_CARE_NOEMPTY_RMDIR + /* the remove packet seems got lost in the trace capture, so replay can not finish */ + if ((proc==RMDIR) && (status==NFS3ERR_NOTEMPTY)) { + RENAME3args args; + RENAME3res reply; /* the reply */ + RMDIR3args * rmdir_argp; + enum clnt_stat rpc_stat; /* result from RPC call */ + + rfs_Ops[proc].setarg (dep_index, dep_tab[dep_index].line, arg_res, buf1, buf2); + rmdir_argp = (RMDIR3args *)arg_res; + + memcpy(&args.from, &(rmdir_argp->object), sizeof (diropargs3)); + memcpy(&args.to.dir, &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3)); + args.from.name = buf1; /* the buf1 is already filled when parsing rmdir */ + args.to.name = buf2; + sprintf(buf2, "rmdir_%d_%s", dep_tab[dep_index].disk_index, rmdir_argp->object.name); + + rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME, + xdr_RENAME3args, (char *) &args, + xdr_RENAME3res, (char *) &reply, + Nfs_timers[Init]); + RFS_ASSERT (rpc_stat == RPC_SUCCESS); + if (reply.status!=NFS3_OK) + printf ("change rmdir into rename, reply.status %d\n", reply.status); + RFS_ASSERT (reply.status==NFS3_OK); + rmdir_not_empty_reply_num ++; +#endif +#ifndef TAKE_CARE_ACCESS_ERROR + } else if ((status==0) && (trace_status==NFS3ERR_ACCES)) { + loose_access_control_reply_num ++; +#endif +#ifdef NO_DEPENDENCY_TABLE + } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3_OK)) { + lookup_err_due_to_rename_num ++; + } else if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status == NFS3ERR_NOENT)) { + /* if there is a remove in front of the lookup, but it is + * actually executed later than the lookup + */ + lookup_err_due_to_parallel_remove_num ++; +#endif +#ifndef TAKE_CARE_LOOKUP_EACCESS_ENOENT_MISMATCH + /* if the looked return EACCESS in the trace, means the object still exists + * should have initialized, right not don't initialize it, hence play status + * could be ENOENT + */ + } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3ERR_ACCES)) { + lookup_eaccess_enoent_mismatch_num ++; +#endif +#ifdef TOLERANT_READ_IO_ERR + } else if ((proc==READ) && (status==NFS3ERR_IO) && (trace_status==NFS3_OK)) { + read_io_err_num ++; +#endif +#ifdef TOLERANT_STALE_FHANDLE_ERR + } else if ((status==NFS3ERR_STALE) && (trace_status==NFS3_OK)) { + printf ("!!!!!!! STALE FILE HANDLE \n"); + //sleep(1); + stale_fhandle_err_num ++; +#endif + } else { + int i; + for (i=dep_window_index.tail; CYCLIC_LESS(dep_window_index,i,dep_window_index.head); i++) { + if (dep_tab[i].flag!=1) + printf ("dep_tab[%d].disk_index %d, flag %d line %s\n", i, dep_tab[i].disk_index, dep_tab[i].flag, dep_tab[i].line); + } + + if (status==EEXIST) { + abnormal_EEXIST_num ++; + } else if (status == ENOENT) { + abnormal_ENOENT_num ++; + } else { + printf ("!!!!!!!!!!!!!1 should fail\n"); + //RFS_ASSERT (0); + } + } + } else { + proper_reply_num ++; + if (total_profile.in.tv_sec >= WARMUP_TIME) + run_stage_proper_reply_num ++; + } + +} + +/* return -1 if there is no reply being received + * return the dep_index if the corresponding reply has been received + */ +int receive_next_reply (int busy_flag) +{ + int dep_index; + int biod_index; + int proc; + char * line; + char * reply_line; + sfs_op_type *op_ptr; /* per operation info */ + int ret; + int status; + int trace_status; + char * errmsg; + int poll_timeout = 0; /* timeout in usecs */ + + /* wait for reply */ + start_profile (&valid_poll_and_get_reply_profile); + start_profile (&invalid_poll_and_get_reply_profile); + + if (busy_flag == BUSY) { + poll_timeout = 0; + poll_timeout_0_num ++; + } else { + poll_timeout = 2000; /* 10000 or 2000 is a better number in non-debugging state */ + //poll_timeout = 0; /* 10000 or 2000 is a better number in non-debugging state */ + poll_timeout_pos_num ++; + } + + biod_index = poll_and_get_reply (poll_timeout); + if (biod_index==-1) { + end_profile (&invalid_poll_and_get_reply_profile); + return -1; + }; + end_profile (&valid_poll_and_get_reply_profile); + + start_profile (&decode_reply_profile); + /* check the corresponding request */ + dep_index = biod_reqp[biod_index].dep_tab_index; + if (biod_reqp[biod_index].in_use==1) { + RFS_ASSERT (dep_tab[dep_index].biod_req_index == biod_index); + } else { + printf ("biod_index %d reply received but the request has been time out\n", biod_index); + return -1; + } + + proc = dep_tab[dep_index].proc; + op_ptr = &Ops[proc]; + + if (dep_tab[dep_index].flag != DEP_FLAG_SENT) { + printf("dep_tab[%d].flag %d proc %d status %d start %d:%d stop %d:%d\n", + dep_index, dep_tab[dep_index].flag, proc, dep_tab[dep_index].status, + dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec, + dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec ); + printf ("received reply for timeout requests dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index); + return dep_index; + } + RFS_ASSERT (dep_tab[dep_index].flag == DEP_FLAG_SENT); + + /* decode the reply */ + rfs_Ops[proc].setres (arg_res, buf1); + ret = proc_header (NFS_client, rfs_Ops[proc].xdr_res, arg_res); + RFS_ASSERT (ret == RPC_SUCCESS); + status = *((int *)arg_res); + errmsg = nfs3_strerror (status); + end_profile (&decode_reply_profile); + + start_profile (&check_reply_profile); + /* compare with the reply in the trace */ + line = dep_tab[dep_index].line; + reply_line = dep_tab[dep_index].reply_line; + trace_status = dep_tab[dep_index].trace_status; + + if (per_packet_debug || rfs_debug ) + fprintf (stdout, "dep_tab[%d], disk_index %d, receive reply, rpc_ret %d xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d \n", dep_index, dep_tab[dep_index].disk_index, ret, biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec); + + /* error checking */ + check_reply (proc, biod_index, dep_index, status, errmsg, trace_status); + + /* free resources */ + finish_request (biod_index, dep_index, status, DEP_FLAG_DONE); + recv_num ++; + + /* we set 100 seconds warm up time */ + if ((total_profile.in.tv_sec >= WARMUP_TIME)) { + /* get statistics */ + if (status == trace_status || (status==NFS3_OK && trace_status==NFS3ERR_RFS_MISS) ) { + op_ptr->results.good_calls++; + Ops[TOTAL].results.good_calls++; + } else { + op_ptr->results.bad_calls++; + Ops[TOTAL].results.bad_calls++; + } + sfs_elapsedtime (op_ptr, &(biod_reqp[biod_index].start), &(biod_reqp[biod_index].stop)); + end_profile (&check_reply_profile); + } + + //start_profile (&add_create_object_profile); + + if (trace_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==SYMLINK || proc==MKNOD)) { +#ifndef REDUCE_MEMORY_TRACE_SIZE + RFS_ASSERT (reply_line); +#endif + if (status!=NFS3_OK) { + /* commented out for 1022 */ + printf ("!!!!!! Should have been an ASSERTION FAILURE \n"); + RFS_ASSERT (proc==SYMLINK); + RFS_ASSERT (0); + } else { + if (proc!=SYMLINK || line[TRACE_VERSION_POS]!='2') + add_new_file_system_object(proc, dep_index, line, reply_line); + } + } + //end_profile (&add_create_object_profile); +} + +inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line) +{ + char * child_trace_fh; + fh_map_t * parent_entryp; + char component_name[MAX_PLAY_PATH_SIZE]; + char * parent_trace_fh; + char child_path[MAX_PLAY_PATH_SIZE]; + post_op_fh3 * post_op_fh3_child; + char * reply_trace_fh; + nfs_fh3 * child_fh3; + + parent_trace_fh = strstr (line, "fh"); + RFS_ASSERT (parent_trace_fh); + parent_trace_fh +=3; + parent_entryp = lookup_fh (parent_trace_fh); + RFS_ASSERT (parent_entryp); + parse_name (parent_trace_fh+65, component_name); + strcpy (child_path, parent_entryp->path); + strcat (child_path, "/"); + strcat (child_path, component_name); + + /* find the corresponding create request */ + //printf ("before find reply trace_fh reply_line %s\n", reply_line); +#ifdef REDUCE_MEMORY_TRACE_SIZE + reply_trace_fh = dep_tab[dep_index].reply_trace_fh; +#else + reply_trace_fh = find_reply_trace_fh (reply_line); +#endif + RFS_ASSERT (reply_trace_fh != NULL); + switch (proc) { + case CREATE: + RFS_ASSERT (((CREATE3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((CREATE3res *)arg_res)->res_u.ok.obj.handle; + break; + case MKDIR: + RFS_ASSERT (((MKDIR3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((MKDIR3res *)arg_res)->res_u.ok.obj.handle; + break; + case SYMLINK: + RFS_ASSERT (((SYMLINK3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((SYMLINK3res *)arg_res)->res_u.ok.obj.handle; + break; + case MKNOD: + RFS_ASSERT (((MKNOD3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE); + child_fh3 = &((MKNOD3res *)arg_res)->res_u.ok.obj.handle; + break; + case LOOKUP: + RFS_ASSERT (proc==LOOKUP); + child_fh3 = &((LOOKUP3res *)arg_res)->res_u.ok.object; + break; + default: + RFS_ASSERT (0); + } +#ifndef REDUCE_MEMORY_TRACE_SIZE + RFS_ASSERT (reply_trace_fh[TRACE_FH_SIZE]==' '); +#endif + reply_trace_fh[TRACE_FH_SIZE] = 0; + add_fh (FH_MAP_FLAG_COMPLETE, reply_trace_fh, child_path, child_fh3); /* exist flag is not used now, set to 1 */ +#ifndef REDUCE_MEMORY_TRACE_SIZE + /* just to preserve the original reply line not changed */ + reply_trace_fh[TRACE_FH_SIZE] = ' '; +#endif +} + +/* initialize timestamp and proc field of dep_tab entry */ +void trace_play(void) +{ + + /* The flag to indicate whether trace_player is BUSY. Trace_player is BUSY + * when either there is request to send or there is reply being + * received. Otherwise it is IDLE. The timeout for polling replies + * is set to 0 when BUSY, it is set to the waiting time to the first + * request outside of current <min_dep_index, dep_window_max> window when IDLE. + */ + int busy_flag = BUSY; + //int dep_index; /* index into dependency table: dep_tab */ + //int biod_index; /* index into outstanding requests: biod_reqp */ + static int last_print_time = -1; + int poll_timeout = 0; + +#ifndef IO_THREAD + disk_io_status = read_trace(); +#endif + + RFS_ASSERT (!CYCLIC_EMPTY(dep_tab_index)); + CYCLIC_MOVE_HEAD(dep_window_index); + + adjust_play_window(busy_flag, &poll_timeout); + + start_profile (&total_profile); + while (!CYCLIC_EMPTY(dep_tab_index)) { + end_profile(&total_profile); + if ((total_profile.in.tv_sec - last_print_time >= 10)) { + int i; + + last_print_time = total_profile.in.tv_sec; + fprintf (stdout, ">>>> sendng thread: time %d send_num %d recv_num %d timeout_num %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, send_num, recv_num, timeout_num, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE); + for (i=0; i<=MAX_OUTSTANDING_REQ; i++) { + if (num_out_reqs_statistics[i]!=0) { + printf("num_out_req[%d]=%d,", i, num_out_reqs_statistics[i]); + num_out_reqs_statistics[i]=0; + } + } + printf("\n"); + for (i=0; i<=MAX_OUTSTANDING_REQ; i++) { + if (num_out_reqs_statistics_at_timeout[i]!=0) { + printf("num_out_req_at_timeout[%d]=%d,", i, num_out_reqs_statistics_at_timeout[i]); + num_out_reqs_statistics_at_timeout[i]=0; + } + } + printf("\n"); + // CYCLIC_PRINT(dep_tab_index); + } + + if ((total_profile.in.tv_sec > 6000)) { + printf ("the process has run for more than 600 seconds, exit\n"); + goto END; + } + + if (busy_flag == IDLE) { +#ifndef RECV_THREAD + //start_profile (&check_timeout_profile); + check_timeout(); + //end_profile (&check_timeout_profile); +#endif +#ifndef IO_THREAD + if (disk_io_status!=TRACE_FILE_END) { + disk_io_status = read_trace(); + }; +#endif + } + + //start_profile (&adjust_play_window_profile); + adjust_play_window (busy_flag, &poll_timeout); + if (rfs_debug) + printf("num_out_reqs %d\n", num_out_reqs); + num_out_reqs_statistics[num_out_reqs]++; + busy_flag = IDLE; + //end_profile (&adjust_play_window_profile); + + start_profile (&execute_next_request_profile); + while (execute_next_request()!=-1) { + busy_flag = BUSY; + } + end_profile (&execute_next_request_profile); + +#ifndef RECV_THREAD + start_profile (&receive_next_reply_profile); + /* actually the performance of two policy seems to be same */ +//#define SEND_HIGHER_PRIORITY_POLICY +#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY + +#ifdef SEND_HIGHER_PRIORITY_POLICY + receive_next_reply(IDLE); +#endif +#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY + busy_flag = IDLE; + while (receive_next_reply(busy_flag)!=-1) + busy_flag = BUSY; +#endif + end_profile (&receive_next_reply_profile); +#endif + CYCLIC_ASSERT (0); + } + end_profile (&total_profile); + + RFS_ASSERT (disk_io_status == TRACE_FILE_END); + if (num_out_reqs !=0 ) { + printf ("num_out_reqs %d\n", num_out_reqs); + CYCLIC_PRINT(dep_tab_index); + } + RFS_ASSERT(num_out_reqs==0); + +END: + printf ("trace starttime %d, trace_end_time %d trace_duration %d\n", trace_timestamp1, trace_timestamp2, + trace_timestamp2 - trace_timestamp1); + printf ("can_not_catch_speed_num_total %d can_not_catch_speed_num_last_10_seconds %d", + can_not_catch_speed_num_total, can_not_catch_speed_num); + printf ("total_profile.about: %s\n", total_profile.about); + print_profile ("total_profile", &total_profile); + printf("\n"); + //print_profile ("check_timeout", &check_timeout_profile); + //printf("\n"); + //print_profile ("adjust_play_window", &adjust_play_window_profile); + //printf("\n"); + print_profile ("execute_next_request_profile", &execute_next_request_profile); + print_profile ("valid_get_nextop_profile", &valid_get_nextop_profile); + print_profile ("invalid_get_nextop_profile", &invalid_get_nextop_profile); + print_profile ("prepare_argument_profile", &prepare_argument_profile); + print_profile ("biod_clnt_call_profile", &biod_clnt_call_profile); + printf("\n"); + print_profile ("receive_next_reply_profile", &receive_next_reply_profile); + print_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile); + print_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile); + print_profile ("decode_reply_profile", &decode_reply_profile); + print_profile ("check_reply_profile", &check_reply_profile); + print_profile ("fgets_profile", &fgets_profile); + print_profile ("read_line_profile", &read_line_profile); + print_profile ("read_trace_profile", &read_trace_profile); + //print_profile ("add_create_object", &add_create_object_profile); + printf("\n"); + + printf ("dep_tab_index.tail %d dep_tab_index.head %d num_out_reqs %d\n", dep_tab_index.tail, dep_tab_index.head, num_out_reqs); +} + + +int CYCLIC_SET_TAIL_TO(cyclic_index_t * index, int dest) +{ + cyclic_index_t indextmp, indextmp2; + int oldnum, num; + indextmp = *index; + indextmp2 = indextmp; + oldnum = CYCLIC_NUM(indextmp); + + if (! ((dest>=0) && (dest<indextmp.size))) { + CYCLIC_PRINT(indextmp); + printf("dest %d\n", dest); + } + RFS_ASSERT ((dest>=0) && (dest<indextmp.size)); + index->tail = dest; + indextmp2.tail = dest; + num = CYCLIC_NUM(indextmp2); + + if (num > oldnum) { + CYCLIC_PRINT(indextmp); + CYCLIC_PRINT(indextmp2); + printf("dest %d old_num %d num %d\n", dest, oldnum, num); + } + RFS_ASSERT (num <= oldnum); +} + +int flush_junk() +{ + int i; + for (i=0; i<500; i++) { + printf ("*************************************************************\n"); + } + fflush(stdout); +} + +int CYCLIC_ASSERT (int i) +{ + int j; + if (!(dep_tab_index.tail == dep_window_index.tail)) { + printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size); + printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size); + printf("pos %d\n", i); + flush_junk(); + sleep (10); + RFS_ASSERT (0); + }; + + if (!((dep_window_index.head == dep_tab_index.head) || + CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head ) )) { + printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size); + printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size); + printf("pos %d\n", i); + flush_junk(); + sleep (10); + RFS_ASSERT (0); + }; + for (i=0, j=0; i<max_biod_reqs; i++) { + if (biod_reqp[i].in_use == 1) + j++; + } +#ifndef RECV_THREAD + RFS_ASSERT (num_out_reqs==j); +#endif +/* + RFS_ASSERT ((dep_window_index.head == dep_tab_index.head) || + CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head )); +*/ +} + +/* sfs_c_chd.c */ diff --git a/TBBT/trace_play/sfs_c_clk.c b/TBBT/trace_play/sfs_c_clk.c new file mode 100644 index 0000000..afe9a44 --- /dev/null +++ b/TBBT/trace_play/sfs_c_clk.c @@ -0,0 +1,309 @@ +#ifndef lint +static char sfs_c_clkSid[] = "@(#)sfs_c_clk.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ---------------------- sfs_c_clk.c --------------------- + * + * Clock handling. Routines to read the clock, measure elapsed + * time and sleep for a timed interval. + * + *.Exported_Routines + * void sfs_gettime(struct ladtime *) + * void sfs_elapsedtime(sfs_op_type *, struct ladtime *, + * struct ladtime *) + * int msec_sleep(int) + * + *.Local_Routines + * None. + * + *.Revision_History + * 05-Jan-92 Pawlowski Added raw data dump hooks. + * 16-Dec-91 Wittle Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <time.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/times.h> + +#include <sys/signal.h> + + +#include <unistd.h> + +#include "sfs_c_def.h" + +#if !(defined(USE_GETTIMEOFDAY) || defined(USE_TIMES)) +#define USE_GETTIMEOFDAY +#endif + +#define CLK_RESOLUTION 10 /* clock resolution in msec */ + +/* + * ------------------------- Clock Handling ------------------------- + */ + +#if defined(USE_GETTIMEOFDAY) +/* + * The time that the test first starts, all times are relative to this + * value to prevent integer overflows. + */ +extern struct ladtime test_start = {0, 0, 0}; + +/* + * Get the time of day, offset by 'tz_ptr', and return it in 'time_ptr'. + * This is a local time of day interface to allow support for system + * dependent clock resolution. + */ +void +sfs_gettime( + struct ladtime *time_ptr) +{ + time_t t; + struct timeval tv; + + (void) gettimeofday(&tv, NULL); + /* + * Use standard time function to get epoch time since 1970 for + * setattr/create. + */ + t = time((time_t *)NULL); + + if (test_start.sec == 0 && test_start.usec == 0) { + test_start.sec = tv.tv_sec; + test_start.usec = 0; + } + time_ptr->sec = tv.tv_sec - test_start.sec; + time_ptr->usec = tv.tv_usec; + + time_ptr->esec = (int32_t)t; +} /* sfs_gettime */ +#endif /* USE_GETTIMEOFDAY */ + +#if defined(USE_TIMES) +static uint32_t test_start = 0; + +void +sfs_gettime( + struct ladtime *time_ptr) +{ + time_t t; + uint32_t clock_ticks; + struct tms tms; + int32_t ticks_per_sec = 100; + + /* + * Try use possible conversions from least accurate to most accurate + */ +#if defined(HZ) + ticks_per_sec = HZ; +#endif /* HZ */ +#if defined(CLK_TCK) + ticks_per_sec = CLK_TCK; +#endif /* CLK_TCK */ +#if defined(_SC_CLK_TCK) + ticks_per_sec = sysconf(_SC_CLK_TCK); +#endif /* _SC_CLK_TCK */ + + if ((clock_ticks = (uint32_t)times(&tms)) == -1) { + (void) fprintf(stderr, "%s: can't get time of day from system: %s\n", + sfs_Myname, strerror(errno)); + (void) generic_kill(0, SIGINT); + exit(175); + } + + /* + * Use standard time function to get epoch time since 1970 for + * setattr/create. + */ + t = time((time_t *)NULL); + + if (test_start == 0) { + test_start = clock_ticks; + } + + time_ptr->sec = (clock_ticks - test_start) / ticks_per_sec; + time_ptr->usec = ((clock_ticks - test_start) - + (time_ptr->sec * ticks_per_sec)) * + (1000000/ticks_per_sec); + time_ptr->esec = (int32_t)t; +} +#endif /* USE_TIMES */ + +/* + * Compute the elapsed time between 'stop_ptr' and 'start_ptr', and + * add the result to the time acculated for operation 'op_ptr'. + */ +void +sfs_elapsedtime( + sfs_op_type * op_ptr, + struct ladtime * start_ptr, + struct ladtime * stop_ptr) +{ + double msec2; + struct ladtime time_ptr1, time_ptr2; + + /* get the elapsed time */ + if (stop_ptr->usec >= 1000000) { + stop_ptr->sec += (stop_ptr->usec / 1000000); + stop_ptr->usec %= 1000000; + } + + if (stop_ptr->usec < start_ptr->usec) { + stop_ptr->usec += 1000000; + stop_ptr->sec--; + } + + if (stop_ptr->sec < start_ptr->sec) { + stop_ptr->sec = 0; + stop_ptr->usec = 0;; + } else { + stop_ptr->usec -= start_ptr->usec; + stop_ptr->sec -= start_ptr->sec; + } + + /* count ops that take zero time */ + if ((stop_ptr->sec == 0) && (stop_ptr->usec == 0)) + op_ptr->results.fast_calls++; + + /* add the elapsed time to the total time for this operation */ + + time_ptr1 = op_ptr->results.time; + time_ptr2 = *stop_ptr; + + ADDTIME(time_ptr1, time_ptr2); + + op_ptr->results.time = time_ptr1; + stop_ptr = &time_ptr2; + + /* square the elapsed time */ + msec2 = (stop_ptr->sec * 1000.0) + (stop_ptr->usec / 1000.0); + msec2 *= msec2; + + /* add the squared elapsed time to the total of squared elapsed time */ + op_ptr->results.msec2 += msec2; + + /* + * Log this point if logging is on. The (op_ptr - Ops) + * calculation is a baroque way of deriving the "NFS Operation + * code" we just executed--given available information. + * + * stop_ptr at this time contains the *elapsed* time, or + * response time of the operation. + */ + log_dump(start_ptr, stop_ptr, op_ptr - Ops); + +} /* sfs_elapsedtime */ + +long cumulative_resets; +long cumulative_adjusts; +long msec_calls; +/* + * Use select to sleep for 'msec' milliseconds. + * Granularity is CLK_RESOLUTION msec. + * Return the amount we actually slept. + */ +int +msec_sleep( + int msecs) +{ + int actual_msecs; + static long cumulative_error_msecs = 0; + int select_msecs; + struct timeval sleeptime; + int Saveerrno; + struct ladtime start; + struct ladtime end; + + sfs_gettime(&start); + + select_msecs = msecs + cumulative_error_msecs; + if (select_msecs < 0) { + sleeptime.tv_sec = 0; + sleeptime.tv_usec = 0; + } else { + sleeptime.tv_sec = select_msecs / 1000; + sleeptime.tv_usec = (select_msecs % 1000) * 1000; + } + + /* sleep */ + if (select(0, NULL, NULL, NULL, &sleeptime) == -1) { + if (errno != EINTR) { + Saveerrno = errno; + (void) fprintf(stderr, "%s: select failed ", + sfs_Myname); + errno = Saveerrno; + perror("select"); + (void) generic_kill(0, SIGINT); + exit(176); + } + } + sfs_gettime(&end); + + SUBTIME(end, start); + actual_msecs = ((end.sec * 1000) + (end.usec / 1000)); + + cumulative_error_msecs += (msecs - actual_msecs); + if(cumulative_error_msecs > 100 || + cumulative_error_msecs < -100) /* Clip tops */ + { + cumulative_error_msecs = 0; + cumulative_resets++; + } + else + { + if(cumulative_error_msecs != 0) + cumulative_adjusts++; + } + msec_calls++; + return actual_msecs; +} /* msec_sleep */ + + +/* sfs_c_clk.c */ diff --git a/TBBT/trace_play/sfs_c_clnt.c b/TBBT/trace_play/sfs_c_clnt.c new file mode 100644 index 0000000..6c18e0d --- /dev/null +++ b/TBBT/trace_play/sfs_c_clnt.c @@ -0,0 +1,108 @@ +#ifndef lint +static char sfs_c_clntSid[] = "@(#)sfs_c_clnt.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + +#if !defined(_XOPEN_SOURCE) +#include <sys/socket.h> +#else +#define AF_INET 2 +#endif + +CLIENT * +lad_clnt_create(int prot, struct hostent *hostent, uint32_t program, + uint32_t version, int sock, struct timeval *wait) +{ + struct sockaddr_in sin; + CLIENT *client_ptr; + uint_t sendsz = 0; + uint_t recvsz = 0; + + /* set up the socket address for the remote call */ + (void) memset((char *) &sin, '\0', sizeof(sin)); + (void) memmove((char *) &sin.sin_addr, + hostent->h_addr, + hostent->h_length); + sin.sin_family = AF_INET; + + switch (prot) { + case 0: /* UDP */ + if (sendsz == 0) + client_ptr = sfs_cudp_create(&sin, program, version, + *wait, &sock); + else + client_ptr = sfs_cudp_bufcreate(&sin, program, version, + *wait, &sock, + sendsz, recvsz); + break; + case 1: /* TCP */ + sendsz = NFS_MAXDATA + 1024; + recvsz = NFS_MAXDATA + 1024; + + client_ptr = sfs_ctcp_create(&sin, program, version, &sock, + sendsz, recvsz); + break; + } + + if (client_ptr == ((CLIENT *) NULL)) { + char buf[128]; + + (void) sprintf(buf, "%s: server not responding", + sfs_Myname); + clnt_pcreateerror(buf); + return((CLIENT *) NULL); + } + + return (client_ptr); +} diff --git a/TBBT/trace_play/sfs_c_dat.c b/TBBT/trace_play/sfs_c_dat.c new file mode 100644 index 0000000..a5fc22e --- /dev/null +++ b/TBBT/trace_play/sfs_c_dat.c @@ -0,0 +1,339 @@ +#ifndef lint +static char sfs_c_datSid[] = "@(#)sfs_c_dat.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * + * ------------------------- sfs_c_dat.c -------------------------- + * + * Space declarations for sfs. + * + *.Revision_History + * 11-Jul-94 ChakChung Ng Add codes for NFS/v3 + * 24-Aug-92 Wittle New file set access. + * 05-Jan-92 Pawlowski Added hooks for raw data dump. + * 04-Dec-91 Bruce Keith Include string.h for SYSV/SVR4. + * 17-May-90 Richard Bean Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + + +/* + * ------------------------- NFS Operations ------------------------- + */ + +/* + * RPC timeout values by call type. + * Index'ed by #defines in sfs_def.h. + */ +struct timeval *Nfs_timers; + +struct timeval Nfs_udp_timers[] = { + + /* secs usecs */ + { 10, 0 }, /* All commands during initialization phase */ + { 1, 0 }, /* Lookup during warmup and test run phases */ + { 2, 0 }, /* Read during warmup and test run phases */ + { 3, 0 }, /* Write during warmup and test run phases */ +}; + +/* + * TCP is a "reliable" protocol so timeouts should never occur. Set + * the values to be large enough to ensure all servers will respond + * but not too large so a broken implementation will still complete. + */ +struct timeval Nfs_tcp_timers[] = { + + /* secs usecs */ + { 60, 0 }, /* All commands during initialization phase */ + { 60, 0 }, /* Lookup during warmup and test run phases */ + { 60, 0 }, /* Read during warmup and test run phases */ + { 60, 0 }, /* Write during warmup and test run phases */ +}; + +/* + * ----------------------- Transfer Size Distribution ----------------------- + * + * Over-the-wire transfer sizes are divided into 2 cases: read and write. + * For each case, a percentile rank determines the basic size unit of the + * transfer which is multiplied by a count to give the total size for the + * percentile. + * + * The block transfer size distribution is specified by a table of values. + * The first column gives the percent of operations that will be a + * specific block transfer size. The second column gives the number of + * blocks units that will be transferred. Normally the block unit size + * is 8KB. The third column is a boolean specifying whether a trailing + * fragment block should be transferred. The fragment size for each transfer + * is a random multiple of 1 KB, up to the block size - 1 KB. Two tables + * are needed, one for read operation and one for write operations. The + * following table gives the default distributions. + * + * Read - Default Block Transfer Size Distribution Table + * percent block count fragment resulting transfer (8KB block size) + * ------- ----------- -------- ----------------------------------- + * 0 0 0 0% 1 - 7 KB + * 85 1 0 85% 9 - 15 KB + * 8 2 1 8% 17 - 23 KB + * 4 4 1 4% 33 - 39 KB + * 2 8 1 2% 65 - 71 KB + * 1 16 1 1% 129 - 135 KB + * + * Write - Default Block Transfer Size Distribution Table + * percent block count fragment resulting transfer (8KB block size) + * ------- ----------- -------- ----------------------------------- + * 49 0 1 49% 1 - 7 KB + * 36 1 1 36% 9 - 15 KB + * 8 2 1 8% 17 - 23 KB + * 4 4 1 4% 33 - 39 KB + * 2 8 1 2% 65 - 71 KB + * 1 16 1 1% 129 - 135 KB + * + * The user may specify a a different distribution by using the '-b' option. + * The format for the block size distribution file consists of the first + * three columns given above: percent, block count, and fragment. Read + * and write distribution tables are identified by the keywords "Read" and + * "Write". An example input file, using the default values, is given below: + * + * Read + * 0 0 0 + * 85 1 0 + * 8 2 1 + * 4 4 1 + * 2 8 1 + * 1 16 1 + * Write + * 49 0 1 + * 36 1 1 + * 8 2 1 + * 4 4 1 + * 2 8 1 + * 1 16 1 + * + * A second parameter controlled by the block transfer size distribution table + * is the network transport packet size. The distribution tables define the + * relative proportion of full blocks packets to fragment packets. For + * instance, the default tables have been constructed to produce a specific + * distribution of ethernet packet sizes for i/o operations by controlling + * the amount of data in each packet. The write packets produced consist + * of 50% 8-KB packets, and 50% 1-7 KB packets. The read packets consist + * of 85% 8-KB packets, and 15% 1-7 KB packets. These figures are + * determined by multiplying the percentage for the type of transfer by + * the number of blocks and fragments generated, and adding the totals. + * These conmputations are performed below for the default block size + * distribution tables: + * + * Read blocks fragments + * 0 0 0 0 0 + * 85 1 0 85 0 + * 8 2 1 16 8 + * 4 4 1 16 4 + * 2 8 1 16 2 + * 1 16 1 16 1 + * --- --- + * 149 (90%) 15 (10%) + * + * Write + * 49 0 1 0 49 + * 36 1 1 36 36 + * 8 2 1 16 8 + * 4 4 1 16 4 + * 2 8 1 16 2 + * 1 16 1 16 1 + * --- --- + * 100 (50%) 100 (50%) + * + * + */ +static sfs_io_op_dist_type Default_read_size_dist[] = { + /* percentile 8KB xfers fragments */ + { 0, 0, 0 }, /* 0% 1 - 7 KB */ + { 85, 1, 0 }, /* 85% 8 KB */ + { 93, 2, 1 }, /* 8% 17 - 23 KB */ + { 97, 4, 1 }, /* 4% 33 - 39 KB */ + { 99, 8, 1 }, /* 2% 65 - 71 KB */ + { 100, 16, 1 }, /* 1% 129 - 135 KB */ +}; + +static sfs_io_op_dist_type Default_write_size_dist[] = { + /* percentile 8KB xfers fragments */ + { 49, 0, 1 }, /* 49% 1 - 7 KB */ + { 85, 1, 1 }, /* 36% 9 - 15 KB */ + { 93, 2, 1 }, /* 8% 17 - 23 KB */ + { 97, 4, 1 }, /* 4% 33 - 39 KB */ + { 99, 8, 1 }, /* 2% 65 - 71 KB */ + { 100, 16, 1 }, /* 1% 129 - 135 KB */ +}; + +static sfs_io_dist_type Default_io_dist = { + Default_read_size_dist, /* read size distribution */ + Default_write_size_dist, /* write size distribution */ + 17, /* max file size in Block_size units */ + 1.64, /* average read ops / request */ + 2 /* average write ops / request */ +}; + +sfs_io_file_size_dist Default_file_size_dist[] = { + /* percentage KB size */ + { 33, 1}, /* 33% */ + { 54, 2}, /* 21% */ + { 67, 4}, /* 13% */ + { 77, 8}, /* 10% */ + { 85, 16}, /* 8% */ + { 90, 32}, /* 5% */ + { 94, 64}, /* 4% */ + { 97, 128}, /* 3% */ + { 99, 256}, /* 2% */ + { 100, 1024}, /* 1% */ + { 0, 0} +}; + +/* + * ------------------------- Remote File Information ------------------------- + */ +int Num_io_file_sizes; /* # of different size of files */ +int Num_io_files; /* # of files used for i/o */ +int Num_non_io_files; /* # of non-i/o regular files */ +int Num_dirs; /* # of pre-created directories */ +int Num_dir_files; /* # of directories */ +int Num_symlinks; /* # of pre-created symlinks */ +int Num_symlink_files; /* # of symlinks */ + +int Num_working_io_files; /* # of i/o files in working set */ +int Num_working_non_io_files; /* # of non i/o files in working set */ +int Num_working_dirs; /* # of dirs in working set */ +int Num_working_symlinks; /* # of symlinks in working set */ +int files_per_megabyte; /* # of files created of each MB */ + + +sfs_io_dist_type *Io_dist_ptr= /* block transfer distribution info */ + &Default_io_dist; + +sfs_fh_type *Io_files; /* list of i/o files */ +sfs_fh_type *Non_io_files; /* list of non-i/o files */ +sfs_fh_type *Dirs; /* list of directories */ +sfs_fh_type *Symlinks; /* list of symlinks */ + +sfs_fh_type *Cur_file_ptr; /* current file */ +char Cur_filename[SFS_MAXNAMLEN]; /* current dir entry name */ +char Filespec[SFS_MAXNAMLEN] /* sprintf spec for file names */ + = "file_en.%05d"; +char Dirspec[SFS_MAXNAMLEN] /* sprintf spec for directory names */ + = "dir_ent.%05d"; +char Symspec[SFS_MAXNAMLEN] /* sprintf spec for symlink names */ + = "symlink.%05d"; + +/* + * ------------------------- Run Parameters ------------------------- + */ + +int nfs_version; +sfs_phase_type Current_test_phase; /* current phase of the test */ + +sfs_fh_type Export_dir; /* filehandle for exported fs */ +CLIENT * NFS_client; /* RPC client handle */ +CLIENT * NFS_client_recv; /* RPC client handle used for recv_thread */ + +bool_t Timed_run = TRUE; /* Timed run or call target ? */ +int Runtime = DEFAULT_RUNTIME; /* seconds in benchmark run */ +int Warmuptime = DEFAULT_WARMUP; /* seconds to warmup */ +int Access_percent = DEFAULT_ACCESS; /* % of file set to access */ +int Append_percent = DEFAULT_APPEND; /* % of writes that append */ +int Fss_delta_percent = DEFAULT_DELTA_FSS; /* allowed change to file set */ +int Kb_per_block = DEFAULT_KB_PER_BLOCK; /* i/o pkt block sz in KB */ +int Bytes_per_block = DEFAULT_KB_PER_BLOCK * 1024;/* i/o pkt sz in bytes */ +int Num_failed_lookup = DEFAULT_FAILED_LOOKUP; /* percent failed lookups */ +int Testop = -1; /* test mode operation number */ +int Interactive = 0; /* test synchronized by user*/ +int Populate_only = 0; /* populate test dirs & quit */ +int Debug_level = 0xFFFF; /* debugging switch */ + + +/* + * --------------------- Biod Simulation Variables --------------------- + */ + +int Biod_max_outstanding_writes = DEFAULT_BIOD_MAX_WRITE; +int Biod_max_outstanding_reads = DEFAULT_BIOD_MAX_READ; + + +/* + * ------------------- File Set Size Control ------------------------- + */ + +int avg_bytes_per_file = 136*1024; /* calculated average file size */ +int Base_fss_bytes = 0; /* base file set size in bytes */ +int Most_fss_bytes = 0; /* most bytes ever in file set */ +int Least_fss_bytes = 0; /* least bytes ever in file set */ +int Limit_fss_bytes = 0; /* target upper limit on fileset size */ +int Cur_fss_bytes = 0; /* bytes currently in file set */ +int Total_fss_bytes = 0; /* Total bytes created */ + + +/* + * ------------- Per Child Load Generation Rate Variables ----------- + */ + +int Msec_per_period; /* total msec during the current run period */ + + +/* + * ------------------------- Miscellaneous ------------------------- + */ + +struct ladtime Cur_time; /* current time of day */ +struct ladtime Starttime; /* start of test */ +int start_run_phase = 0; + +char lad_hostname[HOSTNAME_LEN]; +/* sfs_c_dat.c */ diff --git a/TBBT/trace_play/sfs_c_def.h b/TBBT/trace_play/sfs_c_def.h new file mode 100644 index 0000000..e35e73d --- /dev/null +++ b/TBBT/trace_play/sfs_c_def.h @@ -0,0 +1,634 @@ +#ifndef _sfs_c_def_h +#define _sfs_c_def_h + +#define RFS +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * @(#)sfs_c_def.h 2.1 97/10/23 + * + * -------------------------- sfs_c_def.h ----------------------- + * + * Literals and types for the sfs program. + * + *.Revision_History + * Wittle 24-Aug-92 - New file set access code. + * Bruce Keith 04-Dec-91 - Fix SVR4 definitions for NGROUPS. + * - Include param.h/limits.h as + * appropriate (BSD/SVR4). + * - Use bcopy, bzero, bcmp for BSD; + * memcpy, memset, memcmp for SYSV. + * Richard Bean 17-May-90 Created. + */ + +#include <sys/stat.h> + +#include <limits.h> + +#if !defined(_XOPEN_SOURCE) +/* + * Some non-XOPEN_compilent systems are missing these definitions + */ +#if !defined(SVR4) /* Assume BSD */ +#include <sys/param.h> +#endif /* SVR4 */ +#endif /* !_XOPEN_SOURCE */ + +#include <sys/time.h> + +#if defined(SVR4) +#include <sys/select.h> +#endif + +#include "rpc/rpc.h" + +#if (defined (SVR4) || defined (AIX)) +#include <netinet/in.h> +#endif + +#include <netdb.h> + +#define SFS_MIN_RES 100 /* 100 usec resolution */ +#define SFS_MAXNAMLEN 32 /* max test file name length */ +#define SFS_MAXPATHLEN 1024 /* max test path name length */ +#define HOSTNAME_LEN 31 /* length of client's hostname */ + +#include "sfs_c_nfs.h" + +/* -------------------------- Constants ----------------------- */ + +#define SFS_VERSION_DATE "11 July 2001" +#define SFS_VERSION_NUM "3.0" + +/* + * Debugging levels + */ +#define CHILD_TO_DEBUG 0 /* per child debugging uses child 0 */ + +#define DEBUG_NEW_CODE (Debug_level & 0x00000001) /* 1 */ +#define DEBUG_PARENT_GENERAL (Debug_level & 0x00000002) /* 2 */ +#define DEBUG_PARENT_SIGNAL (Debug_level & 0x00000004) /* 3 */ +#define DEBUG_CHILD_ERROR (Debug_level & 0x00000008) /* 4 */ +#define DEBUG_CHILD_SIGNAL (Debug_level & 0x00000010) /* 5 */ +#define DEBUG_CHILD_XPOINT (Debug_level & 0x00000020) /* 6 */ +#define DEBUG_CHILD_GENERAL ((Debug_level & 0x00000040) && \ + (Child_num == CHILD_TO_DEBUG)) /* 7 */ +#define DEBUG_CHILD_OPS ((Debug_level & 0x00000080) && \ + (Child_num == CHILD_TO_DEBUG)) /* 8 */ +#define DEBUG_CHILD_FILES ((Debug_level & 0x00000100) && \ + (Child_num == CHILD_TO_DEBUG)) /* 9 */ +#define DEBUG_CHILD_RPC ((Debug_level & 0x00000200) && \ + (Child_num == CHILD_TO_DEBUG)) /* 10 */ +#define DEBUG_CHILD_TIMING ((Debug_level & 0x00000400) && \ + (Child_num == CHILD_TO_DEBUG)) /* 11 */ +#define DEBUG_CHILD_SETUP ((Debug_level & 0x00000800) && \ + (Child_num == CHILD_TO_DEBUG)) /* 12 */ +#define DEBUG_CHILD_FIT ((Debug_level & 0x00001000) && \ + (Child_num == CHILD_TO_DEBUG)) /* 13 */ +#define DEBUG_CHILD_BIOD ((Debug_level & 0x00002000) && \ + (Child_num == CHILD_TO_DEBUG)) /* 14 */ + + +/* + * General constants for benchmark + */ +#define DEFAULT_LOAD 60 /* calls per sec */ +#define DEFAULT_CALLS 5000 /* number of calls */ +#define DEFAULT_NPROCS 4 /* number of children to run */ +#define DEFAULT_RUNTIME (5 * 60) /* runtime in seconds */ +#define DEFAULT_WARMUP (5 * 60) /* warmup time in seconds */ +#define DEFAULT_ACCESS 10 /* % of file set accessed */ +#define DEFAULT_APPEND 70 /* % of writes that append */ +#define DEFAULT_DELTA_FSS 10 /* % change to file set size */ +#define DEFAULT_KB_PER_BLOCK 8 /* block xfer size in KB */ +#define DEFAULT_BYTES_PER_OP (10*1024*1024) /* bytes per unit load */ +#define DEFAULT_NFILES 100 /* # of regular NON-IO files */ +#define DEFAULT_FILES_PER_DIR 30 /* # of files per directories */ +#define DEFAULT_NSYMLINKS 20 /* # of symlinks */ +#define DEFAULT_FAILED_LOOKUP 35 /* # of failed lookups */ + +#define DEFAULT_WARM_RATE_CHECK 2 /* check progress each 2 secs */ +#define DEFAULT_RUN_RATE_CHECK 10 /* check progress each 10 sec */ +#define DEFAULT_MAX_BUFSIZE NFS_MAXDATA /* max buffer size for i/o */ + +#define DEFAULT_BIOD_MAX_WRITE 2 /* max outstanding biod write */ +#define DEFAULT_BIOD_MAX_READ 2 /* max outstanding biod reads */ +#define MAX_BIODS 32 + + +/* + * For now we only read a fixed number of files from a directory. Ideally + * we would like to read a random number from 0-MAX but we will need a new + * workload. + */ +#define SFS_MAXDIRENTS 128 + +/* + * If you change the default Chi_Sqr value, + * then also change the field label in the results print out. + */ +#define DEFAULT_CHI_SQR_CI CHI_SQR_95 /* chi-sqr value to use */ +#define CHI_SQR_90 2.71 /* chi-sqr value for 90% CI */ +#define CHI_SQR_95 3.84 /* chi-sqr value for 95% CI */ +#define CHI_SQR_975 5.02 /* chi-sqr value for 97.5% CI */ +#define CHI_SQR_99 6.63 /* chi-sqr value for 99% CI */ +#define CHI_SQR_995 7.88 /* chi-sqr value for 99.5% CI */ + +/* + * NFS operation numbers + */ +#define NULLCALL 0 +#define GETATTR 1 +#define SETATTR 2 +#define ROOT 3 +#define LOOKUP 4 +#define READLINK 5 +#define READ 6 +#define WRCACHE 7 +#define WRITE 8 +#define CREATE 9 +#define REMOVE 10 +#define RENAME 11 +#define LINK 12 +#define SYMLINK 13 +#define MKDIR 14 +#define RMDIR 15 +#define READDIR 16 +#define FSSTAT 17 +#define ACCESS 18 +#define COMMIT 19 +#define FSINFO 20 +#define MKNOD 21 +#define PATHCONF 22 +#define READDIRPLUS 23 +#define NOPS (READDIRPLUS+1) /* number of NFS ops */ +#define TOTAL NOPS /* slot for totals */ + +/* + * Constants for i/o distribution table + */ +#define IO_DIST_START 0 +#define IO_DIST_READ 1 +#define IO_DIST_WRITE 2 + +/* + * Ratio of non_io_files that are initialized + * NOTE: initializing half the non-i/o files works ok with the + * default op mix. If the mix is changed affecting the + * ratio of creations to removes, there may not be enough + * empty slots for file creation (or there may not be + * enough created during initialization to handle a lot of + * removes that occur early in the test run), and this would + * cause do_op() to fail to find a file appropriate for the + * chosen op. This will result in the global variable + * Ops[op].no_calls being incremented (turn on child level + * debugging to check this count), and the do_op() local + * variable aborted_ops to be incremented and checked during + * runtime for too many failures. + */ +#define RATIO_NON_IO_INIT 0.5 + +/* + *---------------------------- TCP stuff --------------------- + */ + +#define TCPBUFSIZE 1024 * 32 + 200 + +/* -------------------------- Macros ----------------------- */ + +struct ladtime { + uint32_t sec; /* seconds */ + uint32_t usec; /* and microseconds */ + uint32_t esec; /* seconds since standard epoch */ +}; + +#define SUBTIME(t1, t2) { \ + if ((t1.sec < t2.sec) || \ + ((t1.sec == t2.sec) && (t1.usec < t2.usec))) { \ + t1.sec = t1.usec = 0 ; \ + } else { \ + if (t1.usec < t2.usec) { \ + t1.usec += 1000000; \ + t1.sec--; \ + } \ + t1.usec -= t2.usec; \ + t1.sec -= t2.sec; \ + } \ +} + +#define ADDTIME(t1, t2) {if ((t1.usec += t2.usec) >= 1000000) {\ + t1.sec += (t1.usec / 1000000); \ + t1.usec %= 1000000; \ + } \ + t1.sec += t2.sec; \ + } + +#define MINIMUM(a, b) ((a < b) ? (a) : (b)) +#define MAXIMUM(a, b) ((a > b) ? (a) : (b)) + +#define MULTIME(t1, s) { \ + t1.usec *=s; \ + t1.sec *=s; \ + if (t1.usec >= 1000000) {\ + t1.sec += (t1.usec/1000000); \ + t1.usec %= 1000000; \ + }\ + } +#define DIVTIME(t1, s) { \ + t1.usec += (t1.sec % s ) *1000000; \ + t1.sec /= s; \ + t1.usec /= s; \ + } +#define LARGERTIME(t1, t2) \ + ((t1.sec>t2.sec) || ((t1.sec==t2.sec) && (t1.usec>t2.usec))) + +/* -------------------------- Types ----------------------- */ + +/* + * SFS test phases. Values are well-ordered for use of "<" operations. + */ +#define Mount_phase 1 /* test directories are being mounted */ +#define Populate_phase 2 /* files being created in the test directories */ +#define Warmup_phase 3 /* reach steady state (load is being generated) */ +#define Testrun_phase 4 /* timed test run (load is being generated) */ +#define Results_phase 5 /* results collection and reporting */ +typedef int sfs_phase_type; + +/* + * Index constants for lookups into the RPC timer array. + */ +#define Init 0 +#define Lookup 1 +#define Read 2 +#define Write 3 + +/* + * SFS results information structure + */ +typedef struct { + int good_calls; /* successful calls */ + int bad_calls; /* failed (timed out) calls */ + int timeout_calls; /* RFS timeout calls */ + int fast_calls; /* calls that competed in 0 time */ + struct ladtime time; /* cumulative execution time */ + double msec2; /* cumulative squared time - msecs**2 */ +} sfs_results_type; + +/* + * SFS information reported from child back to parent. + */ +typedef struct { + int version; + sfs_results_type results_buf[NOPS+1]; + int total_fss_bytes; + int least_fss_bytes; + int most_fss_bytes; + int base_fss_bytes; +} sfs_results_report_type; + + +/* + * SFS operation information structure + */ + +typedef struct { + char * name; /* operation name */ + int mix_pcnt; /* percentage of call target */ +#ifndef RFS + int (*funct)(); /* op routine */ +#endif + int call_class; /* call class: client handle & timeo */ + int target_calls; /* number of calls to make */ + int no_calls; /* # of times a call couldn't be made */ + double req_pcnt; /* cumulative request percentile */ + int req_cnt; /* number of requests made */ + int target_reqs; /* number of req to be made */ + sfs_results_type results; /* test results */ +} sfs_op_type; + + +/* + * Flags used with randfh + */ +#define RANDFH_TRUNC 0x01 /* pick a file to truncate */ +#define RANDFH_APPEND 0x02 /* pick a file to append to */ + +/* + * SFS file information structure + * The particular values assiged are used to perform mod operations. + */ +#define Sfs_io_file 0 /* read, write ops only (0) */ +#define Sfs_non_io_file 1 /* other regular file ops only (1) */ +#define Sfs_symlink 2 /* symlink ops only (2) */ +#define Sfs_dir 3 /* dir ops only (3) */ +#define Sfs_regular 4 /* any regular file (0,1) */ +#define Sfs_non_dir 5 /* any non-directory file (0,1,2) */ +#define Sfs_any_file 6 /* any file, link, or dir (0,1,2,3) */ +typedef int sfs_file_type; + +#define Exists 1 /* op needs an object that already exists */ +#define Nonexistent 2 /* op will create an object */ +#define Empty_dir 3 /* op needs an empty directory */ +typedef char sfs_state_type; + +/* + * One file (dir, or symlink) in the file set. + */ +typedef struct sfs_fh_data { + union { + fhandle_t f_fh2; /* the NFS V2 file handle */ + nfs_fh3 f_fh3; /* the NFS V3 file handle */ + } sfs_fh_un; + union { + fattr a_attributes2; /* its V2 attributes */ + fattr3 a_attributes3; /* its V3 attributes */ + } sfs_fattr_un; + char file_name[SFS_MAXNAMLEN]; /* path component*/ +} sfs_fh_data; + +typedef struct sfs_fh_type { + struct sfs_fh_type *dir; /* Parent Directory */ + struct sfs_fh_data *fh_data; /* Data area */ + int size; /* its size */ + int unique_num; /* unique part of filename */ + int use_cnt; /* count of op to this file */ + int xfer_cnt; /* count of KB xfered */ + sfs_state_type state; /* existence state */ + char working_set; /* is in the working set */ + char initialize; /* should be initialized */ +#define attributes2 fh_data->sfs_fattr_un.a_attributes2 +#define attributes3 fh_data->sfs_fattr_un.a_attributes3 +#define fh2 fh_data->sfs_fh_un.f_fh2 +#define fh3 fh_data->sfs_fh_un.f_fh3 +#define file_name fh_data->file_name +} sfs_fh_type; + +#define fh_size(fhptr) (nfs_version == NFS_VERSION ? \ + (fhptr)->attributes2.size : \ + (fhptr)->attributes3.size._p._l) + +#define fh_uid(fhptr) (nfs_version == NFS_VERSION ? \ + (uint32_t)((fhptr)->attributes2.uid) : \ + (uint32_t)((fhptr)->attributes3.uid)) + +#define fh_gid(fhptr) (nfs_version == NFS_VERSION ? \ + (uint32_t)((fhptr)->attributes2.gid) : \ + (uint32_t)((fhptr)->attributes3.gid)) + +#define fh_mode(fhptr) (nfs_version == NFS_VERSION ? \ + (uint32_t)((fhptr)->attributes2.mode) : \ + (uint32_t)((fhptr)->attributes3.mode)) + +#define fh_isdir(fhptr) (nfs_version == NFS_VERSION ? \ + ((fhptr)->attributes2.type == NFDIR) : \ + ((fhptr)->attributes3.type == NF3DIR)) + +#define fh_isfile(fhptr) (nfs_version == NFS_VERSION ? \ + ((fhptr)->attributes2.type == NFREG) : \ + ((fhptr)->attributes3.type == NF3REG)) + +/* + * One file (dir, or symlink) in the working file set. + */ +typedef struct { + int range; /* random access range for this file */ + int index; /* points into actual file array */ +} sfs_work_fh_type; + +typedef struct { + sfs_work_fh_type *entries; /* array of working files */ + int access_group_size; /* # files in a group */ + int access_group_cnt; /* # groups in workset */ + int max_range; /* access range of workset */ +} sfs_work_set_type; + + + +/* + * SFS file size distribution structures + */ +typedef struct { + int pcnt; /* percentile */ + int bufs; /* Block_size KB xfers */ + int frags; /* boolean - is a frag present */ +} sfs_io_op_dist_type; + +typedef struct { + sfs_io_op_dist_type *read; /* read size table */ + sfs_io_op_dist_type *write; /* write size table */ + int max_bufs; /* max # of Block_size xfers */ + double avg_ops_per_read_req; /* avg read ops/req */ + double avg_ops_per_write_req; /* avg write ops/req */ +} sfs_io_dist_type; + +typedef struct { + int pcnt; /* percentile */ + int size; /* file size in KB */ +} sfs_io_file_size_dist; + + +/* sfs child processes synchronization logfile */ +#define CHILD_SYNC_LOG "/tmp/sfs_log" +#define SFS_PNT_PID "/tmp/sfs_pnt_pid" +#define SFS_PRM_PID "/tmp/sfs_prm_pid" +#define SFS_SYNCD_PID "/tmp/sfs_syncd_pid" + +/* + * Values for invalid runs + */ +#define INVALID_UNKNOWN 1 /* Old value reserved as unknown */ +#define INVALID_IODIST 2 +#define INVALID_MIX 3 +#define INVALID_RUNTIME 4 +#define INVALID_ACCESS 5 +#define INVALID_APPEND 6 +#define INVALID_KB 7 +#define INVALID_NDIRS 8 +#define INVALID_FSS 9 +#define INVALID_BIODREAD 10 +#define INVALID_NSYMLINKS 11 +#define INVALID_BIODWRITE 12 +#define INVALID_WARMUP 13 +#define INVALID_GOODCALLS 14 +#define INVALID_FAILEDRPC 15 +#define INVALID_NOTMIX 16 +#define INVALID_MAX (INVALID_NOTMIX + 1) + +/* + * External variable declarations + */ +extern int Access_percent; +extern int Append_percent; +extern int Base_fss_bytes; +extern int Biod_max_outstanding_reads; +extern int Biod_max_outstanding_writes; +extern int Bytes_per_block; +extern int Child_num; +extern int Client_num; +extern sfs_fh_type *Cur_file_ptr; +extern char Cur_filename[]; +extern gid_t Cur_gid; +extern struct ladtime Cur_time; +extern uid_t Cur_uid; +extern sfs_phase_type Current_test_phase; +extern int Debug_level; +extern sfs_io_file_size_dist Default_file_size_dist[]; +extern sfs_fh_type *Dirs; +extern uint32_t Dump_count; +extern int dump_create_existing_file; +extern int Dump_data; +extern uint32_t Dump_length; +extern uint32_t Dump_offset; +extern int dump_truncate_op; +extern sfs_fh_type Export_dir; +extern char Filespec[]; +extern char Dirspec[]; +extern char Symspec[]; +extern int avg_bytes_per_file; +extern int files_per_megabyte; +extern int Fss_delta_percent; +extern int Interactive; +extern sfs_io_dist_type * Io_dist_ptr; +extern sfs_fh_type *Io_files; +extern int Kb_per_block; +extern char * sfs_Myname; +extern int Least_fss_bytes; +extern int Limit_fss_bytes; +extern int Cur_fss_bytes; +extern int Total_fss_bytes; +extern int Log_fd; +extern char Logname[]; +extern int Most_fss_bytes; +extern int Msec_per_period; +extern CLIENT * NFS_client; +extern CLIENT * NFS_client_recv; +extern struct timeval *Nfs_timers; +extern struct timeval Nfs_udp_timers[]; +extern struct timeval Nfs_tcp_timers[]; +extern int nfs_version; +extern sfs_fh_type *Non_io_files; +extern int Num_dirs; +extern int Num_dir_files; +extern int Num_failed_lookup; +extern int Num_io_files; +extern int Num_non_io_files; +extern int Num_symlinks; +extern int Num_symlink_files; +extern int Num_working_dirs; +extern int Num_working_io_files; +extern int Num_working_non_io_files; +extern int Num_working_symlinks; +extern sfs_op_type *Ops; +extern int Populate_only; +extern char * Prime_client; +extern uid_t Real_uid; +extern int Runtime; +extern struct ladtime Starttime; +extern int start_run_phase; +extern sfs_fh_type *Symlinks; +extern int Tcp; +extern int Testop; +extern int Files_per_dir; +extern int Tot_client_num_io_files; +extern int Tot_client_num_non_io_files; +extern int Tot_client_num_symlinks; +extern int Timed_run; +extern int Validate; +extern int Warmuptime; +extern char *invalid_str[]; +extern char lad_hostname[]; + + +/* + * External function declarations + */ +extern int biod_init(int, int); +extern void biod_term(void); +extern void biod_turn_on(void); +extern int check_access(struct stat *); +extern int check_fh_access(sfs_fh_type *); +extern void child(int, int, float, int, char **); +extern void generic_catcher(int); +extern int generic_kill(int, int); +extern void init_counters(void); +extern void init_fileinfo(void); +extern void init_mount_point(int, char *, CLIENT *); +extern void init_ops(void); +extern char * init_write_buffer(void); +extern CLIENT * lad_getmnt_hand(char *); +extern CLIENT * lad_clnt_create(int, struct hostent *, uint32_t, + uint32_t, int, struct timeval *); +extern char * lad_timestamp(void); +extern int set_debug_level(char *s); +extern void sfs_alarm(int); +extern void sfs_cleanup(int); +extern void sfs_elapsedtime(sfs_op_type *, struct ladtime *, + struct ladtime *); +extern void sfs_gettime(struct ladtime *); +extern int32_t sfs_random(void); +extern void sfs_srandom(int); +extern int init_rand_range(int); +extern int rand_range(int); +extern void sfs_startup(int); +extern void sfs_stop(int); +extern void log_dump(struct ladtime *, struct ladtime *, int); +extern void parent(int, int, char *, char *); +extern void print_dump(int, int); +extern sfs_fh_type * + randfh(int, int, uint_t, sfs_state_type, sfs_file_type); +extern int msec_sleep(int); +extern void Validate_ops(int, char **); + +/* Reliable RPC functions for initialization */ +extern int lad_lookup(sfs_fh_type *, char *); +extern int lad_remove(sfs_fh_type *, char *); +extern int lad_rmdir(sfs_fh_type *, char *); +extern int lad_symlink(sfs_fh_type *, char *, char *); +extern int lad_mkdir(sfs_fh_type *, char *); +extern int lad_write(sfs_fh_type *, int32_t, int32_t); +extern int lad_create(sfs_fh_type *, char *); +extern int lad_truncate(sfs_fh_type *, int32_t); + +/* RFS: moved the definition from sfs_c_bio.c to here because it is used in + * sfs_c_chd.c in the new code, information associated with outstanding requests + */ +struct biod_req { + uint32_t xid; /* RPC transmission ID */ + bool_t in_use; /* Indicates if the entry is in use */ + int dep_tab_index; /* corresponding index in dep_tab */ + unsigned int count; /* Count saved for Dump routines */ + unsigned int offset; /* Offset saved for Dump routines */ + struct ladtime start; /* Time RPC call was made */ + struct ladtime stop; /* Time RPC reply was received */ + struct ladtime timeout; /* Time RPC call will time out */ +}; + +#endif /* sfs_def.h */ diff --git a/TBBT/trace_play/sfs_c_dmp.c b/TBBT/trace_play/sfs_c_dmp.c new file mode 100644 index 0000000..82d9a38 --- /dev/null +++ b/TBBT/trace_play/sfs_c_dmp.c @@ -0,0 +1,298 @@ +#ifndef lint +static char sfs_c_dmpSid[] = "@(#)sfs_c_dmp.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ---------------------- sfs_c_dmp.c --------------------- + * + * Raw data dump routines for SFS. + * + * The routines contained here capture and dump the raw data + * points (operation, response time) to allow the researcher + * to perform detailed analysis on the response time characteristics + * of an NFS server. + * + *.Exported Routines + * void log_dump(ladtime *, ladtime *, int) + * void print_dump(int, int) + * + *.Local Routines + * None. + * + *.Revision_History + * 11-Jul-94 ChakChung Ng Add codes for NFS/v3 + * 03-Feb-92 0.0.20 Pawlowski + * Use of "Current_test_phase" + * obviates need for dump init + * routine, so it has been deleted. + * 10-Jan-92 0.0.19 Teelucksingh + * Changed dump file names to be + * < 14 chars. Added header to + * output. + * 04-Jan-92 0.0.18 Pawlowski + * Added raw data dump code. + */ + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "sfs_c_def.h" + +struct savedata { + struct savedata *next; + int32_t rtv_sec; /* The response time */ + int32_t rtv_usec; + int32_t stv_sec; /* The start time */ + int32_t stv_usec; + uint32_t count; /* Used only for read and write */ + uint32_t offset; /* Used only for read and write */ + uint32_t length; /* Used only for read and write */ + uint16_t unique_num; /* unique id for file */ + unsigned int flags; /* Things like whether truncating, etc. */ + int16_t op; +}; + +uint32_t Dump_count; /* place to put read and write count */ +uint32_t Dump_offset; /* place to put r/w offset */ +uint32_t Dump_length; /* place to put r/w offset */ +int Dump_data; /* Flag set by command line option */ +int dump_create_existing_file = FALSE; +int dump_truncate_op = FALSE; + +/* + * ---------------------- Static Declarations ---------------------- + */ + +static struct savedata *saveary = 0; + +/* + * -------------------------- Constants -------------------------- + */ + +/* flags bit values */ +#define CREATE_OF_EXISTING_FILE 0x0001 +#define TRUNCATE_OPERATION 0x0002 + +/* + * ---------------------- Dump Routines ---------------------- + */ + +/* + * log_dump() + * + * This function is called on the completion of every operation + * to log the data point. We log the operation and the elapsed time + * (storing the microsecond component also). + * + * The data is kept in a singly linked list, elements dynamically + * allocated as needed. + * + * Dynamic allocation of elements using malloc and single link list + * pointer adds overhead to the storage space for the data for each + * point. Dynamic allocation can result in system calls to get more + * space for elements, adding to execution overhead. However, if you're + * doing a raw data dump run, you assume costs are negligible. + */ + +void +log_dump( + struct ladtime *start, + struct ladtime *elapsed, + int op) +{ + static struct savedata *saveptr = 0; + + if (!Dump_data || Current_test_phase != Testrun_phase) + return; + + if (saveary == (struct savedata *)NULL) { + if ((saveary = (struct savedata *) + malloc(sizeof(struct savedata))) + == (struct savedata *)NULL) { + (void) fprintf(stderr, "Unable to allocate dump element\n"); + return; + } + saveptr = saveary; + } else { + if ((saveptr->next = (struct savedata *) + malloc(sizeof(struct savedata))) + == (struct savedata *)NULL) { + (void) fprintf(stderr, "Unable to allocate dump element\n"); + return; + } + saveptr = saveptr->next; + } + saveptr->next = (struct savedata *)NULL; + saveptr->flags = 0; + saveptr->op = op; + saveptr->rtv_sec = elapsed->sec; + saveptr->rtv_usec = elapsed->usec; + saveptr->stv_sec = start->sec; + saveptr->stv_usec = start->usec; + saveptr->unique_num = Cur_file_ptr->unique_num; + if (op == NFSPROC3_READ || op == NFSPROC3_WRITE || + op == NFSPROC_READ || op == NFSPROC_WRITE) { + saveptr->count = (uint16_t) Dump_count; + saveptr->offset = Dump_offset; + saveptr->length = Dump_length; + } + + if (dump_create_existing_file == TRUE) { + saveptr->flags |= CREATE_OF_EXISTING_FILE; + dump_create_existing_file = FALSE; + } + + if (dump_truncate_op == TRUE) { + saveptr->flags |= TRUNCATE_OPERATION; + dump_truncate_op = FALSE; + } +} + +/* + * print_dump() + * + * Dumps the raw data to a file in the format: + * + * opcode sec.msec sec.msec file-unique-id <length> <offset> <count> + * opcode sec.msec sec.msec file-unique-id + * . . . + * + * The opcode is the numeric NFS operation code as defined in the + * NFS protocol specification. The first "sec.msec" field is the + * response time of the operation. The second "sec.msec" field + * is the start time of the operation. For read and write calls, + * the "length", "offset" and "count" from the operation arguments are put out + * as the fourth, fifth, and sixth field. + * + * This simple (x, y) pairing should be suitable input for a wide variety + * of plotting programs. + * + * Note that the raw data is precious! Several points to be observed: + * + * 1. The raw data for each individual child is dumped to + * its own data file. So each individual child process + * data can be inspected (possibly useful to debug client + * load generation per child process anomalies). + * + * 2. More importantly, each raw data dump file presents + * the operations generated by the child in their exact + * order of generation. This can be used to analyze possible + * time dependent behaviour of the server. + * + * Client process output files can be merged for analysis using cat(1). + * + * If any other data (additional fields) are added to raw data dump + * file, please add those fields after primary fields. awk(1) scripts + * and the like can be used to re-arrange data files, but it would + * be nice if the primary (x, y) data points are the default format. + */ + +void +print_dump( + int clientnum, + int childnum) +{ + char buf[256]; + FILE *datap; + struct savedata *p = saveary; + + buf[0] = 0; + + if (!Dump_data) + return; + + /* + * We write raw data files to the /tmp directory, and + * the manager will retrieve to the prime client. + * + * Removed preladraw prefix from file names to fit + * in 14 chars - K.T. + */ + + (void) sprintf(buf, "/tmp/c%3.3d-p%3.3d", clientnum, childnum); + + if ((datap = fopen(buf, "w")) == NULL) { + (void) fprintf(stderr, "couldn't open %s for writing\n", buf); + return; + } + + (void) fprintf(datap,"%s\n%s\n%s\n%s\n", +"-----------------------------------------------------------------------------", +" Op Response Start Unique File", +" Code Time Time File Id Length Offset Size", +"-----------------------------------------------------------------------------"); + + p = saveary; + while(p) { + (void) fprintf(datap, "%11s %8.3f %19.3f %8d", + Ops[p->op].name, + (p->rtv_sec * 1000.0) + (p->rtv_usec / 1000.0), + (p->stv_sec * 1000.0) + (p->stv_usec / 1000.0), + p->unique_num); + if (p->op == NFSPROC3_READ || p->op == NFSPROC3_WRITE || + p->op == NFSPROC_READ || p->op == NFSPROC_WRITE) { + (void) fprintf(datap, + " %8d %8d %5d\n", p->length, p->offset, p->count); + } + else if (p->op == NFSPROC3_SETATTR || p->op == NFSPROC3_CREATE || + p->op == NFSPROC_SETATTR || p->op == NFSPROC_CREATE) { + if (p->flags & TRUNCATE_OPERATION) { + (void) fprintf(datap, " %s", "TRUNCATE"); + } + if (p->flags & CREATE_OF_EXISTING_FILE) { + (void) fprintf(datap, " %s", "CREATE_EXISTING"); + } + (void) fprintf(datap, "\n"); + } + else { + (void) fprintf(datap, "\n"); + } + p = p->next; + } + + (void) fprintf(datap, +"-----------------------------------------------------------------------------\n\n"); + (void) fclose(datap); +} diff --git a/TBBT/trace_play/sfs_c_man.c b/TBBT/trace_play/sfs_c_man.c new file mode 100644 index 0000000..33bf28a --- /dev/null +++ b/TBBT/trace_play/sfs_c_man.c @@ -0,0 +1,1998 @@ +#ifndef lint +static char sccsid_hp[] = "@(#)sfs_c_man.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/* + * Generates an artifical NFS client load based on a given mix of operations, + * and block transfer distribution. + * + * Usage: sfs [-l load] [-p procs] [-w warmup] [-t time] + * [-m mix_file] [-B block_size] [-b blocksz_file] + * [-f file_set_delta] [-a access_pnct] + * [-A append_pcnt] [-D dir_cnt] [-F file_cnt] [-S symlink_cnt] + * [-d debug_level] [-i] [-P] [-T op_num] + * [-V validation_level] [-z] [-Q] + * [-R biod_reads] [-W biod_writes] + * [-M prime_client_hostname] [-N client_cnt] + * + * NOTE: REFER TO SFS MAN PAGE (sfs.1) FOR A DESCRIPTION OF ALL + * SFS OPTIONS + * + * + * Single Client Options + * + * option description default + * --------------- ------------------------------------------------ ----------- + * -a access_pcnt % of file set to access 20% access + * -A append_pcnt % of writes that append rather than overwrite 70% append + * -b blocksz_file file specifying distribution of block xfer sizes (see below) + * -B block_size # of KB in block, up to 8 KB 8 KB + * -Q Do TCP connection for NFS rather than UDP off + * -d debug_level debug level (higher number gives more output) off + * -D dir_cnt # directories used for directory operations 20 dirs + * -f fileset_delta % change in file set size allowed 10% + * -F file_cnt # files used for read and write operations 100 files + * -i interactive; wait for input before starting test off + * -l load # NFS calls/second to generate from each client 60 calls/sec + * -m mix_file file specifying NFS call distribution (see below) + * -p procs # processes used to generate load on each client 7 procs + * -P populate test directories, but don't run a test off + * -R biod_reads max # of outstanding read requests at one time 2 reqs + * -S symlink_cnt # symbolic links used for symlink operations 20 symlinks + * -t time # seconds to generate load for the timed test 600 secs + * -T op_num test the NFS operation specified one time off + * -V validate correctness of server's NFS off + * -W biod_writes max # of outstanding writes req at one time 2 reqs + * -w warmup # secs to generate load before starting test 60 secs + * -z If specified, collect and dump raw data. off + * + * + * Multi Client Options + * + * option description default + * ------------------------ ----------------------------------- ----------- + * -M prime_client_hostname hostname of prime client no default + * -N client_cnt # client machines in test no default + * + * + * + * Block Transfer Size Distribution + * + * The block transfer size distribution is specified by a table of values. + * The first column gives the percent of operations that will be a + * specific block transfer size. The second column gives the number of + * blocks units that will be transferred. Normally the block unit size + * is 8KB. The third column is a boolean specifying whether a trailing + * fragment block should be transferred. The fragment size for each transfer + * is a random multiple of 1 KB, up to the block size - 1 KB. Two tables + * are needed, one for read operation and one for write operations. The + * following table gives the default distributions. + * + * Read - Default Block Transfer Size Distribution Table + * percent block count fragment resulting transfer (8KB block size) + * ------- ----------- -------- ----------------------------------- + * 0 0 0 0% 1 - 7 KB + * 85 1 0 85% 9 - 15 KB + * 8 2 1 8% 17 - 23 KB + * 4 4 1 4% 33 - 39 KB + * 2 8 1 2% 65 - 71 KB + * 1 16 1 1% 129 - 135 KB + * + * Write - Default Block Transfer Size Distribution Table + * percent block count fragment resulting transfer (8KB block size) + * ------- ----------- -------- ----------------------------------- + * 49 0 1 49% 1 - 7 KB + * 36 1 1 36% 9 - 15 KB + * 8 2 1 8% 17 - 23 KB + * 4 4 1 4% 33 - 39 KB + * 2 8 1 2% 65 - 71 KB + * 1 16 1 1% 129 - 135 KB + * + * The user may specify a a different distribution by using the '-b' option. + * The format for the block size distribution file consists of the first + * three columns given above: percent, block count, and fragment. Read + * and write distribution tables are identified by the keywords "Read" and + * "Write". An example input file, using the default values, is given below: + * + * Read + * 0 0 0 + * 85 1 0 + * 8 2 1 + * 4 4 1 + * 2 8 1 + * 1 16 1 + * Write + * 49 0 1 + * 36 1 1 + * 8 2 1 + * 4 4 1 + * 2 8 1 + * 1 16 1 + * + * A second parameter controlled by the block transfer size distribution + * table is ethernet packet size. The distribution tables define the + * relative proportion of full blocks packets to fragment packets. For + * instance, the default tables have been constructed to produce a specific + * distribution of ethernet packet sizes for i/o operations by controlling + * the amount of data in each packet. The write packets produced consist + * of 50% 8-KB packets, and 50% 1-7 KB packets. The read packets consist + * of 85% 8-KB packets, and 15% 1-7 KB packets. These figures are + * determined by multiplying the percentage for the type of transfer by + * the number of blocks and fragments generated, and adding the totals. + * These conmputations are performed below for the default block size + * distribution tables: + * + * Read blocks fragments + * 0 0 0 0 0 + * 85 1 0 85 0 + * 8 2 1 16 8 + * 4 4 1 16 4 + * 2 8 1 16 2 + * 1 16 1 16 1 + * --- --- + * 149 (90%) 15 (10%) + * + * Write + * 49 0 1 0 49 + * 36 1 1 36 36 + * 8 2 1 16 8 + * 4 4 1 16 4 + * 2 8 1 16 2 + * 1 16 1 16 1 + * --- --- + * 100 (50%) 100 (50%) + * + * + * + * + * + * NFS Operation Mix + * + * The operation mix is described assigning a percentage to each type + * of NFS operation. The default mix of operations is: + * + * operation percent + * --------- ------- + * null 0 + * getattr 13 + * setattr 1 + * root 0 + * lookup 34 + * readlink 8 + * read 22 + * wrcache 0 + * write 15 + * create 2 + * remove 1 + * rename 0 + * link 0 + * symlink 0 + * mkdir 0 + * rmdir 0 + * readdir 3 + * fsstat 1 + * + * The user may specify a a different operation mix by using the '-m' option. + * The format for the mix file consists of the output from an nfsstat(1) + * command, which lists an operation count and percentage for each NFS + * operation. + * + * + * File Set Size + * + * !!! needs to be re-written - mew 8/24 + * This still needs to be rewritten + * - The default number of i/o files is based on load level. + * - For non I/O files, the number of initialized versus empty + * slots is hardcoded + * - dr 2/8/94 + * + * The file set used by SFS is determined by 2 factors. First, + * SFS creates a base set of files. By default this consists of + * 100 files for i/o operations, 100 files for non-i/o operations, 20 + * directories, and 20 symbolic links. These default values can be + * changed from the command line by using the "-F", "-D", and "-S" options. + * This file set is divided evenly among the set of processes used to + * generate load. + * + * Then, enough resources are allocated to allow for the eventual creation + * of new files, directories and symlinks by the creat, link, mkdir, + * and symlink operations. The number of extra slots allocated depends + * on the mix percentages assigned to each of the create and deletion + * operations, multiplied by an estimate of the total number of operations + * to be performed. For instance, the default number of extra files names + * allocated for non-i/o operations is computed as follows: + * 300 secs * 60 ops/sec = 18000 total operations + * 2% create * 18000 ops = 360 create ops + * 1% remove * 18000 ops = 180 remove ops + * 260 creates ops - 180 remove ops = 180 extra files to be created. + * These 90 files are distributed evenly among the processes used to + * generate load. With the default settings, no extra directories are + * created or removed, so no extra allocation is done for directories + * beyond the base file set. The same is true for symbolic links. + * + * Thus, the total default file set size is: + * 100 files for i/o operations + * 280 files for non-i/o operations + * 20 directories + * 20 symlinks + * + * By allocating all required space before the test begins, any space + * allocation problems encountered by SFS are discovered before the + * test is started. + * + * + * Program Control + * + * Strategy: loop for some number of NFS calls doing a random sleep + * followed by a call to one of the op generator routines. The routines + * are called based on a weighting factor determined by the set of + * default percentages or a mix supplied by the user. + * + * The generator routines are able to keep an accurate count of the + * NFS operations they are generating by using the NFS protocol + * directly and not going through the kernel. This eliminates the + * effects of kernel name caches and retry mechanisms that + * complicate control of what actually hits the wire. The calling + * routine benefits by avoiding having to get the NFS statistics + * from the kernel because they KNOW what calls they've made. + * + * By using the NFS protocol directly : + * "lookup" operations sidestep the client kernel name cache, + * "getattr" operations avoid the client kernel attribute cache, + * "read" operations avoid the client kernel buffer cache, + * and so on. + * + * A consequence of not going thru the client kernel is that the sfs + * program must maintain a table of file handles rather than open + * file descriptors. + * + * The parent process starts children to do the real work of generating load. + * The parent coordinates them so that they all start at the same time, and + * collects statistics from them when they are done. To coordinate the + * start up, the parent waits for each child to write one byte into + * a common log file (opened in append mode to avoid overwriting). + * After they write a byte the children pause, and the parent send SIGUSR1 + * when it has heard from all of the kids. The children write their statistics + * into the same common log file and the parent reads and accumulates the + * statistics and prints them out. + * + * + *.Exported_Routines + * int main(int, char*) + * + *.Local_Routines + * void init_logfile(void) + * void usage(void) + * int setmix(char *) + * int setiodist(FILE *) + * int parseiodist(FILE *, int) + * void init_iodist(sfs_io_dist_type *) + * void init_fss(void) + * void init_filedist(void) + * int lad_substr(char *, char *) + * + *.Revision_History + * 21-Aug-92 0.1.11 Wittle File set access code. + * 14-Jul-92 0.1.9 Teelucksingh + * Implemented Mark Wittle's proposal to + * base File Set Size on peak load value, + * added "-L peak_load" option. + * 10-Jan-92 0.0.0.19 Teelucksingh + * Reworked setpgrp() usage to + * better handle BSD vs SYSV variations. + * 04-Jan-92 0.0.0.18 Pawlowski + * Added raw data dump code. + * 04-Dec-91 0.0.0.15 Keith + * Include string.h if SVR4. + * 28-Nov-91 0.0.0.13 Teelucksingh + * Modified code to use unique sfs /tmp + * logfiles; sfs can now be used on + * clients that have a shared /tmp area. + * Added ANSI C features. Fixed 'multiple + * signals from the Prime-Client' problem. + * Added code to allow clients to + * check for and create client specific + * directories under each mount point - + * clients share partitions. (code from + * M.Molloy). + * 22-Nov-91 Wittle Updated program description comment. + * Added new op generation code. + * Added block_dist_table and block_size + * options, removed 8KB packet assumptions. + * 04-Oct-91 0.0.0.12 Teelucksingh + * Changed SFS sources and executables + * to use the "prelad" prefix. + * 23-Sep-91 0.0.0.11 Teelucksingh + * Changed format of sfs output. + * 01-Aug-91 0.0.9 Wiryaman Use the SPEC random number generator. + * Since this RNG cannot take seed = 0, + * use child_num+1 instead. + * 17-Jul-91 0.0.8 Teelucksingh + * Enhance multi-client code and + * documentation. + * Keith Map "nhfsstone" to "laddis" in + * README, nhfsstone_mgr.c. Create + * initial DESCR.SFS for SPEC + * submission. + * 15-Jul-91 0.0.8 Wiryaman Add getmnt() for ULTRIX + * 25-Jun-91 0.0.7 Wiryaman Added validation option to test all + * of the NFS operations. + * 17-Jun-91 0.0.7 Teelucksingh + * Added multi-client synchronization + * support. By designating a client as + * "Prime client" you can synchronize + * multi-client SFS execution. + * 12-May-91 0.0.6 Wittle Fix standard deviation. + * 02-May-91 0.0.5 Wittle Fix SunOS signal bug; use default + * warmuptime; add local time routine; + * check for calls that underflow elapsed + * time measurement; add std deviation + * statistics; rework verbose output. + * fix init invalid protocol rmdir calls; + * 15-Apr-91 0.0.4 Wittle Test can be repeated without removing + * test directories - initialization + * restores base file set count and sizes. + * Fix lack of call rate & mix accuracy - + * set op_check before artificially + * increasing call_targets. + * Don't pre-create files/dirs that are + * meant to be created during the test. + * 10-Mar-91 0.0.3 Wittle Longer RPC timeout while populating + * testdir; strstr() bug fix; + * '-i' and '-e' options + * 06-Mar-91 0.0.2 Wittle Loop forever pre-filling files. + * 22-Feb-91 0.0.1 Wittle Use signal(2) instead of sigset(2). + * 18-Feb-91 0.0.0 Wittle Change algorythm for determining i/o + * sizes, preserve i/o file working set + * by using separate files; bugs fixes. + * + * nhfsstone renamed to laddis + * + * 31-Oct-90 2.0.4 Wittle Many bug fixes. + * 24-Aug-90 2.0.3 Wittle Output compatible w/graphing tools. + * 24-July-90 2.0.2 Wittle Handle mounting below symlinks. + * 24-June-90 2.0.1 Wittle Prefill files with data. + * 17-May-90 2.0.0 Bean Rewrote the guts to use NFS + * protocol directly. + * Cleaned up self-pacing mechanism. + * 08-Nov-89 Guillermo Roa Ported original version to DG/UX. + * 07-Jul-89 Legato Systems Created. + */ + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <sys/signal.h> + +#include <sys/file.h> +#include <fcntl.h> + +#include <unistd.h> + +extern getmyhostname(char *, int); + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + +/* + * ------------------------- External Definitions ------------------------- + */ + +/* external routines from RPC and system libraries */ +#if defined(SETPGRP_BSD) +extern int setpgrp(int, int); +#endif /* SETPGRP_BSD */ +#if defined(SETPGRP_SYSV) +extern pid_t setpgrp(void); +#endif /* SETPGRP_SYSV */ + +/* forward definitions for local routines */ +static void init_logfile(void); +static void usage(void); +static int setmix(char *); +static int setiodist(FILE *); +static int parseiodist(FILE *, int); +static void init_iodist(sfs_io_dist_type *); +static void init_fss(void); +static void init_filedist(void); +static int lad_substr(char *, char *); +static double time_so_far1(void); +static double get_resolution(void); +static void check_clock(void); + +int Tot_client_num_io_files = 0; /* # of files used for i/o per client */ +int Tot_client_num_non_io_files = /* # of files used for i/o per client */ + DEFAULT_NFILES; +int Files_per_dir = /* # of pre-created dirs */ + DEFAULT_FILES_PER_DIR; +int Tot_client_num_symlinks = /* # of pre-created symlinks/client */ + DEFAULT_NSYMLINKS; +int Child_num; +char * Prime_client = NULL; /* Prime client hostname */ +int Client_num = 1; /* My client number */ +int Tcp = 0; /* Flag set on command line */ +char *sfs_Myname; /* name program invoked under */ +int Log_fd; /* log fd */ +char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */ +uid_t Real_uid; /* real uid */ +uid_t Cur_uid; /* my uid */ +gid_t Cur_gid; /* my gid list */ + +static char Client_logname[SFS_MAXNAMLEN]; + +/* + * ----------------- SFS Main and Initialization Code ----------------- + */ + +/* + * Read the command line arguments, fork off child processes to + * generate NFS load, and perform the local (ie, on this client) + * book-keeping for the test and the results. + */ +int +main( + int argc, + char *argv[]) +{ + + char *mix_file; /* name of mix file */ + char *iodist_file; /* name of block i/o dist table file */ + int children; /* number of children */ + int child_num; /* child index */ + int total_load; /* total client load factor */ + float child_load; /* per child load factor */ + int pid; /* process id */ + FILE *pid_fp; + FILE *iodist_fp; /* block io dist table file */ + int i; + int c; + int Saveerrno; + int ret; + int nsigs = 32; /* reasonable default */ + extern char *optarg; + extern int optind; + + /* + * Place pid in pid log file + */ + if ((pid_fp = fopen(SFS_PNT_PID, "a+")) == NULL) { + perror(SFS_PNT_PID); + exit(1); + } + + (void) fprintf(pid_fp, "%d\n", getpid()); + + /* Get program name for stderr printing */ + sfs_Myname = argv[0]; + + check_clock(); + getmyhostname(lad_hostname, HOSTNAME_LEN); + + init_ops(); + +/* + * Get the uid and gid information. + */ + Real_uid = getuid(); + Cur_gid = getgid(); + +/* + * Form a new process group so our syncrhonization signals don't + * cause our parent shell to exit. Clear the umask. + * Default is to use the standard setsid + */ +#ifdef SETPGRP3 + ret = setpgrp3(); /* Work around HP-UX bug */ +#else +#ifdef SETPGRP_SYSV + ret = setpgrp(); +#else +#ifdef SETPGRP_BSD + ret = setpgrp(0, getpid()); +#else + ret = setsid(); +#endif /* SETPGRP_BSD */ +#endif /* SETPGRP_SYSV */ +#endif /* SETPGRP3 */ + + if (ret == -1) { + (void) fprintf(stderr, "%s: failed on setsid/setpgrp\n", + sfs_Myname); + exit(95); + } + + (void) umask(0); + +/* Set up default parameters */ + Validate = 0; + + children = DEFAULT_NPROCS; + total_load = DEFAULT_LOAD; + mix_file = 0; + iodist_file = 0; + Nfs_timers = Nfs_udp_timers; + + /* Parse command line arguments */ + while ((c = getopt(argc, argv, "a:A:b:B:cd:D:f:F:il:m:M:N:p:PQR:S:T:t:V:W:w:z")) != EOF) + switch (c) { + + case 'a': /* Percent of files to access */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal access value %s\n", + sfs_Myname, optarg); + exit(96); + } + Access_percent = atoi(optarg); + if (Access_percent < 0 || Access_percent > 100) { + (void) fprintf(stderr, + "%s: %% access must be between 0 and 100\n", + sfs_Myname); + exit(97); + } + break; + + case 'A': /* Percent of writes that append */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal append value %s\n", + sfs_Myname, optarg); + exit(98); + } + Append_percent = atoi(optarg); + if (Append_percent < 0 || Append_percent > 100) { + (void) fprintf(stderr, + "%s: %% append must be between 0 and 100\n", + sfs_Myname); + exit(99); + } + break; + + case 'b': /* Set block size distribution table from file */ + if ((iodist_fp = fopen(optarg, "r")) == NULL) { + Saveerrno = errno; + (void) fprintf(stderr, "%s: bad block size file", + sfs_Myname); + errno = Saveerrno; + perror(optarg); + exit(100); + } + if (setiodist(iodist_fp) < 0) { + exit(101); + } + iodist_file = optarg; + (void) fclose(iodist_fp); + break; + + case 'B': /* Set the per packet maximum block size */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, + "%s: illegal block size value %s\n", + sfs_Myname, optarg); + exit(102); + } + Kb_per_block = atoi(optarg); + if ((Kb_per_block < 1) || + (Kb_per_block > (DEFAULT_MAX_BUFSIZE/1024))) { + (void) fprintf(stderr, + "%s: illegal block size value %s\n", + sfs_Myname, optarg); + exit(103); + } + Bytes_per_block = Kb_per_block * 1024; + break; + + + case 'c': /* Set number of calls */ + (void) fprintf(stderr, "%s: '-c option no longer supported\n", + sfs_Myname); + exit(104); + break; + + case 'd': /* Set debugging level */ + Debug_level = set_debug_level(optarg); + break; + + case 'D': /* Set number of directories */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal dirs value %s\n", + sfs_Myname, optarg); + exit(105); + } + Files_per_dir = atoi(optarg); + break; + + case 'f': /* Percent change in file set size */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal file set delta value %s\n", + sfs_Myname, optarg); + exit(106); + } + Fss_delta_percent = atoi(optarg); + if (Fss_delta_percent < 0 || Fss_delta_percent > 100) { + (void) fprintf(stderr, + "%s: %% file set delta must be between 0 and 100\n", + sfs_Myname); + exit(107); + } + break; + + case 'F': /* Set number of io files */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal files value %s\n", + sfs_Myname, optarg); + exit(108); + } + Tot_client_num_io_files = atoi(optarg); + break; + + case 'i': /* Set interactive mode */ + if (Prime_client != NULL) { + (void) fprintf(stderr, + "%s: -i and -M options are incompatible\n", + sfs_Myname); + exit(109); + } + Interactive++; + break; + + case 'l': /* Set load */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal load value %s\n", + sfs_Myname, optarg); + exit(110); + } + total_load = atoi(optarg); + if (total_load < 0) { + (void) fprintf(stderr, "%s: load must be > 0\n", + sfs_Myname); + exit(111); + } + break; + + case 'm': /* Set mix from a file */ + mix_file = optarg; + if (setmix(mix_file) < 0) { + exit(112); + } + break; + + case 'M': /* Set prime_client host name for multi-client sync */ + if (Interactive) { + (void) fprintf(stderr, + "%s: -M and -i options are incompatible\n", + sfs_Myname); + exit(113); + } + Prime_client = optarg; + break; + + case 'N': /* Set client number in multi-client run */ + Client_num = atoi(optarg); + if (Client_num <= 0) { + (void) fprintf(stderr, + "%s: client number must be > 0\n", + sfs_Myname); + exit(114); + } + break; + + case 'p': /* Set number of child processes */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal procs value %s\n", + sfs_Myname, optarg); + exit(115); + } + children = atoi(optarg); + if (children < 0) { + (void) fprintf(stderr, "%s: number of children must be > 0\n", + sfs_Myname); + exit(116); + } + break; + + case 'P': /* Populate only */ + Populate_only++; + break; + + case 'Q': /* Set NFS/TCP behaviour */ + Tcp = 1; + Nfs_timers = Nfs_tcp_timers; + break; + + case 'R': /* set maximum async read concurrency level */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal read count value %s\n", + sfs_Myname, optarg); + exit(117); + } + Biod_max_outstanding_reads = atoi(optarg); + if (Biod_max_outstanding_reads < 0 || + Biod_max_outstanding_reads > MAX_BIODS) { + (void) fprintf(stderr, + "%s: read count must be >= 0 and <= %d\n", + sfs_Myname, MAX_BIODS); + exit(118); + } + break; + + case 'S': /* Set number of symlinks */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, + "%s: illegal symlinks value %s\n", + sfs_Myname, optarg); + exit(119); + } + Tot_client_num_symlinks = atoi(optarg); + break; + + case 'T': /* Set test mode, number following is opnum */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal test value %s\n", + sfs_Myname, optarg); + exit(120); + } + Testop = atoi(optarg); + if (Testop >= NOPS) { + (void) fprintf(stderr, "%s: illegal test value %d\n", + sfs_Myname, Testop); + exit(121); + } + break; + + case 't': /* Set run time */ + if (Ops[TOTAL].target_calls > 0) { + (void) fprintf(stderr, + "%s: -t and -c options are incompatible\n", + sfs_Myname); + exit(122); + } + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal time value %s\n", + sfs_Myname, optarg); + exit(123); + } + Runtime = atoi(optarg); + if (Runtime < 0) { + (void) fprintf(stderr, "%s: run time must be >= 0\n", + sfs_Myname); + exit(124); + } + break; + + case 'V': /* Set Validate Level */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal validate value %s\n", + sfs_Myname, optarg); + exit(125); + } + Validate = atoi(optarg); + if (Validate < 1 || Validate > 3) { + (void) fprintf(stderr, "%s: validate must be between 1 and 3\n", + sfs_Myname); + exit(126); + } + break; + + case 'W': /* set maximum async write concurrency level */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal write count value %s\n", + sfs_Myname, optarg); + exit(127); + } + Biod_max_outstanding_writes = atoi(optarg); + if (Biod_max_outstanding_writes < 0 || + Biod_max_outstanding_writes > MAX_BIODS) { + (void) fprintf(stderr, + "%s: write count must be >= 0 and <= %d\n", + sfs_Myname, MAX_BIODS); + exit(128); + } + break; + + case 'w': /* Set warmup time */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal warmup value %s\n", + sfs_Myname, optarg); + exit(129); + } + Warmuptime = atoi(optarg); + if (Warmuptime < 0) { + (void) fprintf(stderr, "%s: warmup time must be >= 0\n", + sfs_Myname); + exit(130); + } + break; + + case 'z': /* Do raw data dumps */ + Dump_data++; + break; + + case '?': + default: + usage(); + exit(131); + + } /* end switch on arg */ + + + /* compute ops/request for i/o operations */ + init_iodist(Io_dist_ptr); + + /* compute bytes/file and number of files */ + init_filedist(); + + /* validate all the NFS operations that sfs will use */ + if (Validate > 0) { + /* + * -F <number of files > or else + * DEFAULT_NFILES + */ + if (Tot_client_num_io_files == 0) { + Tot_client_num_io_files = DEFAULT_NFILES; + } + Num_io_files = Tot_client_num_io_files/children + 1; + /* number of non-io files, dir and symlinks base on constants */ + Num_non_io_files = Tot_client_num_non_io_files/children + 1; + Num_dirs = Num_io_files/Files_per_dir + 1; + Num_symlinks = Tot_client_num_symlinks/children + 1; + + /* io operations access a subset of the files */ + Num_working_io_files = ((Num_io_files * Access_percent) / 100) + 1; + /* non-io and other operations access all of the files */ + Num_working_non_io_files = Num_io_files; + Num_working_dirs = Num_dirs; + Num_working_symlinks = Num_symlinks; + + Validate_ops(argc - optind, &argv[optind]); + exit(133); + } + + /* + * Initial check on the mount arguments, must be at least an + * even multiple of the number of procs. + */ + if ((argc - optind) % children) { + (void) fprintf(stderr, +"%s: Invalid mount point list: Not a multiple of number of procs\n", + sfs_Myname); + exit(182); + } + + /* + * -F <number of io files > or else + * base files set on load ; this in NON-SPEC though + */ + if (Tot_client_num_io_files == 0) { + Tot_client_num_io_files = ((DEFAULT_BYTES_PER_OP / 1024 * total_load) + / (1024)) * files_per_megabyte; + } + Num_io_files = Tot_client_num_io_files/children + 1; + + /* + * Number of non-io files scales with load and is set at 2% of all files, + * but at least DEFAULT_NFILES worth. + */ + Tot_client_num_non_io_files = Tot_client_num_io_files * 0.02; + if (Tot_client_num_non_io_files < DEFAULT_NFILES) + Tot_client_num_non_io_files = DEFAULT_NFILES; + Num_non_io_files = Tot_client_num_non_io_files/children + 1; + + /* number of dir and symlinks base on constants */ + Num_dirs = Num_io_files/Files_per_dir + 1; + Num_symlinks = Tot_client_num_symlinks/children + 1; + + /* io operations access a subset of the files */ + Num_working_io_files = ((Num_io_files * Access_percent) / 100) + 1; + + /* non-io and other operations access all of the files */ + Num_working_non_io_files = Num_io_files; + Num_working_dirs = Num_dirs; + Num_working_symlinks = Num_symlinks; + + /* + * If we are doing a timed test, we still need an + * estimate of how many calls are needed in order to + * judge our progress. + * If we are doing a test for a number of calls, we still need an + * estimate of how long the test will take in order to + * establish the time interval between progress checks. + */ + if (Timed_run) { + /* + * the total number of calls will be divided between the children + * when they are forked off. + */ + Ops[TOTAL].target_calls = Runtime * total_load; + } else { + Runtime = (int) ((float) Ops[TOTAL].target_calls / (float) total_load); + } + + /* + * multi-client sync support + * offset the Runtime value by MULTICLIENT_OFFSET seconds. + * This offset prevents the client from finishing before + * the Prime Client tells it to 'STOP'. The MULTICLIENT_OFFSET is larger + * than the time_out value on the Prime-Client; so in case the client + * does not stop when it's told to, the Prime-client should time_out. + */ + if (Prime_client && Timed_run) + Runtime += MULTICLIENT_OFFSET; + + /* compute file set sizes */ + init_fss(); + + /* Set up synchronization and results log file */ + init_logfile(); + + /* + * setup value of nsigs + */ +#ifdef __NSIG + nsigs = __NSIG; +#endif +#ifdef _NSIG + nsigs = _NSIG; +#endif +#ifdef NSIG + nsigs = NSIG; +#endif +#if defined(SOLARIS2) && !defined(_sys_nsig) + nsigs = _sys_siglistn; +#endif + + /* Set up the signal handlers for all signals */ + +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + { + struct sigaction sig_act, old_sig_act; + + /* use XOPEN signal handling */ + + sig_act.sa_handler = generic_catcher; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + + if (DEBUG_PARENT_GENERAL) { + if (nsigs == 0) { + (void) fprintf (stderr, + "WARNING: nsigs not defined, no extra signals caught\n"); + } + for (i = 1; i < nsigs; i++) { +/* attempt to set up signal handler for these signals give an error !! K.T. */ + if (i!=SIGCHLD && i!=SIGKILL && i!=SIGSTOP && i!=SIGCONT) { + if (sigaction(i,&sig_act,&old_sig_act) == -1) { + if (errno == EINVAL) { + (void) fprintf (stderr, + "Skipping invalid signal %d\n", i); + } else { + perror("sigaction failed"); + exit(134); + } + } + } + } + } + + /* signals handlers for signals used by sfs */ + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGINT"); + exit(135); + } + + sig_act.sa_handler = sfs_alarm; + if (sigaction(SIGALRM,&sig_act,&old_sig_act) != 0) { + perror("sigaction failed: SIGALRM"); + exit(136); + } + + sig_act.sa_handler = sfs_cleanup; + if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) { + perror("sigaction failed: SIGTERM"); + exit(137); + } + + sig_act.sa_handler = sfs_startup; + if (sigaction(SIGUSR1,&sig_act,&old_sig_act) != 0) { + perror("sigaction failed: SIGUSR1"); + exit(138); + } + + sig_act.sa_handler = sfs_stop; + if (sigaction(SIGUSR2,&sig_act,&old_sig_act) != 0) { + perror("sigaction failed: SIGUSR2"); + exit(139); + } + } +#else + if (DEBUG_PARENT_GENERAL) { + if (nsigs == 0) { + (void) fprintf (stderr, + "WARNING: nsigs not defined, no extra signals caught\n"); + } + for (i = 1; i < nsigs; i++) { + if (i!=SIGCHLD) + (void) signal(i, generic_catcher); + } + } + /* signals handlers for signals used by sfs */ + (void) signal(SIGINT, sfs_cleanup); + (void) signal(SIGALRM, sfs_alarm); + (void) signal(SIGTERM, sfs_cleanup); + (void) signal(SIGUSR1, sfs_startup); + (void) signal(SIGUSR2, sfs_stop); +#endif + + /* Fork children */ + for (child_num = 0; child_num < children; child_num++) { + pid = fork(); + if (pid == -1) { + Saveerrno = errno; + (void) fprintf(stderr, "%s: can't fork children.", sfs_Myname); + errno = Saveerrno; + perror("fork"); + (void) generic_kill(0, SIGINT); + exit(140); + } else if (pid == 0) { + break; /* get out of child creation */ + } + (void) fprintf(pid_fp, "%d\n", pid); + } /* end for forking kids */ + (void) fclose(pid_fp); + + /* + * Parent: wait for kids to get ready, start them, wait for them to + * finish, read and accumulate results. + */ + if (pid != 0) { + if (setuid(Real_uid) != 0) { + (void) fprintf(stderr,"%s: %s%s\n", + sfs_Myname, "cannot perform setuid operation.\n", + "Do `make install` as root.\n"); + } + + /* I'm the parent - let the common code signal handlers know it */ + Child_num = -1; + + parent(children, total_load, mix_file, iodist_file); + + /* Clean up and exit. */ + (void) close(Log_fd); + (void) unlink(Logname); + exit(0); + + } /* parent */ + + /* + * Children : initialize, then notify parent through log file, + * wait to get signal, beat the snot out of the server, write + * stats to the log file, and exit. + */ + if (pid == 0) { + + /* I'm a child - let the common code signal handlers know it */ + Child_num = child_num; + + /* + * Determine my share of the calls and load (including any left over) + * The call target for each child differs by at most 1 call. + * The load rate for each child differs by at most 1 call/sec. + */ + Ops[TOTAL].target_calls = Ops[TOTAL].target_calls / children; + if (child_num <= Ops[TOTAL].target_calls % children) { + Ops[TOTAL].target_calls++; + } + child_load = (float) total_load / (float) children; + + /* + * Sleep a bit so the parent can catch up after procreating all us + * children. + */ + (void) sleep(10); + + child(child_num, children, child_load, argc - optind, &argv[optind]); + exit(0); + + } /* child */ + + (void) unlink(SFS_PNT_PID); + + return(0); + +} /* main */ + + +/* + * ----------------- Initalization of Parent/Child --------------------- + */ + +/* + * Open the multi-client synchronization file with append mode. + */ +static void +init_logfile(void) +{ + FILE *cl_log_fd; + int Saveerrno; + + (void) sprintf(Logname, "%s%d", CHILD_SYNC_LOG, Client_num); + Log_fd = open(Logname, (O_RDWR | O_CREAT | O_TRUNC | O_APPEND), 0666); + if (Log_fd == -1) { + Saveerrno = errno; + (void) fprintf(stderr, "%s: can't open log file %s ", sfs_Myname, Logname); + errno = Saveerrno; + perror(Logname); + exit(141); + } + if (chown(Logname, Real_uid, Cur_gid) ==-1) { + perror("chown"); + (void) fprintf(stderr, "%s: chown failed\n", sfs_Myname); + } + + /* if multi-client execution then init client sync log */ + if (Prime_client != NULL) { + /* init logfile and write process id */ + (void) sprintf(Client_logname, "%s%d", + SFS_CLIENT_SYNC_LOG, Client_num); + cl_log_fd = fopen(Client_logname, "w+"); + if (chown(Client_logname, Real_uid, Cur_gid) ==-1) { + perror("chown"); + (void) fprintf(stderr, "%s: chown failed\n", sfs_Myname); + } + if (cl_log_fd == NULL) { + Saveerrno = errno; + (void) fprintf(stderr, + "%s: can't open Client synchronization file %s ", + sfs_Myname, Client_logname); + errno = Saveerrno; + perror(Client_logname); + exit(142); + } else { + /* store parent pid */ + (void) fprintf(cl_log_fd, "%d", (int)getpid()); + (void) fclose(cl_log_fd); + } + } /* init multi-client sync log */ + +} /* init_logfile */ + +/* + * ------------------------ Utility Routines -------------------------- + */ + + +/* + * Print the program's usage message. + * Usage: sfs [-l load] [-p procs] [-w warmup] [-t time] + * [-m mix_file] [-B block_size] [-b blocksz_file] + * [-f file_set_delta] [-a access_pnct] + * [-A append_pcnt] [-D dir_cnt] [-F file_cnt] [-S symlink_cnt] + * [-d debug_level] [-i] [-P] [-T op_num] + * [-V validation_level] [-z] [-Q] + * [-R biod_reads] [-W biod_writes] + * [-M prime_client_hostname] [-N client_cnt] + */ +static void +usage(void) +{ + (void) fprintf(stderr, + "Usage: %s [-l load] [-p procs] [-w warmup] [-t time]\n", sfs_Myname); + (void) fprintf(stderr, + " [-m mix_file] [-B block_size] [-b blocksz_file]\n"); + (void) fprintf(stderr, + " [-f file_set_delta] [-a access_pnct]\n"); + (void) fprintf(stderr, + " [-A append_pcnt] [-D dir_cnt] [-F file_cnt] [-S symlink_cnt]\n"); + (void) fprintf(stderr, + " [-d debug_level] [-i] [-P] [-T op_num]\n"); + (void) fprintf(stderr, + " [-V validation_level] [-z] [-Q]\n"); + (void) fprintf(stderr, + " [-R biod_reads] [-W biod_writes]\n"); + (void) fprintf(stderr, + " [-M prime_client_hostname] [-N client_cnt]\n"); +} /* usage */ + + + +/* + * -------------- Command Line File Parsing ------------------- + */ + +/* + * Constants for mix file + */ +#define LINELEN 128 /* max bytes/line in mix file */ +#define MIX_START 0 +#define MIX_DATALINE 1 +#define MIX_DONE 2 +#define MIX_FIRSTLINE 3 + +/* + * Parse the operation mix file 'mix_file'. + * + * ORIGINAL PRE-SFS1.2 format: + * Assumes that the input file is in the same format as + * the output of the nfsstat(8) command. + * + * Uses a simple state transition to keep track of what to expect. + * Parsing is done a line at a time. + * + * State Input action New state + * MIX_START ".*nfs:.*" skip one line MIX_FIRSTLINE + * MIX_FIRSTLINE ".*[0-9]*.*" get calls MIX_DATALINE + * MIX_DATALINE "[0-9]* [0-9]*%"X6 get op counts MIX_DATALINE + * MIX_DATALINE "[0-9]* [0-9]*%"X4 get op counts MIX_DONE + * MIX_DONE EOF return + * + * We read operation counts from the mix file + * and compute our own mix percentages, + * rather than using those in the mix file. + * + * NEW SFS1.2 format version #2: + * SFS MIXFILE VERSION 2 Version header (must come first line) + * "^#.*" Comment (any line except first) + * "%s [0-9]*%" Op name Op percentage + */ +static int +setmix( + char * mix_file) +{ + int state; /* current state of state machine */ + int got; /* number of items read from input line */ + int opnum; /* operation number index */ + int calls; /* total number of calls in mix */ + char line[LINELEN]; /* input line buffer */ + char op_name[LINELEN]; /* name buffer */ + int mix_pcnt; + unsigned int len; /* length of input line */ + FILE *mix_fp; /* mix file */ + int vers; /* mix file version number */ + sfs_op_type *op_ptr; + + if ((mix_fp = fopen(mix_file, "r")) == NULL) { + (void) fprintf(stderr, "%s: bad mix file", sfs_Myname); + perror(mix_file); + return(-1); + } + + if (fgets(line, LINELEN, mix_fp) == NULL) { + (void) fprintf(stderr, "%s: bad mix format - unexpected empty file\n", + sfs_Myname); + (void) fclose(mix_fp); + return (-1); + } + + opnum = 0; + + /* + * Look for initial version string + */ + got = sscanf(line, "SFS MIXFILE VERSION %d", &vers); + if (got != 1) { + /* + * Check to see if this is old mixfile + */ + len = strlen(line); + if (len < 4 || lad_substr(line, "nfs:") == 0) { + (void) fprintf(stderr, "%s: bad mix format - initial line '%s'\n", + sfs_Myname, line); + (void) fclose(mix_fp); + return (-1); + } + vers = 1; + } + + if (vers == 1) { + /* + * Old style mix file + */ + state = MIX_START; + while (state != MIX_DONE && fgets(line, LINELEN, mix_fp)) { + + switch (state) { + case MIX_START: + /* + * Ate first line after nfs: + */ + state = MIX_FIRSTLINE; + break; + + case MIX_FIRSTLINE: + got = sscanf(line, "%d", &calls); + if (got != 1) { + (void) fprintf(stderr, + "%s: bad mix format - can't find 'calls' value %d\n", + sfs_Myname,got); + (void) fclose(mix_fp); + return (-1); + } + if (fgets(line, LINELEN, mix_fp) == NULL) { + (void) fprintf(stderr, + "%s: bad mix format - unexpected EOF after 'calls'\n", + sfs_Myname); + (void) fclose(mix_fp); + return (-1); + } + state = MIX_DATALINE; + break; + + case MIX_DATALINE: + got = sscanf(line, + "%d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%%", + &Ops[opnum].mix_pcnt, + &Ops[opnum + 1].mix_pcnt, + &Ops[opnum + 2].mix_pcnt, + &Ops[opnum + 3].mix_pcnt, + &Ops[opnum + 4].mix_pcnt, + &Ops[opnum + 5].mix_pcnt, + &Ops[opnum + 6].mix_pcnt); + + if (got == 4 && opnum == 14) { + /* looks like the last line */ + state = MIX_DONE; + } else if (got == 7) { + opnum += 7; + if (fgets(line, LINELEN, mix_fp) == NULL) { + (void) fprintf(stderr, + "%s: bad mix format - unexpected EOF after 'calls'\n", + sfs_Myname); + (void) fclose(mix_fp); + return (-1); + } + } else { + (void) fprintf(stderr, + "%s: bad mix format - can't find %d op values\n", + sfs_Myname, got); + (void) fclose(mix_fp); + return (-1); + } + break; + + default: + (void) fprintf(stderr, + "%s: error parsing mix file - bad state %d\n", + sfs_Myname, state); + (void) fclose(mix_fp); + return (-1); + } /* end switch on state */ + } /* end while there are lines to read */ + + if (state != MIX_DONE) { + (void) fprintf(stderr, "%s: bad mix format - unexpected EOF\n", + sfs_Myname); + (void) fclose(mix_fp); + return (-1); + } + for (opnum = 0; opnum < NOPS; opnum++) { + Ops[opnum].mix_pcnt = Ops[opnum].mix_pcnt * 100 / calls + + ((Ops[opnum].mix_pcnt * 1000 / calls % 10) >= 5); + } + (void) fclose(mix_fp); + return (0); + } + if (vers == 2) { + /* + * New style mix file + */ + while (fgets(line, LINELEN, mix_fp) != NULL) { + if (line[0] == '#') /* Comment line */ + continue; + got = sscanf(line, "%s %d", op_name, &mix_pcnt); + if (got != 2) { + (void) fprintf(stderr, + "%s: bad mix format - can't find op values: %s\n", + sfs_Myname, line); + (void) fclose(mix_fp); + return (-1); + } + op_ptr = Ops; + while (strcmp(op_ptr->name, "TOTAL") != 0) { + if (strcmp(op_ptr->name, op_name) == 0) { + op_ptr->mix_pcnt = mix_pcnt; + break; + } + op_ptr++; + } + if (strcmp(op_ptr->name, "TOTAL") == 0) { + (void) fprintf(stderr, + "%s: unknown op name: %s\n", + sfs_Myname, op_name); + (void) fclose(mix_fp); + return (-1); + } + } + /* + * Make sure that the total mix percentages == 100 + */ + op_ptr = Ops; + mix_pcnt = 0; + while (strcmp(op_ptr->name, "TOTAL") != 0) { + mix_pcnt += op_ptr->mix_pcnt; + op_ptr++; + } + if (mix_pcnt != 100) { + (void) fprintf(stderr, + "%s: WARNING total mix percentage %d != 100\n", + sfs_Myname, mix_pcnt); + } + (void) fclose(mix_fp); + return (0); + } + + (void) fprintf(stderr, "%s: Unknown mix file version number %d\n", + sfs_Myname, vers); + (void) fclose(mix_fp); + return (-1); +} /* setmix */ + + +/* + * Parse the block I/O distribution file 'fp'. + */ +static int +setiodist( + FILE * fp) +{ + int i; + + /* first, read and parse the i/o distribution file for syntax and size */ + if (parseiodist(fp, 1) == -1) { + exit(143); + } + (void) fseek(fp, 0, SEEK_SET); + + /* read the i/o distribution file into the i/o dist table */ + if (parseiodist(fp, 2) == -1) { + exit(144); + } + + if (DEBUG_PARENT_GENERAL) { + (void) fprintf(stdout, "I/o Distribution Table\n"); + (void) fprintf(stdout, "Read:\n"); + (void) fprintf(stdout, "\tpcnt bufs frags\n"); + for (i = 0 ; ; i++) { + (void) fprintf(stdout, "\t%4d %4d %5d\n", Io_dist_ptr->read[i].pcnt, + Io_dist_ptr->read[i].bufs, Io_dist_ptr->read[i].frags); + if (Io_dist_ptr->read[i].pcnt == 100) + break; + } + + (void) fprintf(stdout, "Write:\n"); + (void) fprintf(stdout, "\tpcnt bufs frags\n"); + for (i = 0; ; i++) { + (void) fprintf(stdout, "\t%4d %4d %5d\n", + Io_dist_ptr->write[i].pcnt, + Io_dist_ptr->write[i].bufs, + Io_dist_ptr->write[i].frags); + if (Io_dist_ptr->write[i].pcnt == 100) + break; + } + (void) fprintf(stdout, "Maximum file size: %d KB (%d * %d KB)\n", + Io_dist_ptr->max_bufs * Kb_per_block, + Io_dist_ptr->max_bufs, Kb_per_block); + } + return(0); +} /* setiodist */ + + +/* + * Block/File Distribution file parser. + * Assumes that the input file is in the following format: + * + * + * READ_KEY_WORD + * percent block_cnt fragment_flag + * . . . + * . . . + * . . . + * WRITE_KEY_WORD + * percent block_cnt fragment_flag + * . . . + * . . . + * . . . + * + * + * Notes: + * - The READ_KEY_WORD is "Read", the WRITE_KEY_WORD is "Write". + * - For each key word, the percent fields must sum to 100. + * - Fragment is either true (1) or false (0) + * - Maximum file size (and transfer size) is the largest + * eight_k_cnt * 8KB plus 7 Kb for fragments + * + * + * Uses a simple state transition to keep track of what to expect. + * Parsing is done a line at a time. + * + * State Input action New state + * ----- -------------------- ------------- --------- + * START "Read" skip one line READ + * START "Write" skip one line WRITE + * READ "[0-9]* [0-9]* [01]" get values READ + * READ "Write" skip one line WRITE + * WRITE "[0-9]* [0-9]* [01]" get values WRITE + * WRITE "Read" skip one line READ + * DONE EOF return + * + * Pass 1 reads the file and allocates table space. + * Pass 2 reads the file data into the tables. + */ +static int +parseiodist( + FILE * fp, + int pass) +{ + int state; /* current state of state machine */ + int got; /* number of items read from input line */ + int pcnt; /* percent read from input line */ + int bufs; /* eight_kb_buffer_cnt read from input line */ + int frags; /* fragment flag read from input line */ + int rbucket; /* current read distribution table bucket */ + int wbucket; /* current write distribution table bucket */ + int rpcnt; /* cumulative percent for read buckets */ + int wpcnt; /* cumulative percent for write buckets */ + char key[5]; /* keyword buffer */ + char line[LINELEN]; /* input line buffer */ + int nextline; + + /* + * Pass 1 reads, sizes, and error checks the input + * and then allocates space for the distribution tables. + * Pass 2 reads the input into the allocated tables. + */ + + rbucket = 0; + wbucket = 0; + rpcnt = 0; + wpcnt = 0; + state = IO_DIST_START; + + while (fgets(line, LINELEN, fp)) { + + nextline = 0; + while (nextline == 0) { + + if (state == IO_DIST_READ) { + got = sscanf(line, "%d %d %d", &pcnt, &bufs, &frags); + if (got != 3) { + state = IO_DIST_START; + continue; /* same line, but goto new state */ + } + if (pass == 1) { + rbucket++; + rpcnt += pcnt; + if (frags != 0 && frags != 1) { + (void) fprintf(stderr, + "%s: bad i/o dist format - bad fragment value\n", + sfs_Myname); + return(-1); + } + } else { + rpcnt += pcnt; + Io_dist_ptr->read[rbucket].pcnt = rpcnt; + Io_dist_ptr->read[rbucket].bufs = bufs; + Io_dist_ptr->read[rbucket].frags = frags; + rbucket++; + } + if (DEBUG_CHILD_FILES) { + (void) fprintf(stdout, "p=%d b=%d f=%d rpcnt=%d\n", + pcnt, bufs, frags, rpcnt); + (void) fflush(stdout); + } + + /* read next line in file */ + nextline++; + break; + } + + if (state == IO_DIST_WRITE) { + got = sscanf(line, "%d %d %d", &pcnt, &bufs, &frags); + if (got != 3) { + state = IO_DIST_START; + continue; /* same line, but goto new state */ + } + if (pass == 1) { + wbucket++; + wpcnt += pcnt; + if (frags != 0 && frags != 1) { + (void) fprintf(stderr, + "%s: bad i/o dist format - bad fragment value\n", + sfs_Myname); + return(-1); + } + } else { + wpcnt += pcnt; + Io_dist_ptr->write[wbucket].pcnt = wpcnt; + Io_dist_ptr->write[wbucket].bufs = bufs; + Io_dist_ptr->write[wbucket].frags = frags; + wbucket++; + } + if (DEBUG_CHILD_FILES) { + (void) fprintf(stdout, "p=%d b=%d f=%d wpcnt=%d\n", + pcnt, bufs, frags, wpcnt); + (void) fflush(stdout); + } + /* read next line in file */ + nextline++; + break; + } + + if (state == IO_DIST_START) { + got = sscanf(line, "%s", key); + if (got != 1 || (strlen(key) != 5)){ + (void) fprintf(stderr, + "%s: bad i/o dist format - invalid keyword %s\n", + sfs_Myname, key); + return(-1); + } + if (!strcmp(key, "Read") || !strcmp(key, "read")) { + if (rbucket != 0) { + (void) fprintf(stderr, + "%s: bad i/o dist format - too many read keywords\n", + sfs_Myname); + return(-1); + } + rpcnt = 0; + state = IO_DIST_READ; + + /* read next line in file */ + nextline++; + break; + } + if (!strcmp(key, "Write") || !strcmp(key, "write")) { + if (wbucket != 0) { + (void) fprintf(stderr, + "%s: bad i/o dist format - too many write keywords\n", + sfs_Myname); + return(-1); + } + wpcnt = 0; + state = IO_DIST_WRITE; + + /* read next line in file */ + nextline++; + break; + } + (void) fprintf(stderr, + "%s: bad i/o dist format - unknown keyword %s\n", + sfs_Myname, key); + return(-1); + } + + } /* end while processing this line */ + } /* end while more lines */ + + if (pass == 1) { + + /* error check the input */ + if (rbucket == 0) { + (void) fprintf(stderr, + "%s: bad i/o dist format - no read distribution data\n", + sfs_Myname); + return(-1); + } + if (rpcnt != 100) { + (void) fprintf(stderr, + "%s: bad i/o dist format - read total percent != 100\n", + sfs_Myname); + return(-1); + } + if (wbucket == 0) { + (void) fprintf(stderr, + "%s: bad i/o dist format - no write distribution data\n", + sfs_Myname); + return(-1); + } + if (wpcnt != 100) { + (void) fprintf(stderr, + "%s: bad i/o dist format - write percent total != 100\n", + sfs_Myname); + return(-1); + } + + /* allocate space for the table */ + if ((Io_dist_ptr = (sfs_io_dist_type *) + malloc(sizeof(sfs_io_dist_type))) == NULL) { + (void) fprintf(stderr, + "%s: block i/o distribution table allocation failed\n", + sfs_Myname); + return(-1); + } + if ((Io_dist_ptr->read = (sfs_io_op_dist_type *) + malloc(rbucket*sizeof(sfs_io_op_dist_type))) == NULL) { + (void) fprintf(stderr, + "%s: read distribution table allocation failed\n", sfs_Myname); + return(-1); + } + if ((Io_dist_ptr->write = (sfs_io_op_dist_type *) + malloc(wbucket*sizeof(sfs_io_op_dist_type)))==NULL) { + (void) fprintf(stderr, + "%s: write distribution table allocation failed\n", sfs_Myname); + return(-1); + } + + } + return(0); + +} /* parseiodist */ + + +/* + * Compute the max i/o transfer size and average ops per request + * for the block transfer distribution table. + */ +static void +init_iodist( + sfs_io_dist_type * io_dist_ptr) +{ + int max_bufs; + double weighted_ops; + double previous_pcnt; + int i; + + /* + * compute expected number of ops for multi-op requests. + * the calculation assumes that if a i/o distribution table + * entry specifies that a fragment is to be generated, then + * exactly one OTW operation will result. + */ + max_bufs = 0; + + weighted_ops = 0.0; + previous_pcnt = 0.0; + for (i = 0; ; i++) { + weighted_ops += (io_dist_ptr->read[i].pcnt - previous_pcnt) * + (io_dist_ptr->read[i].bufs + io_dist_ptr->read[i].frags); + previous_pcnt = io_dist_ptr->read[i].pcnt; + if (io_dist_ptr->read[i].bufs > max_bufs) + max_bufs = io_dist_ptr->read[i].bufs; + if (io_dist_ptr->read[i].pcnt == 100) + break; + } + io_dist_ptr->avg_ops_per_read_req = weighted_ops / 100.0; + + weighted_ops = 0.0; + previous_pcnt = 0.0; + for (i = 0; ; i++) { + weighted_ops += (io_dist_ptr->write[i].pcnt - previous_pcnt) * + (io_dist_ptr->write[i].bufs + io_dist_ptr->write[i].frags); + previous_pcnt = io_dist_ptr->write[i].pcnt; + if (io_dist_ptr->write[i].bufs > max_bufs) + max_bufs = io_dist_ptr->write[i].bufs; + if (io_dist_ptr->write[i].pcnt == 100) + break; + } + io_dist_ptr->avg_ops_per_write_req = weighted_ops / 100.0; + + io_dist_ptr->max_bufs = max_bufs + 1; + +} /* init_iodist */ + +static void +init_filedist() +{ + int i; + int cur_pcnt; + int num_files = 0; + int prev_pcnt = 0; + int tot_size = 0; + + /* + * Calculate the average number of bytes per file + */ + for (i = 0; Default_file_size_dist[i].size != 0; i++) { + cur_pcnt = Default_file_size_dist[i].pcnt - prev_pcnt; + num_files += cur_pcnt; + tot_size += (Default_file_size_dist[i].size * 1024) * cur_pcnt; + prev_pcnt = Default_file_size_dist[i].pcnt; + } + + avg_bytes_per_file = tot_size / num_files; + files_per_megabyte = (((1024*1024) + avg_bytes_per_file) \ + / avg_bytes_per_file); +} + +static void +init_fss() +{ + int Delta_fss_bytes; + + Base_fss_bytes = Num_working_io_files * (avg_bytes_per_file / 1024); + Total_fss_bytes = Num_io_files * (avg_bytes_per_file / 1024); + Cur_fss_bytes = Base_fss_bytes; + Delta_fss_bytes = (Base_fss_bytes * Fss_delta_percent) / 100; + Limit_fss_bytes = Base_fss_bytes + Delta_fss_bytes; + Most_fss_bytes = Base_fss_bytes; + Least_fss_bytes = Base_fss_bytes; +} + +/* + * return true if 'sp' contains the substring 'subsp', false otherwise + */ +static int +lad_substr( + char * sp, + char * subsp) +{ + unsigned int found; + int want; + char * s2; + + if (sp == NULL || subsp == NULL) { + return (0); + } + + want = strlen(subsp); + + while (*sp != '\0') { + while (*sp != *subsp && *sp != '\0') { + sp++; + } + found = 0; + s2 = subsp; + while (*sp == *s2 && *sp != '\0') { + sp++; + s2++; + found++; + } + if (found == want) { + return (1); + } + } + return (0); + +} /* lad_substr */ + +/* + * Check the gettimeofday() resolution. If the resolution + * is in chunks bigger than SFS_MIN_RES then the client + * does not have a usable resolution for running the + * benchmark. + */ +static void +check_clock(void) +{ + double time_res; + char tmp_hostname[HOSTNAME_LEN]; + + time_res = get_resolution(); + getmyhostname(tmp_hostname, HOSTNAME_LEN); + if( time_res > (double)SFS_MIN_RES ) + { + (void) fprintf(stderr, + "\n%s: Clock resolution too poor to obtain valid results.\n", + tmp_hostname); + (void) fprintf(stderr, + "%s: Clock resolution %f Micro seconds.\n", tmp_hostname, + time_res); + exit(175); + } + else + { + (void) fprintf(stderr, + "\n%s: Good clock resolution [ %f ] Micro seconds.\n", + tmp_hostname, time_res); + } +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns the resolution of the gettimeofday() function + * in microseconds. + */ +static double +get_resolution(void) +{ + double starttime, finishtime, besttime; + long j,delay; + int k; + + finishtime=time_so_far1(); /* Warm up the instruction cache */ + starttime=time_so_far1(); /* Warm up the instruction cache */ + delay=j=0; /* Warm up the data cache */ + for(k=0;k<10;k++) + { + while(1) + { + starttime=time_so_far1(); + for(j=0;j< delay;j++) + ; + finishtime=time_so_far1(); + if(starttime==finishtime) + delay++; + else + { + if(k==0) + besttime=(finishtime-starttime); + if((finishtime-starttime) < besttime) + besttime=(finishtime-starttime); + break; + } + } + } + return(besttime); +} + +/* + * Lifted code from Iozone with permission from author. (Don Capps) + * Returns current result of gettimeofday() in microseconds. + */ +/************************************************************************/ +/* Time measurement routines. */ +/* Return time in microseconds */ +/************************************************************************/ + +static double +time_so_far1(void) +{ + /* For Windows the time_of_day() is useless. It increments in 55 */ + /* milli second increments. By using the Win32api one can get */ + /* access to the high performance measurement interfaces. */ + /* With this one can get back into the 8 to 9 microsecond */ + /* resolution. */ +#ifdef Windows + LARGE_INTEGER freq,counter; + double wintime; + double bigcounter; + + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&counter); + bigcounter=(double)counter.HighPart *(double)0xffffffff + + (double)counter.LowPart; + wintime = (double)(bigcounter/(double)freq.LowPart); + return((double)wintime*1000000.0); +#else +#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5) + struct timespec gp; + + if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1) + perror("getclock"); + return (( (double) (gp.tv_sec)*1000000.0) + + ( ((float)(gp.tv_nsec)) * 0.001 )); +#else + struct timeval tp; + + if (gettimeofday(&tp, (struct timezone *) NULL) == -1) + perror("gettimeofday"); + return ((double) (tp.tv_sec)*1000000.0) + + (((double) tp.tv_usec) ); +#endif +#endif +} + +/* sfs_c_chd.c */ +/* sfs_c_man.c */ + diff --git a/TBBT/trace_play/sfs_c_mnt.c b/TBBT/trace_play/sfs_c_mnt.c new file mode 100644 index 0000000..8f60387 --- /dev/null +++ b/TBBT/trace_play/sfs_c_mnt.c @@ -0,0 +1,573 @@ +#ifndef lint +static char sfs_c_mntSid[] = "@(#)sfs_c_mnt.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ---------------------- sfs_c_mnt.c --------------------- + * + * The sfs child. Routines to handle mount points. + * + *.Exported_Routines + * void init_mount_point(int, char *, CLIENT *) + * + *.Local_Routines + * int pseudo_mount(char *, int, char *, CLIENT *) + * + *.Revision_History + * 2-Jul-92 Teelucksingh Added code for OSF/1 + * use of getmntinfo(). + * 16-Dec-91 Wittle Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <fcntl.h> + +#include <unistd.h> + +#include "sfs_c_def.h" + +struct hostent *Server_hostent; + +/* + * ------------------------- Constants ------------------------- + */ + +/* + * Number of times a load generating process will retry amount. + * Each load generating process also picks a client shifted + * mount start time, and executes a backoff on retry time on + * failure. + */ +#define NUMBER_MOUNT_RETRIES 10 + +/* + * ------------------------- External Definitions ------------------------- + */ + +/* forward definitions for local routines */ +CLIENT * lad_getmnt_hand(char *); +static int pseudo_mount(char *, int, char *, CLIENT *); + +/* + * Mounts are retried when an RPC timeout occurs, in the mainline + * code. They are not retried by the RPC clnt_call routine, as the + * timeout values are set now. + */ +static struct timeval Mount_timer = { 10, 0 }; + + +/* + * ------------------------- Mount Point Routines ------------------------- + */ + + +/* + * mount the testdir 'dirnum' under the parent directory 'parentdir'. + */ +void +init_mount_point( + int dirnum, + char * parentdir, + CLIENT * mount_client_ptr) +{ + char pnt_dir[SFS_MAXPATHLEN]; /* test dir component name */ + char testdirname[SFS_MAXPATHLEN]; /* test dir component name */ + char export_fsname[SFS_MAXPATHLEN]; /* "host:path" exported fs */ + sfs_fh_type file_handle; + char *fh_ptr; + char *cp; + int ret; + sfs_fh_data *fh_datap, *Ex_fh_datap; + + fh_datap= calloc(1,sizeof(sfs_fh_data)); + (void) memset((char *)fh_datap, 0, sizeof(sfs_fh_data)); + (void) memset((char *)&file_handle, 0, sizeof(file_handle)); + file_handle.fh_data = fh_datap; + file_handle.dir = &Export_dir; + + Ex_fh_datap = (sfs_fh_data *) calloc(1,sizeof(sfs_fh_data)); + Export_dir.fh_data = Ex_fh_datap; + + (void) strcpy(pnt_dir, parentdir); + + cp = strchr(pnt_dir, ':'); + if (cp == NULL) { + (void) fprintf(stderr, "%s: malformed fsname %s\n", + sfs_Myname, parentdir); + if (!Validate) + (void) generic_kill(0, SIGINT); + exit(86); + } + + *cp++ = '\0'; + + /* + * Now we have the parent directory in the form: + * host:host_path + * + * First we get the file handle for parent directory + * + * Verify that the server is running the correct version of + * the NFS protocol specification and then proceed to get + * the exported fh from the server. + */ + (void) strcpy(testdirname, pnt_dir); + (void) strcat(testdirname, ":"); + (void) strcat(testdirname, cp); + (void) strcpy(export_fsname, testdirname); + + if (nfs_version == NFS_VERSION) { + (void) memset((char *) &Export_dir.fh2, '\0', sizeof (Export_dir.fh2)); + fh_ptr = (char *)&Export_dir.fh2; + } else if (nfs_version == NFS_V3) { + (void) memset((char *) &Export_dir.fh3, '\0', sizeof (Export_dir.fh3)); + fh_ptr = (char *)&Export_dir.fh3; + } + + ret = pseudo_mount(export_fsname, nfs_version, + fh_ptr, mount_client_ptr); + if (ret < 0) { + if (ret == -2) { + (void) fprintf(stderr, + "%s: NFS Protocol Version %lu verification failed.\n", + sfs_Myname, (uint32_t)nfs_version); + } + else { + (void) fprintf(stderr, "%s: can't pseudo mount %s\n", + sfs_Myname, export_fsname); + } + if (!Validate) + (void) generic_kill(0, SIGINT); + exit(87); + } + + /* + * Setup initial state of export directory + */ + Export_dir.state = Exists; + (void) strcpy(Export_dir.file_name, testdirname); + Export_dir.dir = &Export_dir; + +#ifndef RFS + /* + * Check for and create the client directory. Stat it first, if not + * there then mkdir, if that fails with EEXIST we lost the race but + * that's OK. + */ + if (Validate) { + (void) sprintf(testdirname, "%s", "validatedir"); + } else { + (void) sprintf(testdirname, "CL%d", Client_num); + } + + if ((ret = lad_lookup(&file_handle, testdirname)) == -1) { + if (!Validate) + (void) generic_kill(0, SIGINT); + exit(88); + } + + if (ret == 1) { + /* + * Directory doesn't exist so create it + * if it already exists thats OK + */ + if ((ret = lad_mkdir(&file_handle, testdirname)) == -1) { + if (!Validate) + (void) generic_kill(0, SIGINT); + exit(89); + } + /* + * If someone else created this out from underneath us simply + * lookup the result and continue on. + */ + if (ret != 0 && (ret = lad_lookup(&file_handle, testdirname)) == -1) { + if (!Validate) + (void) generic_kill(0, SIGINT); + exit(90); + } + } + + /* testdirname now exists, verify it is a directory and writeable */ + if (!fh_isdir(&file_handle) || + (check_fh_access(&file_handle) == -1)) { + (void) fprintf(stderr, + "%s: %s is either not a directory or not accessible\n", + sfs_Myname, testdirname); + if (!Validate) + (void) generic_kill(0, SIGINT); + exit(91); + } + + /* + * logically chdir into CL directory + */ + /* Export_dir = file_handle; Implied bcopy here */ + (void) memmove(&Export_dir,&file_handle,sizeof(sfs_fh_type)); + Export_dir.fh_data = Ex_fh_datap; + (void ) memmove(Export_dir.fh_data, file_handle.fh_data, + sizeof(sfs_fh_data)); + (void) memset((char *)&file_handle, 0, sizeof(file_handle)); + (void) memset((char *)fh_datap, 0, sizeof(sfs_fh_data)); + file_handle.fh_data = fh_datap; + file_handle.dir = &Export_dir; + + /* + * Validation only occurs one directory deep so we can exit early + */ + if (Validate) + return; + + (void) sprintf(testdirname, "testdir%d", dirnum); + + if ((ret = lad_lookup(&file_handle, testdirname)) == -1) { + (void) generic_kill(0, SIGINT); + exit(92); + } + + if (ret == 1) { + /* + * Directory doesn't exist so create it + */ + if (lad_mkdir(&file_handle, testdirname) != 0) { + (void) fprintf(stderr, "%s: Unable to create %s\n", + sfs_Myname, testdirname); + (void) generic_kill(0, SIGINT); + exit(93); + } + } + + /* testdirname now exists, verify it is a directory and writeable */ + if (!fh_isdir(&file_handle) || + (check_fh_access(&file_handle) == -1)) { + (void) fprintf(stderr, + "%s: %s is either not a directory or not accessible\n", + sfs_Myname, testdirname); + (void) generic_kill(0, SIGINT); + exit(94); + } + + /* + * logically chdir into testdir directory + */ + /* Export_dir = file_handle;*/ + (void) memmove(&Export_dir, &file_handle, sizeof(struct sfs_fh_type)); + Export_dir.fh_data = Ex_fh_datap; /* Put pointer back */ + (void) memmove(Export_dir.fh_data, file_handle.fh_data, sizeof + (sfs_fh_data)); +#endif +} /* init_mount_point */ + +/* + * Get the filehandle for 'mount_fsname', and return it + * Returns NULL for error ... not mounted || no NFS client. + * + * Children should only call this routine 1 time. + */ +CLIENT * +lad_getmnt_hand( + char * mount_point) +{ + char mnt_pnt[SFS_MAXPATHLEN]; /* working buffer */ + char host[SFS_MAXPATHLEN]; /* host with exported fs */ + static struct hostent hp; + struct hostent *thp; + CLIENT *mount_client_ptr; /* Mount client handle */ + char *cp; + int rpc_result; /* rpc call result */ + uint32_t mount_vers = 0; + + /* + * If the mount point is of the form host:path just use the explicit + * name instead of grovelling through the mount table. + */ + (void) strcpy(mnt_pnt, mount_point); + cp = strchr(mnt_pnt, ':'); + if (cp == NULL) { + (void) fprintf(stderr, "%s: malformed fsname %s\n", + sfs_Myname, mount_point); + return(NULL); + } + + *cp++ = '\0'; + (void) strcpy(host, mnt_pnt); + + /* Verify NFS Version */ + rpc_result = callrpc(host, + (uint32_t) NFS_PROGRAM, + (uint32_t) nfs_version, + (uint32_t) NFSPROC_NULL, (xdrproc_t) xdr_void, + (char *) NULL, (xdrproc_t) xdr_void, (char *) NULL); + if (rpc_result != 0) { + clnt_perrno((enum clnt_stat)rpc_result); + (void) fprintf(stderr, +"\nUnable to contact NFS server %s.\n", host); + (void) fprintf(stderr, +"Verify NFS server daemon supporting version %u is running and\n", + (uint32_t)nfs_version); + (void) fprintf(stderr, "registered with the portmapper.\n"); + return(NULL); + } + + /* Get host's address */ + if ((thp = gethostbyname(host)) == NULL) { + /* Failure may be due to yellow pages, try again */ + if ((thp = gethostbyname(host)) == NULL) { + (void) fprintf(stderr, "%s: %s not in hosts database\n", + sfs_Myname, host); + return(NULL); + } + } + + hp = *thp; + Server_hostent = &hp; + + if (nfs_version == NFS_VERSION) + mount_vers = MOUNTVERS; + if (nfs_version == NFS_V3) + mount_vers = MOUNTVER3; + + mount_client_ptr = lad_clnt_create(0, Server_hostent, + (uint32_t) MOUNTPROG, + mount_vers, + RPC_ANYSOCK, &Mount_timer); + + + if (mount_client_ptr == ((CLIENT*) NULL)) { + (void) fprintf(stderr, + "%s: portmap/mountd %s server not responding", + sfs_Myname, mount_point); + return(NULL); + } + + mount_client_ptr->cl_auth = authunix_create_default(); + return (mount_client_ptr); + +} /* lad_getmnt_hand */ + + +/* + * Get the filehandle for 'mount_fsname', and return it in 'fh_ptr'. + * Returns 0 for OK, -1 for error ... not mounted || no NFS client. + * + * Children should only call this routine 1 time. + */ +static int +pseudo_mount( + char * mount_fsname, + int version, + char * fh_ptr, + CLIENT * mount_client_ptr) +{ + char * host_ptr; /* host with exported fs */ + char * path_ptr; /* ptr to path for RPC */ + + struct fhstatus fhs; /* status of mountd call */ + nfs_fh3 * fh_ptr3; + mountres3 mntres3; /* status of mountd call */ + char * cp; + enum clnt_stat rpc_stat; + int tries = 0; /* Number of retries */ + /* Space by 200ms intervals. */ + int pacesleep = Child_num * 200; + + /* Parse the fsname for host and path strings */ + cp = strchr(mount_fsname, ':'); + + if (cp == NULL) { + (void) fprintf(stderr, "%s: malformed fsname %s\n", + sfs_Myname, mount_fsname); + return(-1); + } + + *cp++ = '\0'; + host_ptr = mount_fsname; + path_ptr = cp; + + /* Check host's address */ + if (gethostbyname(host_ptr) == NULL) { + /* Failure may be due to yellow pages, try again */ + if (gethostbyname(host_ptr) == NULL) { + (void) fprintf(stderr, "%s: %s not in hosts database\n", + sfs_Myname, host_ptr); + return(-1); + } + } + + if (DEBUG_CHILD_GENERAL) { + (void) fprintf(stderr, "%s: mount clnt_call\n", sfs_Myname); + } + + /* get fhandle of remote path from host's mountd */ + +retry_mount: + /* + * Many children on many clients hammer a server with + * mounts. Crude fix is to pace them. Some run rule interpretations + * are to have *many* children on each client. This can + * cause problems. + */ + (void) msec_sleep(pacesleep); + + if (version == NFS_VERSION) { + (void) memset((char *) &fhs, '\0', sizeof (fhs)); + rpc_stat = clnt_call(mount_client_ptr, MOUNTPROC_MNT, xdr_path, + (char *) &path_ptr,xdr_fhstatus,(char *) &fhs, + Mount_timer); + } else if (version == NFS_V3) { + (void) memset((char *) &mntres3, '\0', sizeof (mntres3)); + rpc_stat = clnt_call(mount_client_ptr, MOUNTPROC_MNT, xdr_dirpath, + (char *) &path_ptr, xdr_mntres3, (char *) &mntres3, + Mount_timer); + } else + rpc_stat = RPC_PROGVERSMISMATCH; + + errno = 0; + if (rpc_stat != RPC_SUCCESS) { + + switch (rpc_stat) { + + case RPC_TIMEDOUT: + errno = ETIMEDOUT; + (void) fprintf(stderr, + "%s: mounting %s:%s server not responding: %s (%d)\n", + sfs_Myname, host_ptr, path_ptr, + strerror(errno), errno); + if (tries++ < NUMBER_MOUNT_RETRIES) { + /* Randomize the backoff on retry */ + pacesleep = pacesleep + (sfs_random() % 2000); + goto retry_mount; + } + break; + + + case RPC_PMAPFAILURE: + errno = ENETDOWN; /* reasonable error */ + (void) fprintf(stderr, + "%s: mounting %s portmap call failed: %s (%d)\n", + sfs_Myname, host_ptr, strerror(errno), errno); + break; + + case RPC_PROGNOTREGISTERED: + errno = ENETDOWN; /* reasonable error */ + (void) fprintf(stderr, + "%s: mounting %s nfsd not registered: %s (%d)\n", + sfs_Myname, host_ptr, strerror(errno), errno); + break; + + case RPC_AUTHERROR: + errno = EACCES; + (void) fprintf(stderr, + "%s: mounting %s authentication failed: %s (%d)\n", + sfs_Myname, host_ptr, strerror(errno), errno); + break; + + default: + errno = ENETDOWN; /* reasonable error */ + (void) fprintf(stderr, + "%s: mounting %s:%s failed: %s (%d)\n", + sfs_Myname, host_ptr, path_ptr, + strerror(errno), errno); + break; + } + + clnt_perror(mount_client_ptr, ""); + return(-1); + + } /* MOUNTPROC_MNT call failed */ + + if (version == NFS_VERSION) { + if (fhs.fhs_status != 0) { + if (fhs.fhs_status == EACCES) { + (void) fprintf(stderr, "%s: mounting %s:%s - access denied\n", + sfs_Myname, host_ptr, path_ptr); + } else { + (void) fprintf(stderr, + "%s: mounting %s:%s - bad fh status %d\n ", + sfs_Myname, host_ptr, path_ptr, fhs.fhs_status); + } + return(-1); + } /* bad fhs status */ + + /* + * fill in the caller's file handle + */ + (void) memmove(fh_ptr, (char *) &fhs.fhs_fh, NFS_FHSIZE); + + } else if (version == NFS_V3) { + + if (mntres3.fhs_status != MNT_OK) { + if (mntres3.fhs_status == MNT3ERR_ACCES) { + (void) fprintf(stderr, "%s: mounting %s:%s - access denied\n", + sfs_Myname, host_ptr, path_ptr); + } else { + (void) fprintf(stderr, + "%s: mounting %s:%s - bad fh status %d\n ", + sfs_Myname, host_ptr, path_ptr, mntres3.fhs_status); + } + return(-1); + } /* bad fhs status */ + + /* + * fill in the caller's file handle + * space pointed by fhandle3_val is allocated through xdr_mntres3 + */ + fh_ptr3 = (nfs_fh3 *)fh_ptr; + fh_ptr3->fh3_length = mntres3.mntres3_u.mntinfo.fhandle.fhandle3_len; + (void) memmove((char *) fh_ptr3->fh3_u.data, + (char *) mntres3.mntres3_u.mntinfo.fhandle.fhandle3_val, + fh_ptr3->fh3_length); + } + + return(0); + +} /* pseudo_mount */ + + +/* sfs_c_mnt.c */ diff --git a/TBBT/trace_play/sfs_c_nfs.h b/TBBT/trace_play/sfs_c_nfs.h new file mode 100644 index 0000000..eb34652 --- /dev/null +++ b/TBBT/trace_play/sfs_c_nfs.h @@ -0,0 +1,1470 @@ +#ifndef __sfs_c_nfs_h +#define __sfs_c_nfs_h + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * @(#)sfs_c_nfs.h 2.1 97/10/23 + * + * -------------------------- sfs_c_nfs.h ------------------------- + * + * Literals and types for the NFS protocol, Version 2. + * + *.Revision_History + * + * Richard Bean 17-May-90 Created. + */ + + +/* + * for RPC calls + */ +/* #define NFS_PORT 2049 */ +#define NFS_PROGRAM ((uint32_t)100003) +#define NFS_VERSION ((uint32_t)2) +#define NFS_V3 ((uint32_t)3) + +/* + * fixed sizes + */ +#define NFS_MAXDATA 8192 +//#define NFS_MAXDATA 16384 +#define NFS_MAXPATHLEN 1024 +#define NFS_MAXNAMLEN 255 +#define NFS_COOKIESIZE 4 +#define NFS_FHSIZE 32 + + +#if !defined(AIX) +typedef struct { + char data[NFS_FHSIZE]; +} fhandle_t; +#endif /* AIX */ + +/* + * Definition of "standard" mount parameters + */ +#define MOUNTPROG 100005 +#define MOUNTVERS 1 +#define MOUNTPROC_MNT 1 + +struct fhstatus { + int fhs_status; + fhandle_t fhs_fh; +}; + +typedef fhandle_t nfs_fh; + +/* + * fattr modes + */ +/* + * The mode mask is used to mask off server vendor specific mode bits + * from the mode field. This allows clients from different vendors to + * validate servers from different vendors. + */ +#define NFSMODE_MASK 0177777 +#define NFSMODE_FMT 0170000 +#define NFSMODE_DIR 0040000 +#define NFSMODE_CHR 0020000 +#define NFSMODE_BLK 0060000 +#define NFSMODE_REG 0100000 +#define NFSMODE_LNK 0120000 +#define NFSMODE_SOCK 0140000 +#define NFSMODE_FIFO 0010000 + + +/* + * NFS Procedures + */ +#define NFSPROC_NULL 0 +#define NFSPROC_GETATTR 1 +#define NFSPROC_SETATTR 2 +#define NFSPROC_ROOT 3 +#define NFSPROC_LOOKUP 4 +#define NFSPROC_READLINK 5 +#define NFSPROC_READ 6 +#define NFSPROC_WRITECACHE 7 +#define NFSPROC_WRITE 8 +#define NFSPROC_CREATE 9 +#define NFSPROC_REMOVE 10 +#define NFSPROC_RENAME 11 +#define NFSPROC_LINK 12 +#define NFSPROC_SYMLINK 13 +#define NFSPROC_MKDIR 14 +#define NFSPROC_RMDIR 15 +#define NFSPROC_READDIR 16 +#define NFSPROC_STATFS 17 +#define NFS_PROCEDURE_COUNT (NFSPROC_STATFS + 1) + + +/* + * call and return types + */ +enum nfsstat { + NFS_OK = 0, + NFSERR_PERM = 1, + NFSERR_NOENT = 2, + NFSERR_IO = 5, + NFSERR_NXIO = 6, + NFSERR_ACCES = 13, + NFSERR_EXIST = 17, + NFSERR_XDEV = 18, + NFSERR_NODEV = 19, + NFSERR_NOTDIR = 20, + NFSERR_ISDIR = 21, + NFSERR_INVAL = 22, + NFSERR_FBIG = 27, + NFSERR_NOSPC = 28, + NFSERR_ROFS = 30, + NFSERR_OPNOTSUPP = 45, + NFSERR_NAMETOOLONG = 63, + NFSERR_NOTEMPTY = 66, + NFSERR_DQUOT = 69, + NFSERR_STALE = 70, + NFSERR_REMOTE = 71, + NFSERR_WFLUSH = 99 +}; +typedef enum nfsstat nfsstat; + + +enum ftype { + NFNON = 0, + NFREG = 1, + NFDIR = 2, + NFBLK = 3, + NFCHR = 4, + NFLNK = 5, + NFSOCK = 6, + NFBAD = 7, + NFFIFO = 8 +}; +typedef enum ftype ftype; +#define NUM_TYPES 5 + + +struct nfstime { + unsigned int seconds; + unsigned int useconds; +}; +typedef struct nfstime nfstime; + + +struct fattr { + ftype type; + unsigned int mode; + unsigned int nlink; + unsigned int uid; + unsigned int gid; + unsigned int size; + unsigned int blocksize; + unsigned int rdev; + unsigned int blocks; + unsigned int fsid; + unsigned int fileid; + nfstime atime; + nfstime mtime; + nfstime ctime; +}; +typedef struct fattr fattr; + + +struct sattr { + unsigned int mode; + unsigned int uid; + unsigned int gid; + unsigned int size; + nfstime atime; + nfstime mtime; +}; +typedef struct sattr sattr; + + +typedef char *filename; + + +typedef char *nfspath; + + +struct attrstat { + nfsstat status; + union { + fattr attributes; + } attrstat_u; +}; +typedef struct attrstat attrstat; + + +struct sattrargs { + nfs_fh file; + sattr attributes; +}; +typedef struct sattrargs sattrargs; + + +struct diropargs { + nfs_fh dir; + filename name; +}; +typedef struct diropargs diropargs; + + +struct diropokres { + nfs_fh file; + fattr attributes; +}; +typedef struct diropokres diropokres; + + +struct diropres { + nfsstat status; + union { + diropokres diropres; + } diropres_u; +}; +typedef struct diropres diropres; + + +struct readlinkres { + nfsstat status; + struct { + nfspath data; + int len; /* for convenience only, not in the protocol */ + } readlinkres_u; +}; +typedef struct readlinkres readlinkres; + + +struct readargs { + nfs_fh file; + unsigned int offset; + unsigned int count; + unsigned int totalcount; /* unused field, but in the protocol */ +}; +typedef struct readargs readargs; + + +struct readokres { + fattr attributes; + struct { + unsigned int data_len; + char *data_val; + } data; +}; +typedef struct readokres readokres; + + +struct readres { + nfsstat status; + union { + readokres reply; + } readres_u; +}; +typedef struct readres readres; + + +struct writeargs { + nfs_fh file; + unsigned int beginoffset; /* unused field, but in the protocol */ + unsigned int offset; + unsigned int totalcount; /* unused field, but in the protocol */ + struct { + unsigned int data_len; + char *data_val; + } data; +}; +typedef struct writeargs writeargs; + + +struct createargs { + diropargs where; + sattr attributes; +}; +typedef struct createargs createargs; + + +struct renameargs { + diropargs from; + diropargs to; +}; +typedef struct renameargs renameargs; + + +struct linkargs { + nfs_fh from; + diropargs to; +}; +typedef struct linkargs linkargs; + + +struct symlinkargs { + diropargs from; + nfspath to; + sattr attributes; +}; +typedef struct symlinkargs symlinkargs; + + +struct mkdirargs { + diropargs where; + sattr attributes; +}; +typedef struct mkdirargs mkdirargs; + + +typedef char nfscookie[NFS_COOKIESIZE]; + + +struct readdirargs { + nfs_fh dir; + nfscookie cookie; + unsigned int count; +}; +typedef struct readdirargs readdirargs; + + +struct entry { + bool_t valid; /* bool for entry is present */ + unsigned int fileid; + uint16_t name_len; + filename name; + nfscookie cookie; +}; +typedef struct entry entry; + + +struct dirlist { + int max_entries; /* for convenience only, not in the protocol */ + entry *entries; /* a stream of consecutive entry's */ + bool_t eof; +}; +typedef struct dirlist dirlist; + + +struct readdirres { + nfsstat status; + union { + dirlist reply; + } readdirres_u; +}; +typedef struct readdirres readdirres; + + +struct statfsokres { + unsigned int tsize; + unsigned int bsize; + unsigned int blocks; + unsigned int bfree; + unsigned int bavail; +}; +typedef struct statfsokres statfsokres; + + +struct statfsres { + nfsstat status; + union { + statfsokres reply; + } statfsres_u; +}; +typedef struct statfsres statfsres; + + +/* + * Literals and types for the NFS protocol, Version 3. + */ + + +/* + * for RPC calls + */ + +/* + * fixed sizes + */ +#define NFS3_FHSIZE 64 +#define NFS3_COOKIEVERFSIZE 8 +#define NFS3_CREATEVERFSIZE 8 +#define NFS3_WRITEVERFSIZE 8 + +#define nfs3nametoolong ((char *)-1) + +/* + * Not all systems have a built in long long type so we define a + * special version for sfs to use. + */ +typedef union { + struct { + uint32_t _u; + uint32_t _l; + } _p; + char _f[8]; +} nfs_uint64_t; + +typedef char *filename3; + +typedef char *nfspath3; + +typedef char cookieverf3[NFS3_COOKIEVERFSIZE]; + +typedef char createverf3[NFS3_CREATEVERFSIZE]; + +typedef char writeverf3[NFS3_WRITEVERFSIZE]; + +struct nfs_fh3 { + unsigned int fh3_length; + union nfs_fh3_u { + struct { + char _u[NFS_FHSIZE]; + char _l[NFS_FHSIZE]; + } _p; + char data[NFS3_FHSIZE]; + } fh3_u; +}; +#define fh3_fsid fh3_u.nfs_fh3_i.fh3_i.fh_fsid +#define fh3_len fh3_u.nfs_fh3_i.fh3_i.fh_len /* fid length */ +#define fh3_data fh3_u.nfs_fh3_i.fh3_i.fh_data /* fid bytes */ +#define fh3_xlen fh3_u.nfs_fh3_i.fh3_i.fh_xlen +#define fh3_xdata fh3_u.nfs_fh3_i.fh3_i.fh_xdata +typedef struct nfs_fh3 nfs_fh3; + +struct diropargs3 { + nfs_fh3 dir; + filename3 name; +}; +typedef struct diropargs3 diropargs3; + +struct nfstime3 { + uint32_t seconds; + uint32_t nseconds; +}; +typedef struct nfstime3 nfstime3; + +struct specdata3 { + uint32_t specdata1; + uint32_t specdata2; +}; +typedef struct specdata3 specdata3; + + +/* + * call and return types + */ +enum nfsstat3 { + NFS3_OK = 0, + NFS3ERR_PERM = 1, + NFS3ERR_NOENT = 2, + NFS3ERR_IO = 5, + NFS3ERR_NXIO = 6, + NFS3ERR_ACCES = 13, + NFS3ERR_EXIST = 17, + NFS3ERR_XDEV = 18, + NFS3ERR_NODEV = 19, + NFS3ERR_NOTDIR = 20, + NFS3ERR_ISDIR = 21, + NFS3ERR_INVAL = 22, + NFS3ERR_FBIG = 27, + NFS3ERR_NOSPC = 28, + NFS3ERR_ROFS = 30, + NFS3ERR_MLINK = 31, + NFS3ERR_NAMETOOLONG = 63, + NFS3ERR_NOTEMPTY = 66, + NFS3ERR_DQUOT = 69, + NFS3ERR_STALE = 70, + NFS3ERR_REMOTE = 71, + NFS3ERR_BADHANDLE = 10001, + NFS3ERR_NOT_SYNC = 10002, + NFS3ERR_BAD_COOKIE = 10003, + NFS3ERR_NOTSUPP = 10004, + NFS3ERR_TOOSMALL = 10005, + NFS3ERR_SERVERFAULT = 10006, + NFS3ERR_BADTYPE = 10007, + NFS3ERR_JUKEBOX = 10008, + NFS3ERR_RFS_TIMEOUT = 10009, // RFS + NFS3ERR_RFS_MISS = 10010 // RFS reply missed in trace +}; +typedef enum nfsstat3 nfsstat3; + +enum ftype3 { + NF3NON = 0, + NF3REG = 1, + NF3DIR = 2, + NF3BLK = 3, + NF3CHR = 4, + NF3LNK = 5, + NF3SOCK = 6, + NF3FIFO = 7 +}; +typedef enum ftype3 ftype3; +#define NUM_TYPES 5 + + +struct fattr3 { + ftype3 type; + uint32_t mode; + uint32_t nlink; + uint32_t uid; + uint32_t gid; + nfs_uint64_t size; + nfs_uint64_t used; + specdata3 rdev; + nfs_uint64_t fsid; + nfs_uint64_t fileid; + nfstime3 atime; + nfstime3 mtime; + nfstime3 ctime; +}; +typedef struct fattr3 fattr3; + +struct post_op_attr { + bool_t attributes; + fattr3 attr; +}; +typedef struct post_op_attr post_op_attr; + +struct wcc_attr { + nfs_uint64_t size; + nfstime3 mtime; + nfstime3 ctime; +}; +typedef struct wcc_attr wcc_attr; + +struct pre_op_attr { + bool_t attributes; + wcc_attr attr; +}; +typedef struct pre_op_attr pre_op_attr; + +struct wcc_data { + pre_op_attr before; + post_op_attr after; +}; +typedef struct wcc_data wcc_data; + +struct post_op_fh3 { + bool_t handle_follows; + nfs_fh3 handle; +}; +typedef struct post_op_fh3 post_op_fh3; + +enum time_how { + DONT_CHANGE = 0, + SET_TO_SERVER_TIME = 1, + SET_TO_CLIENT_TIME = 2 +}; +typedef enum time_how time_how; + +struct set_mode3 { + bool_t set_it; + uint32_t mode; +}; +typedef struct set_mode3 set_mode3; + +struct set_uid3 { + bool_t set_it; + uint32_t uid; +}; +typedef struct set_uid3 set_uid3; + +struct set_gid3 { + bool_t set_it; + uint32_t gid; +}; +typedef struct set_gid3 set_gid3; + +struct set_size3 { + bool_t set_it; + nfs_uint64_t size; +}; +typedef struct set_size3 set_size3; + +struct set_atime { + time_how set_it; + nfstime3 atime; +}; +typedef struct set_atime set_atime; + +struct set_mtime { + time_how set_it; + nfstime3 mtime; +}; +typedef struct set_mtime set_mtime; + +struct sattr3 { + set_mode3 mode; + set_uid3 uid; + set_gid3 gid; + set_size3 size; + set_atime atime; + set_mtime mtime; +}; +typedef struct sattr3 sattr3; + + +#define resok res_u.ok +#define resfail res_u.fail + +struct GETATTR3args { + nfs_fh3 object; +}; +typedef struct GETATTR3args GETATTR3args; + +struct GETATTR3resok { + fattr3 obj_attributes; +}; +typedef struct GETATTR3resok GETATTR3resok; + +struct GETATTR3res { + nfsstat3 status; + union { + GETATTR3resok ok; + } res_u; +}; +typedef struct GETATTR3res GETATTR3res; + +struct sattrguard3 { + bool_t check; + nfstime3 obj_ctime; +}; +typedef struct sattrguard3 sattrguard3; + +struct SETATTR3args { + nfs_fh3 object; + sattr3 new_attributes; + sattrguard3 guard; +}; +typedef struct SETATTR3args SETATTR3args; + +struct SETATTR3resok { + wcc_data obj_wcc; +}; +typedef struct SETATTR3resok SETATTR3resok; + +struct SETATTR3resfail { + wcc_data obj_wcc; +}; +typedef struct SETATTR3resfail SETATTR3resfail; + +struct SETATTR3res { + nfsstat3 status; + union { + SETATTR3resok ok; + SETATTR3resfail fail; + } res_u; +}; +typedef struct SETATTR3res SETATTR3res; + +struct LOOKUP3args { + diropargs3 what; +}; +typedef struct LOOKUP3args LOOKUP3args; + +struct LOOKUP3resok { + nfs_fh3 object; + post_op_attr obj_attributes; + post_op_attr dir_attributes; +}; +typedef struct LOOKUP3resok LOOKUP3resok; + +struct LOOKUP3resfail { + post_op_attr dir_attributes; +}; +typedef struct LOOKUP3resfail LOOKUP3resfail; + +struct LOOKUP3res { + nfsstat3 status; + union { + LOOKUP3resok ok; + LOOKUP3resfail fail; + } res_u; +}; +typedef struct LOOKUP3res LOOKUP3res; + +struct ACCESS3args { + nfs_fh3 object; + uint32_t access; +}; +typedef struct ACCESS3args ACCESS3args; +#define ACCESS3_READ 0x1 +#define ACCESS3_LOOKUP 0x2 +#define ACCESS3_MODIFY 0x4 +#define ACCESS3_EXTEND 0x8 +#define ACCESS3_DELETE 0x10 +#define ACCESS3_EXECUTE 0x20 + +struct ACCESS3resok { + post_op_attr obj_attributes; + uint32_t access; +}; +typedef struct ACCESS3resok ACCESS3resok; + +struct ACCESS3resfail { + post_op_attr obj_attributes; +}; +typedef struct ACCESS3resfail ACCESS3resfail; + +struct ACCESS3res { + nfsstat3 status; + union { + ACCESS3resok ok; + ACCESS3resfail fail; + } res_u; +}; +typedef struct ACCESS3res ACCESS3res; + +struct READLINK3args { + nfs_fh3 symlink; +}; +typedef struct READLINK3args READLINK3args; + +struct READLINK3resok { + post_op_attr symlink_attributes; + nfspath3 data; +}; +typedef struct READLINK3resok READLINK3resok; + +struct READLINK3resfail { + post_op_attr symlink_attributes; +}; +typedef struct READLINK3resfail READLINK3resfail; + +struct READLINK3res { + nfsstat3 status; + union { + READLINK3resok ok; + READLINK3resfail fail; + } res_u; +}; +typedef struct READLINK3res READLINK3res; + +struct READ3args { + nfs_fh3 file; + nfs_uint64_t offset; + uint32_t count; +}; +typedef struct READ3args READ3args; + +struct READ3resok { + post_op_attr file_attributes; + uint32_t count; + bool_t eof; + struct { + unsigned int data_len; + char *data_val; + } data; + unsigned int size; +}; +typedef struct READ3resok READ3resok; + +struct READ3resfail { + post_op_attr file_attributes; +}; +typedef struct READ3resfail READ3resfail; + +struct READ3res { + nfsstat3 status; + union { + READ3resok ok; + READ3resfail fail; + } res_u; +}; +typedef struct READ3res READ3res; + +enum stable_how { + UNSTABLE = 0, + DATA_SYNC = 1, + FILE_SYNC = 2 +}; +typedef enum stable_how stable_how; + +struct WRITE3args { + nfs_fh3 file; + nfs_uint64_t offset; + uint32_t count; + stable_how stable; + struct { + unsigned int data_len; + char *data_val; + } data; +}; +typedef struct WRITE3args WRITE3args; + +struct WRITE3resok { + wcc_data file_wcc; + uint32_t count; + stable_how committed; + writeverf3 verf; +}; +typedef struct WRITE3resok WRITE3resok; + +struct WRITE3resfail { + wcc_data file_wcc; +}; +typedef struct WRITE3resfail WRITE3resfail; + +struct WRITE3res { + nfsstat3 status; + union { + WRITE3resok ok; + WRITE3resfail fail; + } res_u; +}; +typedef struct WRITE3res WRITE3res; + +enum createmode3 { + UNCHECKED = 0, + GUARDED = 1, + EXCLUSIVE = 2 +}; +typedef enum createmode3 createmode3; + +struct createhow3 { + createmode3 mode; + union { + sattr3 obj_attributes; + createverf3 verf; + } createhow3_u; +}; +typedef struct createhow3 createhow3; + +struct CREATE3args { + diropargs3 where; + createhow3 how; +}; +typedef struct CREATE3args CREATE3args; + +struct CREATE3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; +typedef struct CREATE3resok CREATE3resok; + +struct CREATE3resfail { + wcc_data dir_wcc; +}; +typedef struct CREATE3resfail CREATE3resfail; + +struct CREATE3res { + nfsstat3 status; + union { + CREATE3resok ok; + CREATE3resfail fail; + } res_u; +}; +typedef struct CREATE3res CREATE3res; + +struct MKDIR3args { + diropargs3 where; + sattr3 attributes; +}; +typedef struct MKDIR3args MKDIR3args; + +struct MKDIR3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; +typedef struct MKDIR3resok MKDIR3resok; + +struct MKDIR3resfail { + wcc_data dir_wcc; +}; +typedef struct MKDIR3resfail MKDIR3resfail; + +struct MKDIR3res { + nfsstat3 status; + union { + MKDIR3resok ok; + MKDIR3resfail fail; + } res_u; +}; +typedef struct MKDIR3res MKDIR3res; + +struct symlinkdata3 { + sattr3 symlink_attributes; + nfspath3 symlink_data; +}; +typedef struct symlinkdata3 symlinkdata3; + +struct SYMLINK3args { + diropargs3 where; + symlinkdata3 symlink; +}; +typedef struct SYMLINK3args SYMLINK3args; + +struct SYMLINK3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; +typedef struct SYMLINK3resok SYMLINK3resok; + +struct SYMLINK3resfail { + wcc_data dir_wcc; +}; +typedef struct SYMLINK3resfail SYMLINK3resfail; + +struct SYMLINK3res { + nfsstat3 status; + union { + SYMLINK3resok ok; + SYMLINK3resfail fail; + } res_u; +}; +typedef struct SYMLINK3res SYMLINK3res; + +struct devicedata3 { + sattr3 dev_attributes; + specdata3 spec; +}; +typedef struct devicedata3 devicedata3; + +struct mknoddata3 { + ftype3 type; + union { + devicedata3 device; + sattr3 pipe_attributes; + } mknoddata3_u; +}; +typedef struct mknoddata3 mknoddata3; + +struct MKNOD3args { + diropargs3 where; + mknoddata3 what; +}; +typedef struct MKNOD3args MKNOD3args; + +struct MKNOD3resok { + post_op_fh3 obj; + post_op_attr obj_attributes; + wcc_data dir_wcc; +}; +typedef struct MKNOD3resok MKNOD3resok; + +struct MKNOD3resfail { + wcc_data dir_wcc; +}; +typedef struct MKNOD3resfail MKNOD3resfail; + +struct MKNOD3res { + nfsstat3 status; + union { + MKNOD3resok ok; + MKNOD3resfail fail; + } res_u; +}; +typedef struct MKNOD3res MKNOD3res; + +struct REMOVE3args { + diropargs3 object; +}; +typedef struct REMOVE3args REMOVE3args; + +struct REMOVE3resok { + wcc_data dir_wcc; +}; +typedef struct REMOVE3resok REMOVE3resok; + +struct REMOVE3resfail { + wcc_data dir_wcc; +}; +typedef struct REMOVE3resfail REMOVE3resfail; + +struct REMOVE3res { + nfsstat3 status; + union { + REMOVE3resok ok; + REMOVE3resfail fail; + } res_u; +}; +typedef struct REMOVE3res REMOVE3res; + +struct RMDIR3args { + diropargs3 object; +}; +typedef struct RMDIR3args RMDIR3args; + +struct RMDIR3resok { + wcc_data dir_wcc; +}; +typedef struct RMDIR3resok RMDIR3resok; + +struct RMDIR3resfail { + wcc_data dir_wcc; +}; +typedef struct RMDIR3resfail RMDIR3resfail; + +struct RMDIR3res { + nfsstat3 status; + union { + RMDIR3resok ok; + RMDIR3resfail fail; + } res_u; +}; +typedef struct RMDIR3res RMDIR3res; + +struct RENAME3args { + diropargs3 from; + diropargs3 to; +}; +typedef struct RENAME3args RENAME3args; + +struct RENAME3resok { + wcc_data fromdir_wcc; + wcc_data todir_wcc; +}; +typedef struct RENAME3resok RENAME3resok; + +struct RENAME3resfail { + wcc_data fromdir_wcc; + wcc_data todir_wcc; +}; +typedef struct RENAME3resfail RENAME3resfail; + +struct RENAME3res { + nfsstat3 status; + union { + RENAME3resok ok; + RENAME3resfail fail; + } res_u; +}; +typedef struct RENAME3res RENAME3res; + +struct LINK3args { + nfs_fh3 file; + diropargs3 link; +}; +typedef struct LINK3args LINK3args; + +struct LINK3resok { + post_op_attr file_attributes; + wcc_data linkdir_wcc; +}; +typedef struct LINK3resok LINK3resok; + +struct LINK3resfail { + post_op_attr file_attributes; + wcc_data linkdir_wcc; +}; +typedef struct LINK3resfail LINK3resfail; + +struct LINK3res { + nfsstat3 status; + union { + LINK3resok ok; + LINK3resfail fail; + } res_u; +}; +typedef struct LINK3res LINK3res; + +struct READDIR3args { + nfs_fh3 dir; + nfs_uint64_t cookie; + cookieverf3 cookieverf; + uint32_t count; +}; +typedef struct READDIR3args READDIR3args; + +struct entry3 { + nfs_uint64_t fileid; + char name[SFS_MAXNAMLEN]; + nfs_uint64_t cookie; +}; +typedef struct entry3 entry3; + +struct dirlist3 { + entry3 *entries; + bool_t eof; +}; +typedef struct dirlist3 dirlist3; + +struct READDIR3resok { + post_op_attr dir_attributes; + cookieverf3 cookieverf; + dirlist3 reply; + unsigned int size; + unsigned int count; + nfs_uint64_t cookie; +}; +typedef struct READDIR3resok READDIR3resok; + +struct READDIR3resfail { + post_op_attr dir_attributes; +}; +typedef struct READDIR3resfail READDIR3resfail; + +struct READDIR3res { + nfsstat3 status; + union { + READDIR3resok ok; + READDIR3resfail fail; + } res_u; +}; +typedef struct READDIR3res READDIR3res; + +struct READDIRPLUS3args { + nfs_fh3 dir; + nfs_uint64_t cookie; + cookieverf3 cookieverf; + uint32_t dircount; + uint32_t maxcount; +}; +typedef struct READDIRPLUS3args READDIRPLUS3args; + +struct entryplus3 { + nfs_uint64_t fileid; + char name[SFS_MAXNAMLEN]; + nfs_uint64_t cookie; + post_op_attr name_attributes; + post_op_fh3 name_handle; +}; +typedef struct entryplus3 entryplus3; + +struct dirlistplus3 { + entryplus3 *entries; + bool_t eof; +}; +typedef struct dirlistplus3 dirlistplus3; + +struct READDIRPLUS3resok { + post_op_attr dir_attributes; + cookieverf3 cookieverf; + dirlistplus3 reply; + unsigned int size; + unsigned int count; + unsigned int maxcount; + post_op_attr *attributes; + post_op_fh3 *handles; +}; +typedef struct READDIRPLUS3resok READDIRPLUS3resok; + +struct READDIRPLUS3resfail { + post_op_attr dir_attributes; +}; +typedef struct READDIRPLUS3resfail READDIRPLUS3resfail; + +struct READDIRPLUS3res { + nfsstat3 status; + union { + READDIRPLUS3resok ok; + READDIRPLUS3resfail fail; + } res_u; +}; +typedef struct READDIRPLUS3res READDIRPLUS3res; + +struct FSSTAT3args { + nfs_fh3 fsroot; +}; +typedef struct FSSTAT3args FSSTAT3args; + +struct FSSTAT3resok { + post_op_attr obj_attributes; + nfs_uint64_t tbytes; + nfs_uint64_t fbytes; + nfs_uint64_t abytes; + nfs_uint64_t tfiles; + nfs_uint64_t ffiles; + nfs_uint64_t afiles; + uint32_t invarsec; +}; +typedef struct FSSTAT3resok FSSTAT3resok; + +struct FSSTAT3resfail { + post_op_attr obj_attributes; +}; +typedef struct FSSTAT3resfail FSSTAT3resfail; + +struct FSSTAT3res { + nfsstat3 status; + union { + FSSTAT3resok ok; + FSSTAT3resfail fail; + } res_u; +}; +typedef struct FSSTAT3res FSSTAT3res; + +struct FSINFO3args { + nfs_fh3 fsroot; +}; +typedef struct FSINFO3args FSINFO3args; + +struct FSINFO3resok { + post_op_attr obj_attributes; + uint32_t rtmax; + uint32_t rtpref; + uint32_t rtmult; + uint32_t wtmax; + uint32_t wtpref; + uint32_t wtmult; + uint32_t dtpref; + nfs_uint64_t maxfilesize; + nfstime3 time_delta; + uint32_t properties; +}; +typedef struct FSINFO3resok FSINFO3resok; + +struct FSINFO3resfail { + post_op_attr obj_attributes; +}; +typedef struct FSINFO3resfail FSINFO3resfail; +#define FSF3_LINK 0x1 +#define FSF3_SYMLINK 0x2 +#define FSF3_HOMOGENEOUS 0x8 +#define FSF3_CANSETTIME 0x10 + +struct FSINFO3res { + nfsstat3 status; + union { + FSINFO3resok ok; + FSINFO3resfail fail; + } res_u; +}; +typedef struct FSINFO3res FSINFO3res; + +struct PATHCONF3args { + nfs_fh3 object; +}; +typedef struct PATHCONF3args PATHCONF3args; + +struct PATHCONF3resok { + post_op_attr obj_attributes; + uint32_t link_max; + uint32_t name_max; + bool_t no_trunc; + bool_t chown_restricted; + bool_t case_insensitive; + bool_t case_preserving; +}; +typedef struct PATHCONF3resok PATHCONF3resok; + +struct PATHCONF3resfail { + post_op_attr obj_attributes; +}; +typedef struct PATHCONF3resfail PATHCONF3resfail; + +struct PATHCONF3res { + nfsstat3 status; + union { + PATHCONF3resok ok; + PATHCONF3resfail fail; + } res_u; +}; +typedef struct PATHCONF3res PATHCONF3res; + +struct COMMIT3args { + nfs_fh3 file; + nfs_uint64_t offset; + uint32_t count; +}; +typedef struct COMMIT3args COMMIT3args; + +struct COMMIT3resok { + wcc_data file_wcc; + writeverf3 verf; +}; +typedef struct COMMIT3resok COMMIT3resok; + +struct COMMIT3resfail { + wcc_data file_wcc; +}; +typedef struct COMMIT3resfail COMMIT3resfail; + +struct COMMIT3res { + nfsstat3 status; + union { + COMMIT3resok ok; + COMMIT3resfail fail; + } res_u; +}; +typedef struct COMMIT3res COMMIT3res; + +/* + * NFS Procedures + */ +#define NFSPROC3_NULL 0 +#define NFSPROC3_GETATTR 1 +#define NFSPROC3_SETATTR 2 +#define NFSPROC3_LOOKUP 3 +#define NFSPROC3_ACCESS 4 +#define NFSPROC3_READLINK 5 +#define NFSPROC3_READ 6 +#define NFSPROC3_WRITE 7 +#define NFSPROC3_CREATE 8 +#define NFSPROC3_MKDIR 9 +#define NFSPROC3_SYMLINK 10 +#define NFSPROC3_MKNOD 11 +#define NFSPROC3_REMOVE 12 +#define NFSPROC3_RMDIR 13 +#define NFSPROC3_RENAME 14 +#define NFSPROC3_LINK 15 +#define NFSPROC3_READDIR 16 +#define NFSPROC3_READDIRPLUS 17 +#define NFSPROC3_FSSTAT 18 +#define NFSPROC3_FSINFO 19 +#define NFSPROC3_PATHCONF 20 +#define NFSPROC3_COMMIT 21 +#define NFS3_PROCEDURE_COUNT (NFSPROC3_COMMIT + 1) + + +/* + * mount.h definitions + */ +#define MOUNTVER3 ((uint32_t)3) +#define MNTPATHLEN 1024 +#define MNTNAMLEN 255 + +#define MOUNTPROC_NULL ((uint32_t)(0)) +#define MOUNTPROC_DUMP ((uint32_t)(2)) +#define MOUNTPROC_UMNT ((uint32_t)(3)) +#define MOUNTPROC_UMNTALL ((uint32_t)(4)) +#define MOUNTPROC_EXPORT ((uint32_t)(5)) +#define MOUNTPROC_EXPORTALL ((uint32_t)(6)) +#define MOUNTPROC_PATHCONF ((uint32_t)(7)) + +struct fhandle3 { + unsigned int fhandle3_len; + char *fhandle3_val; +}; +typedef struct fhandle3 fhandle3; + +enum mntstat3 { + MNT_OK = 0, + MNT3ERR_PERM = 1, + MNT3ERR_NOENT = 2, + MNT3ERR_IO = 5, + MNT3ERR_ACCES = 13, + MNT3ERR_NOTDIR = 20, + MNT3ERR_INVAL = 22, + MNT3ERR_NAMETOOLONG = 63, + MNT3ERR_NOTSUPP = 10004, + MNT3ERR_SERVERFAULT = 10006 +}; +typedef enum mntstat3 mntstat3; + +struct mntres3_ok { + fhandle3 fhandle; + struct { + unsigned int auth_flavors_len; + int *auth_flavors_val; + } auth_flavors; +}; +typedef struct mntres3_ok mntres3_ok; + +struct mountres3 { + mntstat3 fhs_status; + union { + mntres3_ok mntinfo; + } mntres3_u; +}; +typedef struct mountres3 mountres3; + +typedef char *dirpath; + + +/* + * External XDR functions + */ + +/* + * Mount protocol + */ +extern bool_t xdr_path(XDR *, char **); +extern bool_t xdr_fhstatus(XDR *, struct fhstatus *); +extern bool_t xdr_dirpath(XDR *, dirpath *); +extern bool_t xdr_mntres3(XDR *, mountres3 *); + +/* + * V2 + */ +extern bool_t xdr_create(XDR *, char *); +extern bool_t xdr_getattr(XDR *xdrs, char *); +extern bool_t xdr_link(XDR *xdrs, char *); +extern bool_t xdr_lookup(XDR *xdrs, char *); +extern bool_t xdr_mkdir(XDR *xdrs, char *); +extern bool_t xdr_read(XDR *xdrs, char *); +extern bool_t xdr_readdir(XDR *xdrs, char *); +extern bool_t xdr_readlink(XDR *xdrs, char *); +extern bool_t xdr_remove(XDR *xdrs, char *); +extern bool_t xdr_rename(XDR *xdrs, char *); +extern bool_t xdr_rmdir(XDR *xdrs, char *); +extern bool_t xdr_setattr(XDR *xdrs, char *); +extern bool_t xdr_statfs(XDR *xdrs, char *); +extern bool_t xdr_symlink(XDR *xdrs, char *); +extern bool_t xdr_write(XDR *xdrs, char *); + +/* + * V3 + */ +extern bool_t xdr_GETATTR3args(XDR *, GETATTR3args *); +extern bool_t xdr_GETATTR3res(XDR *, GETATTR3res *); +extern bool_t xdr_SETATTR3args(XDR *, SETATTR3args *); +extern bool_t xdr_SETATTR3res(XDR *, SETATTR3res *); +extern bool_t xdr_LOOKUP3args(XDR *, LOOKUP3args *); +extern bool_t xdr_LOOKUP3res(XDR *, LOOKUP3res *); +extern bool_t xdr_ACCESS3args(XDR *, ACCESS3args *); +extern bool_t xdr_ACCESS3res(XDR *, ACCESS3res *); +extern bool_t xdr_READLINK3args(XDR *, READLINK3args *); +extern bool_t xdr_READLINK3res(XDR *, READLINK3res *); +extern bool_t xdr_READ3args(XDR *, READ3args *); +extern bool_t xdr_READ3res(XDR *, READ3res *); +extern bool_t xdr_WRITE3args(XDR *, WRITE3args *); +extern bool_t xdr_WRITE3res(XDR *, WRITE3res *); +extern bool_t xdr_CREATE3args(XDR *, CREATE3args *); +extern bool_t xdr_CREATE3res(XDR *, CREATE3res *); +extern bool_t xdr_MKDIR3args(XDR *, MKDIR3args *); +extern bool_t xdr_MKDIR3res(XDR *, MKDIR3res *); +extern bool_t xdr_SYMLINK3args(XDR *, SYMLINK3args *); +extern bool_t xdr_SYMLINK3res(XDR *, SYMLINK3res *); +extern bool_t xdr_MKNOD3args(XDR *, MKNOD3args *); +extern bool_t xdr_MKNOD3res(XDR *, MKNOD3res *); +extern bool_t xdr_REMOVE3args(XDR *, REMOVE3args *); +extern bool_t xdr_REMOVE3res(XDR *, REMOVE3res *); +extern bool_t xdr_RMDIR3args(XDR *, RMDIR3args *); +extern bool_t xdr_RMDIR3res(XDR *, RMDIR3res *); +extern bool_t xdr_RENAME3args(XDR *, RENAME3args *); +extern bool_t xdr_RENAME3res(XDR *, RENAME3res *); +extern bool_t xdr_LINK3args(XDR *, LINK3args *); +extern bool_t xdr_LINK3res(XDR *, LINK3res *); +extern bool_t xdr_READDIR3args(XDR *, READDIR3args *); +extern bool_t xdr_READDIR3res(XDR *, READDIR3res *); +extern bool_t xdr_READDIRPLUS3args(XDR *, READDIRPLUS3args *); +extern bool_t xdr_READDIRPLUS3res(XDR *, READDIRPLUS3res *); +extern bool_t xdr_FSSTAT3args(XDR *, FSSTAT3args *); +extern bool_t xdr_FSSTAT3res(XDR *, FSSTAT3res *); +extern bool_t xdr_FSINFO3args(XDR *, FSINFO3args *); +extern bool_t xdr_FSINFO3res(XDR *, FSINFO3res *); +extern bool_t xdr_PATHCONF3args(XDR *, PATHCONF3args *); +extern bool_t xdr_PATHCONF3res(XDR *, PATHCONF3res *); +extern bool_t xdr_COMMIT3args(XDR *, COMMIT3args *); +extern bool_t xdr_COMMIT3res(XDR *, COMMIT3res *); + + +#endif /* __sfs_c_nfs_h */ diff --git a/TBBT/trace_play/sfs_c_pnt.c b/TBBT/trace_play/sfs_c_pnt.c new file mode 100644 index 0000000..cab4f43 --- /dev/null +++ b/TBBT/trace_play/sfs_c_pnt.c @@ -0,0 +1,1163 @@ +#ifndef lint +static char sfs_c_pntSid[] = "@(#)sfs_c_pnt.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * + *.Exported_Routines + * void parent(int, int, char *, char *) + * + *.Local_Routines + * void synchronize_children(int) + * int signal_Prime_Client(char *, char *) + * void collect_counters(int) + * int check_parameters(char *, char *, int) + * int check_counters(void) + * void print_results(int, int, char *, int, int, char *) + * + *.Revision_History + * 10-Jan-92 Teelucksingh + * Client passes standard deviation compute + * values to Prime-Client as well as an + * "INVALID RUN" flag. + * + * 16-Dec-91 Wittle Created. + */ + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <signal.h> + +#include <fcntl.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> +#include <sys/wait.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + +#if !defined(_XOPEN_SOURCE) +#include <sys/socket.h> +#else +#define AF_INET 2 +#endif + +/* + * ------------------------- External Definitions ------------------------- + */ + +/* forward definitions for local routines */ +static void synchronize_children(int); +static int signal_Prime_Client(char *, char *); +static void collect_counters(int); +static int check_parameters(char *, char *, int); +static int check_counters(void); +static void print_results(int, int, char *, int, int, char *); +static void sfs_reaper(int); + +/* Aggregate results storage */ +static char Client_results[(NOPS+3)*MAX_LINE_LEN]; + +/* + * ------------------------- SFS Parent Code ------------------------- + */ + +/* + * Parent: wait for kids to get ready, start them, wait for them to + * finish, read and accumulate results. + */ +void +parent( + int children, + int load, + char * mix_file, + char * iodist_file) +{ + char string[80]; /* for interactive startup */ + int result; + int invalid_run; /* holds INVALID RUN status */ + int runtime_val; /* store Runtime value to be printed later */ + int Saveerrno; + char *nameptr; +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + struct sigaction sig_act, old_sig_act; +#endif + + /* + * Setup a SIGCHLD handler in case one of our beloved children dies + * before its time. + */ +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + /* use XOPEN signal handling */ + + sig_act.sa_handler = sfs_reaper; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGCHLD"); + exit(66); + } +#else + (void) signal(SIGCHLD, sfs_reaper); +#endif + + /* Change my name for error logging */ + if ((nameptr = strrchr(sfs_Myname, '/')) != NULL) + sfs_Myname = ++nameptr; + + /* + * store the Runtime value; to be printed in results + */ + if (Prime_client) + runtime_val = Runtime - MULTICLIENT_OFFSET; + else runtime_val = Runtime; + + /* print logfile header information */ + (void) fprintf(stdout,"\n"); + (void) fprintf(stdout, + "************************************************************************"); + (void) fprintf(stdout,"\n"); + (void) fflush(stdout); + + /* print sfs information */ + if (Prime_client) { + (void) fprintf(stderr, + "\nSFS NFS Version %d Benchmark Client Logfile, %s\n", + nfs_version, lad_timestamp()); + (void) fprintf(stderr, "\tClient hostname = %s\n", lad_hostname); + (void) fprintf(stderr, "\tPrime Client hostname = %s\n", + Prime_client); + } + + (void) fprintf(stderr, "\nSPEC SFS Benchmark Version %s, Creation - %s\n", + SFS_VERSION_NUM, SFS_VERSION_DATE); + (void) fprintf(stderr, "NFS Protocol Version %d\n", nfs_version); + + /* mount test directories */ + (void) fprintf(stderr, "%s Mounting %d remote test directories.\n", + lad_timestamp(), children); + synchronize_children(children); + (void) fprintf(stderr, "%s Completed.", lad_timestamp()); + + /* + * if multi-client execution then tell Prime-Client I'm done mounting + * test directories. + */ + if (Prime_client) { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s Sending DONE-MOUNT message to Prime Client(%s).\n", + lad_timestamp(), Prime_client); + if ((result = + (int) signal_Prime_Client("CLIENT_SIGNAL", "")) + == (int) RPC_SUCCESS) { + (void) fprintf(stderr, "%s Completed.",lad_timestamp()); + (void) fflush(stderr); + } else { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s: error %d sending DONE-MOUNT message to Prime Client\n", + sfs_Myname, result); + /* cleanup and exit */ +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + sig_act.sa_handler = SIG_DFL; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGCHLD"); + exit(67); + } +#else + (void) signal(SIGCHLD, SIG_DFL); +#endif + (void) generic_kill(0, SIGINT); + exit(68); + } + + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s Waiting on DO-INIT message from Prime Client(%s).\n", + lad_timestamp(), Prime_client); + (void) fflush(stderr); + + /* + * wait for DO-INIT message from Prime Client + * sfs_syncd (rpc server) sends a SIGUSR1 signal; + * user can also terminate experiment anytime they wish + * with SIGINT or SIGTERM signal + */ + (void) pause(); + (void) fprintf(stderr, "%s Received.",lad_timestamp()); + (void) fflush(stderr); + + } /* send DONE-MOUNT and got DO-INIT message */ + + /* initialize test directories */ + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, "%s Initializing test directories.\n", + lad_timestamp()); + + /* send SIGUSR1 to child processes */ + (void) generic_kill(0, SIGUSR1); + synchronize_children(children); + (void) fprintf(stderr, "%s Completed.", lad_timestamp()); + (void) fflush(stderr); + + /* + * if multi-client execution then tell Prime-Client I'm done initializing + * and wait for synchronized do warmupmessage. + */ + if (Prime_client) { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s Sending DONE-INIT message to Prime Client(%s).\n", + lad_timestamp(), Prime_client); + if ((result = + (int) signal_Prime_Client("CLIENT_SIGNAL","")) + == (int) RPC_SUCCESS) { + (void) fprintf(stderr, "%s Completed.",lad_timestamp()); + (void) fflush(stderr); + } else { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s: error %d sending DONE-INIT message to Prime Client\n", + sfs_Myname, result); + /* cleanup and exit */ +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + sig_act.sa_handler = SIG_DFL; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGCHLD"); + exit(69); + } +#else + (void) signal(SIGCHLD, SIG_DFL); +#endif + (void) generic_kill(0, SIGINT); + exit(70); + } + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s Waiting on DO-WARMUP message from Prime Client(%s).\n", + lad_timestamp(), Prime_client); + (void) fflush(stderr); + + /* + * wait for DO-WARMUP message from Prime Client + * sfs_syncd (rpc server) sends a SIGUSR1 signal; + * user can also terminate experiment anytime they wish + * with SIGINT or SIGTERM signal + */ + (void) pause(); + (void) fprintf(stderr, "%s Received.",lad_timestamp()); + (void) fflush(stderr); + + } /* send DONE-INIT and got DO-WARMUP message */ + + if (Populate_only) { + (void) fprintf(stderr, "\nPopulating directories and exiting.\n"); +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + sig_act.sa_handler = SIG_DFL; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGCHLD"); + exit(71); + } +#else + (void) signal(SIGCHLD, SIG_DFL); +#endif + (void) generic_kill(0, SIGUSR1); + while (wait((int *) 0) != -1) { + /* nop */ + } + return; + } + + /* do warm-up */ + if (Warmuptime) { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, "%s Performing %d seconds pretest warmup.\n", + lad_timestamp(), Warmuptime); + (void) generic_kill(0, SIGUSR1); + (void) sleep(Warmuptime); + (void) fprintf(stderr, "%s Completed.", lad_timestamp()); + (void) fflush(stderr); + } + + if (Interactive) { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, "Hit <return> when ready to start test ..."); + (void) fgets(string,10,stdin); + } + + /* + * if multi-client execution then tell Prime-Client I'm done warm-up + * and wait for synchronized Start message. + */ + if (Prime_client) { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s Sending READY message to Prime Client(%s).\n", + lad_timestamp(), Prime_client); + if ((result = + (int) signal_Prime_Client("CLIENT_SIGNAL","")) + == (int) RPC_SUCCESS) { + (void) fprintf(stderr, "%s Completed.",lad_timestamp()); + (void) fflush(stderr); + } else { + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s: error %d sending READY message to Prime Client\n", + sfs_Myname, result); + /* cleanup and exit */ +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + sig_act.sa_handler = SIG_DFL; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGCHLD"); + exit(72); + } +#else + (void) signal(SIGCHLD, SIG_DFL); +#endif + (void) generic_kill(0, SIGINT); + exit(73); + } + + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s Waiting on START message from Prime Client(%s).\n", + lad_timestamp(), Prime_client); + (void) fflush(stderr); + + /* + * wait for START message from Prime Client + * sfs_syncd (rpc server) sends a SIGUSR1 signal; + * user can also terminate experiment anytime they wish + * with SIGINT or SIGTERM signal + */ + (void) pause(); + (void) fprintf(stderr, "%s Received.",lad_timestamp()); + (void) fflush(stderr); + + } /* send READY and got START message */ + + (void) fprintf(stderr, "\n"); + if (Timed_run) { + if (Prime_client) { + (void) fprintf(stderr, "%s Starting %d seconds test run.\n", + lad_timestamp(), Runtime - MULTICLIENT_OFFSET); + } else { + (void) fprintf(stderr, "%s Starting %d seconds test run.\n", + lad_timestamp(), Runtime); + } + } else { + (void) fprintf(stderr, "%s Starting %d call test run.\n", + lad_timestamp(), Ops[TOTAL].target_calls); + } + (void) fflush(stderr); + + /* signal child processes to go */ + (void) generic_kill(0, SIGUSR1); + + if (Timed_run) + (void) sleep(Runtime); + +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + sig_act.sa_handler = SIG_DFL; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) { + perror("sigaction failed: SIGCHLD"); + exit(74); + } +#else + (void) signal(SIGCHLD, SIG_DFL); +#endif + + if (Timed_run) { + /* + * The parent and the prime are both sleeping for Runtime. + * If the parent wakes up first, he'll tell the children to stop. + * If the prime wakes up first, he'll send an SIGALRM (via syncd) + * to the parent. That alarm may arrive while the parent is still + * asleep, which is ok, or after he has starting running. Since + * the parent SIGARLM catcher does nothing, there is no harm done + * by the extra signal in this case. + * + * Perhaps, if running multi we should just wait (pause()) for + * the STOP signal, like we waited for the start signal. It would + * be more obvious. The only drawback is the OTW rpc delay in + * receiving the stop signal from the prime. + */ + (void) generic_kill(0, SIGUSR2); /* tell children to finish */ + } + + /* Wait for all the children to finish/die */ + while (wait((int *) 0) != -1) { + /* nop */ + } + + (void) fprintf(stderr, "%s Completed.", lad_timestamp()); + (void) fflush(stdout); + (void) fflush(stderr); + + /* Initialize and sum up counters */ + collect_counters(children); + if ((invalid_run = check_counters()) == 0) + invalid_run = check_parameters(iodist_file, mix_file, runtime_val); + + /* print test results */ + print_results(children, load, mix_file, + invalid_run, runtime_val, iodist_file); + + /* + * if multi-client execution then tell Prime client that + * I'm done with 'real' work and wait for move-data message + * and send data across + */ + if (Prime_client) { + (void) fprintf(stderr, + "%s Sending DONE-TEST message to Prime Client(%s).\n", + lad_timestamp(), Prime_client); + if ((result = + (int) signal_Prime_Client("CLIENT_SIGNAL","")) + == (int) RPC_SUCCESS) { + (void) fprintf(stderr, "%s Completed.", lad_timestamp()); + (void) fflush(stderr); + } else { + Saveerrno = errno; + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s: error %d sending DONE-TEST message to Prime Client\n", + sfs_Myname, result); + errno = Saveerrno; + perror("signal_Prime_Client"); + /* cleanup and exit */ + (void) generic_kill(0, SIGINT); + exit(75); + } + + /* + * wait for MOVE-DATA message from Prime Client before + * sending send results. + */ + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s Waiting on MOVE-DATA message from Prime Client(%s).\n", + lad_timestamp(), Prime_client); + (void) fflush(stderr); + (void) pause(); + (void) fprintf(stderr, "%s Received.", lad_timestamp()); + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, "%s Sending results to Prime Client(%s)\n", + lad_timestamp(), Prime_client); + (void) fflush(stderr); + + + if ((result = (int) signal_Prime_Client("CLIENT_DATA", + Client_results)) == (int) RPC_SUCCESS) { + (void) fprintf(stderr, "%s Completed.\n", lad_timestamp()); + (void) fflush(stderr); + } else { + Saveerrno = errno; + (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, + "%s: error %d sending client's result to Prime Client\n", + sfs_Myname, result); + errno = Saveerrno; + perror("signal_Prime_Client"); + /* cleanup and exit */ + (void) generic_kill(0, SIGINT); + exit(76); + } + } /* sent done, got move-data and sent data */ + + (void) fprintf(stdout,"\n"); + (void) fprintf(stdout, + "************************************************************************"); + (void) fprintf(stdout,"\n"); + +} /* parent */ + + +/* + * ------------------------ Utility Routines -------------------------- + */ + + +/* + * Monitor Logfile until its size reaches 'children' bytes. + * This means that all of the children are waiting for the next instruction. + */ +static void +synchronize_children( + int children) +{ + struct stat statb; /* for fstat */ + + do { + (void) sleep(1); + if (fstat(Log_fd, &statb) == -1) { + (void) fprintf(stderr, "%s: can't stat log %s", sfs_Myname, Logname); + (void) generic_kill(0, SIGINT); + exit(77); + } + } while (statb.st_size < children); + + /* + * Truncate the log file + */ + (void)close(Log_fd); + Log_fd = open(Logname, (O_RDWR | O_CREAT | O_TRUNC | O_APPEND), 0666); + if (Log_fd == -1) { + (void) fprintf(stderr, "%s: can't truncate log %s", + sfs_Myname, Logname); + (void) generic_kill(0, SIGINT); + exit(78); + } + +} /* synchronize_children */ + + +/* + * Multi-client execution support routine. + * Call remote procedure on Prime client + * to send message to sfs_prime_clnt program. + * The message will contain a type field set to 'type', + * and a data field set to 'data'. + */ +static int +signal_Prime_Client( + char * type, + char * data) +{ + static CLIENT * clnt_handle = NULL; + static int transaction_id = 0; + int * result; + static int socket; + sync_string sync_signal; + char transaction_string[MAX_STR1_LEN]; + char buf[128]; + + if ((int)strlen(data) > MAX_STR2_LEN) { + (void) fprintf(stderr, + "%s: %s too much data len = %d max = %d\n", + sfs_Myname, Prime_client, strlen(data), + MAX_STR2_LEN); + return((int)RPC_CANTENCODEARGS); + } + + if (clnt_handle == NULL) { + struct sockaddr_in prime_addr; + struct hostent * host_info; + + socket = RPC_ANYSOCK; + + /* get host information for prime_client */ + if ((host_info = gethostbyname(Prime_client)) == NULL) { + (void) fprintf(stderr, "%s: %s is unknown host\n", + sfs_Myname, Prime_client); + return ((int) RPC_UNKNOWNHOST); + } + + (void) memset((char *) &prime_addr, '\0', sizeof(prime_addr)); + (void) memmove((char *) &prime_addr.sin_addr, + (char *) host_info->h_addr, host_info->h_length); + + prime_addr.sin_family = AF_INET; + prime_addr.sin_port = 0; + + /* + * Create client "handle" used for calling SFS_SYNCPROG on the + * Prime Client. We tell the RPC package to use the "tcp" + * protocol when contacting the prime_client. + */ + clnt_handle = clnttcp_create(&prime_addr, SFS_SYNCPROG, + SFS_SYNCVERS, &socket, MAX_STR2_LEN, MAX_STR2_LEN); + + if (clnt_handle == ((CLIENT *) NULL)) { + /* + * Couldn't establish connection with the Prime_Client. + * Print error message and return error. + */ + clnt_pcreateerror(Prime_client); + (void) fprintf(stderr, + "%s: %s Could not establish client handle to contact %s\n", + lad_timestamp(), sfs_Myname, Prime_client); + return((int) RPC_FAILED); + } + } + + /* fill up xdr structure with data to send to Prime Client */ + (void) sprintf(transaction_string,"%d_%i", Client_num, ++transaction_id); + sync_signal.clnt_type = type; + sync_signal.clnt_id = Client_num; + sync_signal.clnt_data = data; + sync_signal.clnt_transaction = transaction_string; + + /* Call the remote procedure "signal_sfs_1" on the Prime Client */ + result = signal_sfs_1(&sync_signal, clnt_handle); + if (result == NULL) { + /* + * An error occurred while making RPC to the Prime Client. + * Print error message and return error. + */ + sprintf(buf, "%s Transaction %s: Could not call prime client %s", + lad_timestamp(), transaction_string, Prime_client); + clnt_perror(clnt_handle, buf); + return((int) RPC_CANTSEND); + } + + /* OK, we successfully called the remote procedure. */ + if (*result == 0) { + /* + * remote procedure was unable to successfully perform required + * operation on the Prime Client. + * Print error message and return error. + */ + (void) fprintf(stderr, + "%s: %s Prime Client couldn't write to PC_sync file \n", + sfs_Myname, Prime_client); + return((int) RPC_FAILED); + } + + /* remote procedure success - wrote to Prime Client sync file */ + return((int) RPC_SUCCESS); + +} /* signal_Prime_Client */ + + +/* + * Read results arrays for 'children' children from Logfile + * and accumulate them in "Ops". + * Complain about any problems we see with the log file. + */ +static void +collect_counters( + int children) +{ + int i; + int j; + struct stat statb; + sfs_results_report_type report; /* final results log */ + int Saveerrno; + + if (fstat(Log_fd, &statb) == -1) { + Saveerrno = errno; + (void) fprintf(stderr, "%s: can't stat log %s ", sfs_Myname, Logname); + errno = Saveerrno; + perror(Logname); + (void) generic_kill(0, SIGINT); + exit(79); + } + + if (statb.st_size != (children * sizeof(report))) { + (void) fprintf(stderr, "%s: log file %s has bad format\n", + sfs_Myname, Logname); + (void) generic_kill(0, SIGINT); + exit(80); + } + + if (lseek(Log_fd, 0L, 0) == -1) { + Saveerrno = errno; + (void) fprintf(stderr, "%s: can't lseek log %s ", sfs_Myname, Logname); + errno = Saveerrno; + perror("lseek"); + (void) generic_kill(0, SIGINT); + exit(81); + } + + for (j = 0; j < NOPS + 1; j++) { + Ops[j].results.good_calls = 0; + Ops[j].results.bad_calls = 0; + Ops[j].results.fast_calls = 0; + Ops[j].results.time.sec = 0; + Ops[j].results.time.usec = 0; + Ops[j].results.msec2 = 0; + } + Total_fss_bytes = 0; + Least_fss_bytes = 0; + Most_fss_bytes = 0; + Base_fss_bytes = 0; + + for (i = 0; i < children; i++) { + if (read(Log_fd, (char *) &report, sizeof(report)) == -1) { + Saveerrno = errno; + (void) fprintf(stderr, "%s: can't read log %s", sfs_Myname, Logname); + errno = Saveerrno; + perror("Logname"); + (void) generic_kill(0, SIGINT); + exit(82); + } + + for (j = 0; j < NOPS + 1; j++) { + Ops[j].results.good_calls += report.results_buf[j].good_calls; + Ops[j].results.bad_calls += report.results_buf[j].bad_calls; + Ops[j].results.fast_calls += report.results_buf[j].fast_calls; + ADDTIME(Ops[j].results.time, report.results_buf[j].time); + Ops[j].results.msec2 += report.results_buf[j].msec2; + } + Total_fss_bytes += report.total_fss_bytes; + Least_fss_bytes += report.least_fss_bytes; + Most_fss_bytes += report.most_fss_bytes; + Base_fss_bytes += report.base_fss_bytes; + } + +} /* collect_counters */ + + +/* + * Check the parameters for validity. + */ +static int +check_parameters( +char * iodist_file, +char * mix_file, +int runtime_val) +{ + int retval = 0; + char detail[40]; + + detail[0] = '\0'; + + if (iodist_file != NULL) { + retval = INVALID_IODIST; + } + if (mix_file != NULL) { + retval = INVALID_MIX; + } + if (runtime_val != DEFAULT_RUNTIME) { + (void) sprintf(detail, "%d != %d", runtime_val, DEFAULT_RUNTIME); + retval = INVALID_RUNTIME; + } + if (Access_percent != DEFAULT_ACCESS) { + (void) sprintf(detail, "%d != %d", Access_percent, DEFAULT_ACCESS); + retval = INVALID_ACCESS; + } + if (Append_percent != DEFAULT_APPEND) { + (void) sprintf(detail, "%d != %d", Append_percent, DEFAULT_APPEND); + retval = INVALID_APPEND; + } + if (Kb_per_block != DEFAULT_KB_PER_BLOCK) { + (void) sprintf(detail, "%d != %d", Kb_per_block, DEFAULT_KB_PER_BLOCK); + retval = INVALID_KB; + } + if (Files_per_dir != DEFAULT_FILES_PER_DIR) { + (void) sprintf(detail, "%d != %d", Files_per_dir, DEFAULT_FILES_PER_DIR); + retval = INVALID_NDIRS; + } + if (Fss_delta_percent != DEFAULT_DELTA_FSS) { + (void) sprintf(detail, "%d != %d", + Fss_delta_percent, DEFAULT_DELTA_FSS); + retval = INVALID_FSS; + } + if (Biod_max_outstanding_reads < DEFAULT_BIOD_MAX_READ) { + (void) sprintf(detail, "%d < %d", + Biod_max_outstanding_reads, DEFAULT_BIOD_MAX_READ); + retval = INVALID_BIODREAD; + } + if (Tot_client_num_symlinks != DEFAULT_NSYMLINKS) { + (void) sprintf(detail, "%d != %d", + Tot_client_num_symlinks, DEFAULT_NSYMLINKS); + retval = INVALID_NSYMLINKS; + } + if (Biod_max_outstanding_writes < DEFAULT_BIOD_MAX_WRITE) { + (void) sprintf(detail, "%d < %d", + Biod_max_outstanding_writes, DEFAULT_BIOD_MAX_WRITE); + retval = INVALID_BIODWRITE; + } + if (Warmuptime != DEFAULT_WARMUP) { + (void) sprintf(detail, "%d != %d", + Warmuptime, DEFAULT_WARMUP); + retval = INVALID_WARMUP; + } + + if (retval != 0) + (void) fprintf(stdout, + "%s: INVALID RUN, ILLEGAL PARAMETER: Non-standard %s %s\n", + sfs_Myname, invalid_str[retval], detail); + return (retval); +} + +/* + * Check the results in Ops[] for validity. + */ +static int +check_counters(void) +{ + double mix_pcnt; + int bad_pcnt; + int i; + int ret = 0; + + if (Ops[TOTAL].results.good_calls <= 0) { + (void) fprintf(stdout, "%s: INVALID RUN %s\n", + sfs_Myname, invalid_str[INVALID_GOODCALLS]); + ret = INVALID_GOODCALLS; + } + if (Ops[TOTAL].results.good_calls != 0) + bad_pcnt = (Ops[TOTAL].results.bad_calls * 100) + / Ops[TOTAL].results.good_calls; + else + bad_pcnt = 100; + + if (bad_pcnt >= 1) { + (void) fprintf(stdout, "%s: INVALID RUN, %d%% %s\n", + + sfs_Myname, bad_pcnt, + invalid_str[INVALID_FAILEDRPC]); + ret = INVALID_FAILEDRPC; + } + + if (Ops[TOTAL].results.good_calls == 0) { + (void) fprintf(stdout, "%s: INVALID RUN, no good calls\n", sfs_Myname); + return (INVALID_NOTMIX); + } + + for (i = 0; i < NOPS; i++) { + mix_pcnt = ((double)Ops[i].results.good_calls / + Ops[TOTAL].results.good_calls) * 100.0; + if (mix_pcnt != (double)Ops[i].mix_pcnt) { + if ((mix_pcnt - (double)Ops[i].mix_pcnt > 1.5) || + ((double)Ops[i].mix_pcnt - mix_pcnt > 1.5)) { + (void) fprintf(stdout, "%s: INVALID RUN, %s target %d%% actual %4.1f%% %s\n", + + sfs_Myname, Ops[i].name, Ops[i].mix_pcnt, mix_pcnt, + invalid_str[INVALID_NOTMIX]); + ret = INVALID_NOTMIX; + } + } + } + + return (ret); + +} /* check_counters */ + + +/* + * Print the test run results, for 'load' load, the operation percentages + * in 'mixfile' percentages, and 'children' processes. + */ +static void +print_results( + int children, + int load, + char * mix_file, + int invalid_flag, + int runtime_val, + char * iodist_file) +{ + uint_t runtime; + uint_t total_msec; + uint_t msec; + uint_t total_calls; + uint_t calls; + int i; + double squared_time_msec; + double sum2_msec; + double var_msec; + double stdev_msec; + double sq_conf_interval_msec; + double conf_interval_msec; + sfs_op_type * op_ptr; + sfs_results_type * results_ptr; + char result_string[MAX_LINE_LEN]; + + + /* compute total time for all ops combined */ + total_msec = 0; + for (i = 0; i < NOPS; i++) { + total_msec += Ops[i].results.time.sec * 1000; + total_msec += Ops[i].results.time.usec / 1000; + } + + /* + * Report statistics based on successful calls only. The per + * operation routines accumulate time and count only good_calls. + */ + total_calls = Ops[TOTAL].results.good_calls; + + + /* + * Print the client's test parameters + */ + (void) fprintf(stderr, "\n\nClient Test Parameters: \n"); + (void) fprintf(stderr, "\tNumber of processes = %d\n", children); + (void) fprintf(stderr, "\tRequested Load (NFS V%d operations/second) = %d\n", + nfs_version, load); + (void) fprintf(stderr, "\tMaximum number of outstanding biod writes = %d\n", + Biod_max_outstanding_writes); + (void) fprintf(stderr, "\tMaximum number of outstanding biod reads = %d\n", + Biod_max_outstanding_reads); + (void) fprintf(stderr, "\tWarm-up time (seconds) = %d\n\tRun time (seconds) = %d\n", + Warmuptime, runtime_val); + if (mix_file) + (void) fprintf(stderr,"\tNFS Mixfile = %s\n", mix_file); + if (iodist_file) + (void) fprintf(stderr,"\tBlock Size Distribution file = %s\n", + iodist_file); + (void) fprintf(stderr, "\tFile Set = %4d Files created for I/O operations\n", + (Tot_client_num_io_files/children + 1) * children); + (void) fprintf(stderr, "\t\t %4d Files accessed for I/O operations\n", + (((Tot_client_num_io_files/children + 1) * Access_percent) + / 100) * children); + (void) fprintf(stderr, "\t\t %4d Files for non-I/O operations\n", + (Tot_client_num_non_io_files/children + 1) * children); + (void) fprintf(stderr, "\t\t %4d Symlinks\n", + (Tot_client_num_symlinks/children + 1) * children); + (void) fprintf(stderr, "\t\t %4d Directories\n", + ((Tot_client_num_io_files/children + 1) / Files_per_dir ) * children); + (void) fprintf(stderr, "\t\t\tAdditional non-I/O files created as necessary\n\n"); + + (void) sprintf(Client_results,"%d %d %d %d %d %d\n", + nfs_version, + (Tot_client_num_io_files/children + 1) * children, + (((Tot_client_num_io_files/children + 1) * Access_percent) + / 100) *children, + (Tot_client_num_non_io_files/children + 1) * children, + (Tot_client_num_symlinks/children + 1) * children, + ((Tot_client_num_io_files/children + 1) / Files_per_dir ) * children); + + /* print the client's results header information */ + (void) fprintf(stderr, "\nSPEC SFS Benchmark Version %s, Creation - %s\n", + SFS_VERSION_NUM, SFS_VERSION_DATE); + (void) fprintf(stdout, "SFS Single Client (%s) Results, %s\n", + lad_hostname, lad_timestamp()); + (void) fflush(stdout); + + /* print column headers for per operation statistics */ + (void) fprintf(stdout, +"----------------------------------------------------------------------------\n"); + (void) fprintf(stdout, "\n"); + (void) fprintf(stdout, +"NFS V%d Target Actual NFS NFS Mean Std Dev Std Error Pcnt \n", +nfs_version); + (void) fprintf(stdout, +"Op NFS NFS Op Op Response Response of Mean,95%% of \n"); + (void) fprintf(stdout, +"Type Mix Mix Success Error Time Time Confidence Total\n"); + (void) fprintf(stdout, +" Pcnt Pcnt Count Count Msec/Op Msec/Op +- Msec/Op Time \n"); + (void) fprintf(stdout, +"----------------------------------------------------------------------------\n"); + (void) fflush(stdout); + + /* print per operation statistics */ + for (i = 0; i < NOPS; i++) { + /* init to 0 */ + squared_time_msec = 0.0; + sum2_msec = 0.0; + calls = 0; + msec = 0; + stdev_msec = 0; + + op_ptr = &Ops[i]; + results_ptr = &op_ptr->results; + + /* get the number successful calls and total time */ + calls = op_ptr->results.good_calls; + msec = (results_ptr->time.sec * 1000) + + (results_ptr->time.usec / 1000); + + /* compute the standard deviation for the mean response time */ + if (calls <= 1) + stdev_msec = 0; + else { + /* get the standard deviation */ + squared_time_msec = results_ptr->msec2; + /* compute the square of the total elapsed time */ + sum2_msec = (results_ptr->time.sec * 1000.0) + + (results_ptr->time.usec / 1000.0); + sum2_msec *= sum2_msec; + + /* variance = 1/(n-1) * (sum(x^2) - 1/n * (sum(x))^2) */ + var_msec = (squared_time_msec - (sum2_msec / calls)) / (calls-1); + if (var_msec == 0.0) { + stdev_msec = 0.0; + } else + stdev_msec = sqrt(var_msec); + } + + /* compute the confidence interval */ + if (calls != 0) { + sq_conf_interval_msec = DEFAULT_CHI_SQR_CI * (stdev_msec / calls); + if (sq_conf_interval_msec == 0.0) { + conf_interval_msec = 0.0; + } else + conf_interval_msec = sqrt(sq_conf_interval_msec); + } else + conf_interval_msec = 0.0; + + /* print the per op statistics */ + (void) fprintf(stdout, + "%-12s%3d%% %4.1f%% %5d %5d %5.2f %8.2f %8.2f %3.1f%%\n", + op_ptr->name, /* op name */ + op_ptr->mix_pcnt, /* target mix */ + /* actual mix */ + total_calls ? ((double)calls / total_calls) * 100.0 : 0.0, + results_ptr->good_calls, /* successes */ + results_ptr->bad_calls, /* errors */ + calls ? ((double)msec / calls) : 0.0, /* msec/call */ + stdev_msec, /* std dev */ + conf_interval_msec, /* conf int */ + /* % of time */ + total_msec ? ((double)msec / total_msec) * 100 : 0.0); + (void) fflush(stdout); + + /* + * Store client data in result_string. + * This string is different from client result display. + * The squared_time_msec and sum2_msec values are passed along + * to be used by the prime client to calculate the stddev value for + * each operation. + */ + if (Prime_client) { + (void) sprintf(result_string, + "%-12s %3d%% %3.1f%% %5d %5d %4ld.%1ld %6.2f %3.1f%% %f %f\n", + op_ptr->name, /* op name */ + op_ptr->mix_pcnt, /* target mix */ + /* actual mix */ + total_calls ? ((double)calls / total_calls) * 100.0 : 0.0, + results_ptr->good_calls, /* successes */ + results_ptr->bad_calls, /* errors */ + results_ptr->time.sec, /* total time1*/ + results_ptr->time.usec / 100000, /* total time2*/ + calls ? ((double)msec / calls) : 0.0, /* msec/call */ + /* % of time */ + total_msec ? ((double)msec / total_msec) * 100 : 0.0, + squared_time_msec, /* sum of sqs */ + sum2_msec); /* sq of sums */ + (void) strcat(Client_results, result_string); + } + + } /* end for each op */ + + (void) fprintf(stdout, +"----------------------------------------------------------------------------\n\n"); + (void) fflush(stdout); + + /* Average child runtime. (should this be the longest runtime?) */ + runtime = Ops[TOTAL].results.time.sec / children; + + /* Print summary */ + (void) fprintf(stdout, + " ------------------------------------------------------------\n"); + (void) fprintf(stdout, + " | SPEC SFS VERSION %6s SINGLE CLIENT RESULTS SUMMARY |\n", + SFS_VERSION_NUM); + (void) fprintf(stdout, + " ------------------------------------------------------------\n"); + (void) fprintf(stdout, "NFS V%d THROUGHPUT: ", nfs_version); + (void) fprintf(stdout, + "%4d.%02d Ops/Sec AVG. RESPONSE TIME: %4d.%02d Msec/Op\n", + runtime ? (total_calls / runtime) : 0, + runtime ? ((total_calls % runtime) * 100 / runtime) : 0, + total_calls ? (total_msec / total_calls) : 0, + total_calls ? ((total_msec % total_calls) * 100 / total_calls) : 0); + (void) fprintf(stdout, "%s PROTOCOL\n", Tcp ? "TCP" : "UDP"); + (void) fprintf(stdout, "FAST CALLS: %d\n", Ops[TOTAL].results.fast_calls); + (void) fprintf(stdout, "NFS MIXFILE: "); + if (mix_file) + (void) fprintf(stdout,"%s\n", mix_file); + else + (void) fprintf(stdout,"[ SFS Default ]\n"); + (void) fprintf(stdout, "CLIENT REQUESTED LOAD: %d Ops/Sec \n", load); + (void) fprintf(stdout, + "TOTAL NFS OPERATIONS: %-6d TEST TIME: %d Sec \n", + total_calls, runtime); + (void) fprintf(stdout, "FILE SET SIZE CREATED: %d KB\n", + Total_fss_bytes); + (void) fprintf(stdout, + "FILE SET SIZE ACCESSED: %d - %d KB (%d%% to %d%% of Base)\n", + Least_fss_bytes, Most_fss_bytes, + (100 * Least_fss_bytes) / Base_fss_bytes, + (100 * Most_fss_bytes) / Base_fss_bytes); + (void) fprintf(stdout, "\n"); + (void) fprintf(stdout, + "------------------------------------------------------------------------"); + (void) fprintf(stdout, "\n\n"); + (void) fflush(stdout); + + /* + * store client summary results and Invalid run indicator + * to send to the Prime_client + */ + if (Prime_client) { + (void) sprintf(result_string,"%d.%02d %d.%02d %d %d %d %d %d %d %d\n", + runtime ? (total_calls / runtime) : 0, /* ops/sec1 */ + runtime ? ((total_calls % runtime) * 100 / runtime) : 0, /* ops/sec2 */ + total_calls ? (total_msec / total_calls) : 0, /* mean1 */ + total_calls ? ((total_msec % total_calls) * 100 / total_calls) : 0, /* mean2 */ + runtime, /* run time */ + total_calls, /* # ops */ + invalid_flag, /* valid flag */ + Total_fss_bytes, /* total fileset */ + Least_fss_bytes, /* fileset low */ + Most_fss_bytes, /* fileset high */ + Base_fss_bytes); /* fileset base */ + (void) strcat(Client_results, result_string); + } + +} /* print_results */ + + +/* ARGSUSED */ +static void +sfs_reaper( + int sig_id) +{ + (void) fprintf(stderr, "%s: caught unexpected SIGCHLD. Exiting...\n", + sfs_Myname); + /* cleanup and exit */ + (void) signal_Prime_Client("CLIENT_STOP", ""); + (void) generic_kill(0, SIGINT); + exit(83); +} +/* sfs_c_pnt.c */ diff --git a/TBBT/trace_play/sfs_c_rnd.c b/TBBT/trace_play/sfs_c_rnd.c new file mode 100644 index 0000000..b3a0174 --- /dev/null +++ b/TBBT/trace_play/sfs_c_rnd.c @@ -0,0 +1,247 @@ +#ifndef lint +static char sfs_c_rndSid[] = "@(#)sfs_c_rnd.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ---------------------- sfs_c_rnd.c --------------------- + * + * Random number generator. + * + *.Exported_routines + * int32_t sfs_random(void) + * void sfs_srandom(int) + * + *.Local_routines + * double ran(void) + * int32_t spec_rand(void) + * void spec_srand(int) + * + *.Revision_History + * 28-Nov-91 Teelucksingh ANSI C + * 01-Aug-91 Wiryaman sfs_srandom() and sfs_random() + * now use spec_srand() and spec_rand() + * instead of srandom() and random(). + * 17-Apr-91 Wittle Created. + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + +/* + * Here's the source for the random number generator that SPEC uses. + * The function to be called is "spec_rand" which returns an integer + * between 1 and MAX_INT-1. + * + * One question we may wanna think about is the seeding of the random + * number generator. Do we start with the same seed everytime (for + * repeatability) or use a array of possible seeds (some seeds are better + * than others and SPEC prople mention that they have a list of 15 + * "good" seeds). + */ + + +/* + * ------------------------- Static Declarations ------------------------- + */ + +static int32_t seedi = 2231; + + +/* + * ------------------------- External Definitions ------------------------- + */ + +static double ran(void); +static int32_t spec_rand(void); +static void spec_srand(int); + +/* + * ----------------------- Random Number Routines ----------------------- + */ + + +/* + * Seed the random number generator. + */ +static void +spec_srand( + int seed) +{ + seedi = seed; +} + + +/* + * Returns a random number. + */ +static int32_t +spec_rand(void) +{ + (void) ran(); + return(seedi); +} + + +/* + * Compute the next random number. + */ +static double +ran(void) + +/* See "Random Number Generators: Good Ones Are Hard To Find", */ +/* Park & Miller, CACM 31#10 October 1988 pages 1192-1201. */ +/***********************************************************/ +/* THIS IMPLEMENTATION REQUIRES AT LEAST 32 BIT INTEGERS ! */ +/***********************************************************/ + +#define _A_MULTIPLIER 16807L +#define _M_MODULUS 2147483647L /* (2**31)-1 */ +#define _Q_QUOTIENT 127773L /* 2147483647 / 16807 */ +#define _R_REMAINDER 2836L /* 2147483647 % 16807 */ +{ + int32_t lo; + int32_t hi; + int32_t test; + + hi = seedi / _Q_QUOTIENT; + lo = seedi % _Q_QUOTIENT; + test = _A_MULTIPLIER * lo - _R_REMAINDER * hi; + if (test > 0) { + seedi = test; + } else { + seedi = test + _M_MODULUS; + } + return((float) seedi / _M_MODULUS); +} + +/* + * Local interface to seed random number generator. + */ +void +sfs_srandom( + int seed) +{ + spec_srand(seed); +} + + +/* + * Local interface to obtain a random number. + */ +int32_t +sfs_random(void) +{ + return(spec_rand()); +} + +static struct r_array { + int n1; + int n2; +} *r_array; + +static int r_length = 0; + +static int +r_array_compare(const void *i, const void *j) +{ + if (((struct r_array *)i)->n2 > ((struct r_array *)j)->n2) + return (1); + if (((struct r_array *)i)->n2 < ((struct r_array *)j)->n2) + return (-1); + return (0); +} + +int +init_rand_range(int length) +{ + int i; + + /* + * If array already exists free it + */ + if (r_length != 0) { + (void)free(r_array); + r_length = 0; + } + + /* + * If length is zero just free memory and return + */ + if (length == 0) + return (0); + + /* + * Allocate array of sequential numbers and random numbers + */ + if ((r_array = malloc(length * sizeof(struct r_array))) == NULL) + return (1); + + r_length = length; + + /* + * Initialize array of sequential values and random values + */ + for (i = 0; i < length; i++) { + r_array[i].n1 = i; + r_array[i].n2 = sfs_random(); + } + + /* + * Sort random array values to put sequential values in random order + */ + qsort(r_array, length, sizeof(struct r_array), r_array_compare); + + return (0); +} + +int +rand_range(int index) +{ + return (r_array[index].n1); +} +/* sfs_c_rnd.c */ diff --git a/TBBT/trace_play/sfs_c_sig.c b/TBBT/trace_play/sfs_c_sig.c new file mode 100644 index 0000000..7826377 --- /dev/null +++ b/TBBT/trace_play/sfs_c_sig.c @@ -0,0 +1,170 @@ +#ifndef lint +static char sfs_c_sigSid[] = "@(#)sfs_c_sig.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ---------------------- sfs_c_sig.c --------------------- + * + * Signal handling. Routines to send and catch signals. + * + *.Exported_Routines + * void sfs_alarm(int) + * void sfs_startup(int) + * void sfs_stop(int) + * void sfs_cleanup(int) + * + *.Local_Routines + * None. + * + *.Revision_History + * 16-Dec-91 Wittle Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <sys/signal.h> + +#include <unistd.h> + +#include "sfs_c_def.h" + +#ifndef RFS +/* + * ------------------------- Signal Handlers ------------------------- + */ + +void +sfs_alarm( + int sig_id) +{ +#if !(defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + (void) signal(sig_id, sfs_alarm); +#endif + if (DEBUG_CHILD_SIGNAL) + (void) fprintf(stderr, "%s: caught Signal %d\n", sfs_Myname, sig_id); + (void) fflush(stderr); + +} /* sfs_alarm */ + + +/* + * Signal Handler + * Catch the parent's "start" signal. + */ +void +sfs_startup( + int sig_id) +{ +#if !(defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + (void) signal(SIGUSR1, sfs_startup); +#endif + if (DEBUG_CHILD_SIGNAL) + (void) fprintf(stderr, "%s: caught Signal %d SIGUSR1\n", + sfs_Myname, sig_id); + + if (Child_num == -1) + /* I'm the parent, ignore the signal */ + return; + + /* + * SIGUSR1 is used to make all phase transitions, but we + * only want to make the Warmup -> Testrun switch here + */ + if (Current_test_phase != Warmup_phase) + return; + + Current_test_phase = Testrun_phase; + start_run_phase++; +} /* sfs_startup */ + + +/* + * Signal Handler + * Catch the parent's "stop" signal. + */ +void +sfs_stop( + int sig_id) +{ +#if !(defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + (void) signal(SIGUSR2, sfs_stop); +#endif + if (DEBUG_CHILD_SIGNAL) + (void) fprintf(stderr, "%s: caught Signal %d SIGUSR2\n", + sfs_Myname, sig_id); + + /* end the test */ + Runtime = 0; + Current_test_phase = Results_phase; + +} /* sfs_stop */ + +#endif + + +/* + * SIGINT, SIGTERM handler + * Clean up and exit due to an error/abort condition. + * We assume if NFS_client was valid, then MOUNT_client was also valid. + */ +void +sfs_cleanup( + int sig_id) +{ + if (DEBUG_CHILD_SIGNAL) + (void) fprintf(stderr, "%s: caught Signal %d SIGINT\n", + sfs_Myname, sig_id); + + (void) unlink(Logname); + if (NFS_client != ((CLIENT *) NULL)) + clnt_destroy(NFS_client); + exit(65); + +} /* sfs_cleanup */ + +/* sfs_c_sig.c */ diff --git a/TBBT/trace_play/sfs_c_sub.c b/TBBT/trace_play/sfs_c_sub.c new file mode 100644 index 0000000..7f91b89 --- /dev/null +++ b/TBBT/trace_play/sfs_c_sub.c @@ -0,0 +1,198 @@ +#ifndef lint +static char sfs_c_subSid[] = "@(#)sfs_c_sub.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * ---------------------- sfs_c_sub.c --------------------- + * + * Subroutines common to both sfs and sfs_prime_client. + * + *.Exported_Routines + * int generic_kill(int, int) + * void generic_catcher(int) + * char * lad_timestamp(void) + * + *.Local_Routines + * None. + * + *.Revision_History + * 16-Dec-91 Wittle Created. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <signal.h> +#include <time.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <sys/signal.h> + +#include <unistd.h> + +#include "sfs_c_def.h" + +/* + * Common data shared between sfs and sfs_prime + * + * Values for invalid runs + */ +char *invalid_str[INVALID_MAX] = { + "No error", + "Unknown", + "IO distribution file", + "Mix file", + "Runtime", + "Access percentage", + "Append percentage", + "KB per block", + "Number client directories", + "Fileset delta", + "Max biod reads", + "Number symlinks", + "Max biod writes", + "Warmup time", + "No good calls", + "Failed RPC calls", + "Op mix missed", +}; + +/* + * ------------------------- Signal Handlers ------------------------- + */ + +/* + * Signal Sender. Send signal 'sig' to process 'pid'. + */ +int +generic_kill( + int pid, + int signal) +{ + if (DEBUG_PARENT_SIGNAL) + (void) fprintf(stderr, + "%s: sending Pid %d Signal %d\n", sfs_Myname, pid ,signal); + return(kill((pid_t)pid, signal)); + +} /* generic_kill */ + + +/* + * Signal Handler. Catch and reset the handler for signal 'i'. + */ +void +generic_catcher( + int i) +{ +#if !(defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + (void) signal(i, generic_catcher); +#endif + if (DEBUG_CHILD_SIGNAL) + (void) fprintf(stderr, "%s: caught Signal %d\n", sfs_Myname, i); + (void) fflush(stderr); + +} /* generic_catcher */ + + + +/* + * get the date and time and return a string, remove the END-OF-LINE (\n) + */ +char * +lad_timestamp(void) +{ + static time_t run_date; + static char date_string[26]; + + run_date = time((time_t *)NULL); + (void) strncpy((char *) date_string, (char *) ctime(&run_date), 24); + return(date_string); + +} /* lad_timestamp */ + +int +set_debug_level(char *s) +{ + unsigned int i; + unsigned int first; + unsigned int last; + unsigned int level = 0; + + if (s == NULL || *s == '\0') + return(0); + + for (;;) { + if (*s == ',') { + s++; + continue; + } + + /* find first flag to set */ + i = 0; + while (isdigit(*s)) + i = i * 10 + (*s++ - '0'); + first = i; + + /* find last flag to set */ + if (*s == '-') { + i = 0; + while (isdigit(*++s)) + i = i * 10 + (*s - '0'); + } + last = i; + + if (first != 0 && last != 0 && first < 32 && last < 32) { + for (i = first - 1; i < last; i++) { + level |= (1 << i); + } + } + + /* more arguments? */ + if (*s++ == '\0') + return (level); + } +} + +/* sfs_c_sub.c */ diff --git a/TBBT/trace_play/sfs_ext_mon b/TBBT/trace_play/sfs_ext_mon new file mode 100755 index 0000000..841e9a1 --- /dev/null +++ b/TBBT/trace_play/sfs_ext_mon @@ -0,0 +1,58 @@ +#!/bin/sh +# @(#)sfs_ext_mon 2.1 97/10/23 +# +# This sample shell script can be used to start and stop +# external processes at the beginning and ending of the +# sfs load generation period respectively. The name of +# this program is pased in via the PRIME_MON_SCRIPT variable +# in the sfs_rc or equivalent file. The sfs_prime program +# executes this shell script just before it issues the START +# message to all the clients ie. "script_name START"; and likewise +# another call after all the clients have completed load generation ie. +# "script_name DONE". +# +# All environment variables exported from sfs_mgr are available for use. +# +EXT_MON_ARG="" +if [ $# -gt 1 ] +then + EXT_MON_ARG="$2" +fi + +# +#----------------- START section ----------------- +# Code section that handles starting of external processes. +# ALL PROCESSES SHOULD BE STARTED IN THE BACKGROUND OR ELSE +# THE TEST WILL BLOCK WAITING FOR THEIR COMPLETION!! +# +if [ "$1" = "START" ]; then + # + # place commands to start performance monitoring utilities as + # background processes here. + # + echo "$0: started external monitoring utilities" >&2 + exit 0 +fi + +# +#----------------- DONE section ----------------- +# Code section that handles stopping of external processes. +# ALL PROCESSES SHOULD BE STARTED IN THE BACKGROUND OR ELSE +# THE TEST WILL BLOCK WAITING FOR THEIR COMPLETION!! +# +if [ "$1" = "DONE" ]; then + # + # place commands to stop performance monitoring utilities as + # background processes here. + # + echo "$0: stopped external monitoring utilities" >&2 + exit 0 +fi + +# +#----------------- ERROR section ----------------- +# ERROR: BAD PARAMETER +# +echo "$0: bad param. " >&2 +echo "usage: $0 START | DONE " >&2 +exit 1 diff --git a/TBBT/trace_play/sfs_m_def.h b/TBBT/trace_play/sfs_m_def.h new file mode 100644 index 0000000..bd01ec6 --- /dev/null +++ b/TBBT/trace_play/sfs_m_def.h @@ -0,0 +1,59 @@ +/* + * @(#)sfs_m_def.h 2.1 97/10/23 + */ + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +#define MAX_LINE_LEN 160 /* max per line results string */ +#define MAX_STR1_LEN 31 /* max msg, number, and xid str len */ +#define MAX_STR2_LEN 2560 /* total results data string length */ +#define MULTICLIENT_OFFSET 500 /* Runtime offset */ + +/* multi-client sync logfiles and prefixes */ +#define SFS_CLIENT_SYNC_LOG "/tmp/sfs_CL" /* client logfile prefix */ +#define SFS_PRIME_SYNC_LOG "/tmp/sfs_PC_sync" /* prime client logfile */ +#define PRIME_RESULTS_LOG "/tmp/sfs_res" /* prime results logfile prefix */ + +struct sync_string { + int clnt_id; /* client number */ + char *clnt_type; /* message type, hard coded */ + char *clnt_transaction; /* transaction id */ + char *clnt_data; /* results strings */ +}; +typedef struct sync_string sync_string; + +#define SFS_SYNCPROG ((uint32_t) 100500) +#define SFS_SYNCVERS ((uint32_t) 1) +#define SIGNAL_NULLPROC ((uint32_t) 0) +#define SIGNAL_SFS ((uint32_t) 1) + +extern bool_t xdr_sync_string(XDR *, sync_string *); +extern int * signal_sfs_1(sync_string *, CLIENT *); diff --git a/TBBT/trace_play/sfs_m_msg.c b/TBBT/trace_play/sfs_m_msg.c new file mode 100644 index 0000000..80fbeec --- /dev/null +++ b/TBBT/trace_play/sfs_m_msg.c @@ -0,0 +1,103 @@ +#ifndef lint +static char sfs_m_msgSid[] = "@(#)sfs_m_msg.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + *.Exported_routines + * int * signal_sfs_1(sync_string *, CLIENT *); + * + *.Local_routines + * None. + * + *.Revision_history + * 04-Dec-91 Keith Include sfs_def.h for SYSV/SVR4 + * mem* routines. + * 17-Jun-91 Teelucksingh Create multi-client synchronization + * rpc definition + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + +/* + * ----------------------- Static Definitions ----------------------- + */ + +/* + * Almost all rpcs are sent over TCP so we have a reliable transport, + * unfortunately the initialization phase may highly overload the + * server and the previous default of 5 seconds might timeout before + * it gets acknowledged. The risk of a large timeout is that there is + * an added period after a stop message is sent. This is usually not + * a problem. + */ +static struct timeval TIMEOUT = { 60, 0 }; + + +/* + * ------------------- Multi-client Message Handling ------------------- + */ + +int * +signal_sfs_1( + sync_string * argp, + CLIENT * clnt) +{ + static int res; + + (void) memset((char *) &res, '\0', sizeof(res)); + if (clnt_call(clnt, SIGNAL_SFS, xdr_sync_string, (caddr_t)argp, xdr_int, + (caddr_t)&res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} /* signal_sfs_1 */ + + +/* sfs_m_msg.c */ diff --git a/TBBT/trace_play/sfs_m_prm.c b/TBBT/trace_play/sfs_m_prm.c new file mode 100644 index 0000000..5cc5ac3 --- /dev/null +++ b/TBBT/trace_play/sfs_m_prm.c @@ -0,0 +1,1545 @@ +#ifndef lint +static char sccsid[] = "@(#)sfs_m_prm.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + * This program is started from sfs_mgr. It runs on some system + * designated as the Prime Client and synchronizes the the different + * phases of a SFS benchmark execution amongst multiple clients by + * means of RPCs. The Prime client can also be running SFS. + * + *.Exported_routines + * int main(int, char **) + * + *.Local_routines + * void multi_cleanup(int) + * void do_initialize(int, char **) + * void sync_PC_with_clients(void) + * int signal_sfs_clients(char *, int) + * void print_multi_results(void) + * void prog_usage(void) + * void printdeadclients(void) + * + *.Revision_history + * 11-Jul-94 ChakChung Ng Add codes for NFS/v3 + * 02-Jul-92 0.1.9 Teelucksingh + * Use tcp handles for synchronization + * instead of udp ones. Added code to make + * call to shell script to start and stop + * external monitoring (no longer creates + * /tmp/SFS_START and /tmp/SFS_DONE). + * 10-Jan-92 0.00.19 Teelucksingh + * Added code for the Prime-Client to report + * 'INVALID RUN' from any of the clients. + * Also added code to compute and report + * the aggregate 'average response time + * standard deviation values'. + * 04-Jan-92 0.00.18 Pawlowski + * Add hooks for raw data dump support. + * 04-Dec-91 0.00.15 Keith + * Include string.h for SYSV/SVR4 + * 28-Nov-91 0.00.13 Teelucksingh + * Fixed 'multiple signals' problem. + * Sync rpcs now pass a 'transaction id' + * to 'sfs_syncd', the sync server. + * 'sfs_syncd' keeps track of previous rpc + * calls that successfully executed. + * Added ANSI C features. + * + * 23-Sep-91 0.00.11 Teelucksingh + * Modified the format of sfs_prime output + * 17-Jun-91 Teelucksingh Created. + * + * + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <ctype.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <sys/file.h> +#include <fcntl.h> + +#include <unistd.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + +#if !defined(_XOPEN_SOURCE) +#include <sys/socket.h> +#else +#define AF_INET 2 +#endif + +/* + * ----------------------- External Definitions ----------------------- + */ + + +/* sfs_mpr.c function declarations */ +/* external sfs routines */ + +/* forward definitions for local routines */ +static void multi_cleanup(int); +static void do_initialize(int, char **); +static void sync_PC_with_clients(void); +static int signal_sfs_clients(char *); +static void print_multi_results(void); +static void prog_usage(void); +static void printdeadclients(void); + +/* + * ----------------------- Static Definitions ----------------------- + */ + +#define EXTRA_INIT_TIME 3600 /* extra time to initialize before timing out */ + +int Debug_level = 0; /* flag indicates prime client debug mode */ +char *sfs_Myname; /* program name */ +int nfs_version; + +static int Asleep = 0; /* flag: parent sfs process is asleep */ +static int Pc_log_fd; /* Prime client sync log fd */ +static int Num_clients = 0; /* number of clients used in run */ +static char **Client_names; /* [HOSTNAME_LEN]; array of clients host names */ + /* sleep period before issuing a go ahead signal */ +static int Prime_sleep_time = 0; + /* time to wait before exiting with error */ +static int Prime_time_out = 400; +static int Prime_runtime = DEFAULT_RUNTIME; /* seconds in benchmark run */ + +/* + * the Prime_client only uses the P_* variables + * for calculating and reporting the Aggregate + * run parameters. + */ +static int P_children = DEFAULT_NPROCS; /* processes per client */ +static int P_total_load = DEFAULT_LOAD; /* NFS operations per second */ +static int P_percent_append = DEFAULT_APPEND; /* % of writes that append */ +static int P_percent_access = DEFAULT_ACCESS; /* % of file set accessed */ +static int P_kb_per_block = DEFAULT_KB_PER_BLOCK; /* i/o pkt block sz in KB */ +static int P_dump_data = 0; /* raw output switch */ +static int P_testop = -1; /* test mode operation number */ +static int P_percent_fss_delta = /* allowed change to file set */ + DEFAULT_DELTA_FSS; + /* allowed change to file set */ +static int P_warmuptime = DEFAULT_WARMUP; /* seconds to warmup */ +static char *P_iodist_file = 0; /* block io dist table file */ +static char *P_mix_file = 0; /* mix file */ +static char *P_script_name = 0; /* external script name */ +static char *P_script_args = ""; /* external script parameters */ +static int P_tcp = 0; /* TCP */ +static FILE *sum_result_fp = NULL; + +/* + * --------------------- Biod Simulation Variables --------------------- + */ +static int P_biod_max_outstanding_reads = DEFAULT_BIOD_MAX_READ; +static int P_biod_max_outstanding_writes = DEFAULT_BIOD_MAX_WRITE; + +/* list of nfs operations - used to verify results file */ +static char *Ops_name[NOPS] = { "null", "getattr", "setattr", "root", "lookup", + "readlink", "read", "wrcache", "write", + "create", "remove", "rename", "link", + "symlink", "mkdir", "rmdir", "readdir", + "fsstat", "access", "commit", "fsinfo", + "mknod", "pathconf", "readdirplus" }; + + +/* + * -------------------------- Prime-client -------------------------- + */ + + +/* + * SIGINT Signal Handler + * - rpc to all clients to cleanup and exit + */ +static void +multi_cleanup( + int sig_id) +{ + /* wake up this process if asleep */ + if (Asleep == 1) { + (void) generic_kill(0,SIGALRM); + } + (void) fprintf(stderr,"\n%s: Caught Signal %d SIGINT\n", sfs_Myname, sig_id); + (void) fprintf(stderr,"\nSending interupt signal to clients ... "); + (void) fflush(stderr); + if ((int) signal_sfs_clients("PRIME_STOP") !=0) + exit(9); + (void) fprintf(stderr,"done\n"); + (void) fflush(stderr); + (void) close(Pc_log_fd); + (void) unlink(SFS_PRIME_SYNC_LOG); + exit(10); + +} /* multi_cleanup */ + + +/* + * main + * synchronizes multi-client sfs run + * - wait for 'DONE-MOUNT' from all clients + * - tell them to `DO-INIT` + * - wait for `DONE-INIT` from all clients + * - tell them to `DO-WARMUP` + * - wait for 'READY from all clients + * - tells them to 'START' and goes to sleep + * - wakes up and tells clients to 'STOP' + * - wait for 'DONE-TEST' from all clients + * - tells clients to 'MOVE-DATA' + * - averages multi-client data and exits + */ +int +main( + int argc, + char * argv[]) +{ + int i; + char monitor_cmd[SFS_MAXPATHLEN+5]; + int nsigs = 32; /* reasonable default */ + FILE *pid_fp; + +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + struct sigaction sig_act, old_sig_act; +#endif + + /* + * Place pid in pid log file + */ + if ((pid_fp = fopen(SFS_PRM_PID, "a+")) == NULL) { + perror(SFS_PRM_PID); + exit(1); + } + (void) fprintf(pid_fp, "%d\n", getpid()); + (void) fclose(pid_fp); + + (void) fprintf(stderr, "\nSPEC SFS Benchmark Version %s, Creation - %s\n", + SFS_VERSION_NUM, SFS_VERSION_DATE); + (void) fflush(stderr); + + /* initialize variables, parse input, etc. */ + do_initialize(argc, argv); + + /* + * setup value of nsigs + */ +#ifdef __NSIG + nsigs = __NSIG; +#endif +#ifdef _NSIG + nsigs = _NSIG; +#endif +#ifdef NSIG + nsigs = NSIG; +#endif +#if defined(SOLARIS2) && !defined(_sys_nsig) + nsigs = _sys_siglistn; +#endif + + /* trap for all signals */ + +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + /* use XOPEN signal handling */ + sig_act.sa_handler = generic_catcher; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + if (DEBUG_PARENT_GENERAL) { + if (nsigs == 0) { + (void) fprintf (stderr, + "WARNING: nsigs not defined, no extra signals caught\n"); + } + for (i = 1; i < nsigs; i++) { + /* attempt to set up signal handler for these signals gives an error. */ + if (i!=SIGCHLD && i!=SIGKILL && i!=SIGSTOP && i!=SIGCONT) { + if (sigaction(i,&sig_act,&old_sig_act) == -1) { + if (errno == EINVAL) + (void) fprintf (stderr, + "Skipping invalid signal %d\n", i); + else { + perror("sigaction failed"); + exit(11); + } + } + } + } + } + + /* signals handlers for signals used by sfs_prime */ + sig_act.sa_handler = multi_cleanup; + if (sigaction(SIGINT,&sig_act,&old_sig_act) != 0) { + perror("sigaction failed: SIGINT"); + exit(12); + } +#else + + if (DEBUG_PARENT_GENERAL) { + if (nsigs == 0) { + (void) fprintf (stderr, + "WARNING: nsigs not defined, no extra signals caught\n"); + } + for (i = 1; i < nsigs; i++) { + (void) signal(i, generic_catcher); + } + } + + /* set up SIGINT signal handler */ + (void) signal(SIGINT, multi_cleanup); + +#endif /* USE_POSIX_SIGNALS */ + + (void) fprintf(stderr, "Executing SFS Benchmark on %d Client(s).\n", + Num_clients); + (void) fflush(stderr); + + /* wait for 'DONE-MOUNT message from the clients */ + (void) fprintf(stderr, + "%s Waiting on DONE-MOUNT message from %d client(s).\n", + lad_timestamp(), Num_clients); + (void) fflush(stderr); + + sync_PC_with_clients(); /* wait for clients DONE-MOUNT */ + + (void) fprintf(stderr, "%s Received.\n", lad_timestamp()); + (void) fflush(stderr); + + /* send DO-INIT message to all the clients */ + (void) fprintf(stderr, "%s Sending DO-INIT message to %d client(s).\n", + lad_timestamp(), Num_clients); + (void) fflush(stderr); + if ((int) signal_sfs_clients("PRIME_SIGNAL") !=0) + exit(13); + (void) fprintf(stderr, "%s Completed.\n", lad_timestamp()); + (void) fflush(stderr); + + /* wait for 'DONE-INIT' message from the clients */ + (void) fprintf(stderr, + "%s Waiting on DONE-INIT message from %d client(s).\n", + lad_timestamp(), Num_clients); + (void) fflush(stderr); + + /* + * add an extra time to time_out value. + * initializing the SFS testdirs on clients can take a while. + */ + Prime_time_out += EXTRA_INIT_TIME; + sync_PC_with_clients(); /* wait for clients DONE-INIT */ + Prime_time_out -= EXTRA_INIT_TIME; /* reset time_out */ + + (void) fprintf(stderr, "%s Received.\n", lad_timestamp()); + (void) fflush(stderr); + + /* send DO-WARMUP message to all the clients */ + (void) fprintf(stderr, "%s Sending DO-WARMUP message to %d client(s).\n", + lad_timestamp(), Num_clients); + (void) fflush(stderr); + if ((int) signal_sfs_clients("PRIME_SIGNAL") !=0) + exit(14); + (void) fprintf(stderr, "%s Completed.\n", lad_timestamp()); + (void) fflush(stderr); + + /* wait for 'READY' message from the clients */ + (void) fprintf(stderr, "%s Waiting on READY message from %d client(s).\n", + lad_timestamp(), Num_clients); + (void) fflush(stderr); + + (void) sleep(P_warmuptime); + + sync_PC_with_clients(); /* wait for clients READY */ + + (void) fprintf(stderr, "%s Received.\n", lad_timestamp()); + (void) fflush(stderr); + + /* + * call the program to trigger START of external monitoring + */ + if (P_script_name) { + (void) sprintf(monitor_cmd,"%s %s %s",P_script_name, + "START",P_script_args); + if (system(monitor_cmd) != 0) { + (void) fprintf(stderr,"%s: external monitoring command (%s) failed - %d - continuing.\n ", + sfs_Myname, monitor_cmd, errno); + (void) fflush(stderr); + P_script_name = NULL; + } + + } + + /* + * wait period before telling clients to START - gives external + * performance monitoring utilities enough time to start up. + * Value set in sfs_rc (PRIME_SLEEP) - default is 0 seconds. + */ + (void) sleep(Prime_sleep_time); + + /* send START message to all the clients */ + (void) fprintf(stderr, "%s Sending START message to %d client(s).\n", + lad_timestamp(), Num_clients); + (void) fflush(stderr); + if ((int) signal_sfs_clients("PRIME_SIGNAL") !=0) + exit(15); + (void) fprintf(stderr, "%s Completed.\n", lad_timestamp()); + (void) fflush(stderr); + + /* + * Sleep for <Prime_runtime> seconds + * and then send STOP message to tell the sfs clients + * that is time to wrap things up. + */ + /* set the Asleep flag go to sleep while clients are executing sfs */ + Asleep = 1; + (void) sleep(Prime_runtime); + Asleep = 0; + + (void) fprintf(stderr, "%s Sending STOP message to %d client(s).\n", + lad_timestamp(), Num_clients); + (void) fflush(stderr); + if ((int) signal_sfs_clients("PRIME_ALARM") !=0) + exit(16); + (void) fprintf(stderr, "%s Completed.\n", lad_timestamp()); + (void) fflush(stderr); + + + /* wait for DONE-TEST message from clients indicating they completed run */ + (void) fprintf(stderr, "%s Waiting on DONE-TEST message from %d client(s).\n", + lad_timestamp(), Num_clients); + (void) fflush(stderr); + sync_PC_with_clients(); + (void) fprintf(stderr, "%s Received.\n", lad_timestamp()); + (void) fflush(stderr); + + /* + * call the program to trigger STOP of external monitoring + */ + if (P_script_name) { + (void) sprintf(monitor_cmd,"%s %s %s",P_script_name, + "DONE",P_script_args); + if (system(monitor_cmd) != 0) { + (void) fprintf(stderr,"%s: external monitoring command (%s) failed - %d - continuing.\n ", + sfs_Myname, monitor_cmd, errno); + (void) fflush(stderr); + P_script_name = NULL; + } + } + + /* give enough time to stop external performance monitoring utilities. */ + (void) sleep(Prime_sleep_time); + + /* + * send MOVE-DATA message to Clients to move data across + */ + (void) fprintf(stderr, "%s Sending MOVE-DATA message to %d client(s).\n", + lad_timestamp(), Num_clients); + (void) fflush(stderr); + if ((int) signal_sfs_clients("PRIME_SIGNAL") !=0) + exit(17); + (void) fprintf(stderr, "%s Completed.\n", lad_timestamp()); + (void) fflush(stderr); + + /* wait for SEND-DATA message from all the clients */ + (void) fprintf(stderr, + "%s Waiting on SEND-DATA message from %d client(s).\n", + lad_timestamp(), Num_clients); + (void) fflush(stderr); + sync_PC_with_clients(); + (void) fprintf(stderr, "%s Received.\n", lad_timestamp()); + (void) fflush(stderr); + + /* summarize and print aggregate results */ + print_multi_results(); + + /* close files and exit success */ + (void) close(Pc_log_fd); + (void) unlink(SFS_PRIME_SYNC_LOG); + (void) unlink(SFS_PRM_PID); + return(0); + +} /* main */ + +/* + * initialize control variables, open logfiles etc. + */ +static void +do_initialize( + int argc, + char *argv[]) +{ + int c; + FILE *check_fp; + int i; + char *cp; + extern char *optarg; + extern int optind; + + + sfs_Myname = argv[0]; + if (argc <= 1) { + prog_usage(); + /* NOTREACHED */ + } + + while ((c = getopt(argc, argv, "a:A:b:B:C:d:f:k:K:l:m:p:QR:s:t:T:W:w:x:z")) != EOF) + switch (c) { + case 'a': /* Percent of file set to access; + * used in aggregate report. + */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal acces value %s\n", + sfs_Myname, optarg); + exit(18); + } + P_percent_access = atoi(optarg); + if (P_percent_access < 0 || P_percent_access > 100) { + (void) fprintf(stderr, + "%s: %% access must be between 0 and 100\n", + sfs_Myname); + exit(19); + } + break; + + case 'A': /* Percent of writes that append; + * used in aggregate report. + */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal append value %s\n", + sfs_Myname, optarg); + exit(20); + } + P_percent_append = atoi(optarg); + if (P_percent_append < 0 || P_percent_append > 100) { + (void) fprintf(stderr, + "%s: %% append must be between 0 and 100\n", + sfs_Myname); + exit(21); + } + break; + + case 'b': /* Set block size distribution table file + * used in aggregate report. + */ + if ((check_fp = fopen(optarg, "r")) == NULL) { + cp = strerror(errno); + (void) fprintf(stderr, "%s: bad block size file %s: %s\n", + sfs_Myname, optarg, cp); + exit(22); + } + P_iodist_file = optarg; + (void) fclose(check_fp); + break; + + + case 'B': /* Set the per packet maximum block size + * used in aggregate reporting only. + */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal block size value %s\n", + sfs_Myname, optarg); + exit(23); + } + P_kb_per_block = atoi(optarg); + if ((P_kb_per_block < 1) || + (P_kb_per_block > (DEFAULT_MAX_BUFSIZE/1024))) { + (void) fprintf(stderr, "%s: illegal block size value %s\n", + sfs_Myname, optarg); + exit(24); + } + break; + + case 'C': /* + * Set summary result file + */ + if ((sum_result_fp = fopen(optarg, "a+")) == NULL) { + cp = strerror(errno); + (void) fprintf(stderr, + "%s: Unable to create summary result file %s: %s\n", + sfs_Myname, optarg, cp); + exit(222); + } + break; + + case 'd': /* + * Set Debug_level + */ + Debug_level = set_debug_level(optarg); + break; + + case 'f': /* Percent change in file set size + * used in aggregate reporting only. + */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal file set delta value %s\n", + sfs_Myname, optarg); + exit(26); + } + P_percent_fss_delta = atoi(optarg); + if (P_percent_fss_delta < 0 || P_percent_fss_delta > 100) { + (void) fprintf(stderr, + "%s: %% file set delta must be between 0 and 100\n", + sfs_Myname); + exit(27); + } + break; + + case 'k': /* + * program to start and stop external + * performance monitoring. Called at start + * and completion of core load generation period. + */ + if ((check_fp = fopen(optarg, "r")) == NULL) { + cp = strerror(errno); + (void) fprintf(stderr, + "%s: program %s protected or missing: %s\n", + sfs_Myname, optarg, cp); + exit(28); + } + P_script_name = optarg; + (void) fclose(check_fp); + break; + + case 'K': /* + * Command-line parameters for the external monitor + * (see the "-k" option, above) + */ + P_script_args = optarg; + break; + + case 'l': /* Set load + * used in aggregate reporting only. + */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal load value %s\n", + sfs_Myname, optarg); + exit(29); + } + P_total_load = atoi(optarg); + if (P_total_load < 0) { + (void) fprintf(stderr, "%s: load must be > 0\n", sfs_Myname); + exit(30); + } + break; + + case 'm': /* Set mix from a file + * used in aggregate reporting only. + */ + if ((check_fp = fopen(optarg, "r")) == NULL) { + cp = strerror(errno); + (void) fprintf(stderr, "%s: bad mix file: %s: %s\n", + sfs_Myname, optarg, cp); + exit(31); + } + P_mix_file = optarg; + (void) fclose(check_fp); + break; + + case 'p': /* Set number of child processes + * used in aggregate reporting only. + */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal procs value %s\n", + sfs_Myname, optarg); + exit(32); + } + P_children = atoi(optarg); + if (P_children < 0) { + (void) fprintf(stderr, "%s: number of children must be > 0\n", + sfs_Myname); + exit(33); + } + break; + + case 'Q': /* Set NFS/TCP behaviour */ + P_tcp = 1; + break; + + case 'R': /* set maximum async read concurrency level + * used in aggregate reporting only. + */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal read count value %s\n", + sfs_Myname, optarg); + exit(34); + } + P_biod_max_outstanding_reads = atoi(optarg); + if (P_biod_max_outstanding_reads < 0) { + (void) fprintf(stderr, "%s: read count must be >= 0\n", + sfs_Myname); + exit(35); + } + break; + + case 's': + /* + * Set sleep time so external processes will + * have time to startup + */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal sleep value %s\n", + sfs_Myname, optarg); + exit(36); + } + Prime_sleep_time = atoi(optarg); + break; + + case 't': /* Set SFS Runtime value */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal time value %s\n", + sfs_Myname, optarg); + exit(37); + } + Prime_runtime = atoi(optarg); + if (Prime_runtime < 0) { + (void) fprintf(stderr, "%s: run time must be >= 0\n", + sfs_Myname); + exit(38); + } + break; + + case 'T': /* Set Test mode operation + * used in aggregate reporting only. + */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal time_out value %s\n", + sfs_Myname, optarg); + exit(39); + } + P_testop = atoi(optarg); + if (P_testop >= NOPS) { + (void) fprintf(stderr, "%s: illegal test value %d\n", + sfs_Myname, P_testop); + exit(40); + } + break; + + case 'W': /* set maximum async write concurrency level + * used in aggregate reporting only. + */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal write count value %s\n", + sfs_Myname, optarg); + exit(41); + } + P_biod_max_outstanding_writes = atoi(optarg); + if (P_biod_max_outstanding_writes < 0) { + (void) fprintf(stderr, "%s: write count must be >= 0\n", + sfs_Myname); + exit(42); + } + break; + + case 'w': /* Set warmup time + * used in aggregate reporting only. + */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, "%s: illegal warmup value %s\n", + sfs_Myname, optarg); + exit(43); + } + P_warmuptime = atoi(optarg); + if (P_warmuptime < 0) { + (void) fprintf(stderr, "%s: warmup time must be >= 0\n", + sfs_Myname); + exit(44); + } + break; + + case 'x': /* Set Prime-Client time_out value */ + if (!isdigit(optarg[0])) { + (void) fprintf(stderr, + "%s: illegal time_out value %s\n", + sfs_Myname, optarg); + exit(45); + } + Prime_time_out = atoi(optarg); + break; + + case 'z': /* Do raw data dumps + * used in aggregate reporting only. + */ + P_dump_data++; + break; + + default: + prog_usage(); + /* NOTREACHED */ + } /* end switch on argument */ + + Num_clients = argc - optind; + + /* + * allocate space and store clients names + */ + Client_names = (char **) malloc(Num_clients * sizeof(char *)); + if (Client_names == (char **) 0) { + (void) fprintf(stderr, "%s: client name malloc %d bytes failed", + sfs_Myname, Num_clients * sizeof(char **)); + exit(46); + } + + for (i = 0; optind < argc; i++, optind++) { + Client_names[i] = argv[optind]; + if (gethostbyname(argv[optind]) == NULL) { + (void) fprintf(stderr, "\n%s: unknown client - %s\n", + sfs_Myname, argv[optind]); + exit(47); + } + } + + if (sum_result_fp == NULL) + sum_result_fp = stdout; + + Pc_log_fd = open(SFS_PRIME_SYNC_LOG, (O_RDWR | O_CREAT), 0666); + if (Pc_log_fd == -1) { + perror(SFS_PRIME_SYNC_LOG); + exit(48); + } + +} /* do_initialize */ + + +/* + * Small utility routine to pretty print out the names + * of the clients which did not respond to the message. + */ +static void +printdeadclients(void) +{ + int *clients; + int client; + int i; + FILE *fd; + + if ((clients = (int *)malloc(sizeof(int) * Num_clients)) == NULL) { + (void) fprintf(stderr, "%s: malloc failed\n", sfs_Myname); + (void) fflush(stderr); + return; + } + + for (i = 0; i < Num_clients; i++) { + clients[i] = 0; + } + + fd = fopen(SFS_PRIME_SYNC_LOG, "r"); + if (fd == NULL) { + (void) fprintf(stderr,"%s: Cannot open %s\n", + sfs_Myname, SFS_PRIME_SYNC_LOG); + (void) fflush(stderr); + return; + } + + while(fread(&client, sizeof(int), 1, fd) == 1) { + if (client > 0 && client <= Num_clients) + clients[client - 1] = 1; + } + + for (i = 0; i < Num_clients; i++) { + if (clients[i] == 0) { + (void) fprintf(stderr, "\n%s: Did not get signal from client %s\n", + sfs_Myname, Client_names[i]); + (void) fflush(stderr); + } + } + (void) fclose(fd); + free (clients); +} + +/* + * monitor Logfile until all Clients write to it. + * + * Each client appends its client id to the file. So the size + * of the log file divided by sizeof(int) is the number of + * clients that have responded. + */ +static void +sync_PC_with_clients(void) +{ + struct stat statb; /* for fstat */ + int num_secs; /* keep count of time */ + int clientsremaining; + + num_secs = 0; + do { + (void) sleep(1); + if (fstat(Pc_log_fd, &statb) == -1) { + (void) fprintf(stderr, "%s: can't stat Prime Client log %s", + sfs_Myname, SFS_PRIME_SYNC_LOG); + exit(49); + } + num_secs++; + clientsremaining = Num_clients - (statb.st_size / sizeof(int)); + } while ( (clientsremaining > 0) && (num_secs < Prime_time_out)); + + /* if clients not responding then terminate experiment */ + if (clientsremaining > 0) { + (void) fprintf(stderr, + "\n%s: Prime Client timeout - did not get signal from %d client(s)\n", + sfs_Myname, clientsremaining); + printdeadclients(); + /* send message to clients to stop */ + (int) signal_sfs_clients("PRIME_STOP"); + exit(50); + } + + /* if more clients than exist responded then syncd is telling us to exit */ + if (clientsremaining < 0) { + (void) fprintf(stderr, + "\n%s: Prime Client got too many signals - expected %d got %ld\n", + sfs_Myname, Num_clients, statb.st_size / sizeof(int)); + /* send message to clients to stop */ + (int) signal_sfs_clients("PRIME_STOP"); + exit(51); + } + + /* success, so go ahead and truncate the sync logfile */ + (void)close(Pc_log_fd); + Pc_log_fd = open(SFS_PRIME_SYNC_LOG, + (O_RDWR | O_CREAT | O_TRUNC | O_APPEND), 0666); + if (Pc_log_fd == -1) { + /* problem in truncating sync logfile - stop experiment */ + (void) fprintf(stderr, "%s: can't truncate Prime Client log %s", + sfs_Myname, SFS_PRIME_SYNC_LOG); + (void) fflush(stderr); + (int) signal_sfs_clients("PRIME_STOP"); + exit(52); + } + +} /* sync_PC_with_clients */ + + +#ifdef CLOSE_CLNT_HANDLE +static int close_clnt_handle = 1; +#else +static int close_clnt_handle = 0; +#endif +/* + * makes RPC to all clients, dependent on message + * Save the client handles to keep from doing unnecessary portmapper calls. + * This can help if we have to route through the busy server. + */ +static int +signal_sfs_clients(char *message) +{ + static int Transac_num = 0; /* transaction number */ + static CLIENT ** sfs_clnt_handle = NULL; + static int *sfs_socket = NULL; + int * result; + int i; + sync_string sync_signal; + char transaction_string[MAX_STR1_LEN]; + + (void) sprintf(transaction_string,"Prime_%i",++Transac_num); + sync_signal.clnt_type = message; + sync_signal.clnt_data = ""; + sync_signal.clnt_transaction = transaction_string; + + if (sfs_clnt_handle == NULL) { + sfs_socket = (int *) calloc(Num_clients, sizeof(int)); + if (sfs_socket == (int *) 0) { + (void) fprintf(stderr, "%s: socket malloc failed.\n", + sfs_Myname); + exit(53); + } + + /* allocate space for tcp handles */ + sfs_clnt_handle = (CLIENT **) calloc(Num_clients, sizeof(CLIENT)); + if (sfs_clnt_handle == (CLIENT **)0 ) { + (void) fprintf(stderr, "%s: clnttcp_create out of memory\n", + sfs_Myname); + exit(54); + } + } + + /* set up the tcp handles for all the clients */ + for (i = 0; i < Num_clients; i++) { + if (sfs_clnt_handle[i] == NULL) { + struct hostent *host_info; + struct sockaddr_in clnt_addr; +#ifdef SUNOS + int fd; +#endif + + sfs_socket[i] = RPC_ANYSOCK; + + if ((host_info = gethostbyname(Client_names[i])) == NULL) + return((int) RPC_UNKNOWNHOST); + (void) memset((char *) &clnt_addr, '\0', sizeof(clnt_addr)); + (void) memmove((char *) &clnt_addr.sin_addr, + (char *) host_info->h_addr, + host_info->h_length); + clnt_addr.sin_family = AF_INET; + clnt_addr.sin_port = 0; + + /* + * Create client "handle" used for calling CL_MESSAGEPROG on the + * sfs client(s). We tell the RPC package to use the "tcp" + * protocol when contacting the clients. + */ + sfs_clnt_handle[i] = clnttcp_create(&clnt_addr, SFS_SYNCPROG, + SFS_SYNCVERS, &sfs_socket[i], + MAX_STR2_LEN, MAX_STR2_LEN); + + if (sfs_clnt_handle[i] == (CLIENT *) NULL) { + /* + * Couldn't establish connection with the sfs Client. + * print error message and return status + */ + clnt_pcreateerror(Client_names[i]); + return((int) RPC_FAILED); + } +#ifdef SUNOS + /* + * Some commands in 4.X will fail if there are too many file + * descriptors open, if the sfs_ext_mon uses one + * of those then it will fail. We set the close-on-exec + * flag to help them. + */ + if (clnt_control(sfs_clnt_handle[i], CLGET_FD, (char *)&fd) == + FALSE) { + clnt_pcreateerror(Client_names[i]); + return((int) RPC_FAILED); + } + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { + perror("F_SETFD"); + return((int) RPC_FAILED); + } +#endif + } /* end no client handle */ + + /* + * Call the remote procedure "signal_sfs_cl_1" on each of the clients + * pass the client number so sfs_syncd will know which client files + * to look for in a shared /tmp area + */ + sync_signal.clnt_id = i + 1; + result = signal_sfs_1(&sync_signal, sfs_clnt_handle[i]); + + if (result == NULL) { + /* Error occurred. Print error message and return error. */ + clnt_perror(sfs_clnt_handle[i], Client_names[i]); + return((int) RPC_CANTSEND); + } + + /* Okay, we successfully called the remote procedure. */ + if (*result == 0) { + /* + * remote procedure was unable to write to its sync file. + * Print error message and return failure. + */ + (void) fprintf(stderr, + "\n%s: Unable to perform remote procedure on %s . \n", + sfs_Myname, Client_names[i]); + (void) fflush(stderr); + return((int) RPC_FAILED); + } + if (close_clnt_handle) { + clnt_destroy(sfs_clnt_handle[i]); + sfs_clnt_handle[i] = NULL; + } + } /* end for each client */ + + return(0); + +} /* signal_sfs_clients */ + + +/* + * summarize and print multi-client results + */ +static void +print_multi_results(void) +{ + FILE * fd; /* results files */ + int i; + int k; + char res_file[SFS_MAXPATHLEN]; /* results filename */ + char str[MAX_LINE_LEN]; + + double stdev_msec, var_msec; + float tot_squared_time_msec, tot_sum2_msec; + float tot_got, tot_secs, tot_msec_calls, tot_res_time; + int tot_want, tot_calls, tot_errors; + + double sq_conf_interval_msec; + double conf_interval_msec; + int totals; + int invalid = 0; + +#define MAXOPNAME 19 + struct client_stats { + /* First line */ + int version; + int num_io_files; + int num_access_io_files; + int num_non_io_files; + int num_symlinks; + int num_dirs; + /* NOPS lines */ + struct client_op_stats { + char op[MAXOPNAME]; + int want; + float got; + int calls; + int errors; + float secs; + float msec_calls; + float res_time; + float squared_time_msec; + float sum2_msec; + } op_stats[NOPS]; + /* Last line */ + float sum_calls_sec; + float sum_msec_calls; + int sum_secs; + int sum_calls; + int client_invalid_flag; + uint32_t total_fss_bytes; + uint32_t least_fss_bytes; + uint32_t most_fss_bytes; + uint32_t base_fss_bytes; + } *client_stats; + + + /* + * malloc space for statistics + * Note last entry is for running totals. + */ + client_stats = (struct client_stats *) calloc(Num_clients+1, + sizeof(struct client_stats)); + if (client_stats == (struct client_stats *)0) { + (void) fprintf(stderr, "%s: client_stats malloc failed.\n", + sfs_Myname); + (void) fflush(stderr); + exit(55); + } + + totals = Num_clients; + /* + * Read each client file one at a time gathering statistics + */ + for (i = 0; i < Num_clients;i++) { + (void) sprintf(res_file, "%s%i", PRIME_RESULTS_LOG, i + 1); + fd = fopen(res_file, "r"); + if (fd == (FILE *)NULL) { + (void) fprintf(stderr, "%s: Cannot open results file - %s\n", + sfs_Myname, res_file); + (void) fflush(stderr); + exit(56); + } + + /* + * the SFS clients compute its File set info. at runtime; + * the clients pass back the computed value in the results + * (RPC) info. Get the fileset info used by the clients and compute + * the aggregate value. + */ + if (fgets(str, MAX_LINE_LEN, fd) == NULL) { + (void) fprintf(stderr,"%s: can't read data in results file - %s\n", + sfs_Myname, res_file); + (void) fflush(stderr); + exit(57); + } + if (sscanf(str,"%d %d %d %d %d %d", + &client_stats[i].version, + &client_stats[i].num_io_files, + &client_stats[i].num_access_io_files, + &client_stats[i].num_non_io_files, + &client_stats[i].num_symlinks, + &client_stats[i].num_dirs) != 6) { + (void) fprintf(stderr,"%s: data in results file unparseable - %s\n", + sfs_Myname, res_file); + (void) fflush(stderr); + exit(58); + } + + client_stats[totals].num_io_files += client_stats[i].num_io_files; + client_stats[totals].num_access_io_files += + client_stats[i].num_access_io_files; + client_stats[totals].num_non_io_files += + client_stats[i].num_non_io_files; + client_stats[totals].num_symlinks += + client_stats[i].num_symlinks; + client_stats[totals].num_dirs += client_stats[i].num_dirs; + + /* Gather per operation statistics */ + for (k = 0; k < NOPS; k++) { + if (fgets(str, MAX_LINE_LEN, fd) == NULL) { + (void) fprintf(stderr, + "%s: can't read data in results file - %s\n", + sfs_Myname, res_file); + (void) fflush(stderr); + exit(59); + } + if (sscanf(str, "%s %d%% %f%% %d %d %f %f %f%% %f %f", + client_stats[i].op_stats[k].op, + &client_stats[i].op_stats[k].want, + &client_stats[i].op_stats[k].got, + &client_stats[i].op_stats[k].calls, + &client_stats[i].op_stats[k].errors, + &client_stats[i].op_stats[k].secs, + &client_stats[i].op_stats[k].msec_calls, + &client_stats[i].op_stats[k].res_time, + &client_stats[i].op_stats[k].squared_time_msec, + &client_stats[i].op_stats[k].sum2_msec) != 10) { + (void) fprintf(stderr,"%s: data in results file unparseable - %s\n", + sfs_Myname, res_file); + (void) fflush(stderr); + exit(60); + } + if (strcmp(client_stats[i].op_stats[k].op,Ops_name[k]) != 0) { + (void) fprintf(stderr, "%s: bad data in results file\n", + sfs_Myname); + (void) fflush(stderr); + exit(61); + } + } + + if (fgets(str, 100, fd) == NULL) { + (void) fprintf(stderr,"%s: can't read data in results file - %s\n", + sfs_Myname, res_file); + (void) fflush(stderr); + exit(62); + } + if (sscanf(str,"%f %f %d %d %d %u %u %u %u", + &client_stats[i].sum_calls_sec, + &client_stats[i].sum_msec_calls, + &client_stats[i].sum_secs, + &client_stats[i].sum_calls, + &client_stats[i].client_invalid_flag, + &client_stats[i].total_fss_bytes, + &client_stats[i].least_fss_bytes, + &client_stats[i].most_fss_bytes, + &client_stats[i].base_fss_bytes) != 9) { + (void) fprintf(stderr,"%s: data in results file unparseable - %s\n", + sfs_Myname, res_file); + (void) fflush(stderr); + exit(63); + } + + client_stats[totals].sum_secs += client_stats[i].sum_secs; + client_stats[totals].sum_calls += client_stats[i].sum_calls; + client_stats[totals].sum_calls_sec += + client_stats[i].sum_calls_sec; + client_stats[totals].sum_msec_calls += + client_stats[i].sum_msec_calls; + client_stats[totals].total_fss_bytes += + client_stats[i].total_fss_bytes; + client_stats[totals].least_fss_bytes += + client_stats[i].least_fss_bytes; + client_stats[totals].most_fss_bytes += + client_stats[i].most_fss_bytes; + client_stats[totals].base_fss_bytes += + client_stats[i].base_fss_bytes; + + (void) fclose(fd); + } /* for Num_clients */ + + nfs_version = client_stats[0].version; + + /* + * print the aggregate test parameters + */ + (void) fprintf(stdout, "\nAggregate Test Parameters: \n"); + (void) fprintf(stdout, " Number of processes = %d\n", + P_children * Num_clients); + (void) fprintf(stdout, " Requested Load (NFS V%d operations/second) = %d\n", + nfs_version, P_total_load * Num_clients); + (void) fprintf(stdout, "%s%d\n", + " Maximum number of outstanding biod writes = ", + P_biod_max_outstanding_writes); + (void) fprintf(stdout, "%s%d\n", + " Maximum number of outstanding biod reads = ", + P_biod_max_outstanding_reads); + + (void) fprintf(stdout, "%s%d\n%s%d\n", + " Warm-up time (seconds) = ", P_warmuptime, + " Run time (seconds) = ", Prime_runtime); + if (P_mix_file) + (void) fprintf(stdout,"%s%s\n", " NFS Mixfile = ", P_mix_file); + if (P_iodist_file) + (void) fprintf(stdout,"%s%s\n", " Block Size Distribution file = ", + P_iodist_file); + + + (void) fprintf(stdout, "%s%6d%s\n", " File Set = ", + client_stats[totals].num_io_files, + " Files created for I/O operations"); + (void) fprintf(stdout, "%s%10d%s\n", " ", + client_stats[totals].num_access_io_files, + " Files accessed for I/O operations"); + (void) fprintf(stdout, "%s%10d%s\n", " ", + client_stats[totals].num_non_io_files, + " Files for non-I/O operations"); + (void) fprintf(stdout, "%s%10d%s\n", " ", + client_stats[totals].num_symlinks, + " Symlinks"); + (void) fprintf(stdout, "%s%10d%s\n", " ", + client_stats[totals].num_dirs, + " Directories"); + (void) fprintf(stdout, "%s%s\n", " ", + " Additional non-I/O files created as necessary\n"); + + + (void) fprintf(stdout,"SFS Aggregate Results for %d Client(s), %s\n", + Num_clients, lad_timestamp()); + (void) fprintf(stderr, "SPEC SFS Benchmark Version %s, Creation - %s\n", + SFS_VERSION_NUM, SFS_VERSION_DATE); + (void) fprintf(stdout, "NFS Protocol Version %d\n", nfs_version); + + /* print column headers for per operation statistics */ + (void) fprintf(stdout, +"------------------------------------------------------------------------------"); +(void) fprintf(stdout, "\n"); +(void) fprintf(stdout,"%s\n%s\n%s\n%s\n%s\n", +"NFS Target Actual NFS NFS Mean Std Dev Std Error Pcnt ", +"Op NFS NFS Op Op Response Response of Mean,95% of ", +"Type Mix Mix Success Error Time Time Confidence Total", +" Pcnt Pcnt Count Count Msec/Op Msec/Op +- Msec/Op Time ", +"------------------------------------------------------------------------------"); + (void) fflush(stdout); + + /* print per operation statistics */ + for (k = 0; k < NOPS; k++) { + tot_got = 0; + tot_secs = 0; + tot_msec_calls = 0; + tot_res_time = 0; + tot_want = 0; + tot_calls = 0; + tot_errors = 0; + tot_sum2_msec = 0; + tot_squared_time_msec = 0; + + /* total the results from each client */ + for (i = 0; i < Num_clients; i++) { + + tot_want += client_stats[i].op_stats[k].want; + tot_got += client_stats[i].op_stats[k].got; + tot_calls += client_stats[i].op_stats[k].calls; + tot_errors += client_stats[i].op_stats[k].errors; + tot_secs += client_stats[i].op_stats[k].secs; + tot_msec_calls += client_stats[i].op_stats[k].msec_calls; + tot_res_time += client_stats[i].op_stats[k].res_time; + tot_squared_time_msec += + client_stats[i].op_stats[k].squared_time_msec; + tot_sum2_msec += client_stats[i].op_stats[k].sum2_msec; + + } /* end for each client */ + + /* + * If the total wanted is zero and no operations succeeded or + * errored don't print out the + * summary. However leave it in the individual client logs + * in case there is some interesting error. + */ + if (tot_want == 0 && tot_calls == 0 && tot_errors == 0) + continue; + + /* compute the standard deviation for the mean response time */ + if (tot_calls <= 1) { + stdev_msec = 0; + var_msec = 0; + } else { + /* variance = 1/(n-1) * (sum(x^2) - 1/n * (sum(x))^2) */ + var_msec = (tot_squared_time_msec - (tot_sum2_msec / tot_calls)) / + (tot_calls-1); + + if(var_msec == 0.0) { + stdev_msec = 0.0; + } else + stdev_msec = sqrt(var_msec); + } + + /* compute the confidence interval */ + if (tot_calls > 0) { + sq_conf_interval_msec = DEFAULT_CHI_SQR_CI * + (stdev_msec / tot_calls); + if (sq_conf_interval_msec == 0.0) { + if (DEBUG_PARENT_GENERAL) { + (void) fprintf(stderr, + "Error computing confidence interval for mean\n"); + (void) fflush(stderr); + } + conf_interval_msec = 0.0; + } else + conf_interval_msec = sqrt(sq_conf_interval_msec); + } else { + conf_interval_msec = 0.0; + } + + /* print the per op statistics */ + (void) fprintf(stdout, + "%-12s%3d%% %5.1f%% %7d %5d %8.2f %8.2f %8.2f %5.1f%%\n", + Ops_name[k], /* op name */ + tot_want / Num_clients, /* target mix */ + tot_got / Num_clients, /* actual mix */ + tot_calls, /* successes */ + tot_errors, /* errors */ + tot_msec_calls / Num_clients, /* mean */ + stdev_msec, /* std dev */ + conf_interval_msec, /* conf int */ + tot_res_time / Num_clients); /* % of time */ + + } /* end for each op */ + + (void) fprintf(stdout, +"------------------------------------------------------------------------------\n"); + + /* check and report client INVALID RUN */ + for (i = 0; i < Num_clients; i++) { + if (client_stats[i].client_invalid_flag != 0) { + invalid++; + (void) fprintf(stdout,"INVALID RUN reported for Client %d (%s).\n", + i+1, Client_names[i]); + if (client_stats[i].client_invalid_flag >= INVALID_GOODCALLS) { + (void) fprintf(stdout,"INVALID RUN, %s\n", + invalid_str[client_stats[i].client_invalid_flag]); + } else { + (void) fprintf(stdout, + "INVALID RUN, ILLEGAL PARAMETER: Non-standard %s\n", + invalid_str[client_stats[i].client_invalid_flag]); + } + } + } + + (void) fprintf(stdout, "\n"); + (void) fprintf(stdout, + " --------------------------------------------------------\n"); + (void) fprintf(stdout, + " | SPEC SFS VERSION %6s AGGREGATE RESULTS SUMMARY |\n", + SFS_VERSION_NUM); + (void) fprintf(stdout, + " --------------------------------------------------------\n"); + (void) fprintf(stdout, "NFS V%d THROUGHPUT: ", nfs_version); + (void) fprintf(stdout,"%7.0f Ops/Sec AVG. RESPONSE TIME: %7.1f Msec/Op\n", + client_stats[totals].sum_calls_sec, + client_stats[totals].sum_msec_calls / Num_clients); + + (void) fprintf(stdout, "%s PROTOCOL\n", P_tcp ? "TCP" : "UDP"); + (void) fprintf(stdout, "NFS MIXFILE:"); + if (P_mix_file) + (void) fprintf(stdout,"%s\n", P_mix_file); + else (void) + (void) fprintf(stdout," [ SFS default ]\n"); + + (void) fprintf(stdout, "AGGREGATE REQUESTED LOAD: %d Ops/Sec \n", + (Num_clients * P_total_load)); + + (void) fprintf(stdout,"TOTAL NFS OPERATIONS: %8d TEST TIME: %d Sec \n", + client_stats[totals].sum_calls, + client_stats[totals].sum_secs / Num_clients); + (void) fprintf(stdout,"NUMBER OF SFS CLIENTS: %d\n", Num_clients); + (void) fprintf(stdout, + "TOTAL FILE SET SIZE CREATED: %6.1f MB\n" , + client_stats[totals].total_fss_bytes/1024.0); + (void) fprintf(stdout, + "TOTAL FILE SET SIZE ACCESSED: %6.1f - %6.1f MB (%lu%% to %lu%% of Base)\n", + client_stats[totals].least_fss_bytes/1024.0, + client_stats[totals].most_fss_bytes/1024.0, + (100 * client_stats[totals].least_fss_bytes) / + client_stats[totals].base_fss_bytes, + (100 * client_stats[totals].most_fss_bytes) / + client_stats[totals].base_fss_bytes); + (void) fprintf(stdout, "\n"); + + (void) fprintf(stdout, + "------------------------------------------------------------------------"); + (void) fprintf(stdout,"\n\n"); + (void) fflush(stdout); + + /* + * Summary results file format: + * response total elps prcs biod vers + * load thruput time ops time V P fileset_sz clnt rd wr + * DDDDDDD FFFFFFF FFFFFFF DDDDDDDD DDDD D C DDDDDDDDDD DDD DD DD DD SSSS + */ + (void) fprintf(sum_result_fp, +"%7s %7d %7.0f %7.1f %8d %4d %1d %c %10lu %3d %2d %2d %2d %s\n", + invalid ? "INVALID" : "", + Num_clients * P_total_load, + client_stats[totals].sum_calls_sec, + client_stats[totals].sum_msec_calls / Num_clients, + client_stats[totals].sum_calls, + client_stats[totals].sum_secs / Num_clients, + nfs_version, + P_tcp ? 'T' : 'U', + (unsigned long)client_stats[totals].total_fss_bytes, + Num_clients, + P_children, + P_biod_max_outstanding_reads, + P_biod_max_outstanding_writes, + SFS_VERSION_NUM); + (void) fflush(sum_result_fp); + + (void) free(client_stats); +} /* print_multi_results */ + +static void +prog_usage(void) +{ + (void) fprintf(stderr, + "Usage: %s [-a access_pcnt] [-A append_pcnt] [-b blocksz_file] [-B block_size]\n", + sfs_Myname); + (void) fprintf(stderr, + "\t[-C summary_file] [-d debug_level] [-f file_set_delta]\n"); + (void) fprintf(stderr, + "\t[-k script_name] [-K script_args] [-l load] [-m mix_file]\n"); + (void) fprintf(stderr, + "\t[-p procs] [-Q] [-R biod_reads] [-s sleeptime]\n"); + (void) fprintf(stderr, + "\t[-t time] [-T op_num] [-w warmup] [-x timeout]\n"); + (void) fprintf(stderr, + "\t[-W biod_writes] [-z] <hostname1> [<hostname2>...]\n"); + + + (void) fflush(stderr); + (int) signal_sfs_clients("PRIME_STOP"); + exit(64); + /* NOTREACHED */ +} /* prog_usage */ + +/* sfs_m_prm.c */ diff --git a/TBBT/trace_play/sfs_m_snc.c b/TBBT/trace_play/sfs_m_snc.c new file mode 100644 index 0000000..8a8f2e2 --- /dev/null +++ b/TBBT/trace_play/sfs_m_snc.c @@ -0,0 +1,456 @@ +#ifndef lint +static char sccsid[] = "@(#)sfs_m_snc.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + *.Exported_routines + * None. + * + *.Local_routines + * void sfs_syncprog_1(struct svc_req *, SVCXPRT *) + * int * signal_sync_sfs_1(struct sync_string *) + * void lad_syncd_cleanup(int) + * + *.Revision_history + * 04-Dec-91 Keith Include sfs_def.h for SYSV/SVR4 mem* + * functions. Include string.h for SYSV/SVR4. + * 28-Nov-91 Teeluckingh Fixed 'multiple signals to sfs' + * problem. Uses a 'transaction id' field in + * the sync rpc xdr structure to compare + * previous rpc, if the current transaction id + * matches the previous one then sfs_syncd + * just return 'success' to the client. If the + * transaction ids do not match, the actions + * are performed and the transaction id value + * is saved. + * 17-Jun-91 Teelucksingh Creation - multi-client + * synchronization server sfs_syncd. + * Processes the sfs sync rpcs between systems. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <sys/signal.h> +#include <sys/file.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + + +/* + * ----------------------- External Definitions ----------------------- + */ + +/* forward definitions for local routines */ +static void sfs_syncprog_1(struct svc_req *, SVCXPRT *); +static int * signal_sync_sfs_1(struct sync_string *); +static void lad_syncd_cleanup(int); + +/* + * ----------------------- Static Definitions ----------------------- + */ + +int Debug_level = 0; /* flag indicates prime client debug mode */ +char *sfs_Myname; /* program name */ + +static char previous_transaction[MAX_STR1_LEN]; /* to hold transaction id */ + + +/* + * ------------------- Multi-client Synchronization ------------------- + */ + + +/*ARGSUSED*/ +int +main( + int argc, + char * argv[]) +{ + char *nameptr; + SVCXPRT * transp; + FILE *pid_fp; +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + struct sigaction sig_act, old_sig_act; +#endif /* USE_POSIX_SIGNALS */ + + /* + * Place pid in pid log file + */ + if ((pid_fp = fopen(SFS_SYNCD_PID, "a+")) == NULL) { + perror(SFS_SYNCD_PID); + (void) unlink(SFS_SYNCD_PID); + exit(1); + } + (void) fprintf(pid_fp, "%d\n", getpid()); + (void) fclose(pid_fp); + + sfs_Myname = argv[0]; + + if ((nameptr = strrchr(argv[0], '/')) != NULL) + sfs_Myname = ++nameptr; + +#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS)) + /* use XOPEN signal handling */ + sig_act.sa_handler = generic_catcher; + (void)sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + + /* signals handlers for signals used by sfs_prime */ + sig_act.sa_handler = lad_syncd_cleanup; + if (sigaction(SIGINT,&sig_act,&old_sig_act) != 0) { + perror("sigaction failed: SIGINT"); + (void) unlink(SFS_SYNCD_PID); + exit(4); + } +#else + /* set up SIGINT signal handler */ + (void) signal(SIGINT, lad_syncd_cleanup); +#endif /* USE_POSIX_SIGNALS */ + + (void) fprintf(stderr,"--------------------\n"); + (void) fprintf(stderr,"Start of sfs run.\n"); + + (void) pmap_unset(SFS_SYNCPROG, SFS_SYNCVERS); + + transp = svcudp_create(RPC_ANYSOCK); + if (transp == ((SVCXPRT *) NULL)) { + (void) fprintf(stderr, "%s: cannot create udp service.\n", sfs_Myname); + (void) unlink(SFS_SYNCD_PID); + exit(5); + } + if (!svc_register(transp, SFS_SYNCPROG, SFS_SYNCVERS, + sfs_syncprog_1, IPPROTO_UDP)) { + (void) fprintf(stderr, + "%s: unable to register (SFS_SYNCPROG,SFS_SYNCVERS, udp).\n", + sfs_Myname); + (void) unlink(SFS_SYNCD_PID); + exit(6); + } + + transp = svctcp_create(RPC_ANYSOCK, 0, 0); + if (transp == ((SVCXPRT *) NULL)) { + (void) fprintf(stderr, "%s: cannot create tcp service.\n", sfs_Myname); + (void) unlink(SFS_SYNCD_PID); + exit(6); + } + if (!svc_register(transp, SFS_SYNCPROG, SFS_SYNCVERS, + sfs_syncprog_1, IPPROTO_TCP)) { + (void) fprintf(stderr, + "%s: unable to register (SFS_SYNCPROG, SFS_SYNCVERS, tcp).\n", + sfs_Myname); + (void) unlink(SFS_SYNCD_PID); + exit(7); + } + + svc_run(); + (void) fprintf(stderr, "%s: svc_run returned\n", sfs_Myname); + return(1); + +} /* main */ + + +static void +sfs_syncprog_1( + struct svc_req * rqstp, + SVCXPRT * transp) +{ + union { + sync_string signal_sync_sfs_1_arg; + } argument; + char * result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char * (*local)(); + + switch (rqstp->rq_proc) { + case SIGNAL_NULLPROC: + (void) svc_sendreply(transp, xdr_void, (char *)NULL); + return; + + case SIGNAL_SFS: + xdr_argument = xdr_sync_string; + xdr_result = xdr_int; + local = (char * (*)()) signal_sync_sfs_1; + break; + + default: + svcerr_noproc(transp); + return; + } + (void) memset((char *) &argument, '\0', sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) { + svcerr_decode(transp); + return; + } + result = (*local)(&argument); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) { + (void) fprintf(stderr, "%s: unable to free arguments\n", sfs_Myname); + (void) unlink(SFS_SYNCD_PID); + exit(8); + } + +} /* sfs_syncprog_1 */ + + +/* + * signal_sync_sfs_1 - multi-client synch RPC + * Provides interface between sfs program running + * on multiple clients and the controlling sfs_prime program. + */ +static int * +signal_sync_sfs_1( + struct sync_string * sfs_signal) +{ + static int result = 0 ; /* return status - failure */ + FILE * fp; + int sfs_pid; /* sfs parent process pid */ + char datafile[SFS_MAXPATHLEN]; /* results file */ + char CL_Logname[SFS_MAXPATHLEN]; + + result = 0; + /* if a duplicate transactions then just return success to calling client */ + if (strcmp(sfs_signal->clnt_transaction, previous_transaction) == 0) { + (void) fprintf(stderr,"%s: Got a duplicate signal - %s\n", + sfs_Myname, sfs_signal->clnt_transaction); + result = 1; + return(&result); + } + + if (strcmp(sfs_signal->clnt_type,"CLIENT_SIGNAL") == 0) { + + /* + * message from parent sfs process on client to Prime-client + * (sfs_prime). + * + * Append client id to Prime client sync logfile + */ + fp = fopen(SFS_PRIME_SYNC_LOG, "a"); + if (fp == NULL) { + (void) fprintf(stderr,"%s: Cannot open %s\n", + sfs_Myname, SFS_PRIME_SYNC_LOG); + return (&result); + } + (void) fwrite((char *)&sfs_signal->clnt_id, + sizeof(sfs_signal->clnt_id), 1, fp); + (void) fclose(fp); + result = 1; + (void) sprintf(previous_transaction, sfs_signal->clnt_transaction); + (void) fprintf(stderr,"%s: Got Client_SIGNAL - %s\n", + sfs_Myname, sfs_signal->clnt_transaction); + return (&result); /* success */ + + } else if (strcmp(sfs_signal->clnt_type,"CLIENT_DATA") == 0) { + + /* + * message from parent sfs process on client to Prime-client + * completed run, here are my results. Write it to file and let + * Prime client know about it. + */ + (void) sprintf(datafile,"%s%d", + PRIME_RESULTS_LOG, sfs_signal->clnt_id); + fp = fopen(datafile, "w"); + if (fp == NULL) { + (void) fprintf(stderr,"%s: Cannot open %s\n", + sfs_Myname, datafile); + return (&result); + } + (void) fprintf(fp,"%s",sfs_signal->clnt_data); + (void) fclose(fp); + + /* after writing data write client id to sync log */ + fp = fopen(SFS_PRIME_SYNC_LOG, "a"); + if (fp == NULL) { + (void) fprintf(stderr,"%s: Cannot open %s\n", + sfs_Myname, SFS_PRIME_SYNC_LOG); + return (&result); + } + (void) fwrite((char *)&sfs_signal->clnt_id, + sizeof(sfs_signal->clnt_id), 1, fp); + (void) fclose(fp); + + /* let the remote process know success */ + result = 1; + (void) sprintf(previous_transaction, sfs_signal->clnt_transaction); + (void) fprintf(stderr,"%s: Got Client_DATA - %s\n", + sfs_Myname, sfs_signal->clnt_transaction); + return (&result); + + } else if (strcmp(sfs_signal->clnt_type,"CLIENT_STOP") == 0) { + + /* + * message from parent sfs process on client to Prime-client + * (sfs_prime) to stop due to error. + */ + fp = fopen(SFS_PRIME_SYNC_LOG, "a"); + if (fp == NULL) { + (void) fprintf(stderr,"%s: Cannot open %s\n", + sfs_Myname, SFS_PRIME_SYNC_LOG); + return (&result); + } + /* + * Write out client id 1000 times to fool prime into thinking + * all clients have responded and will get an error when it + * tries to communicate to it. + */ + for (result = 0; result < 1000; result++) + (void) fwrite((char *)&sfs_signal->clnt_id, + sizeof(sfs_signal->clnt_id), 1, fp); + (void) fclose(fp); + result = 1; + (void) sprintf(previous_transaction, sfs_signal->clnt_transaction); + (void) fprintf(stderr,"%s: Got Client_STOP - %s\n", + sfs_Myname, sfs_signal->clnt_transaction); + return (&result); /* success */ + + } else if (strcmp(sfs_signal->clnt_type,"PRIME_SIGNAL") == 0) { + + /* + * message from the Prime client (sfs_prime) + * send SIGUSR1 signal to parent sfs process on + * client - signals it to proceed + */ + (void) sprintf(CL_Logname,"%s%d", + SFS_CLIENT_SYNC_LOG, sfs_signal->clnt_id); + fp = fopen(CL_Logname, "r"); + if (fp == NULL) { + (void) fprintf(stderr,"%s: Cannot open %s\n", + sfs_Myname, CL_Logname); + return(&result); + } + if (fscanf(fp,"%d",&sfs_pid) != 1) + return (&result); + if ((int) generic_kill(sfs_pid, SIGUSR1) == 0) { + result = 1; + (void) sprintf(previous_transaction, + sfs_signal->clnt_transaction); + (void) fprintf(stderr,"%s: Got PRIME_SIGNAL(SIGUSR1) - %s\n", + sfs_Myname, sfs_signal->clnt_transaction); + (void) fprintf(stderr," Sent SIGUSR1\n"); + return (&result); /* success */ + } else + return (&result); + + } else if (strcmp(sfs_signal->clnt_type,"PRIME_ALARM") == 0) { + + /* + * message from the Prime client (sfs_prime) + * send SIGALRM signal to parent sfs process on + * client - tell it to wake up and finish execution at this time + */ + + (void) sprintf(CL_Logname,"%s%d", + SFS_CLIENT_SYNC_LOG, sfs_signal->clnt_id); + fp = fopen(CL_Logname, "r"); + if (fp == NULL) { + (void) fprintf(stderr,"%s: Cannot open %s\n", + sfs_Myname, CL_Logname); + return (&result); + } + if (fscanf(fp,"%d",&sfs_pid) != 1) + return (&result); + if ((int) generic_kill(sfs_pid, SIGALRM) == 0) { + result = 1; + (void) sprintf(previous_transaction, + sfs_signal->clnt_transaction); + (void) fprintf(stderr,"%s: Got PRIME_ALARM(SIGALRM) - %s\n", + sfs_Myname, sfs_signal->clnt_transaction); + (void) fprintf(stderr," Sent SIGALRM\n"); + return (&result); /* success */ + } else + return (&result); + + } else if (strcmp(sfs_signal->clnt_type,"PRIME_STOP") == 0) { + + /* + * message from Prime-client + * sent SIGINT signal to sfs parent process + * to tell it to terminate experiment now + */ + (void) sprintf(CL_Logname,"%s%d", + SFS_CLIENT_SYNC_LOG, sfs_signal->clnt_id); + fp = fopen(CL_Logname, "r"); + if (fp == NULL) { + (void) fprintf(stderr,"%s: Cannot open %s\n", + sfs_Myname, CL_Logname); + return (&result); + } + if (fscanf(fp,"%d",&sfs_pid) != 1) + return (&result); + if ((int) generic_kill(sfs_pid, SIGINT) == 0) { + result = 1; + (void) sprintf(previous_transaction, + sfs_signal->clnt_transaction); + (void) fprintf(stderr,"%s: Got PRIME_STOP(SIGSTOP) - %s\n", + sfs_Myname, sfs_signal->clnt_transaction); + (void) fprintf(stderr," Sent SIGINT\n"); + return (&result); /* success */ + } else + return (&result); + + } else + return (&result); /* failure */ + +} /* signal_sync_sfs_1 */ + +/* ARGSUSED */ +static void +lad_syncd_cleanup( + int sig_id) +{ + (void) pmap_unset(SFS_SYNCPROG, SFS_SYNCVERS); + (void) fprintf(stderr, "Unregistered sfs_syncd.\n"); + (void) unlink(SFS_SYNCD_PID); + exit(0); + +} /* lad_syncd_cleanup */ + +/* sfs_m_snc.c */ diff --git a/TBBT/trace_play/sfs_m_xdr.c b/TBBT/trace_play/sfs_m_xdr.c new file mode 100644 index 0000000..b2cf930 --- /dev/null +++ b/TBBT/trace_play/sfs_m_xdr.c @@ -0,0 +1,103 @@ +#ifndef lint +static char sfs_m_xdrSid[] = "@(#)sfs_m_xdr.c 2.1 97/10/23"; +#endif + +/* + * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation + * All rights reserved. + * Standard Performance Evaluation Corporation (SPEC) + * 6585 Merchant Place, Suite 100 + * Warrenton, VA 20187 + * + * This product contains benchmarks acquired from several sources who + * understand and agree with SPEC's goal of creating fair and objective + * benchmarks to measure computer performance. + * + * This copyright notice is placed here only to protect SPEC in the + * event the source is misused in any manner that is contrary to the + * spirit, the goals and the intent of SPEC. + * + * The source code is provided to the user or company under the license + * agreement for the SPEC Benchmark Suite for this product. + */ + +/***************************************************************** + * * + * Copyright 1991,1992 Legato Systems, Inc. * + * Copyright 1991,1992 Auspex Systems, Inc. * + * Copyright 1991,1992 Data General Corporation * + * Copyright 1991,1992 Digital Equipment Corporation * + * Copyright 1991,1992 Interphase Corporation * + * Copyright 1991,1992 Sun Microsystems, Inc. * + * * + *****************************************************************/ + +/* + *.Exported_routines + * bool_t xdr_sync_string(XDR *, sync_string *) + * + *.Revision_history + * 28-Nov-91 0.0.13 Teelucksingh + * added 'transaction id' field to xdr data + * structure and ANSI C features. + * 17-Jun-91 0.0.7 Teelucksingh - Creation + * Multi-client synchronization rpc xdr + * functions. + */ + + +/* + * ------------------------- Include Files ------------------------- + */ + +/* + * ANSI C headers + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + +/* + * --------------- Multi-client Message XDR Routines --------------- + */ + +bool_t +xdr_sync_string( + XDR * xdrs, + sync_string * objp) +{ + if (!xdr_int(xdrs, (int *) &objp->clnt_id)) { + (void) fprintf(stderr, "%s: can't encode client id %d", + sfs_Myname, objp->clnt_id); + return (FALSE); + } + if (!xdr_string(xdrs, (char **) &objp->clnt_type, + (unsigned int) MAX_STR1_LEN)) { + (void) fprintf(stderr, "%s: can't encode client type %s", + sfs_Myname, objp->clnt_type); + return (FALSE); + } + if (!xdr_string(xdrs, (char **) &objp->clnt_transaction, + (unsigned int) MAX_STR1_LEN)) { + (void) fprintf(stderr, "%s: can't encode client transaction %s", + sfs_Myname, objp->clnt_transaction); + return (FALSE); + } + if (!xdr_string(xdrs, (char **) &objp->clnt_data, + (unsigned int) MAX_STR2_LEN)) { + (void) fprintf(stderr, "%s: can't encode client data %s", + sfs_Myname, objp->clnt_data); + return (FALSE); + } + return (TRUE); +} + + +/* sfs_m_xdr.c */ diff --git a/TBBT/trace_play/sfs_mcr b/TBBT/trace_play/sfs_mcr new file mode 100755 index 0000000..c24fb3e --- /dev/null +++ b/TBBT/trace_play/sfs_mcr @@ -0,0 +1,170 @@ +#! /bin/sh +# @(#)sfs_mcr 2.1 97/10/23 +# +# +# Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation +# All rights reserved. +# Standard Performance Evaluation Corporation (SPEC) +# 6585 Merchant Place, Suite 100 +# Warrenton, VA 20187 +# This product contains benchmarks acquired from several sources who +# understand and agree with SPEC's goal of creating fair and objective +# benchmarks to measure computer performance. +# +# This copyright notice is placed here only to protect SPEC in the +# event the source is misused in any manner that is contrary to the +# spirit, the goals and the intent of SPEC. +# +# The source code is provided to the user or company under the license +# agreement for the SPEC Benchmark Suite for this product. +# +# ***************************************************************** +# * * +# * Copyright 1991,1992 Legato Systems, Inc. * +# * Copyright 1991,1992 Auspex Systems, Inc. * +# * Copyright 1991,1992 Data General Corporation * +# * Copyright 1991,1992 Digital Equipment Corporation * +# * Copyright 1991,1992 Interphase Corporation * +# * Copyright 1991,1992 Sun Microsystems, Inc. * +# * * +# ***************************************************************** +# +# Usage sfs_mcr <sfs parameter string> +# +# Teelucksingh - Creation (6/17/91) +# +# This script is remotely started from the Prime-client +# by sfs_mgr . +# + +# +# Client pid files +# +SFS_PNT_PID="/tmp/sfs_pnt_pid" +SFS_SYNCD_PID="/tmp/sfs_syncd_pid" + +# SFS client log files +CLIENT_LOG_FILES="/tmp/sfs_CL$CLIENT_NUM \ + /tmp/sfs_sig$CLIENT_NUM \ + /tmp/sfs_x$CLIENT_NUM" + +if [ "$1" = "cleanup" ]; then + # + # do cleanup + # + rm -f $CLIENT_LOG_FILES + + # + # clean up any 'old' sfs processes + # + if [ -f $SFS_PNT_PID ]; then + kill -2 `cat $SFS_PNT_PID` > /dev/null 2>&1 + rm -f $SFS_PNT_PID + fi + if [ -f $SFS_SYNCD_PID ]; then + kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1 + rm -f $SFS_SYNCD_PID + fi + + exit +fi + +# read command line arguments +SFS_PROG=$1 +shift +SFS_DIR=$1 +shift +S_LOGFILE=$1 +shift +C_LOGFILE=$1 +shift +CLIENT_NUM=$1 +shift + +# print start message +echo "========================================================================" >> $C_LOGFILE +echo "" >> $C_LOGFILE + +# +# decide whether to use BSD (which one) or SYSV variant of commands +# +# do test to see whether to use hostname or uname +sh -c "hostname > /dev/null 2>&1" > /dev/null 2>&1 +if [ $? -eq 0 ]; then + HOSTNAME_CMD="hostname" +else + sh -c "uname -n > /dev/null 2>&1" > /dev/null 2>&1 + if [ $? -eq 0 ]; then + HOSTNAME_CMD="uname -n" + else + echo "sfs_mcr: can't use hostname(1) or uname(1), exiting." >> $C_LOGFILE + exit 1 + fi +fi + +# +# trap for signals used by sfs +# +# Try to find cpp in the common places, if not there then let PATH find it +if [ "$CPP" = "" ] +then + if [ -f /lib/cpp ] + then + CPP=/lib/cpp + elif [ -f /usr/ccs/lib/cpp ] + then + CPP=/usr/ccs/lib/cpp + else + CPP=cpp + fi +fi + +# +# Allow trap numbers to be defined externally for broken systems +# +if [ "$TRAP_NUMS" = "" ] +then + echo "#include <signal.h>" > /tmp/sfs_sig$CLIENT_NUM + echo "myprint SIGINT SIGALRM SIGTERM SIGUSR1 SIGUSR2" >> \ + /tmp/sfs_sig$CLIENT_NUM + cat /tmp/sfs_sig$CLIENT_NUM | $CPP | grep myprint | \ + awk '{print $2 " " $3 " " $4 " " $5 " " $6}' > /tmp/sfs_x$CLIENT_NUM + TRAP_NUMS=`cat /tmp/sfs_x$CLIENT_NUM` +fi +trap "" $TRAP_NUMS + +# +# start the sync daemon on the client +# +# Let's truncate the syncd log file at the start of each invocation +# of sfs_mcr. Else it grows unbounded. +# +trap "" $TRAP_NUMS +$SFS_DIR/sfs_syncd > $S_LOGFILE 2>&1 & +echo "Started: sfs_syncd on client (`$HOSTNAME_CMD`). " >> $C_LOGFILE + +# +# start SFS +trap "" $TRAP_NUMS +echo "Starting: $SFS_DIR/$SFS_PROG -N $CLIENT_NUM $*" >> $C_LOGFILE + +$SFS_DIR/$SFS_PROG -N $CLIENT_NUM $* >> $C_LOGFILE 2>&1 + +if [ $? -ne 0 ]; then # error condition + # clean up + echo "sfs_mcr: sfs benchmark terminated with error status" >>$C_LOGFILE +fi + +# +# clean up any 'old' sfs processes +# +if [ -f $SFS_PNT_PID ]; then + kill -2 `cat $SFS_PNT_PID` > /dev/null 2>&1 + rm -f $SFS_PNT_PID +fi +if [ -f $SFS_SYNCD_PID ]; then + kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1 + rm -f $SFS_SYNCD_PID +fi + +trap $TRAP_NUMS diff --git a/TBBT/trace_play/sfs_mgr b/TBBT/trace_play/sfs_mgr new file mode 100755 index 0000000..c1203a4 --- /dev/null +++ b/TBBT/trace_play/sfs_mgr @@ -0,0 +1,1093 @@ +#! /bin/sh +# @(#)sfs_mgr 2.1 97/10/23 +# +# Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation +# All rights reserved. +# Standard Performance Evaluation Corporation (SPEC) +# 6585 Merchant Palce, Suite 100 +# Warrenton, VA 20187 +# This product contains benchmarks acquired from several sources who +# understand and agree with SPEC's goal of creating fair and objective +# benchmarks to measure computer performance. +# +# This copyright notice is placed here only to protect SPEC in the +# event the source is misused in any manner that is contrary to the +# spirit, the goals and the intent of SPEC. +# +# The source code is provided to the user or company under the license +# agreement for the SPEC Benchmark Suite for this product. +# +# ***************************************************************** +# * * +# * Copyright 1991,1992 Legato Systems, Inc. * +# * Copyright 1991,1992 Auspex Systems, Inc. * +# * Copyright 1991,1992 Data General Corporation * +# * Copyright 1991,1992 Digital Equipment Corporation * +# * Copyright 1991,1992 Interphase Corporation * +# * Copyright 1991,1992 Sun Microsystems, Inc. * +# * * +# ***************************************************************** +# +# Usage sfs_mgr [-r <rc file>] [-s <suffix>] [-v <level>] +# +# Teelucksingh - Creation (6/17/91) +# +# Starts SFS (sfs_mcr) on clients with parameters +# specified in sfs_rc . +# Starts Prime-client program (sfs_prime) +# Can have multiple runs with incrementing load +# Summarized result(s) placed in sfsres.<suffix> +# Log of multi-client run placed in sfslog.<suffix> +# Individual client result(s) placed in sfs<cnnn>.<suffix> +# +# + +# --------------- defined constants and strings -------------- +# + +STARline='************************************************************************' +RHOSTSCKMSG1="Ensure permissions in .rhosts or hosts.equiv allows remote operation." +RHOSTSCKMSG2="Or check target directory/file existence or permissions." +USAGE="usage: $0 [-r <rc file>] [-s <suffix>] [-v <level>]" + +# ----------------- variable initialization ------------------ +# + +error=FALSE + +# --------------- program initialization phase --------------- +# +# get the command line arguments +# +# init with default +# +SUFFIX=out +RC_FILE=./sfs_rc +VALIDATE=0 +# +if [ $# -gt 6 ] +then + echo $USAGE + exit 1 +fi +while [ $# -gt 0 ] +do + if [ "$#" -lt 2 ] + then + echo $USAGE + exit 1 + fi + case $1 in + -r) + RC_FILE=$2 + ;; + -s) + SUFFIX=$2 + ;; + -v) + VALID_LEVEL=$2 + VALIDATE=1 + ;; + *) echo $USAGE + exit 1 + esac + shift; shift +done + +# +# pass in environment variables from sfs_rc +# +if [ ! -r "$RC_FILE" ]; then + echo "sfs_mgr: missing or protected rc file $RC_FILE" + exit 1 +fi +. "$RC_FILE" + +# +# Make sure WORK_DIR is defined +# +if [ "$WORK_DIR" = "" ] +then + echo "sfs_mgr: WORK_DIR not defined, check sfs_rc file, exiting." + exit 1 +fi + +# +# Prime client output files +# +P_OUTFILE=$WORK_DIR/sfsres.$SUFFIX +P_SUMFILE=$WORK_DIR/sfssum.$SUFFIX +P_LOGFILE=$WORK_DIR/sfslog.$SUFFIX +P_VALFILE=$WORK_DIR/sfsval.$SUFFIX + +# +# Client pid files +# +SFS_PNT_PID="/tmp/sfs_pnt_pid" +SFS_PRM_PID="/tmp/sfs_prm_pid" +SFS_SYNCD_PID="/tmp/sfs_syncd_pid" + +# +# -------------------- +# Setup machine/OS dependant parameters +# +# decide whether to use BSD (which one) or SYSV variant of commands +# +# do echo test to get no end-of-line character +# +op=`echo "\c"` +if [ "$op" = "\c" ]; then + ECHO_NONL="echo -n" + NONL= +else + ECHO_NONL="echo" + NONL="\c" +fi + +# +# do test to see whether to use hostname or uname +# +if sh -c "hostname > /dev/null 2>&1" > /dev/null 2>&1 +then + HOSTNAME_VAL=`hostname` +elif sh -c "uname -n > /dev/null 2>&1" > /dev/null 2>&1 +then + HOSTNAME_VAL=`uname -n` +else + echo "sfs_mgr: can't use hostname(1) or uname(1), exiting." + echo "sfs_mgr: can't use hostname(1) or uname(1), exiting." \ + >> $P_LOGFILE + exit 1 +fi + +# +# Make sure RSH is defined, if not set reasonable default +# RSH_CMD overrides RSH if set +# +if [ "$RSH_CMD" != "" ] +then + RSH=$RSH_CMD +fi + +if [ "$RSH" = "" ] +then + RSH="rsh" +fi + +# +# If CPP is not already defined then +# try to find cpp in the common places, if not there then let PATH find it +# +if [ "$CPP" = "" ]; then + if [ -f /lib/cpp ] + then + CPP=/lib/cpp + elif [ -f /usr/ccs/lib/cpp ] + then + CPP=/usr/ccs/lib/cpp + else + CPP=cpp + fi +fi + +# +# trap for signals used by sfs programs +# +if [ "$TRAP_NUMS" = "" ] +then + echo "#include <signal.h>" > /tmp/sfs_tmp1 + echo "myprint SIGINT SIGALRM SIGTERM SIGUSR1 SIGUSR2" >> /tmp/sfs_tmp1 + cat /tmp/sfs_tmp1 | $CPP | grep myprint | \ + awk '{print $2 " " $3 " " $4 " " $5 " " $6}' > /tmp/sfs_tmp2 + TRAP_NUMS=`cat /tmp/sfs_tmp2` +fi +rm -f /tmp/sfs_tmp1 /tmp/sfs_tmp2 +# +# -------------------- + +# +# Get NFS version number +# +SFS_PROG="sfs" +if [ "$NFS_VERSION" != "" ] +then + if [ "$NFS_VERSION" = "3" ] + then + SFS_PROG="sfs3" + elif [ "$NFS_VERSION" != "2" ] + then + echo "sfs_mgr: Illegal NFS version number: $NFS_VERSION" \ + >> $P_LOGFILE 2>&1 + echo "sfs_mgr: Illegal NFS version number: $NFS_VERSION" + exit 1 + fi +fi + +# +# print logfile header information +# +echo '========================================================================'\ + >>$P_LOGFILE +echo " " >>$P_LOGFILE +echo "SFS NFS Benchmark Prime Client Logfile." >>$P_LOGFILE +echo " Creation Date: `date`" >>$P_LOGFILE +echo " Prime Client hostname: $HOSTNAME_VAL" >>$P_LOGFILE + +# +# check for mixfile and block-size file +# if specified +# +# check for mixfile +# +if [ "$MIXFILE" != "" -a ! -r "$WORK_DIR/$MIXFILE" ] +then + echo "sfs_mgr: error missing or protected mixfile $WORK_DIR/$MIXFILE" \ + >> $P_LOGFILE 2>&1 + echo "sfs_mgr: error missing or protected mixfile $WORK_DIR/$MIXFILE" + exit 1 +fi + +# +# check for block size file +# +if [ "$BLOCK_FILE" != "" -a ! -r "$WORK_DIR/$BLOCK_FILE" ] +then + echo "sfs_mgr: error missing or protected block size file \ + $WORK_DIR/$BLOCK_FILE" >> $P_LOGFILE 2>&1 + echo "sfs_mgr: error missing or protected block size file \ + $WORK_DIR/$BLOCK_FILE" + exit 1 +fi + +# +# +NUM_CLIENTS=0 +PRIME_CLIENT_NUM=0 +for i in $CLIENTS; do + NUM_CLIENTS=`expr $NUM_CLIENTS + 1` + # + # hack: First try a simple remote "echo" to + # /dev/null. If the $RSH fails, then we don't have + # permission to execute the remote command sfs_mcr. + # The initial probe is necessary because we must + # background the sfs_mcr rsh because we're looping + # on all clients, and spawn a bunch, and the rsh won't + # detach from the command. So, the probe. + # + $RSH $i -l $SFS_USER "echo >/dev/null" >/dev/null 2>&1 </dev/null + if [ $? -ne 0 ]; then + echo "sfs_mgr: test rsh to $i failed" + echo " $RHOSTSCKMSG1" + echo "sfs_mgr: test rsh to $i failed" >> $P_LOGFILE + echo " $RHOSTSCKMSG1" >> $P_LOGFILE + exit 1 + fi + + # Get canonical hostname of client $i and see if it is the prime client. + client_name=`$RSH $i -l $SFS_USER 'hostname || uname -n' 2>/dev/null </dev/null` + if [ "$client_name" = $HOSTNAME_VAL ] + then + PRIME_CLIENT_NUM=$NUM_CLIENTS + continue + fi + # + # Also check to make sure the work directory exists + # + exists=`$RSH $i -l $SFS_USER sh -c \"if [ -d $WORK_DIR ]\; then echo 0 \; else echo 1 \; fi\" </dev/null` + if [ "$exists" != 0 ]; then + echo "sfs_mgr: $WORK_DIR on $i does not exist" + echo "sfs_mgr: $WORK_DIR on $i does not exist" >> $P_LOGFILE + exit 1 + fi + # + # propagate the mixfile to remote clients + # + if [ "$MIXFILE" != "" ]; then + rcp "$WORK_DIR/$MIXFILE" \ + "$SFS_USER"@"$i":"$WORK_DIR/$MIXFILE" >> $P_LOGFILE 2>&1 + if [ $? -ne 0 ]; then + echo \ + "sfs_mgr: can't rcp mix file $WORK_DIR/$MIXFILE to client $i." + echo " $RHOSTSCKMSG1" + echo " $RHOSTSCKMSG2" + echo \ + "sfs_mgr: can't rcp mix file $WORK_DIR/$MIXFILE to client $i." \ + >> $P_LOGFILE + echo " $RHOSTSCKMSG1" >> $P_LOGFILE + echo " $RHOSTSCKMSG2" >> $P_LOGFILE + exit 1 + fi + fi + # + # propagate block size file to remote clients + # + if [ "$BLOCK_FILE" != "" ]; then + rcp "$WORK_DIR/$BLOCK_FILE" \ + "$SFS_USER"@"$i":"$WORK_DIR/$BLOCK_FILE" >> $P_LOGFILE 2>&1 + if [ $? -ne 0 ]; then + echo \ + "sfs_mgr: can't rcp block size file $WORK_DIR/$BLOCK_FILE to client $i." + echo " $RHOSTSCKMSG1" + echo " $RHOSTSCKMSG2" + echo \ + "sfs_mgr: can't rcp block size file $WORK_DIR/$BLOCK_FILE to client $i." \ + >> $P_LOGFILE + echo " $RHOSTSCKMSG1" >> $P_LOGFILE + echo " $RHOSTSCKMSG2" >> $P_LOGFILE + exit 1 + fi + fi +done + +if [ "$NUM_CLIENTS" -eq 0 ]; then + echo "Cannot run SFS with no clients." + echo "Assign value to CLIENT variable in sfs_rc." + echo "Cannot run SFS with no clients." >> $P_LOGFILE + echo "Assign value to CLIENT variable in sfs_rc." >> $P_LOGFILE + exit 1 +fi + + +echo " Number of Clients: $NUM_CLIENTS" >>$P_LOGFILE +echo " Client hostname(s): $CLIENTS" >>$P_LOGFILE +echo " " >>$P_LOGFILE + +# +# Loop invariant setup +# ------------------- +# +# check for program that starts external monitoring +# +if [ "$PRIME_MON_SCRIPT" != "" -a ! -x "$WORK_DIR/$PRIME_MON_SCRIPT" ] +then + echo "sfs_mgr: error missing or not executeable program \ + $WORK_DIR/$PRIME_MON_SCRIPT" >> $P_LOGFILE 2>&1 + echo "sfs_mgr: error missing or not executeable program \ + $WORK_DIR/$PRIME_MON_SCRIPT" + exit 1 +fi + +# +# Set default number of procs if missing +# +PRCS=$PROCS +if [ "$PRCS" = "" ]; then + PRCS=4 +fi + +if [ "$MNT_POINTS" = "" ]; then + echo "sfs_mgr: MNT_POINTS not specified" >> $P_LOGFILE 2>&1 + echo "sfs_mgr: MNT_POINTS not specified" + exit 1 +fi + +set `echo $MNT_POINTS` +NUM_MNTS=$# +MPC=`expr $NUM_CLIENTS \* $PRCS` +if [ $NUM_MNTS -ne 1 -a $NUM_MNTS -ne $PRCS -a $NUM_MNTS -ne $MPC ]; then + ESTR="" + if [ $PROCS -ne $MPC ]; then + ESTR="or $MPC" + fi + echo "sfs_mgr: incorrect number of MNT_POINTS ($NUM_MNTS) must be $PROCS $ESTR" >> $P_LOGFILE 2>&1 + echo "sfs_mgr: incorrect number of MNT_POINTS ($NUM_MNTS) must be $PROCS $ESTR" + exit 1 +fi + +# +# ----------------- +# +trap "" $TRAP_NUMS + +# +# clean up any 'old' sfs processes +# +if [ -f $SFS_PRM_PID ]; then + kill -2 `cat $SFS_PRM_PID` > /dev/null 2>&1 + rm -f $SFS_PRM_PID +fi +if [ -f $SFS_PNT_PID ]; then + kill -2 `cat $SFS_PNT_PID` > /dev/null 2>&1 + rm -f $SFS_PNT_PID +fi +if [ -f $SFS_SYNCD_PID ]; then + kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1 + rm -f $SFS_SYNCD_PID +fi + +# +# Prime Client sfs_syncd logfile +# +S_LOGFILE=$WORK_DIR/syncd_$PRIME_CLIENT_NUM.log + +# +# Determine the number of test runs (TOTAL_RUNS) +# from user supplied values in sfs_rc +# +NUM_LOADS=0 +LOAD_ARRAY="" +DEFAULT_LOAD=60 +# +# get the number of LOAD elements (NUM_LOADS) +# +for i in $LOAD; do + NUM_LOADS=`expr $NUM_LOADS + 1` +done +# +# if NUM_LOADS > 1 then the number of test runs (TOTAL_RUNS) = NUM_LOADS and +# Report conflict if user specifies multiple LOAD elements as well as +# NUM_RUNS > 1. +# +# if NUM_LOADS <= 1 then the number of test runs (TOTAL_RUNS) = NUM_RUNS +# +if [ "$NUM_LOADS" -gt 1 ]; then + TOTAL_RUNS=$NUM_LOADS + LOAD_ARRAY=$LOAD + if [ "$NUM_RUNS" -gt 1 ]; then + echo "Cannot specify an array of LOAD values as well as NUM_RUNS >1." + echo "Cannot specify an array of LOAD values as well as NUM_RUNS >1." \ + >> $P_LOGFILE 2>&1 + exit 1 + fi +else + TOTAL_RUNS=$NUM_RUNS + if [ "$NUM_LOADS" -eq 0 ]; then + LOAD=$DEFAULT_LOAD + fi + LOAD_ARRAY=$LOAD + i=1 + while [ "$i" -lt "$NUM_RUNS" ]; do + LOAD_ARRAY="$LOAD_ARRAY `expr $LOAD + $i \* $INCR_LOAD`" + i=`expr $i + 1` + done +fi + +# +# Loop invariant parameters +# create parameter strings here ... from sfs_rc values. +# - SFS_PARAM : sfs parameters +# - SFS_VPARAM : sfs validation parameters +# - SFS_PRIME_PARAM : sfs_prime parameters +SFS_PARAM= +SFS_VPARAM= +SFS_PRIME_PARAM= +# +# get runtime +# +if [ "$RUNTIME" -ne 0 ]; then + SFS_PARAM="$SFS_PARAM -t $RUNTIME" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -t $RUNTIME" +fi + +# +# get mixfile filename, if specified +# +if [ "$MIXFILE" != "" ]; then + SFS_PARAM="$SFS_PARAM -m $WORK_DIR/$MIXFILE" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -m $WORK_DIR/$MIXFILE" +fi + +# +# get sfs DEBUG level +# +if [ "$DEBUG" != "" ]; then + SFS_PARAM="$SFS_PARAM -d $DEBUG" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -d $DEBUG" +fi + +# +# get access percentage +# +if [ "$ACCESS_PCNT" -ne 0 ]; then + SFS_PARAM="$SFS_PARAM -a $ACCESS_PCNT" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -a $ACCESS_PCNT" +fi + +# +# get append percentage +# +if [ "$APPEND_PCNT" -ne 0 ]; then + SFS_PARAM="$SFS_PARAM -A $APPEND_PCNT" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -A $APPEND_PCNT" +fi + +# +# get block size +# +if [ "$BLOCK_SIZE" -ne 0 ]; then + SFS_PARAM="$SFS_PARAM -B $BLOCK_SIZE" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -B $BLOCK_SIZE" +fi + +# +# get block size filename, if specified +# +if [ "$BLOCK_FILE" != "" ]; then + SFS_PARAM="$SFS_PARAM -b $WORK_DIR/$BLOCK_FILE" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -b $WORK_DIR/$BLOCK_FILE" +fi + +# +# get maximum number of outstanding biod reads +# +if [ "$BIOD_MAX_READS" != "" ]; then + SFS_PARAM="$SFS_PARAM -R $BIOD_MAX_READS" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -R $BIOD_MAX_READS" +fi + +# +# get maximum number of outstanding biod writes +# +if [ "$BIOD_MAX_WRITES" != "" ]; then + SFS_PARAM="$SFS_PARAM -W $BIOD_MAX_WRITES" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -W $BIOD_MAX_WRITES" +fi + +# +# get directory count +# +if [ "$DIR_COUNT" -ne 0 ]; then + SFS_PARAM="$SFS_PARAM -D $DIR_COUNT" +fi + +# +# get file count +# +if [ -n "$FILE_COUNT" ]; then + if [ "$FILE_COUNT" -ne 0 ]; then + SFS_PARAM="$SFS_PARAM -F $FILE_COUNT" + fi +fi + +# +# get symbolic link count +# +if [ "$SYMLINK_COUNT" -ne 0 ]; then + SFS_PARAM="$SFS_PARAM -S $SYMLINK_COUNT" +fi + +# +# set flag for raw data dump if option set +# +if [ "$DUMP" != "" ]; then + SFS_PARAM="$SFS_PARAM -z" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -z" +fi + +# +# set flag for NFS/TCP if variable is "1" or "on" +# +if [ "$TCP" != "" ] +then + if [ "$TCP" = "1" -o "$TCP" = "on" ]; then + SFS_PARAM="$SFS_PARAM -Q" + SFS_VPARAM="$SFS_VPARAM -Q" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -Q" + fi +fi + +# +# get number of processes +# +if [ "$PROCS" -ne 0 ]; then + SFS_PARAM="$SFS_PARAM -p $PROCS" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -p $PROCS" +fi + +# +# get warm-up value (allow 0 warmup) +# +if [ "$WARMUP_TIME" != "" ]; then + SFS_PARAM="$SFS_PARAM -w $WARMUP_TIME" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -w $WARMUP_TIME" +fi + +# +# get sfs_prime sleep value +# +if [ "${PRIME_SLEEP:-0}" -gt 0 ]; then + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -s $PRIME_SLEEP" +fi + +# +# get file set percentage delta +# +if [ "$FILESET_DELTA" != "" ]; then + SFS_PARAM="$SFS_PARAM -f $FILESET_DELTA" + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -f $FILESET_DELTA" +fi + +# +# get sfs_prime timeout value +# +if [ "${PRIME_TIMEOUT:-0}" -gt 0 ]; then + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -x $PRIME_TIMEOUT" +fi + +# +# get populate only flag +# +if [ "$POPULATE" != "" ]; then + SFS_PARAM="$SFS_PARAM -P" +fi + +# +# check for program that starts external monitoring +# +if [ "$PRIME_MON_SCRIPT" != "" ]; then + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -k $WORK_DIR/$PRIME_MON_SCRIPT" + # check for parameters to the monitor program; use a different method + # to test for nonempty because the arguments may start with a hyphen, + # which would confuse the "test" command. + if [ "x$PRIME_MON_ARGS" != "x" ]; then + SFS_PRIME_PARAM="$SFS_PRIME_PARAM -K '$PRIME_MON_ARGS'" + fi + + echo "" >> $P_LOGFILE +fi + +# +# Add clients to prime +# +SFS_PRIME_PARAM="$SFS_PRIME_PARAM $CLIENTS" + +# +# get prime client hostname +# +SFS_PARAM="$SFS_PARAM -M $HOSTNAME_VAL" + +#### End client loop invariant section +# +# VALIDATE stuff +# +if [ "$VALIDATE" -gt 0 ]; then + echo "Executing SFS NFS Validation ..." + # + # if validate option used then take the first client + # from the CLIENT array and run the SFS validation + # suite using the first element on the MOUNT_PNTS list. + + set `echo $CLIENTS` + VALID_CLIENT=$1 + + set `echo $MNT_POINTS` + VALID_MOUNT_PNT=$1 + + if [ $NUM_MNTS -eq 1 -a -f "$WORK_DIR/$VALID_MOUNT_PNT" ] + then + # + # If the mount point and is actually a file + # name then we assume that it is a file containing a list + # of mount points one line per client, possibly of the format + # hostname:path + # + MNT_PTS=`while read CLNT_LINE MNT_LINE + do + if [ $VALID_CLIENT = $CLNT_LINE ] + then + echo $MNT_LINE + break + fi + done < $WORK_DIR/$VALID_MOUNT_PNT` + + set `echo $MNT_PTS` + VALID_MOUNT_PNT=$1 + fi + + echo "Starting SFS NFS validation on client ($VALID_CLIENT)" + echo " $SFS_DIR/$SFS_PROG $SFS_VPARAM -V $VALID_LEVEL $VALID_MOUNT_PNT" + echo "Starting SFS Validation suite on client ($VALID_CLIENT)" \ + > $P_VALFILE 2>&1 + echo " $SFS_DIR/$SFS_PROG $SFS_VPARAM -V $VALID_LEVEL $VALID_MOUNT_PNT" \ + >> $P_VALFILE 2>&1 + + # Get canonical hostname of $VALID_CLIENT and see if it is the prime client. + client_name=`$RSH $VALID_CLIENT -l $SFS_USER 'hostname || uname -n' 2>/dev/null </dev/null` + if [ "$client_name" = $HOSTNAME_VAL ]; then + $SFS_DIR/$SFS_PROG $SFS_VPARAM -V $VALID_LEVEL $VALID_MOUNT_PNT \ + >> $P_VALFILE 2>&1 + # if error then clean-up and exit + if [ $? -ne 0 ]; then + echo "SFS NFS validation failed." + echo "See $P_VALFILE for results." + exit 1 + else + echo "SFS NFS validation completed successfully." + echo "See $P_VALFILE for results." + exit 0 + fi + else + $RSH $VALID_CLIENT -l $SFS_USER \ + "( cd $WORK_DIR; \ + $SFS_DIR/$SFS_PROG $SFS_VPARAM -V $VALID_LEVEL $VALID_MOUNT_PNT )" \ + >> $P_VALFILE 2>&1 + if [ $? -ne 0 ]; then + echo \ + "sfs_mgr: can't run validation pass of sfs on client $VALID_CLIENT." + echo " $RHOSTSCKMSG1" + echo \ + "sfs_mgr: can't run validation pass of sfs on client $VALID_CLIENT." \ + >> $P_LOGFILE + echo " $RHOSTSCKMSG1" >> $P_LOGFILE + exit 1 + fi + # if error then clean-up and exit + tail -1 $P_VALFILE | grep -s 'validation completed successfully' + if [ $? -ne 0 ]; then + echo "SFS NFS validation failed." + echo "See $P_VALFILE for results." + echo "SFS NFS validation failed." >> $P_LOGFILE + echo "See $P_VALFILE for results." >> $P_LOGFILE + exit 1 + else + echo "SFS NFS validation completed successfully." + echo "See $P_VALFILE for results." + echo "SFS NFS validation completed successfully." >> $P_LOGFILE + echo "See $P_VALFILE for results." >> $P_LOGFILE + exit 0 + fi + fi +fi + + +# +# Prime client /tmp logfiles - used for clean up +# +PRIME_LOG_FILES="/tmp/sfs_PC_sync \ + /tmp/sfs_x$PRIME_CLIENT_NUM \ + /tmp/sfs_CL$PRIME_CLIENT_NUM \ + /tmp/sfs_mpr$PRIME_CLIENT_NUM \ + /tmp/sfs_res*" + +# +# start test +# MAIN CLIENT LOOP +# +RUN=1 +for LOAD_INDEX in $LOAD_ARRAY; do + LOAD_VALUE=`expr $LOAD_INDEX / $NUM_CLIENTS` + + export LOAD_VALUE LOAD_INDEX SUFFIX WORK_DIR + + echo " ">>$P_LOGFILE + echo "$STARline" >> $P_LOGFILE + echo "$STARline" >> $P_OUTFILE + + # + # clean up /tmp files + # + for i in $PRIME_LOG_FILES; do + if [ -f $i ]; then + if [ -w $i ]; then + rm $i + else + echo "sfs_mgr: error could not remove file - $i" + echo "sfs_mgr: error could not remove file - $i" >> $P_LOGFILE + exit 1 + fi + fi + done + # + # restart the sfs_syncd process + # + if [ -f $SFS_SYNCD_PID ]; then + kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1 + rm -f $SFS_SYNCD_PID + fi + + trap "" $TRAP_NUMS + + echo "Test Run $RUN of $TOTAL_RUNS" >>$P_LOGFILE + echo " " >>$P_LOGFILE + echo " `date`" + $ECHO_NONL " Executing run $RUN of $TOTAL_RUNS ... $NONL" + sh -c "$SFS_DIR/sfs_syncd >> $S_LOGFILE 2>&1 &" + sleep 15 + echo "Started: sfs_syncd on Prime-Client ($HOSTNAME_VAL)" \ + >> $P_LOGFILE + + # + # start sfs on all the clients + # + CLIENTS_NUM=1 + for i in $CLIENTS; do + # + # compose client's logfile name + # + if [ "$CLIENTS_NUM" -lt 10 ]; then + C_LOGFILE="$WORK_DIR"/sfsc00"$CLIENTS_NUM"."$SUFFIX" + elif [ "$CLIENTS_NUM" -lt 100 ]; then + C_LOGFILE="$WORK_DIR"/sfsc0"$CLIENTS_NUM"."$SUFFIX" + else + C_LOGFILE="$WORK_DIR"/sfsc"$CLIENTS_NUM"."$SUFFIX" + fi + + # + # compose client's sfs_syncd logfile name + # + S_LOGFILE=$WORK_DIR/syncd_$CLIENTS_NUM.log + + if [ $NUM_MNTS -eq 1 -a -f "$WORK_DIR/$MNT_POINTS" ] + then + # + # If there is only one mount point and it is actually a file + # name then we assume that it is a file containing a list + # of mount points one line per client, possibly of the format + # hostname:path + # + MNT_PTS=`while read CLNT_LINE MNT_LINE + do + if [ $i = $CLNT_LINE ] + then + echo $MNT_LINE + break + fi + done < $WORK_DIR/$MNT_POINTS` + else + # + # construct MNT_PTS for this particular CLIENTS_NUM (client) + # from MNT_POINTS, using total number of CLIENTS + # and PRCS [number of processes per client] + # + # PRCS must be a multiple of NUM_MNTS, + # no need to resequence the list of mount points + # + MNT_PTS="$MNT_POINTS" + fi + + # + # if prime client in $CLIENT then start sfs locally + # + client_name=`$RSH $i -l $SFS_USER 'hostname || uname -n' 2>/dev/null </dev/null` + if [ "$client_name" = $HOSTNAME_VAL ]; then + echo "`date` $i start:" >>$P_LOGFILE + echo " $SFS_PROG -N $CLIENTS_NUM -l $LOAD_VALUE $SFS_PARAM $MNT_PTS" \ + >> $P_LOGFILE + + trap "" $TRAP_NUMS + $SFS_DIR/$SFS_PROG -N $CLIENTS_NUM -l $LOAD_VALUE $SFS_PARAM $MNT_PTS \ + >> $C_LOGFILE 2>&1 & + else + # + # Cause remote client to cleanup + # + $RSH $i -l $SFS_USER "( cd $WORK_DIR; \ + $SFS_DIR/sfs_mcr cleanup )" >>/dev/null 2>&1 </dev/null + + # + # remotely start sfs_mcr script on clients + # + echo "`date` $i start:" >>$P_LOGFILE + echo " $SFS_PROG -N $CLIENTS_NUM -l $LOAD_VALUE $SFS_PARAM $MNT_PTS" \ + >> $P_LOGFILE + + $RSH $i -l $SFS_USER "( cd $WORK_DIR; \ + $SFS_DIR/sfs_mcr $SFS_PROG $SFS_DIR \ + $S_LOGFILE $C_LOGFILE $CLIENTS_NUM \ + -l $LOAD_VALUE $SFS_PARAM $MNT_PTS ) &"\ + >>/dev/null 2>&1 </dev/null & + fi + + # + # increment client num + # + CLIENTS_NUM=`expr $CLIENTS_NUM + 1` + done + + # + # start the Prime client program, sfs_prime, + # and wait for completion + # + trap "" $TRAP_NUMS + $SFS_DIR/sfs_prime -l $LOAD_VALUE -C $P_SUMFILE $SFS_PRIME_PARAM \ + > /tmp/sfs_mpr$PRIME_CLIENT_NUM 2>> $P_LOGFILE + + # + # if error then clean-up set error flag, and break out + # + if [ $? -ne 0 ]; then + echo "sfs_mgr: sfs_prime returned an error, exiting" + echo "sfs_mgr: sfs_prime returned an error, exiting" \ + >> $P_LOGFILE 2>&1 + if [ -f $SFS_PNT_PID ]; then + kill -2 `cat $SFS_PNT_PID` > /dev/null 2>&1 + rm -f $SFS_PNT_PID + fi + if [ -f $SFS_SYNCD_PID ]; then + kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1 + rm -f $SFS_SYNCD_PID + fi + error=TRUE + break # break out of for loop + fi + + # + # record results + # + cat /tmp/sfs_mpr$PRIME_CLIENT_NUM >> $P_LOGFILE + cat /tmp/sfs_mpr$PRIME_CLIENT_NUM >> $P_OUTFILE + rm /tmp/sfs_mpr$PRIME_CLIENT_NUM >> $P_LOGFILE 2>&1 + + # + # increment RUN value and reset SFS_PARAM + # + RUN=`expr $RUN + 1` + echo " done" + # + echo "" >> $P_LOGFILE + echo "$STARline" >> $P_LOGFILE + echo "$STARline" >> $P_OUTFILE + + # + # test run(s) completed + # +done # END OF MAIN CLIENT LOOP: 'for LOAD_INDEX in $LOAD_ARRAY' + +# +# copy log files from clients +# +CLIENTS_NUM=1 +for i in $CLIENTS; do + # + # compose client's logfile name + # + if [ "$CLIENTS_NUM" -lt 10 ]; then + C_LOGFILE="$WORK_DIR"/sfsc00"$CLIENTS_NUM"."$SUFFIX" + elif [ "$CLIENTS_NUM" -lt 100 ]; then + C_LOGFILE="$WORK_DIR"/sfsc0"$CLIENTS_NUM"."$SUFFIX" + else + C_LOGFILE="$WORK_DIR"/sfsc"$CLIENTS_NUM"."$SUFFIX" + fi + # + # Copy over the logfiles. We copy the files to a temporary + # file on the chance that either the prime client is also + # a load generating client and we might rcp a file over + # itself, or the $WORK_DIR is NFS mounted by all clients, + # in which case we don't want to really remove the remote + # file since it is the same as the 'local' file on the + # prime. While not necessarily efficient, this is correct. + # + client_name=`$RSH $i -l $SFS_USER 'hostname || uname -n' 2>/dev/null </dev/null` + if [ $client_name != $HOSTNAME_VAL ]; then + # + # copy to temporary file: of different name than target + # in off chance /tmp is our $WORK_DIR + # + rcp "$SFS_USER"@"$i":"$C_LOGFILE" \ + /tmp/sfs"$CLIENTS_NUM"."$SUFFIX" + if [ $? -ne 0 ]; then + echo "sfs_mgr: can't rcp $C_LOGFILE from client $i." + echo " $RHOSTSCKMSG1" + echo " $RHOSTSCKMSG2" + echo "sfs_mgr: can't rcp $C_LOGFILE from client $i." >> $P_LOGFILE + echo " $RHOSTSCKMSG1" >> $P_LOGFILE + echo " $RHOSTSCKMSG2" >> $P_LOGFILE + exit 1 + fi + $RSH $i -l $SFS_USER "/bin/rm $C_LOGFILE" + if [ $? -ne 0 ]; then + echo "sfs_mgr: can't remove $C_LOGFILE on client $i." + echo " $RHOSTSCKMSG1" + echo "sfs_mgr: can't remove $C_LOGFILE on client $i." >> $P_LOGFILE + echo " $RHOSTSCKMSG1" >> $P_LOGFILE + exit 1 + fi + mv /tmp/sfs"$CLIENTS_NUM"."$SUFFIX" "$C_LOGFILE" + fi + CLIENTS_NUM=`expr $CLIENTS_NUM + 1` +done + +# +# if we got an error, terminate sfs_mgr +# +if [ "$error" = TRUE ]; then + exit 1 +fi + +# +# copy 'raw data dump' files from clients +# only do this for one point, the final one--it dosn't make +# sense to concatenate dumps from different loads +# +if [ "$DUMP" != "" ]; then + CLIENTS_NUM=1 + for i in $CLIENTS; do + # + # compose client's raw dump logfile name + # + if [ "$CLIENTS_NUM" -lt 10 ]; then + CLNTNUM=00"$CLIENTS_NUM" + elif [ "$CLIENTS_NUM" -lt 100 ]; then + CLNTNUM=0"$CLIENTS_NUM" + else + CLNTNUM="$CLIENTS_NUM" + fi + PRC=$PROCS + if [ "$PRC" = "" ]; then + PRC=4 + fi + PRCJ=0 + while [ $PRCJ -lt $PRC ]; do + if [ "$PRCJ" -lt 10 ]; then + PROCNUM=00"$PRCJ" + elif [ "$PRCJ" -lt 100 ]; then + PROCNUM=0"$PRCJ" + else + PROCNUM="$PRCJ" + fi + RAWFILE=c${CLNTNUM}-p${PROCNUM} + # + # copy over the logfiles + # clean out (remove) originals + # + client_name=`$RSH $i -l $SFS_USER 'hostname || uname -n' 2>/dev/null </dev/null` + if [ $client_name != $HOSTNAME_VAL ]; then + $RSH $i -l $SFS_USER \ + cat /tmp/"$RAWFILE" \ + >> "$WORK_DIR"/"$RAWFILE"."$SUFFIX" + $RSH $i -l $SFS_USER \ + /bin/rm /tmp/"$RAWFILE" + else + cat /tmp/"$RAWFILE" \ + >> "$WORK_DIR"/"$RAWFILE"."$SUFFIX" + /bin/rm /tmp/"$RAWFILE" + fi + PRCJ=`expr $PRCJ + 1` + done + CLIENTS_NUM=`expr $CLIENTS_NUM + 1` + done +fi + +# +# cleanup processes before ending +# +if [ -f $SFS_PRM_PID ]; then + kill -2 `cat $SFS_PRM_PID` > /dev/null 2>&1 + rm -f $SFS_PRM_PID +fi +if [ -f $SFS_PNT_PID ]; then + kill -2 `cat $SFS_PNT_PID` > /dev/null 2>&1 + rm -f $SFS_PNT_PID +fi +if [ -f $SFS_SYNCD_PID ]; then + kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1 + rm -f $SFS_SYNCD_PID +fi + +# +# cleanup temporary files +# +for i in $PRIME_LOG_FILES; do + if [ -f $i ]; then + if [ -w $i ]; then + rm $i + else + echo "sfs_mgr: error could not remove file - $i" + echo "sfs_mgr: error could not remove file - $i" >> $P_LOGFILE + exit 1 + fi + fi +done + +echo '========================================================================'\ >>$P_LOGFILE +exit 0 diff --git a/TBBT/trace_play/sfs_rc b/TBBT/trace_play/sfs_rc new file mode 100755 index 0000000..fcf8cfd --- /dev/null +++ b/TBBT/trace_play/sfs_rc @@ -0,0 +1,149 @@ +############################################################################## +# +# @(#)sfs_rc 2.1 97/10/23 +# +# Specify SFS parameters for sfs runs in this file. +# +# The following parameters are configurable within the SFS run and +# reporting rules. +# +# See below for details. +# +# Example shows a run of 100 to 1000 ops/sec +# +LOAD="100" +INCR_LOAD=100 +NUM_RUNS=10 +PROCS=4 +CLIENTS="client1 client2" +MNT_POINTS="srvr:/a srvr:/b srvr:/c srvr:/d srvr:/e srvr:/f srvr:/g srvr:/h" +BIOD_MAX_WRITES=2 +BIOD_MAX_READS=2 +TCP="on" +NFS_VERSION="3" +SFS_USER="`id | tr '()' ' ' | awk '{print $2}' `" +SFS_DIR="`pwd`/bin" +WORK_DIR="`pwd`/result" +PRIME_MON_SCRIPT="" +PRIME_MON_ARGS="" +RSH="rsh" +# +# The following parameters are strictly defined within the SFS +# run and reporting rules and may not be changed. +# +RUNTIME=300 +WARMUP_TIME=300 +MIXFILE="" +ACCESS_PCNT=10 +APPEND_PCNT=70 +BLOCK_SIZE=8 +BLOCK_FILE="" +DIR_COUNT=30 +FILE_COUNT= +SYMLINK_COUNT=20 +# +# The following parameters are useful for debugging or general system +# tuning. They may not be used during during a reportable SFS run. +# +DEBUG= +DUMP= +POPULATE= +PRIME_SLEEP=0 +PRIME_TIMEOUT=0 +# +# The default SFS options are implied if no values are assigned to +# the variables. The variables and their meaning are as follows. +# +# The following parameters are configurable within the SFS run and +# reporting rules. +# +# LOAD - array of numbers specifying the NFS loads +# (NFS calls per second) to be generated by all clients +# combined. The number of consecutive runs equals the size +# of the array and the peak load equals the largest value +# in the array. +# PROCS - number of SFS sub-processes to generate NFS +# calls (DEFAULT PROCS = 4). +# MNT_POINTS - string containing the mount points of NFS-mounted +# filesystems on the client which will be used in the test. +# BIOD_MAX_WRITES - maximum number of outstanding async (biod) writes +# (DEFAULT BIOD_MAX_WRITES = 2). +# BIOD_MAX_READS - maximum number of outstanding async (biod) reads +# (DEFAULT BIOD_MAX_READS = 2). +# TCP - If set ("on") then use NFS/TCP behaviour rather +# NFS_VERSION - Set the version of the NFS protocol to use +# (DEFAULT [or unset] NFS_VERSION = 2) +# NUM_RUNS - number indicating the number of multi-client runs. NUM_RUNS +# should only be used if the size of the LOAD array <= 1; +# INCR_LOAD - number indicating the load increment factor in NFS call/sec. +# The first run generates LOAD calls/sec, +# subsequent runs are made with LOAD + (N * INCR_LOAD) +# calls/sec; where initial N = 0. INCR_LOAD should +# only be used if the size of the LOAD array <= 1. +# CLIENTS - string containing client's host names; include the Prime- +# Client's host name if you will be using the Prime-Client to +# generate NFS loads. +# SFS_USER - string containing the user account name that has appropriate +# permission to execute SFS on each of the clients. The +# user account name used on the Prime-Client should be added +# to the SFS_USER's .rhosts file on each of the clients. +# SFS_DIR - string containing the directory path where the SFS +# executables are stored; should be the same for all systems +# WORK_DIR - string containing directory path where the SFS output +# files are stored, this should be the same for all systems. +# RSH - OS dependent version of remote shell executable ON THE +# PRIME CLIENT +# PRIME_MON_SCRIPT - string containing the name of a shell script used to +# control the start and stop of any external SFS +# processes like external performance monitors. The sample +# The sample shell script 'sfs_ext_mon' shows the +# expected semantics of the program. +# PRIME_MON_ARGS - string containing optional arguments that are passed +# to the PRIME_MON_SCRIPT. +# +# The following parameters are strictly defined within the SFS +# run and reporting rules and may not be changed. +# +# RUNTIME - number of seconds to generate load +# (DEFAULT RUNTIME = 300). +# WARMUP_TIME - number of seconds to warmup +# (DEFAULT WARMUP_TIME = 300). +# MIXFILE - string containing the NFS call distribution filename; +# a copy of $MIXFILE must be placed in the $WORK_DIR +# directory on the Prime_Client. The Prime-Client will +# propagate a copy to all the other clients in the test. +# ACCESS_PCNT - percent of total file set available for use by i/o +# operations that will be accessed. +# (DEFAULT ACCESS_PCNT = 10). +# APPEND_PCNT - percent of writes that append rather than overwrite +# (DEFAULT APPEND_PCNT = 70). +# BLOCK_SIZE - number of KB in a block, up to 8 KB +# (DEFAULT BLOCK_SIZE = 8 ). +# BLOCK_FILE - string containing the block transfer sizes filename. +# A copy of $BLOCK_FILE must be placed in the $WORK_DIR +# directory on the Prime_Client. The Prime-Client will +# propagate a copy to all the other clients in the test. +# DIR_COUNT - number of files per directory to use for directory +# operations (DEFAULT DIR_COUNT = 30). +# FILE_COUNT - number of files to use for read and write +# operations. By default, number of files is +# calculated from the specified LOAD and ACCESS_PCNT. +# SYMLINK_COUNT - number of symbolic links to use for symlink +# operations (DEFAULT SYMLINK_COUNT = 20). +# +# The following parameters are useful for debugging or general system +# tuning. They may not be used during during a reportable SFS run. +# +# DUMP - If set, dump raw data points at end of run +# POPULATE - If set ("on") the only populate the file set and do +# run test. +# +# PRIME_SLEEP - number of seconds Prime-Client should sleep after starting +# and stopping the SFS external monitoring facility and +# before sending synchronization message to clients. This is a +# provision to allow sufficient time for starting and stopping +# other performance monitoring utilities that could be used +# during SFS execution. +# PRIME_TIMEOUT - number of seconds Prime-Client should wait for a +# response from all the clients. Zero indicates the +# default should be used. diff --git a/TBBT/trace_play/sfs_suchown b/TBBT/trace_play/sfs_suchown new file mode 100755 index 0000000..49ac031 --- /dev/null +++ b/TBBT/trace_play/sfs_suchown @@ -0,0 +1,71 @@ +#!/bin/sh +# +# @(#)sfs_suchown 2.1 97/10/23 +# +# Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation +# All rights reserved. +# Standard Performance Evaluation Corporation (SPEC) +# 6585 Merchant Place, Suite 100 +# Warrenton, VA 20187 +# This product contains benchmarks acquired from several sources who +# understand and agree with SPEC's goal of creating fair and objective +# benchmarks to measure computer performance. +# +# This copyright notice is placed here only to protect SPEC in the +# event the source is misused in any manner that is contrary to the +# spirit, the goals and the intent of SPEC. +# +# The source code is provided to the user or company under the license +# agreement for the SPEC Benchmark Suite for this product. + +# +SPEC=${SPEC-0} + +if [ "$SPEC" != 0 ] ; then + rm -f $BENCH/bin/sfs + rm -f $BENCH/bin/sfs3 + rm -f $BENCH/bin/sfs_syncd + rm -f $BENCH/bin/sfs_prime + rm -f $BENCH/bin/sfs_mgr + rm -f $BENCH/bin/sfs_mcr + rm -f $BENCH/bin/sfs_ext_mon +fi + +# +# If first argument is clobber, just remove executeables +# +if [ "$1" = "clobber" ] +then + exit 0 +fi + +# +# if first argument == -DRESVPORT then we must make executeable +# setuid to root. +# +if [ "$1" = "-DRESVPORT" ] +then + shift + echo "Setting root ownership on sfs setuid executable in order to" + echo "perform binding to privileged port. You may be asked to enter" + echo "the root password." + # + su $ROOTUSER <<EOF + chown root $* + chmod 04755 $* +EOF +fi + +# +# if the SPEC tools are configured then move binaries to RESULTSDIR +# +if [ "$SPEC" != 0 ] ; then + mv sfs $BENCH/bin + mv sfs3 $BENCH/bin + mv sfs_syncd $BENCH/bin + mv sfs_prime $BENCH/bin + cp sfs_mgr $BENCH/bin + cp sfs_mcr $BENCH/bin + cp sfs_ext_mon $BENCH/bin +fi +exit 0 diff --git a/TBBT/trace_play/sfs_sync.x b/TBBT/trace_play/sfs_sync.x new file mode 100755 index 0000000..2a7066e --- /dev/null +++ b/TBBT/trace_play/sfs_sync.x @@ -0,0 +1,25 @@ +/* + * SFS sync daemon rpcgen configuration file + * @(#)sfs_sync.x 2.1 97/10/23 + * + * XXX Not currently used to generate source code + */ + +const MAX_STR1_LEN = 31; +const MAX_STR2_LEN = 2560; + +struct sync_string { + int clnt_id; /* client number */ + string clnt_type<MAX_STR1_LEN>; /* message type, hard coded */ + string clnt_transaction<MAX_STR1_LEN>; /* transaction id */ + string clnt_data<MAX_STR2_LEN>; /* results strings */ +}; + +program SFS { + version SFS { + int + SIGNAL_NULLPROC (void) = 0; + int + SIGNAL_SFS (sync_string) = 1; + } = 1; +} = 100500; diff --git a/TBBT/trace_play/t b/TBBT/trace_play/t new file mode 100755 index 0000000000000000000000000000000000000000..9686a681a22296fbe1d13ccd3f8077f0af398055 GIT binary patch literal 11546 zcmd^FdvsjId7r)a?t1O3$I235iXzr8Y8PuIOTyT$1K6@GE69=~$zY&xvoA>tt6g~? zegH|JvLNPBOraqyZUf~s<Uk9dZBk0o)YODP>FMFn<Q&?P1MM*fQyfYYm%6nZ_4oVc z-qqFOoCXSib@t4i?=|1|&CEBmcV_Mvd-}Ho0s-YELz#-$-e0%ET_~GBr8!+HszPdo zTB6QJsT6r#w?GE@Fkt2&vW09SpI`=$xe+ikdDLC82A)EWfWgfADx	yB|DcR<Mw1 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 Pi$$Cl;B3eL-R%DWqfDn` literal 0 HcmV?d00001 diff --git a/TBBT/trace_play/t.c b/TBBT/trace_play/t.c new file mode 100644 index 0000000..45baab2 --- /dev/null +++ b/TBBT/trace_play/t.c @@ -0,0 +1,6 @@ +main() +{ + int i = 1; + i <<= 2; + printf("%d\n", i); +} -- 2.20.1