Add proper per-file copyright notices/licenses and top-level license.
[bluesky.git] / TBBT / trace_init / hier.pl.old
1 #\r
2 # Copyright (c) 2002-2003\r
3 #      The President and Fellows of Harvard College.\r
4 #\r
5 # Redistribution and use in source and binary forms, with or without\r
6 # modification, are permitted provided that the following conditions\r
7 # are met:\r
8 # 1. Redistributions of source code must retain the above copyright\r
9 #    notice, this list of conditions and the following disclaimer.\r
10 # 2. Redistributions in binary form must reproduce the above copyright\r
11 #    notice, this list of conditions and the following disclaimer in the\r
12 #    documentation and/or other materials provided with the distribution.\r
13 # 3. Neither the name of the University nor the names of its contributors\r
14 #    may be used to endorse or promote products derived from this software\r
15 #    without specific prior written permission.\r
16 #\r
17 # THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
18 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
20 # ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
21 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
22 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
23 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
24 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
26 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
27 # SUCH DAMAGE.\r
28 #\r
29 # $Id: hier.pl,v 1.14 2003/07/26 20:52:03 ellard Exp $\r
30 #\r
31 # hier.pl - Tools to map out the file system hierarchy.  This is\r
32 # accomplished by snooping out the lookup calls.\r
33 #\r
34 # This is expensive because the hierarchy can require a LOT of space\r
35 # to store for a large system with lots of files (especially if files\r
36 # come and go).  Don't construct the hierarchy unless you want it --\r
37 # and be prepared to prune it from time to time.\r
38 \r
39 package hier;\r
40 \r
41 # Tables used by the outside world:\r
42 \r
43 %fh2Parent              = ();\r
44 %fh2Name                = ();\r
45 %fh2Attr                = ();\r
46 %fh2AttrOrig            = ();\r
47 %parent2fh              = ();\r
48 #RFS\r
49 %rootsName              = ();\r
50 %discardFHs = ();\r
51 %rootsFHs = ();\r
52 \r
53 \r
54 # Library-private tables and variables.\r
55 \r
56 %pendingCallsXIDnow     = ();\r
57 %pendingCallsXIDfh      = ();\r
58 %pendingCallsXIDname    = ();\r
59 \r
60 $nextPruneTime          = -1;\r
61 $PRUNE_INTERVAL         = 5 * 60;       # Five minutes.\r
62 \r
63 sub processLine {\r
64         my ($line, $proto, $op, $xid, $client, $now, $response, $fh_type) = @_;\r
65 \r
66         if ($now > $nextPruneTime) {\r
67                 &prunePending ($now - $PRUNE_INTERVAL);\r
68                 $nextPruneTime = $now + $PRUNE_INTERVAL;\r
69         }\r
70 \r
71         my $uxid = "$client-$xid";\r
72 \r
73         # 'lookup', 'create', 'rename', 'delete',\r
74         # 'getattr', 'setattr'\r
75 \r
76         #RFS: add mkdir/rmdir\r
77         if ($op eq 'lookup' || $op eq 'create' || $op eq 'mkdir') {\r
78                 return (&doLookup ($line, $proto, $op, $uxid,\r
79                                 $now, $response, $fh_type));\r
80         }\r
81         elsif ($op eq 'rename') {\r
82         }\r
83         elsif ($op eq 'remove' || $op eq 'rmdir') {\r
84                 # RFS: why remove these entries? Just let them exist since \r
85                 # there is generation number available to distinguish btw removed dir/file \r
86                 # and new dir/file with the same inode number.\r
87                 #return (&doRemove ($line, $proto, $op, $uxid,\r
88                 #               $now, $response, $fh_type));\r
89         }\r
90         elsif ($op eq 'getattr' || $op eq 'read' || $op eq 'write' ) {\r
91                 return (&doGetAttr ($line, $proto, $op, $uxid,\r
92                                 $now, $response, $fh_type));\r
93         }\r
94         elsif ($op eq 'setattr') {\r
95         }\r
96 }\r
97 \r
98 sub doLookup {\r
99         my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
100 \r
101         if ($proto eq 'C3' || $proto eq 'C2') {\r
102                 my $tag = ($proto eq 'C3') ? 'name' : 'fn';\r
103                 my $name = nfsd::nfsDumpParseLineField ($line, $tag);\r
104 \r
105                 # All directories have (at least) three names:  the\r
106                 # given name, and "." and "..".  We're only interested\r
107                 # in the given name.\r
108 \r
109                 if ($name eq '"."' || $name eq '".."') {\r
110                         return ;\r
111                 }\r
112 \r
113                 my $fh = nfsd::nfsDumpCompressFH ($fh_type,\r
114                         nfsd::nfsDumpParseLineField ($line, 'fh'));\r
115 \r
116                 $pendingCallsXIDnow{$uxid} = $now;\r
117                 $pendingCallsXIDfh{$uxid} = $fh;\r
118                 $pendingCallsXIDname{$uxid} = $name;\r
119         }\r
120         elsif ($proto eq 'R3' || $proto eq 'R2') {\r
121                 if (! exists $pendingCallsXIDnow{$uxid}) {\r
122                         return ;\r
123                 }\r
124 \r
125                 my $pfh = $pendingCallsXIDfh{$uxid};\r
126                 my $name = $pendingCallsXIDname{$uxid};\r
127 \r
128                 delete $pendingCallsXIDnow{$uxid};\r
129                 delete $pendingCallsXIDfh{$uxid};\r
130                 delete $pendingCallsXIDname{$uxid};\r
131 \r
132                 if ($response eq 'OK') {\r
133                         my $cfh = nfsd::nfsDumpCompressFH ($fh_type,\r
134                                         nfsd::nfsDumpParseLineField ($line, 'fh'));\r
135 \r
136                         my $type = nfsd::nfsDumpParseLineField ($line, 'ftype');\r
137 \r
138                         if ($type == 2) {\r
139                                 $fhIsDir{$cfh} = 1;\r
140                         }\r
141 \r
142                         $fh2Parent{$cfh} = $pfh;\r
143                         $fh2Name{$cfh} = $name;\r
144                         $parent2fh{"$pfh,$name"} = $cfh;\r
145 \r
146                         my ($size, $mode, $atime, $mtime, $ctime) =\r
147                                         nfsd::nfsDumpParseLineFields ($line,\r
148                                         'size', 'mode',\r
149                                         'atime', 'mtime', 'ctime');\r
150 \r
151                         # RFS: modify here to get/maintain more file attributes\r
152                         # we can just check the ctime and compare it with trace-start-time\r
153                         # to decide whether to create a file/diretory.\r
154                         # atime - last access time of the file\r
155                         # mtime - last modification time of the file\r
156                         # ctime - last file status change time\r
157                         \r
158                         #$fh2Attr{$cfh} = "$size $mode $atime $mtime $ctime";\r
159                         if  (! exists $fh2AttrOrig{$cfh} ) {\r
160                                 $fh2AttrOrig{$cfh} = "$size $mode $op $atime $mtime $ctime";\r
161                         }\r
162                         $fh2Attr{$cfh} = "$size $mode $op $atime $mtime $ctime";\r
163                 }\r
164 \r
165         }\r
166 \r
167         return ;\r
168 }\r
169 \r
170 sub doRemove {\r
171         my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
172 \r
173         if ($proto eq 'C3' || $proto eq 'C2') {\r
174                 my $tag = ($proto eq 'C3') ? 'name' : 'fn';\r
175                 my $name = nfsd::nfsDumpParseLineField ($line, $tag);\r
176 \r
177                 # All directories have (at least) three names:  the\r
178                 # given name, and "." and "..".  We're only interested\r
179                 # in the given name.\r
180 \r
181                 if ($name eq '"."' || $name eq '".."') {\r
182                         return ;\r
183                 }\r
184 \r
185                 my $pfh = nfsd::nfsDumpCompressFH ($fh_type,\r
186                         nfsd::nfsDumpParseLineField ($line, 'fh'));\r
187 \r
188                 if (! exists $parent2fh{"$pfh,$name"}) {\r
189                         return ;\r
190                 }\r
191 \r
192                 $pendingCallsXIDnow{$uxid} = $now;\r
193                 $pendingCallsXIDfh{$uxid} = $pfh;\r
194                 $pendingCallsXIDname{$uxid} = $name;\r
195         }\r
196         elsif ($proto eq 'R3' || $proto eq 'R2') {\r
197                 if (! exists $pendingCallsXIDnow{$uxid}) {\r
198                         return ;\r
199                 }\r
200 \r
201                 my $pfh = $pendingCallsXIDfh{$uxid};\r
202                 my $name = $pendingCallsXIDname{$uxid};\r
203 \r
204                 delete $pendingCallsXIDfh{$uxid};\r
205                 delete $pendingCallsXIDname{$uxid};\r
206                 delete $pendingCallsXIDnow{$uxid};\r
207 \r
208                 if (! exists $parent2fh{"$pfh,$name"}) {\r
209                         return ;\r
210                 }\r
211 \r
212                 my $cfh = $parent2fh{"$pfh,$name"};\r
213 \r
214                 if ($response eq 'OK') {\r
215                         if ($op eq 'remove') {\r
216                                 printFileInfo ($cfh, 'D');\r
217 \r
218                                 delete $fh2Parent{$cfh};\r
219                                 delete $fh2Name{$cfh};\r
220                                 delete $fh2Attr{$cfh};\r
221                                 delete $fhs2AttrOrig{$cfg};\r
222                                 delete $parent2fh{"$pfh,$name"};\r
223                         }\r
224                 }\r
225         }\r
226 \r
227         return ;\r
228 }\r
229 \r
230 sub doGetAttr {\r
231         my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
232 \r
233         if ($proto eq 'C3' || $proto eq 'C2') {\r
234                 my $fh = nfsd::nfsDumpCompressFH ($fh_type,\r
235                         nfsd::nfsDumpParseLineField ($line, 'fh'));\r
236 \r
237                 #if (nfsd::nfsDumpParseLineField ($line, 'fh')\r
238                 #               eq '00018961-57570100-d2440000-61890100') {\r
239                 #       printf STDERR "Seen it ($op)\n";\r
240                 #}\r
241 \r
242                 if (! defined $fh) {\r
243                         return ;\r
244                 }\r
245 \r
246                 $pendingCallsXIDnow{$uxid} = $now;\r
247                 $pendingCallsXIDfh{$uxid} = $fh;\r
248 # RFS debug code\r
249 my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
250 if ($fh eq $wantfh) {\r
251         print "JIAWU: doGetAttr call $wantfh\n";\r
252 }\r
253         }\r
254         else {\r
255                 if (! exists $pendingCallsXIDnow{$uxid}) {\r
256                         return ;\r
257                 }\r
258 \r
259                 my $fh = $pendingCallsXIDfh{$uxid};\r
260                 delete $pendingCallsXIDfh{$uxid};\r
261                 delete $pendingCallsXIDnow{$uxid};\r
262 # RFS debug code\r
263 my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
264 if ($fh eq $wantfh) {\r
265         print "JIAWU: doGetAttr response $wantfh\n";\r
266 }\r
267 \r
268                 if ($response ne 'OK') {\r
269                         return ;\r
270                 }\r
271 \r
272                 my ($ftype) = nfsd::nfsDumpParseLineFields ($line, 'ftype');\r
273                 if (!defined $ftype) {\r
274                         print STDERR "BAD $line";\r
275                         return ;\r
276                 }\r
277 \r
278                 if ($ftype == 2) {\r
279                         $fhIsDir{$fh} = 1;\r
280                 }\r
281 \r
282                 #RFS comment: here if fh is a directory, then it will not be add \r
283                 # in the two hash table %fh2Attr(%fh2AttrOrig) and %fh2Name\r
284                 # if ($ftype != 1) {\r
285                 #       return ;\r
286                 #}\r
287                 if ($ftype != 1) {\r
288                         #return ;\r
289                 }\r
290 \r
291 \r
292                 my ($mode, $size, $atime, $mtime, $ctime) =\r
293                                 nfsd::nfsDumpParseLineFields ($line,\r
294                                 'mode', 'size', 'atime', 'mtime', 'ctime');\r
295 \r
296                         # RFS: modify here to get/maintain more file attributes\r
297                         # we can just check the ctime and compare it with trace-start-time\r
298                         # to decide whether to create a file/diretory.\r
299                         # atime - last access time of the file\r
300                         # mtime - last modification time of the file\r
301                         # ctime - last file status change time\r
302 \r
303                         # $fh2Attr{$fh} = "$size $mode $atime $mtime $ctime";\r
304 \r
305                         if  (! exists $fh2AttrOrig{$fh} ) {\r
306                                 $fh2AttrOrig{$fh} = "$size $mode $op $atime $mtime $ctime";\r
307                         }\r
308                         $fh2Attr{$fh} = "$size $mode $op $atime $mtime $ctime";\r
309         }\r
310 }\r
311 \r
312 # Purge all the pending XID records dated earlier than $when (which is\r
313 # typically at least $PRUNE_INTERVAL seconds ago).  This is important\r
314 # because otherwise missing XID records can pile up, eating a lot of\r
315 # memory. \r
316   \r
317 sub prunePending {\r
318         my ($when) = @_;\r
319 \r
320         foreach my $uxid ( keys %pendingCallsXIDnow ) {\r
321                 if ($pendingCallsXIDnow{$uxid} < $when) {\r
322 # RFS debug code\r
323 my $fh = $pendingCallsXIDfh{$uxid};\r
324 my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
325 if ($fh eq $wantfh) {\r
326         print "JIAWU: prunePending $wantfh\n";\r
327 }\r
328 #enf RFS\r
329                         delete $pendingCallsXIDnow{$uxid};\r
330                 }\r
331         }\r
332 \r
333         return ;\r
334 }\r
335 \r
336 # Return as much of the path for the given fh as possible.  It may or\r
337 # may not reach the root (or the mount point of the file system), but\r
338 # right now we don't check.  Usually on busy systems the data is\r
339 # complete enough so that most paths are complete back to the mount\r
340 # point.\r
341 \r
342 sub findPath {\r
343         my ($fh) = @_;\r
344         my $isdir = 0;\r
345         my $cnt = 0;\r
346         my $MaxPathLen = 40;\r
347 \r
348         if (exists $fhIsDir{$fh}) {\r
349                 $isdir = 1;\r
350         }\r
351 \r
352         my @path = ();\r
353         while ($fh && exists $fh2Name{$fh}) {\r
354                 unshift (@path, $fh2Name{$fh});\r
355                 if ($fh eq $fh2Parent{$fh}) {\r
356                         unshift (@path, '(LOOP)');\r
357                         last;\r
358                 }\r
359 \r
360                 if ($cnt++ > $MaxPathLen) {\r
361                         print STDERR "findPath: path too long (> $MaxPathLen)\n";\r
362                         unshift (@path, '(TOO-LONG)');\r
363                         last;\r
364                 }\r
365 \r
366                 $fh = $fh2Parent{$fh};\r
367         }\r
368 \r
369         # RFS: append the ~user (fh and !exists $fh2Name{$fh} and type is Directory)\r
370         if ($fh && !exists $fh2Name{$fh} && exists $fhIsDir{$fh}) {\r
371                 if (exists $rootsName{$fh}) {\r
372                         print "JIAWU: $rootsName{$fh}\n";\r
373                         unshift(@path, $rootsName{$fh});\r
374                 } else {\r
375                         print "JIAWU: WARNING! No rootsName for this fh: $fh\n";\r
376                         unshift(@path, $fh);\r
377                 }\r
378         } else {\r
379                 if ($fh && !exists $fh2Name{$fh} && !exists $fhIsDir{$fh}) {\r
380                         if (exists $discardFHs{$fh}) {\r
381                                 open NOATTRDIR, ">>noattrdirdiscard";\r
382                                 print NOATTRDIR "$fh DISCARD\n";\r
383                                 close NOATTRDIR;\r
384                         } else {\r
385                                 # RFS: if a possible fh without attr and name, then regard it as a special root ~/RFSNN0\r
386                                 unshift(@path, '"RFSNN0"');\r
387                                 $fhIsDir{$fh}=1;\r
388                                 $fh2Name{$fh} = '"RFSNN0"';\r
389                                 $rootsName{$fh} = '"RFSNN0"';\r
390                                 open NOATTRDIR, ">>noattrdir-root";\r
391                                 print NOATTRDIR "$fh RFSNN0\n";\r
392                                 close NOATTRDIR;\r
393                         }\r
394                 }\r
395         }\r
396 \r
397         \r
398         my $str = '';\r
399         $cnt = 0;\r
400         foreach my $p ( @path ) {\r
401                 $p =~ s/^.//;\r
402                 $p =~ s/.$//;\r
403                 $str .= "/$p";\r
404                 $cnt++;\r
405         }\r
406 \r
407         if ($isdir) {\r
408                 $str .= '/';\r
409         }\r
410 \r
411         if ($cnt == 0) {\r
412                 $str = '.';\r
413         }\r
414 \r
415         return ($str, $cnt);\r
416 }\r
417 \r
418 \r
419 $total_unknown_fh = 0;\r
420 $total_known_fh = 0;\r
421 \r
422 sub printAll {\r
423         my ($start_time, $out) = @_;\r
424 \r
425         my %allfh = ();\r
426         my $fh;\r
427         my $u = 0;\r
428         my $k = 0;\r
429 \r
430         # RFS print more information here\r
431         open (OUT_RFS, ">rfsinfo") ||\r
432                 die "Can't create $OutFileBaseName.rfs.";\r
433                 \r
434         foreach $fh ( keys %fh2Attr ) {\r
435                 $allfh{$fh} = 1; \r
436         }\r
437         foreach $fh ( keys %fh2Name ) {\r
438                 $allfh{$fh} = 1; \r
439         }\r
440 \r
441         #RFS: before printFileInfo, name those roots' name\r
442 \r
443         #RFS there are three kind of fh\r
444         # 1. fh/name paired (fh/attr must)\r
445         # 2. fh/attr but no fh/name: type file (discard related operations)\r
446         # 3. fh/attr but no fh/name: type dir (keep as persuedo root)\r
447         $u = $k = 0;\r
448         my $sn=1;\r
449         foreach $fh ( keys %allfh ) {\r
450                 if (exists $fh2Parent{$fh} ) {\r
451                         $k++;\r
452                 }\r
453                 else {\r
454                         $u++;\r
455                         my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F';\r
456                         if ($type eq 'D') {\r
457                                 $rootsName{$fh} = sprintf("\"RFSNN%d\"", $sn++);\r
458                                 $rootsFHs{$fh} = 1;\r
459                         }\r
460                         else {\r
461                                 $discardFHs{$fh} = 1;\r
462                         }\r
463                 }\r
464         }\r
465         print OUT_RFS "#stat: fh with parent = $k, fh without parent = $u\n";\r
466         $u = keys %rootsFHs;\r
467         print OUT_RFS "#RFS: root fh list($u)\n";\r
468         foreach $fh (keys %rootsName) {\r
469                 print OUT_RFS "#RFS: $rootsName{$fh} $fh\n";\r
470         }\r
471         $u = keys %discardFHs;\r
472         print OUT_RFS "#RFS: discard fh list($u)\n";\r
473         print OUT_RFS join("\n", keys %discardFHs, "");\r
474         \r
475 \r
476         print $out "#F type state fh path pathcount attrOrig(size,mode,op,atime,mt,ct) attrLast(size,mode,op,at,mt,ct)\n";\r
477 \r
478         print $out "#T starttime = $start_time\n";\r
479         foreach $fh ( keys %allfh ) {\r
480                 printFileInfoOutputFile ($fh, 'A', $out);\r
481         }\r
482         \r
483                 \r
484         my $numfh2Name = keys %fh2Name;\r
485         my $numfh2Attr = keys %fh2Attr;\r
486         print OUT_RFS "fh2name has $numfh2Name, fh2Attr has $numfh2Attr\n";\r
487         my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
488         if ($allfh{$wantfh} == 1) {\r
489                 print OUT_RFS "JIAWU: found $wantfh\n";\r
490         } else {\r
491                 print OUT_RFS "JIAWU: NOT found $wantfh\n";\r
492         }\r
493         foreach $fh ( keys %allfh ) {\r
494                 if ( $fh eq $wantfh ) {\r
495                         print OUT_RFS "JIAWU: found $wantfh\n";\r
496                         printFileInfoOutputFile ($fh, 'JIAWU', *OUT_RFS);\r
497                         last;\r
498                 }\r
499         }\r
500         print OUT_RFS "JIAWU: after \n";\r
501 \r
502         \r
503         $u = $k = 0;\r
504         foreach $fh ( keys %allfh ) {\r
505                 if ( exists $fh2Name{$fh} ) {$k++;}\r
506                 else {$u++;}\r
507         }\r
508         print OUT_RFS "#stat: total known fh = $total_known_fh, unknown = $total_unknown_fh\n";\r
509         print OUT_RFS "#stat: total fh with name = $k, without name = $u\n";\r
510 \r
511         print OUT_RFS "#RFS\n";\r
512         close OUT_RFS;  \r
513 \r
514 }\r
515 \r
516 sub printFileInfoOutputFile {\r
517         my ($fh, $state, $out) = @_;\r
518 \r
519         my ($p, $c) = findPath ($fh);\r
520         \r
521         if ($c == 0) {$total_unknown_fh++;}\r
522         else {$total_known_fh++;}\r
523         \r
524         my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F';\r
525         my $attr = (exists $fh2Attr{$fh}) ?\r
526                         $fh2Attr{$fh} : "-1 -1 -1 -1 -1";\r
527         my $attrOrig = (exists $fh2AttrOrig{$fh}) ?\r
528                         $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1";\r
529 \r
530         print $out "F $type $state $fh $p $c $attrOrig $attr\n";\r
531 }\r
532 \r
533 sub printFileInfo {\r
534         my ($fh, $state) = @_;\r
535 \r
536         my ($p, $c) = findPath ($fh);\r
537         \r
538         if ($c == 0) {$total_unknown_fh++;}\r
539         else {$total_known_fh++;}\r
540         \r
541         my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F';\r
542         my $attr = (exists $fh2Attr{$fh}) ?\r
543                         $fh2Attr{$fh} : "-1 -1 -1 -1 -1";\r
544         my $attrOrig = (exists $fh2AttrOrig{$fh}) ?\r
545                         $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1";\r
546 \r
547         print "F $type $state $fh $p $c $attrOrig $attr\n";\r
548 }\r
549 \r
550 1;\r