Import TBBT (NFS trace replay).
[bluesky.git] / TBBT / trace_init / hier.pl.old
diff --git a/TBBT/trace_init/hier.pl.old b/TBBT/trace_init/hier.pl.old
new file mode 100755 (executable)
index 0000000..b7f0cf4
--- /dev/null
@@ -0,0 +1,550 @@
+#\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
+#RFS\r
+%rootsName             = ();\r
+%discardFHs = ();\r
+%rootsFHs = ();\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
+       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\r
+       if ($op eq 'lookup' || $op eq 'create' || $op eq 'mkdir') {\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
+               return (&doGetAttr ($line, $proto, $op, $uxid,\r
+                               $now, $response, $fh_type));\r
+       }\r
+       elsif ($op eq 'setattr') {\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
+                               $fhIsDir{$cfh} = 1;\r
+                       }\r
+\r
+                       $fh2Parent{$cfh} = $pfh;\r
+                       $fh2Name{$cfh} = $name;\r
+                       $parent2fh{"$pfh,$name"} = $cfh;\r
+\r
+                       my ($size, $mode, $atime, $mtime, $ctime) =\r
+                                       nfsd::nfsDumpParseLineFields ($line,\r
+                                       'size', 'mode',\r
+                                       'atime', 'mtime', 'ctime');\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";\r
+                       }\r
+                       $fh2Attr{$cfh} = "$size $mode $op $atime $mtime $ctime";\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
+                       $fhIsDir{$fh} = 1;\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) =\r
+                               nfsd::nfsDumpParseLineFields ($line,\r
+                               'mode', 'size', 'atime', 'mtime', 'ctime');\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";\r
+                       }\r
+                       $fh2Attr{$fh} = "$size $mode $op $atime $mtime $ctime";\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}) {\r
+               $isdir = 1;\r
+       }\r
+\r
+       my @path = ();\r
+       while ($fh && exists $fh2Name{$fh}) {\r
+               unshift (@path, $fh2Name{$fh});\r
+               if ($fh eq $fh2Parent{$fh}) {\r
+                       unshift (@path, '(LOOP)');\r
+                       last;\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}) {\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}) {\r
+                       if (exists $discardFHs{$fh}) {\r
+                               open NOATTRDIR, ">>noattrdirdiscard";\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}=1;\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}) ? 'D' : 'F';\r
+                       if ($type eq 'D') {\r
+                               $rootsName{$fh} = sprintf("\"RFSNN%d\"", $sn++);\r
+                               $rootsFHs{$fh} = 1;\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
+               \r
+       my $numfh2Name = keys %fh2Name;\r
+       my $numfh2Attr = keys %fh2Attr;\r
+       print OUT_RFS "fh2name has $numfh2Name, fh2Attr has $numfh2Attr\n";\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
+       $u = $k = 0;\r
+       foreach $fh ( keys %allfh ) {\r
+               if ( exists $fh2Name{$fh} ) {$k++;}\r
+               else {$u++;}\r
+       }\r
+       print OUT_RFS "#stat: total known fh = $total_known_fh, unknown = $total_unknown_fh\n";\r
+       print OUT_RFS "#stat: total fh with name = $k, without name = $u\n";\r
+\r
+       print OUT_RFS "#RFS\n";\r
+       close OUT_RFS;  \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}) ? 'D' : 'F';\r
+       my $attr = (exists $fh2Attr{$fh}) ?\r
+                       $fh2Attr{$fh} : "-1 -1 -1 -1 -1";\r
+       my $attrOrig = (exists $fh2AttrOrig{$fh}) ?\r
+                       $fh2AttrOrig{$fh} : "-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}) ? 'D' : 'F';\r
+       my $attr = (exists $fh2Attr{$fh}) ?\r
+                       $fh2Attr{$fh} : "-1 -1 -1 -1 -1";\r
+       my $attrOrig = (exists $fh2AttrOrig{$fh}) ?\r
+                       $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1";\r
+\r
+       print "F $type $state $fh $p $c $attrOrig $attr\n";\r
+}\r
+\r
+1;\r