Import TBBT (NFS trace replay).
[bluesky.git] / TBBT / trace_init / hier.pl
diff --git a/TBBT/trace_init/hier.pl b/TBBT/trace_init/hier.pl
new file mode 100755 (executable)
index 0000000..3e2ebfc
--- /dev/null
@@ -0,0 +1,715 @@
+#\r
+# Copyright (c) 2002-2003\r
+#      The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+#    notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+#    notice, this list of conditions and the following disclaimer in the\r
+#    documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+#    may be used to endorse or promote products derived from this software\r
+#    without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: hier.pl,v 1.14 2003/07/26 20:52:03 ellard Exp $\r
+#\r
+# hier.pl - Tools to map out the file system hierarchy.  This is\r
+# accomplished by snooping out the lookup calls.\r
+#\r
+# This is expensive because the hierarchy can require a LOT of space\r
+# to store for a large system with lots of files (especially if files\r
+# come and go).  Don't construct the hierarchy unless you want it --\r
+# and be prepared to prune it from time to time.\r
+\r
+package        hier;\r
+\r
+# Tables used by the outside world:\r
+\r
+%fh2Parent             = ();\r
+%fh2Name               = ();\r
+%fh2Attr               = ();\r
+%fh2AttrOrig           = ();\r
+%parent2fh             = ();\r
+\r
+#RFS: init FS\r
+%rootsName             = ();\r
+%discardFHs = ();\r
+%rootsFHs = ();\r
+#RFS: dependency table\r
+%fhCreate = ();\r
+\r
+%rfsAllFHs = ();\r
+%fhType = (); # we use %fhIsDir instead\r
+$rfsLineNum = 0;\r
+\r
+\r
+\r
+\r
+\r
+\r
+# Library-private tables and variables.\r
+\r
+%pendingCallsXIDnow    = ();\r
+%pendingCallsXIDfh     = ();\r
+%pendingCallsXIDname   = ();\r
+\r
+$nextPruneTime         = -1;\r
+$PRUNE_INTERVAL                = 5 * 60;       # Five minutes.\r
+\r
+sub processLine {\r
+       my ($line, $proto, $op, $xid, $client, $now, $response, $fh_type) = @_;\r
+\r
+       &addRfsAllFHs($line, $proto, $op, $uxid,\r
+                               $now, $response, $fh_type);\r
+\r
+       if ($now > $nextPruneTime) {\r
+               &prunePending ($now - $PRUNE_INTERVAL);\r
+               $nextPruneTime = $now + $PRUNE_INTERVAL;\r
+       }\r
+\r
+       my $uxid = "$client-$xid";\r
+\r
+       # 'lookup', 'create', 'rename', 'delete',\r
+       # 'getattr', 'setattr'\r
+\r
+       #RFS: add mkdir/rmdir/symlink\r
+       if ( $op eq 'lookup' || $op eq 'create' || $op eq 'mkdir' || \r
+            ($op eq 'symlink' && ($proto eq 'C3' || $proto eq 'R3' ) ) ) {\r
+               return (&doLookup ($line, $proto, $op, $uxid,\r
+                               $now, $response, $fh_type));\r
+       }\r
+       elsif ($op eq 'rename') {\r
+       }\r
+       elsif ($op eq 'remove' || $op eq 'rmdir') {\r
+               # RFS: why remove these entries? Just let them exist since \r
+               # there is generation number available to distinguish btw removed dir/file \r
+               # and new dir/file with the same inode number.\r
+               #return (&doRemove ($line, $proto, $op, $uxid,\r
+               #               $now, $response, $fh_type));\r
+       }\r
+       elsif ($op eq 'getattr' || $op eq 'read' || $op eq 'write'  ||\r
+                ($op eq 'readlink' && ($proto eq 'C3' || $proto eq 'R3' ) ) ) {\r
+               return (&doGetAttr ($line, $proto, $op, $uxid,\r
+                               $now, $response, $fh_type));\r
+       }\r
+       elsif ($op eq 'setattr') {\r
+       }\r
+}\r
+\r
+\r
+# get time stamp\r
+\r
+sub getTimeStamp{\r
+       my ($line) = @_;\r
+\r
+       if (! ($line =~ /^[0-9]/)) {\r
+               print "getTimeStamp return undef\n";\r
+               return undef;\r
+       }\r
+       else {\r
+               my @l = split (' ', $line, 2);\r
+               return $l[0];\r
+       }\r
+}\r
+\r
+sub addRfsAllFHs {\r
+       my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
+\r
+       my $fh = undef;\r
+       \r
+       my $checkfh = undef;\r
+       $checkfh = nfsd::nfsDumpParseLineField ($line, 'fh');\r
+       if (defined $checkfh) {\r
+               $fh = nfsd::nfsDumpCompressFH ($fh_type, $checkfh);\r
+       }\r
+       \r
+       my $fh2 = undef;\r
+\r
+       if ($op eq 'rename' || $op eq 'link') {\r
+               $checkfh = nfsd::nfsDumpParseLineField ($line, 'fh2');\r
+               if (defined $checkfh) {\r
+                       $fh2 = nfsd::nfsDumpCompressFH ($fh_type, $checkfh);\r
+               }       \r
+       }\r
+\r
+       if (defined $fh) {\r
+               # record the first appearance of the fh\r
+               if ( !exists  $rfsAllFHs{$fh} )  {\r
+                       $rfsAllFHs{$fh} = $rfsLineNum ;\r
+               }\r
+       }\r
+\r
+       if (defined $fh2) {\r
+               if ( !exists  $rfsAllFHs{$fh2} ) {\r
+                       $rfsAllFHs{$fh2} = $rfsLineNum;\r
+               }\r
+       }\r
+\r
+       return ;\r
+}\r
+\r
+\r
+sub doLookup {\r
+       my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
+\r
+       if ($proto eq 'C3' || $proto eq 'C2') {\r
+               my $tag = ($proto eq 'C3') ? 'name' : 'fn';\r
+               my $name = nfsd::nfsDumpParseLineField ($line, $tag);\r
+\r
+               # All directories have (at least) three names:  the\r
+               # given name, and "." and "..".  We're only interested\r
+               # in the given name.\r
+\r
+               if ($name eq '"."' || $name eq '".."') {\r
+                       return ;\r
+               }\r
+\r
+               my $fh = nfsd::nfsDumpCompressFH ($fh_type,\r
+                       nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+               $pendingCallsXIDnow{$uxid} = $now;\r
+               $pendingCallsXIDfh{$uxid} = $fh;\r
+               $pendingCallsXIDname{$uxid} = $name;\r
+       }\r
+       elsif ($proto eq 'R3' || $proto eq 'R2') {\r
+               if (! exists $pendingCallsXIDnow{$uxid}) {\r
+                       return ;\r
+               }\r
+\r
+               my $pfh = $pendingCallsXIDfh{$uxid};\r
+               my $name = $pendingCallsXIDname{$uxid};\r
+\r
+               delete $pendingCallsXIDnow{$uxid};\r
+               delete $pendingCallsXIDfh{$uxid};\r
+               delete $pendingCallsXIDname{$uxid};\r
+\r
+               if ($response eq 'OK') {\r
+                       my $cfh = nfsd::nfsDumpCompressFH ($fh_type,\r
+                                       nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+                       my $type = nfsd::nfsDumpParseLineField ($line, 'ftype');\r
+\r
+                       #if ($type == 2) \r
+                       {\r
+                               $fhIsDir{$cfh} = $type;\r
+                       }\r
+\r
+                       # Original code\r
+                       # $fh2Parent{$cfh} = $pfh;\r
+                       # $fh2Name{$cfh} = $name;\r
+                       # $parent2fh{"$pfh,$name"} = $cfh;\r
+                       # RFS code: in case of the rename, we will record the name of the old name\r
+                       $fh2Parent{$cfh} = $pfh;\r
+                       if (! exists   $fh2Name{$cfh}) {\r
+                               $fh2Name{$cfh} = $name;\r
+                               $parent2fh{"$pfh,$name"} = $cfh;\r
+                       } else {\r
+                               # keep the old name in the fh2Name{$cfh}\r
+                               # and we also add the newname and pfh mapping\r
+                               #$fh2Name{$cfh} = $name;\r
+                               $parent2fh{"$pfh,$name"} = $cfh;\r
+                       }\r
+\r
+                       my ($size, $mode, $atime, $mtime, $ctime, $nlink) =\r
+                                       nfsd::nfsDumpParseLineFields ($line,\r
+                                       'size', 'mode',\r
+                                       'atime', 'mtime', 'ctime', 'nlink');\r
+                       my $ts = getTimeStamp($line);\r
+\r
+                       # RFS: modify here to get/maintain more file attributes\r
+                       # we can just check the ctime and compare it with trace-start-time\r
+                       # to decide whether to create a file/diretory.\r
+                       # atime - last access time of the file\r
+                       # mtime - last modification time of the file\r
+                       # ctime - last file status change time\r
+                       \r
+                       #$fh2Attr{$cfh} = "$size $mode $atime $mtime $ctime";\r
+                       if  (! exists $fh2AttrOrig{$cfh} ) {\r
+                               $fh2AttrOrig{$cfh} = "$size $mode $op $atime $mtime $ctime $nlink $ts";\r
+                       }\r
+                       $fh2Attr{$cfh} = "$size $mode $op $atime $mtime $ctime $nlink $ts";\r
+               }\r
+\r
+       }\r
+\r
+       return ;\r
+}\r
+\r
+sub doRemove {\r
+       my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
+\r
+       if ($proto eq 'C3' || $proto eq 'C2') {\r
+               my $tag = ($proto eq 'C3') ? 'name' : 'fn';\r
+               my $name = nfsd::nfsDumpParseLineField ($line, $tag);\r
+\r
+               # All directories have (at least) three names:  the\r
+               # given name, and "." and "..".  We're only interested\r
+               # in the given name.\r
+\r
+               if ($name eq '"."' || $name eq '".."') {\r
+                       return ;\r
+               }\r
+\r
+               my $pfh = nfsd::nfsDumpCompressFH ($fh_type,\r
+                       nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+               if (! exists $parent2fh{"$pfh,$name"}) {\r
+                       return ;\r
+               }\r
+\r
+               $pendingCallsXIDnow{$uxid} = $now;\r
+               $pendingCallsXIDfh{$uxid} = $pfh;\r
+               $pendingCallsXIDname{$uxid} = $name;\r
+       }\r
+       elsif ($proto eq 'R3' || $proto eq 'R2') {\r
+               if (! exists $pendingCallsXIDnow{$uxid}) {\r
+                       return ;\r
+               }\r
+\r
+               my $pfh = $pendingCallsXIDfh{$uxid};\r
+               my $name = $pendingCallsXIDname{$uxid};\r
+\r
+               delete $pendingCallsXIDfh{$uxid};\r
+               delete $pendingCallsXIDname{$uxid};\r
+               delete $pendingCallsXIDnow{$uxid};\r
+\r
+               if (! exists $parent2fh{"$pfh,$name"}) {\r
+                       return ;\r
+               }\r
+\r
+               my $cfh = $parent2fh{"$pfh,$name"};\r
+\r
+               if ($response eq 'OK') {\r
+                       if ($op eq 'remove') {\r
+                               printFileInfo ($cfh, 'D');\r
+\r
+                               delete $fh2Parent{$cfh};\r
+                               delete $fh2Name{$cfh};\r
+                               delete $fh2Attr{$cfh};\r
+                               delete $fhs2AttrOrig{$cfg};\r
+                               delete $parent2fh{"$pfh,$name"};\r
+                       }\r
+               }\r
+       }\r
+\r
+       return ;\r
+}\r
+\r
+sub doGetAttr {\r
+       my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
+\r
+       if ($proto eq 'C3' || $proto eq 'C2') {\r
+               my $fh = nfsd::nfsDumpCompressFH ($fh_type,\r
+                       nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+               #if (nfsd::nfsDumpParseLineField ($line, 'fh')\r
+               #               eq '00018961-57570100-d2440000-61890100') {\r
+               #       printf STDERR "Seen it ($op)\n";\r
+               #}\r
+\r
+               if (! defined $fh) {\r
+                       return ;\r
+               }\r
+\r
+               $pendingCallsXIDnow{$uxid} = $now;\r
+               $pendingCallsXIDfh{$uxid} = $fh;\r
+# RFS debug code\r
+#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+#if ($fh eq $wantfh) {\r
+#      print "JIAWU: doGetAttr call $wantfh\n";\r
+#}\r
+       }\r
+       else {\r
+               if (! exists $pendingCallsXIDnow{$uxid}) {\r
+                       return ;\r
+               }\r
+\r
+               my $fh = $pendingCallsXIDfh{$uxid};\r
+               delete $pendingCallsXIDfh{$uxid};\r
+               delete $pendingCallsXIDnow{$uxid};\r
+# RFS debug code\r
+#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+#if ($fh eq $wantfh) {\r
+#      print "JIAWU: doGetAttr response $wantfh\n";\r
+#}\r
+\r
+               if ($response ne 'OK') {\r
+                       return ;\r
+               }\r
+\r
+               my ($ftype) = nfsd::nfsDumpParseLineFields ($line, 'ftype');\r
+               if (!defined $ftype) {\r
+                       print STDERR "BAD $line";\r
+                       return ;\r
+               }\r
+\r
+               #if ($ftype == 2) \r
+               {\r
+                       $fhIsDir{$fh} = $ftype;\r
+               }\r
+\r
+               #RFS comment: here if fh is a directory, then it will not be add \r
+               # in the two hash table %fh2Attr(%fh2AttrOrig) and %fh2Name\r
+               # if ($ftype != 1) {\r
+               #       return ;\r
+               #}\r
+               if ($ftype != 1) {\r
+                       #return ;\r
+               }\r
+\r
+\r
+               my ($mode, $size, $atime, $mtime, $ctime, $nlink) =\r
+                               nfsd::nfsDumpParseLineFields ($line,\r
+                               'mode', 'size', 'atime', 'mtime', 'ctime', 'nlink');\r
+               my $ts = getTimeStamp($line);\r
+\r
+                       # RFS: modify here to get/maintain more file attributes\r
+                       # we can just check the ctime and compare it with trace-start-time\r
+                       # to decide whether to create a file/diretory.\r
+                       # atime - last access time of the file\r
+                       # mtime - last modification time of the file\r
+                       # ctime - last file status change time\r
+\r
+                       # $fh2Attr{$fh} = "$size $mode $atime $mtime $ctime";\r
+\r
+                       if  (! exists $fh2AttrOrig{$fh} ) {\r
+                               $fh2AttrOrig{$fh} = "$size $mode $op $atime $mtime $ctime $nlink $ts";\r
+                       }\r
+                       $fh2Attr{$fh} = "$size $mode $op $atime $mtime $ctime $nlink $ts";\r
+       }\r
+}\r
+\r
+# Purge all the pending XID records dated earlier than $when (which is\r
+# typically at least $PRUNE_INTERVAL seconds ago).  This is important\r
+# because otherwise missing XID records can pile up, eating a lot of\r
+# memory. \r
+  \r
+sub prunePending {\r
+       my ($when) = @_;\r
+\r
+       foreach my $uxid ( keys %pendingCallsXIDnow ) {\r
+               if ($pendingCallsXIDnow{$uxid} < $when) {\r
+# RFS debug code\r
+my $fh = $pendingCallsXIDfh{$uxid};\r
+my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+if ($fh eq $wantfh) {\r
+       print "JIAWU: prunePending $wantfh\n";\r
+}\r
+#enf RFS\r
+                       delete $pendingCallsXIDnow{$uxid};\r
+               }\r
+       }\r
+\r
+       return ;\r
+}\r
+\r
+# Return as much of the path for the given fh as possible.  It may or\r
+# may not reach the root (or the mount point of the file system), but\r
+# right now we don't check.  Usually on busy systems the data is\r
+# complete enough so that most paths are complete back to the mount\r
+# point.\r
+\r
+sub findPath {\r
+       my ($fh) = @_;\r
+       my $isdir = 0;\r
+       my $cnt = 0;\r
+       my $MaxPathLen = 40;\r
+\r
+       if (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) {\r
+               $isdir = 1;\r
+       }\r
+\r
+       my @path = ();\r
+       while ($fh && exists $fh2Name{$fh}) {\r
+               unshift (@path, $fh2Name{$fh});\r
+\r
+               if ( ($fh2Name{$fh} ne '"RFSNN0"' ) ) {\r
+                       if (! exists $fh2Parent{$fh}) {\r
+                               print STDERR "$fh2Name{$fh} ";\r
+                               if ( ($fh2Name{$fh} eq '"RFSNN0"' ) ) {\r
+                                       print STDERR "eq RFSNN0\n";\r
+                               } else {\r
+                                       print STDERR "NOT eq RFSNN0\n";\r
+                               }\r
+                       }\r
+                       if ($fh eq $fh2Parent{$fh}) {\r
+                               unshift (@path, '(LOOP)');\r
+                               last;\r
+                       }\r
+               }\r
+\r
+               if ($cnt++ > $MaxPathLen) {\r
+                       print STDERR "findPath: path too long (> $MaxPathLen)\n";\r
+                       unshift (@path, '(TOO-LONG)');\r
+                       last;\r
+               }\r
+\r
+               $fh = $fh2Parent{$fh};\r
+       }\r
+\r
+       # RFS: append the ~user (fh and !exists $fh2Name{$fh} and type is Directory)\r
+       if ($fh && !exists $fh2Name{$fh} && (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ) {\r
+               if (exists $rootsName{$fh}) {\r
+                       #print "JIAWU: $rootsName{$fh}\n";\r
+                       unshift(@path, $rootsName{$fh});\r
+               } else {\r
+                       print "JIAWU: WARNING! No rootsName for this fh: $fh\n";\r
+                       unshift(@path, $fh);\r
+               }\r
+       } else {\r
+               if ($fh && !exists $fh2Name{$fh} && (!exists $fhIsDir{$fh} || (exists $fhIsDir{$fh} && $fhIsDir{$fh}!=2)) ) {\r
+                       if (exists $discardFHs{$fh}) {\r
+                               open NOATTRDIR, ">>noattrdirdiscard" || die "open noattrdirdiscard failed\n";\r
+                               print NOATTRDIR "$fh DISCARD\n";\r
+                               close NOATTRDIR;\r
+                       } else {\r
+                               # RFS: if a possible fh without attr and name, then regard it as a special root ~/RFSNN0\r
+                               unshift(@path, '"RFSNN0"');\r
+                               $fhIsDir{$fh}=2;\r
+                               $fh2Name{$fh} = '"RFSNN0"';\r
+                               $rootsName{$fh} = '"RFSNN0"';\r
+                               open NOATTRDIR, ">>noattrdir-root";\r
+                               print NOATTRDIR "$fh /RFSNN0/\n";\r
+                               close NOATTRDIR;\r
+                       }\r
+               }\r
+       }\r
+\r
+       \r
+       my $str = '';\r
+       $cnt = 0;\r
+       foreach my $p ( @path ) {\r
+               $p =~ s/^.//;\r
+               $p =~ s/.$//;\r
+               $str .= "/$p";\r
+               $cnt++;\r
+       }\r
+\r
+       if ($isdir) {\r
+               $str .= '/';\r
+       }\r
+\r
+       if ($cnt == 0) {\r
+               $str = '.';\r
+       }\r
+\r
+       return ($str, $cnt);\r
+}\r
+\r
+\r
+$total_unknown_fh = 0;\r
+$total_known_fh = 0;\r
+\r
+sub printAll {\r
+       my ($start_time, $out) = @_;\r
+\r
+       my %allfh = ();\r
+       my $fh;\r
+       my $u = 0;\r
+       my $k = 0;\r
+\r
+       # RFS print more information here\r
+       open (OUT_RFS, ">rfsinfo") ||\r
+               die "Can't create $OutFileBaseName.rfs.";\r
+               \r
+       foreach $fh ( keys %fh2Attr ) {\r
+               $allfh{$fh} = 1; \r
+       }\r
+       foreach $fh ( keys %fh2Name ) {\r
+               $allfh{$fh} = 1; \r
+       }\r
+\r
+       #RFS: before printFileInfo, name those roots' name\r
+\r
+       #RFS there are three kind of fh\r
+       # 1. fh/name paired (fh/attr must)\r
+       # 2. fh/attr but no fh/name: type file (discard related operations)\r
+       # 3. fh/attr but no fh/name: type dir (keep as persuedo root)\r
+       $u = $k = 0;\r
+       my $sn=1;\r
+       foreach $fh ( keys %allfh ) {\r
+               if (exists $fh2Parent{$fh} ) {\r
+                       $k++;\r
+               }\r
+               else {\r
+                       $u++;\r
+                       my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F';\r
+                       if ($type eq 'D') {\r
+                               $rootsName{$fh} = sprintf("\"RFSNN%d\"", $sn++);\r
+                               $rootsFHs{$fh} = 2;\r
+                       }\r
+                       else {\r
+                               $discardFHs{$fh} = 1;\r
+                       }\r
+               }\r
+       }\r
+       print OUT_RFS "#stat: fh with parent = $k, fh without parent = $u\n";\r
+       $u = keys %rootsFHs;\r
+       print OUT_RFS "#RFS: root fh list($u)\n";\r
+       foreach $fh (keys %rootsName) {\r
+               print OUT_RFS "#RFS: $rootsName{$fh} $fh\n";\r
+       }\r
+       $u = keys %discardFHs;\r
+       print OUT_RFS "#RFS: discard fh list($u)\n";\r
+       print OUT_RFS join("\n", keys %discardFHs, "");\r
+       \r
+\r
+       print $out "#F type state fh path pathcount attrOrig(size,mode,op,atime,mt,ct) attrLast(size,mode,op,at,mt,ct)\n";\r
+\r
+       print $out "#T starttime = $start_time\n";\r
+       foreach $fh ( keys %allfh ) {\r
+               printFileInfoOutputFile ($fh, 'A', $out);\r
+       }\r
+       \r
+       my $numfh2Name = keys %fh2Name;\r
+       my $numfh2Attr = keys %fh2Attr;\r
+       print OUT_RFS "fh2name has $numfh2Name, fh2Attr has $numfh2Attr\n";\r
+\r
+       \r
+       $u = $k = 0;\r
+       foreach $fh ( keys %allfh ) {\r
+               if ( exists $fh2Name{$fh} ) {$k++;}\r
+               else {$u++;}\r
+       }\r
+       print OUT_RFS "#stat: total fh with name = $k, without name = $u\n";\r
+\r
+       print OUT_RFS "#stat: finally, total known fh = $total_known_fh, unknown = $total_unknown_fh\n";\r
+\r
+# Note: fh with name (8303), fh without name (103)\r
+#          root fh list: 18\r
+#          discard fh list: 85\r
+#          known fh (8321): ( fh with name(8303) + root fh list (18) = 8321)\r
+#          unknown fh (85)\r
+#\r
+# All fh from the those data structures: 8321 + 85 = 8303+103\r
+# Or, in keys %allfh\r
+#\r
+# \r
+       print OUT_RFS "#RFS\n";\r
+       close OUT_RFS;  \r
+\r
+       open (MISSED, ">missdiscardfh") ||\r
+                       die "Can't create missdiscardfh.";\r
+       foreach $fh (keys %rfsAllFHs) {\r
+               if ( !exists $allfh{$fh} && \r
+                    ( (defined $fh2Name{$fh}) && ($fh2Name{$fh} ne '"RFSNN0"')) ) {\r
+                       print MISSED "$fh LN: $rfsAllFHs{$fh}\n"\r
+               }\r
+       }\r
+       close MISSED;\r
+\r
+# check for a special fh\r
+#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+#if ($allfh{$wantfh} == 1) {\r
+#      print OUT_RFS "JIAWU: found $wantfh\n";\r
+#} else {\r
+#      print OUT_RFS "JIAWU: NOT found $wantfh\n";\r
+#}\r
+#foreach $fh ( keys %allfh ) {\r
+#      if ( $fh eq $wantfh ) {\r
+#              print OUT_RFS "JIAWU: found $wantfh\n";\r
+#              printFileInfoOutputFile ($fh, 'JIAWU', *OUT_RFS);\r
+#              last;\r
+#      }\r
+#}\r
+#print OUT_RFS "JIAWU: after \n";\r
+\r
+\r
+}\r
+\r
+sub printFileInfoOutputFile {\r
+       my ($fh, $state, $out) = @_;\r
+\r
+       my ($p, $c) = findPath ($fh);\r
+       \r
+       if ($c == 0) {$total_unknown_fh++;}\r
+       else {$total_known_fh++;}\r
+       \r
+       #my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F';\r
+       my $type = $fhIsDir{$fh};\r
+       if (!defined $type) \r
+       {\r
+               print STDERR "unknown ftype(U) for fh: $fh\n"; \r
+               $type = 'U';\r
+       }\r
+       my $attr = (exists $fh2Attr{$fh}) ?\r
+                       $fh2Attr{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1";\r
+       my $attrOrig = (exists $fh2AttrOrig{$fh}) ?\r
+                       $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1";\r
+\r
+       print $out "F $type $state $fh $p $c $attrOrig $attr\n";\r
+}\r
+\r
+sub printFileInfo {\r
+       my ($fh, $state) = @_;\r
+\r
+       my ($p, $c) = findPath ($fh);\r
+       \r
+       if ($c == 0) {$total_unknown_fh++;}\r
+       else {$total_known_fh++;}\r
+       \r
+       my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F';\r
+       my $attr = (exists $fh2Attr{$fh}) ?\r
+                       $fh2Attr{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1";\r
+       my $attrOrig = (exists $fh2AttrOrig{$fh}) ?\r
+                       $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1";\r
+\r
+       print "F $type $state $fh $p $c $attrOrig $attr\n";\r
+}\r
+\r
+#\r
+# The flow to create the dependency table\r
+# \r
+# create(dirfh, name, attr) -->newfh, new attr\r
+# mkdir(dirfh, name, attr) -> newfh, new attr\r
+#\r
+# remove(dirfh, name) --> status\r
+# rmdir(dirfh, name) --> status\r
+# rename(dirfh, name, todirfh, toname) --> status\r
+#\r
+# link(newdirfh, newname, dirfh, name) --> status (newdir/newname=>dir/name)\r
+# syslink(newdirfh, newname, string) --> status (newdir/newname=>"string")\r
+# readlink(fh) --> string\r
+# lookup(dirfh, name) --> fh, attr\r
+# getattr(fh) --> attr\r
+# setattr(fh, attr) --> attr\r
+# read(fh, offset, count) -> attr, data\r
+# write(fh, offset, count, data) --> attr\r
+# readdir(dirfh, cookie, count) --> entries\r
+# statfs(fh) --> status\r
+#\r
+#\r
+#\r
+#\r
+# for each line the trace file: \r
+#      if (op == R2 or R3) continue; #skip the response line\r
+#      switch (the op)\r
+#      {\r
+#      # CREATION OPs:\r
+#      case create:\r
+#      case remove:\r
+#      # DELETE OPs:\r
+#      case mkdir:\r
+#      case rmdir:\r
+#      # other OPs\r
+#\r
+#\r
+#\r
+#\r
+#\r
+1;\r