7930d60b1d991be56512f8199d06d7cde7de570c
[captive.git] / src / TraceFS / checktrace.pl
1 #! /usr/bin/perl
2
3 # $Id$
4 # Checks assumptions on Cc* (Cache Manager) behaviour by reading TraceFS log
5 # Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; exactly version 2 of June 1991 is required
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20
21 use strict;
22 use warnings;
23 use Data::Dumper;
24 use Carp qw(cluck confess);
25
26
27 my $filter=0;
28 $Data::Dumper::Sortkeys=1;
29 my $ntfs_blocksize=0x200;
30
31 # $Object->{"by"}="CcSomeFunction";
32 # $Object->{"line_enter"}=123;
33 # $Object->{"line_leave"}=124;
34 # $Object->{"process_thread"}="0x12345678/0x12345678";
35 # $Object->{"data"}[dataline]{"FileObject"}="0x12345678";
36 # $Object->{"data"}[dataline]{"FileName"}="\filename" or undef() if NULL;
37 # $Object->{"data"}[dataline]{"Flags"}="0x40100";
38 # $Object->{"data"}[dataline]{"SectionObjectPointer"}="0x12345678";
39 # $Object->{"data"}[dataline]{"SharedCacheMap"}="0x12345678";
40 # $FileObject{$FileObject}{"FileObject"}="0x12345678";
41 # $FileObject{$FileObject}{"SectionObjectPointer"}="0x12345678";
42 # $SectionObjectPointer{$SectionObjectPointer}{"SectionObjectPointer"}="0x12345678";
43 # $SectionObjectPointer{$SectionObjectPointer}{"SharedCacheMap"}="0x12345678";
44 # $SharedCacheMap{$SharedCacheMap}{"SharedCacheMap"}="0x12345678";
45 # $SharedCacheMap{$SharedCacheMap}{"SectionObjectPointer"}="0x12345678";
46 # $SharedCacheMap{$SharedCacheMap}{"Allocation"}="0x12345";
47 # $SharedCacheMap{$SharedCacheMap}{"FileSize"}="0x12345";
48 # $SharedCacheMap{$SharedCacheMap}{"ref_count"}=1;
49 # $SharedCacheMap{$SharedCacheMap}{"map"}="0x12345678" (Bcb);
50 # $SharedCacheMap{$SharedCacheMap}{"pin"}{"0x1000"}="0x12345678" (Bcb);
51 # $SharedCacheMap{$SharedCacheMap}{"PinAccess"}=0 or 1;
52 # $Bcb{$Bcb}{"Bcb"}="0x12345678";
53 # $Bcb{$Bcb}{"SharedCacheMap"}="0x12345678";
54 # $Bcb{$Bcb}{"type"}="pin" or "map";
55 # $Bcb{$Bcb}{"ref_count"}=1;
56 # $Bcb{$Bcb}{"FileOffset"}="0x1000" if {"type"} eq "pin";
57 # $Bcb{$Bcb}{"Buffer"}="0x12345678";    # PAGE_SIZE-aligned for "pin", FileOffset_0-aligned for "map"
58
59 my %FileObject;
60 my %SectionObjectPointer;
61 my %SharedCacheMap;
62 my %Bcb;
63
64 END {
65         print Data::Dumper->Dump([\%FileObject,\%SectionObjectPointer,\%SharedCacheMap,\%Bcb],
66                                [qw(%FileObject  %SectionObjectPointer  %SharedCacheMap  %Bcb)]) if !$filter;
67         }
68
69 my $Object;
70
71 sub tohex($)
72 {
73 my($num)=@_;
74
75         return sprintf("0x%X",$num);
76 }
77
78 sub FObject($)
79 {
80 my($FileObject)=@_;
81
82         my $FObject=$FileObject{$FileObject};
83         if (!$FObject) {
84                 my($package,$filename,$line,$subroutine)=caller 0;
85                 warn "Non-existent FileObject $FileObject by line $line";
86                 }
87         return $FObject;
88 }
89
90 sub SObject($)
91 {
92 my($SectionObjectPointer)=@_;
93
94         my $SObject=$SectionObjectPointer{$SectionObjectPointer};
95         if (!$SObject) {
96                 my($package,$filename,$line,$subroutine)=caller 0;
97                 warn "Non-existent SectionObjectPointer $SectionObjectPointer by line $line"
98                 }
99         return $SObject;
100 }
101
102 sub SObject_from_FileObject($)
103 {
104 my($FileObject)=@_;
105
106         return if !(my $FObject=FObject $FileObject);
107         my $SObject=SObject $FObject->{"SectionObjectPointer"};
108         if (!$SObject) {
109                 my($package,$filename,$line,$subroutine)=caller 0;
110                 warn "by line $line";
111                 }
112         return $SObject;
113 }
114
115 sub delete_CObject($)
116 {
117 my($CObject)=@_;
118
119         my $SharedCacheMap=$CObject->{"SharedCacheMap"};
120         do { warn "Trailing map $_ of SharedCacheMap $SharedCacheMap during its deletion" if $_; } for ($CObject->{"map"});
121         do { warn "Trailing pin $_ of SharedCacheMap $SharedCacheMap during its deletion" if $_; } for (values(%{$CObject->{"pin"}}));
122         delete $SharedCacheMap{$SharedCacheMap};
123 }
124
125 sub CObject($)
126 {
127 my($SharedCacheMap)=@_;
128
129         my $CObject=$SharedCacheMap{$SharedCacheMap};
130         if (!$CObject) {
131                 my($package,$filename,$line,$subroutine)=caller 0;
132                 warn "Non-existent SharedCacheMap $SharedCacheMap by line $line";
133                 }
134         return $CObject;
135 }
136
137 sub CObject_from_FileObject($)
138 {
139 my($FileObject)=@_;
140
141         return if !(my $SObject=SObject_from_FileObject $FileObject);
142         return if !(my $CObject=CObject $SObject->{"SharedCacheMap"});
143         return $CObject;
144 }
145
146 sub SharedCacheMap_valid($)
147 {
148 my($SharedCacheMap)=@_;
149
150         cluck if !defined $SharedCacheMap;
151         return 0 if "0x".("F"x8) eq $SharedCacheMap;
152         return 0 if !eval $SharedCacheMap;
153         return 1;
154 }
155
156 sub check_data($)
157 {
158 my($data)=@_;
159
160         if (!eval $data->{"SectionObjectPointer"}) {
161                 return if $Object->{"by"} eq "IRP_MJ_CREATE";   # SectionObjectPointer is not yet initialized
162                 warn "Existing FileObject ".$data->{"FileObject"}." but no SectionObjectPointer found"
163                                 if $FileObject{$data->{"FileObject"}} && eval($FileObject{$data->{"FileObject"}}{"SectionObjectPointer"});
164                 return;
165                 }
166         my $SectionObjectPointer=$data->{"SectionObjectPointer"};
167         if (!SharedCacheMap_valid $data->{"SharedCacheMap"} && $SectionObjectPointer{$SectionObjectPointer}) {
168                 return if !(my $SObject=SObject $SectionObjectPointer);
169                 my $SharedCacheMap=$SObject->{"SharedCacheMap"};
170                 return if !eval $SharedCacheMap;
171                 my $CObject=CObject $SharedCacheMap;
172                 warn "Existing SectionObjectPointer ".$data->{"SectionObjectPointer"}." but no SharedCacheMap found,"
173                                                 ." ref_count of SharedCacheMap is ".$CObject->{"ref_count"}
174                                 if $CObject->{"ref_count"};
175 #                               if $SectionObjectPointer{$data->{"SectionObjectPointer"}};
176                 # SharedCacheMap was droppped by async task as it had ref_count==0.
177                 delete_CObject $CObject;
178                 $SObject->{"SharedCacheMap"}=tohex(0);
179                 # FileObject is still valid!
180                 return;
181                 }
182         return if !$FileObject{$data->{"FileObject"}};
183         return if !(my $FObject=FObject $data->{"FileObject"});
184         return if !(my $SObject=SObject $FObject->{"SectionObjectPointer"});
185         my $SharedCacheMap=$SObject->{"SharedCacheMap"};
186         warn "FileObject ".$FObject->{"FileObject"}." SectionObjectPointer ".$SObject->{"SectionObjectPointer"}
187                                         ." expected SharedCacheMap $SharedCacheMap"
188                                         ." but found SharedCacheMap ".$data->{"SharedCacheMap"}
189                         if $SharedCacheMap ne $data->{"SharedCacheMap"};
190         warn "INTERNAL: SharedCacheMap $SharedCacheMap of FileObject ".$FObject->{"FileObject"}." got destroyed"
191                         if !$SharedCacheMap{$SharedCacheMap};
192 }
193
194 sub CcInitializeCacheMap($$$$$)
195 {
196 my($FileObject,$AllocationSize,$FileSize,$ValidDataLength,$PinAccess)=@_;
197
198         $ValidDataLength=$FileSize if $ValidDataLength==eval("0x".("F"x8));
199         $Object->{"ref_count"}=1;
200         $Object->{"AllocationSize"}=tohex($AllocationSize);
201         $Object->{"FileSize"}=tohex($FileSize);
202         $Object->{"ValidDataLength"}=tohex($ValidDataLength);
203         $Object->{"map"}=undef();
204         $Object->{"pin"}={};
205         $Object->{"PinAccess"}=$PinAccess;
206         $Object->{"FileObject"}=$FileObject;
207 }
208
209 sub CcInitializeCacheMap_leave()
210 {
211         my $SharedCacheMap=$Object->{"data"}[1]{"SharedCacheMap"};
212         $Object->{"SharedCacheMap"}=$SharedCacheMap;
213         my $old=$SharedCacheMap{$SharedCacheMap};
214         if (!SharedCacheMap_valid $Object->{"data"}[0]{"SharedCacheMap"} && $old) {
215                 # SharedCacheMap got deleted in the meantime
216                 delete_CObject CObject $SharedCacheMap;
217                 my $SObject=SObject $Object->{"data"}[0]{"SectionObjectPointer"};
218                 $SObject->{"SharedCacheMap"}=tohex(0);
219                 $old=undef();
220                 }
221         if (!$old != !SharedCacheMap_valid $Object->{"data"}[0]{"SharedCacheMap"}) {
222                 warn "Expecting old SharedCacheMap validity ".(!!$old)
223                                 ." but found old SharedCacheMap ".$Object->{"data"}[0]{"SharedCacheMap"};
224                 }
225         warn "New SharedCacheMap ".$Object->{"data"}[1]{"SharedCacheMap"}." is not valid"
226                         if !SharedCacheMap_valid $Object->{"data"}[1]{"SharedCacheMap"};
227         if (SharedCacheMap_valid $Object->{"data"}[0]{"SharedCacheMap"}) {
228                 warn "Existing SharedCacheMap changed"
229                                                 ." from ".$Object->{"data"}[0]{"SharedCacheMap"}." to ".$Object->{"data"}[1]{"SharedCacheMap"}
230                                 if $Object->{"data"}[0]{"SharedCacheMap"} ne $Object->{"data"}[1]{"SharedCacheMap"};
231                 }
232         if ($old) {
233                 for my $field (qw(AllocationSize FileSize PinAccess)) {
234                         warn "SharedCacheMap $SharedCacheMap old instance $field ".$old->{$field}
235                                                         ." != new instance $field ".$Object->{$field}
236                                         if $old->{$field} ne $Object->{$field};
237                         }
238                 do { warn "Existing map Bcb $_ during CcInitializeCacheMap()" if $_; } for ($old->{"map"});
239                 do { warn "Existing pin Bcb $_ during CcInitializeCacheMap()" if $_; } for (values(%{$old->{"pin"}}));
240                 $Object->{"ref_count"}+=$old->{"ref_count"};
241                 }
242         $SharedCacheMap{$SharedCacheMap}=$Object;
243
244         warn "Changed SectionObjectPointer inside CcInitializeCacheMap()"
245                                         ." from ".$Object->{"data"}[0]{"SectionObjectPointer"}." to ".$Object->{"data"}[1]{"SectionObjectPointer"}
246                         if $Object->{"data"}[0]{"SectionObjectPointer"} ne $Object->{"data"}[1]{"SectionObjectPointer"};
247         my $SectionObjectPointer=$Object->{"data"}[1]{"SectionObjectPointer"};
248
249         my $FileObject=$Object->{"FileObject"};
250         if (my $FObject=$FileObject{$FileObject}) {
251                 if (my $SObject=$SectionObjectPointer{$FObject->{"SectionObjectPointer"}}) {
252                         warn "Changed SectionObjectPointer of FileObject $FileObject"
253                                                         ." from ".$FObject->{"SectionObjectPointer"}." to ".$SectionObjectPointer
254                                         if $FObject->{"SectionObjectPointer"} ne $SectionObjectPointer;
255                         }
256                 # Otherwise SectionObjectPointer could be deleted and rebuilt async in the meantime.
257                 }
258         $FileObject{$FileObject}={
259                         "FileObject"=>$FileObject,
260                         "SectionObjectPointer"=>$SectionObjectPointer,
261                         };
262
263         if (my $SObject=$SectionObjectPointer{$SectionObjectPointer}) {
264                 warn "Changed SharedCacheMap of SectionObjectPointer $SectionObjectPointer"
265                                                 ." from ".$SObject->{"SharedCacheMap"}." to ".$SharedCacheMap
266                                 if $SObject->{"SharedCacheMap"} ne $SharedCacheMap && eval($SObject->{"SharedCacheMap"});
267                 }
268         $SectionObjectPointer{$SectionObjectPointer}={
269                         "SectionObjectPointer"=>$SectionObjectPointer,
270                         "SharedCacheMap"=>$SharedCacheMap,
271                         };
272
273         CcSetFileSizes($FileObject,map({ eval($Object->{$_}); } qw(AllocationSize FileSize ValidDataLength)));
274         delete $Object->{$_} for (qw(FileObject ValidDataLength));
275 }
276
277 sub CcUninitializeCacheMap($$)
278 {
279 my($FileObject,$TruncateSize)=@_;
280
281         $Object->{"FileObject"}=$FileObject;
282 }
283
284 sub CcUninitializeCacheMap_leave($)
285 {
286 my($r)=@_;
287
288         my $FileObject=$Object->{"FileObject"};
289         # 'r' means function success.
290         # r=0 either if no CcInitializeCacheMap() was called at all
291         # or if Cc was unable to detach SharedCacheMap and it remains valid
292         # (FIXME: Do we SharedCacheMap->ref_count-- on in such case?).
293         my $SectionObjectPointer=$FileObject{$FileObject}->{"SectionObjectPointer"} if $FileObject{$FileObject};
294         my $SharedCacheMap=$SectionObjectPointer{$SectionObjectPointer}->{"SharedCacheMap"}
295                         if $SectionObjectPointer && $SectionObjectPointer{$SectionObjectPointer};
296         warn "Unexpected 'r' result $r for CcUninitializeCacheMap of FileObject $FileObject"
297                         if !(eval($SharedCacheMap) && !SharedCacheMap_valid($Object->{"data"}[1]{"SharedCacheMap"})) != !$r;
298         if (!eval $SharedCacheMap) {
299                 for my $SharedCacheMap ($Object->{"data"}[0]{"SharedCacheMap"},$Object->{"data"}[1]{"SharedCacheMap"}) {
300                         warn "Not expecting valid SharedCacheMap $SharedCacheMap"
301                                         if SharedCacheMap_valid $SharedCacheMap;
302                         }
303                 return;
304                 }
305         for my $SharedCacheMap ($Object->{"data"}[0]{"SharedCacheMap"}) {
306                 warn "Expecting valid SharedCacheMap $SharedCacheMap"
307                                 if !SharedCacheMap_valid $SharedCacheMap;
308                 }
309         return if !(my $FObject=FObject $FileObject);
310         return if !(my $SObject=SObject $FObject->{"SectionObjectPointer"});
311         return if !(my $CObject=CObject $SObject->{"SharedCacheMap"});
312         if (--$CObject->{"ref_count"}) {
313                 for my $SharedCacheMap ($Object->{"data"}[1]{"SharedCacheMap"}) {
314                         warn "Expecting still valid SharedCacheMap $SharedCacheMap after CcUninitializeCacheMap()"
315                                                         ." with ref_count=".$CObject->{"ref_count"}
316                                         if !SharedCacheMap_valid $SharedCacheMap;
317                         }
318                 return;
319                 }
320         if (!SharedCacheMap_valid $Object->{"data"}[1]{"SharedCacheMap"}) {
321                 delete_CObject $CObject;
322                 $SObject->{"SharedCacheMap"}=tohex(0);
323                 # FileObject is still valid!
324                 }
325         else {
326                 # FIXME: Do we SharedCacheMap->ref_count-- on in such case?
327                 }
328 }
329
330 sub CcSetFileSizes($$$$)
331 {
332 my($FileObject,$AllocationSize,$FileSize,$ValidDataLength)=@_;
333
334         return if !(my $CObject=CObject_from_FileObject $FileObject);
335         my $SharedCacheMap=$CObject->{"SharedCacheMap"};
336         if ($AllocationSize!=eval($CObject->{"AllocationSize"})) {
337                 do { warn "Existing map $_ of FileObject $FileObject SharedCacheMap $SharedCacheMap during CcSetAllocationSizes(),"
338                                                 ." AllocationSize=".$CObject->{"AllocationSize"} if $_; }
339                                 for ($CObject->{"map"});
340                 do { warn "Existing pin $_ of FileObject $FileObject SharedCacheMap $SharedCacheMap during CcSetAllocationSizes(),"
341                                                 ." AllocationSize=".$CObject->{"AllocationSize"} if $_; }
342                                 for (values(%{$CObject->{"pin"}}));
343                 }
344         # $ValidDataLength can be > $CObject->{"FileSize"};
345         warn "ValidDataLength ".tohex($ValidDataLength)." > FileSize ".tohex($FileSize)
346                         if $ValidDataLength>$FileSize;
347         warn "0 != AllocationSize ".tohex($AllocationSize)." % ntfs_blocksize ".tohex($ntfs_blocksize)
348                         if 0!=($AllocationSize%$ntfs_blocksize);
349         # $AllocationSize can be higher
350         warn "FileSize ".tohex($FileSize)." > AllocationSize ".tohex($AllocationSize)
351                         if $FileSize>$AllocationSize;
352         $CObject->{"FileSize"}=tohex($FileSize);
353         $CObject->{"AllocationSize"}=tohex($AllocationSize);
354 }
355
356 sub IRP_MJ_CREATE_leave()
357 {
358         do { warn "Non-NULL SectionObjectPointer $_ not expected" if eval($_); } for ($Object->{"data"}[0]{"SectionObjectPointer"});
359         my $FileObject=$Object->{"data"}[0]{"FileObject"};
360         warn "Existing FileObject $FileObject not expected" if $FileObject{$FileObject};
361         my $SectionObjectPointer=$Object->{"data"}[1]{"SectionObjectPointer"};
362         # We want to track even FileObject without SectionObjectPointer yet.
363 #       if ($SectionObjectPointer && $SectionObjectPointer{$SectionObjectPointer})
364         {
365                 $FileObject{$FileObject}={
366                                 "FileObject"=>$FileObject,
367                                 "SectionObjectPointer"=>$SectionObjectPointer,
368                                 };
369                 }
370         if (eval $SectionObjectPointer) {
371                 my $SharedCacheMap=$Object->{"data"}[1]{"SharedCacheMap"};
372                 if (my $SObject=$SectionObjectPointer{$SectionObjectPointer}) {
373                         warn "Changed SharedCacheMap from stored ".$SObject->{"SharedCacheMap"}." to ".$SharedCacheMap
374                                         if $SObject->{"SharedCacheMap"} ne $SharedCacheMap && $Object->{"by"} ne "IRP_MJ_CREATE";
375                         }
376                 $SectionObjectPointer{$SectionObjectPointer}={
377                                 "SectionObjectPointer"=>$SectionObjectPointer,
378                                 "SharedCacheMap"=>$SharedCacheMap,
379                                 };
380                 }
381 }
382
383 sub BObject($)
384 {
385 my($Bcb)=@_;
386
387         my $BObject=$Bcb{$Bcb};
388         warn "Non-existent Bcb $Bcb" if !$BObject;
389         return $BObject;
390 }
391
392 sub Bcb_conflict($;@)
393 {
394 my($CObject,@Bcb_list)=@_;
395
396         my $arg=0;
397         my %check=(
398                 "map"=>$CObject->{"map"},
399                 map(("arg".($arg++)=>$_),@Bcb_list),
400                 %{$CObject->{"pin"}},
401                 );
402         my %reversed;
403         my $BufferBase; # relativized to FileOffset 0
404         my $BufferBase_val;
405         while (my($key,$val)=each(%check)) {
406                 next if !defined $val;
407                 warn "Conflicting Bcb $val of keys $key and ".$reversed{$val}." of SharedCacheMap ".$CObject->{"SharedCacheMap"}
408                                 if $reversed{$val};
409                 # Buffer base should match even between 'map's and 'pin's
410                 # as the data are always mapped only once.
411                 if (my $BObject=BObject $val) {
412                         my $Buffer=eval $BObject->{"Buffer"};
413                         $Buffer-=eval($BObject->{"FileOffset"}) if exists $BObject->{"FileOffset"};
414                         warn "Non-matching Bcb ".$BObject->{"type"}." $val Buffer base ".tohex($Buffer)
415                                                         ." with Bcb ".$Bcb{$BufferBase_val}->{"type"}." $BufferBase_val BufferBase ".tohex($BufferBase)
416                                         if defined($BufferBase) && $Buffer!=$BufferBase;
417                         $BufferBase=$Buffer;
418                         $BufferBase_val=$val;
419                         }
420                 $reversed{$val}=$key;
421                 }
422 }
423
424 # New $BObject will always be forced as the last stored reference.
425 sub Bcb_checkref($$)
426 {
427 my($BObject,$ref)=@_;
428
429         return if !(my $CObject=CObject $BObject->{"SharedCacheMap"});
430         my $type=$BObject->{"type"};
431         my $Bcb=$BObject->{"Bcb"};
432         if ($$ref) {
433                 my $BObject2=$Bcb{$$ref};
434                 warn "new $type Bcb $Bcb != old $type Bcb $$ref"
435                                 if $Bcb ne $$ref;
436                 warn "new $type $Bcb type ".$BObject->{"type"}." != old $type $$ref type ".$BObject2->{"type"}
437                                 if $BObject->{"type"} ne $BObject2->{"type"};
438                 warn "new $type $Bcb Buffer ".$BObject->{"Buffer"}." != old $type $$ref Buffer ".$BObject2->{"Buffer"}
439                                 if $BObject->{"Buffer"} ne $BObject2->{"Buffer"};
440                 }
441         if ($$ref && $$ref eq $Bcb) {
442                 $BObject->{"ref_count"}+=$Bcb{$$ref}->{"ref_count"};
443                 $$ref=undef();
444                 }
445         $Bcb{$Bcb}=$BObject;    # &Bcb_conflict needs this reference
446         Bcb_conflict $CObject,$Bcb;
447         $$ref=$Bcb;
448 }
449
450 sub map_new($;$$)
451 {
452 my($SharedCacheMap,$FileOffset,$Length)=@_;
453
454         return if !(my $CObject=CObject $SharedCacheMap);
455         if (defined($FileOffset) && defined($Length)) {
456                 warn "Mapping data (end ".tohex($FileOffset+$Length).") out of FileSize ".$CObject->{"FileSize"}
457                                 if $FileOffset+$Length>eval($CObject->{"FileSize"});
458                 }
459         $Object->{"SharedCacheMap"}=$CObject->{"SharedCacheMap"};
460         if (defined $FileOffset) {
461                 $Object->{"FileOffset"}=tohex($FileOffset);
462                 }
463         $Object->{"type"}="map";
464         $Object->{"ref_count"}=1;
465 }
466
467 sub map_new_from_FileObject($;$$)
468 {
469 my($FileObject,$FileOffset,$Length)=@_;
470
471         return if !(my $CObject=CObject_from_FileObject $FileObject);
472         map_new $CObject->{"SharedCacheMap"},$FileOffset,$Length;
473 }
474
475 sub map_new_leave($;$)
476 {
477 my($Bcb,$Buffer)=@_;
478
479         $Object->{"Bcb"}=$Bcb;
480         return if !(my $CObject=CObject $Object->{"SharedCacheMap"});
481
482         if (defined $Buffer) {
483                 $Object->{"Buffer"}=tohex(eval($Buffer)-(eval($Object->{"FileOffset"}) || 0));
484                 }
485         delete $Object->{"FileOffset"};
486
487         my $ref=\$CObject->{"map"};
488         Bcb_checkref $Object,$ref;
489 }
490
491 sub CcMapData($$$)
492 {
493 my($FileObject,$FileOffset,$Length)=@_;
494
495         map_new_from_FileObject $FileObject,$FileOffset,$Length;
496 }
497
498 sub CcMapData_leave($$)
499 {
500 my($Bcb,$Buffer)=@_;
501
502         map_new_leave $Bcb,$Buffer;
503 }
504
505 sub pin_new($$$)
506 {
507 my($FileObject,$FileOffset,$Length)=@_;
508
509         return if !(my $CObject=CObject_from_FileObject $FileObject);
510         warn "Pinning of non-PinAccess FileObject $FileObject" if !$CObject->{"PinAccess"};
511         warn "Mapping data (end ".tohex($FileOffset+$Length).") out of FileSize ".$CObject->{"FileSize"}
512                         if $FileOffset+$Length>eval($CObject->{"FileSize"});
513         warn "Pinning Length ".tohex($Length)." > 0x1000" if $Length>0x1000;
514         warn "Pinning across file page (start=".tohex($FileOffset).",end-1=".tohex($FileOffset+$Length-1).")"
515                         if ($FileOffset&~0xFFF)!=(($FileOffset+$Length-1)&~0xFFF);
516         $Object->{"SharedCacheMap"}=$CObject->{"SharedCacheMap"};
517         $Object->{"FileOffset"}=tohex($FileOffset);
518         $Object->{"type"}="pin";
519         $Object->{"ref_count"}=1;
520 }
521
522 sub pin_new_leave($$)
523 {
524 my($Bcb,$Buffer)=@_;
525
526         $Object->{"Bcb"}=$Bcb;
527         return if !(my $CObject=CObject $Object->{"SharedCacheMap"});
528         $Object->{"Buffer"}=tohex(eval($Buffer)-(eval($Object->{"FileOffset"})&0xFFF));
529         my $shift=eval($Object->{"FileOffset"})&0xFFF;
530         $Object->{"FileOffset"}=tohex(eval($Object->{"FileOffset"})-$shift);
531         $Object->{"Buffer"}=tohex(eval($Buffer)-$shift);
532
533         my $ref=\$CObject->{"pin"}{$Object->{"FileOffset"}};
534         Bcb_checkref $Object,$ref;
535 }
536
537 sub CcPinRead($$$)
538 {
539 my($FileObject,$FileOffset,$Length)=@_;
540
541         pin_new $FileObject,$FileOffset,$Length;
542 }
543
544 sub CcPinRead_leave($$)
545 {
546 my($Bcb,$Buffer)=@_;
547
548         pin_new_leave $Bcb,$Buffer;
549 }
550
551 sub CcPreparePinWrite($$$)
552 {
553 my($FileObject,$FileOffset,$Length)=@_;
554
555         pin_new $FileObject,$FileOffset,$Length;
556 }
557
558 sub CcPreparePinWrite_leave($$)
559 {
560 my($Bcb,$Buffer)=@_;
561
562         pin_new_leave $Bcb,$Buffer;
563 }
564
565 sub CcPinMappedData($$$)
566 {
567 my($FileObject,$FileOffset,$Length)=@_;
568
569         pin_new $FileObject,$FileOffset,$Length;
570 }
571
572 sub CcPinMappedData_leave($)
573 {
574 my($Bcb)=@_;
575
576         return if !(my $CObject=CObject $Object->{"SharedCacheMap"});
577         do { warn "CcPinMappedData() with Bcb $Bcb on non-CcMapData()ed SharedCacheMap ".$CObject->{"SharedCacheMap"}; return; }
578                         if !(my $mapBcb=$CObject->{"map"});
579         return if !(my $BmapObject=BObject $mapBcb);
580         my $Buffer=tohex(eval($BmapObject->{"Buffer"})+eval($Object->{"FileOffset"}));
581
582         my $Bcb2=$CObject->{"pin"}{tohex(eval($Object->{"FileOffset"})&~0xFFF)};
583         my $BObject2=BObject $Bcb2 if $Bcb2;
584         if ($BObject2 && $BObject2->{"CcPinMappedData_double"}
585                         && eval($BObject2->{"CcPinMappedData_double"})==eval($Object->{"FileOffset"})) {        # unaligned yet
586                 my $BmapBuffer=eval($BmapObject->{"Buffer"})+(eval($Object->{"FileOffset"})&~0xFFF);
587                 warn "CcPinMappedData-double cludge non-matching new Bcb $Bcb != old Bcb ".$BObject2->{"Bcb"}
588                                 if $Bcb ne $BObject2->{"Bcb"};
589                 warn "CcPinMappedData-double cludge non-matching Buffer new Bcb $Bcb Buffer $BmapBuffer"
590                                                 ." != old Bcb ".$BObject2->{"Bcb"}." Buffer ".$BObject2->{"Buffer"}
591                                 if eval($BmapBuffer)!=eval($BObject2->{"Buffer"});
592                 return;
593                 }
594
595         # It appears as this cludge is not needed:
596 #       $Object->{"CcPinMappedData_double"}=$Object->{"FileOffset"};    # unaligned yet
597
598         pin_new_leave $Bcb,$Buffer;
599 #       print STDERR "$.:".Dumper($Object);
600 }
601
602 sub CcSetDirtyPinnedData($$)
603 {
604 my($Bcb,$Lsn)=@_;
605
606         return if !(my $BObject=BObject $Bcb);
607         delete $BObject->{"CcPinMappedData_double"};
608 }
609
610 sub CcRemapBcb($)
611 {
612 my($Bcb)=@_;
613
614         return if !(my $BObject=BObject $Bcb);
615         map_new $BObject->{"SharedCacheMap"};
616         $Object->{"Buffer"}=tohex(eval($BObject->{"Buffer"})-eval($BObject->{"FileOffset"} || 0));
617 }
618
619 sub CcRemapBcb_leave($)
620 {
621 my($r)=@_;
622
623         map_new_leave $r;
624 }
625
626 sub unpin($)
627 {
628 my($Bcb)=@_;
629
630         return if !(my $BObject=BObject $Bcb);
631         delete $BObject->{"CcPinMappedData_double"};
632         return if --$BObject->{"ref_count"};
633         return if !(my $CObject=CObject $BObject->{"SharedCacheMap"});
634         if ($BObject->{"type"} eq "map") {
635                 for my $pin (values(%{$CObject->{"pin"}})) {
636                         warn "unpin map but CcPinMappedData pin $pin still exists"
637                                         if $Bcb{$pin}->{"by"} eq "CcPinMappedData";
638                         }
639                 }
640         for my $ref ($BObject->{"type"} eq "map" ? \$CObject->{"map"} : \$CObject->{"pin"}{$BObject->{"FileOffset"}}) {
641                 warn "Final unpin but ".$BObject->{"type"}." Bcb $Bcb not registered"
642                                                 ." in SharedCacheMap ".$CObject->{"SharedCacheMap"}." ref ".($$ref || "<undef>")
643                                 if !defined($BObject->{"OwnerPointer"}) && !($$ref && $$ref eq $Bcb);
644                 if ($$ref && $$ref eq $Bcb) {
645                         $$ref=undef();
646                         delete $CObject->{"pin"}{$BObject->{"FileOffset"}} if $BObject->{"type"} eq "pin";
647                         }
648                 }
649         delete $Bcb{$Bcb};
650 }
651
652 sub CcUnpinData($)
653 {
654 my($Bcb)=@_;
655
656         unpin $Bcb;
657 }
658
659 sub CcUnpinDataForThread($)
660 {
661 my($Bcb)=@_;
662
663         unpin $Bcb;
664 }
665
666 sub CcSetBcbOwnerPointer($$)
667 {
668 my($Bcb,$OwnerPointer)=@_;
669
670         return if !(my $BObject=BObject $Bcb);
671         warn "CcSetBcbOwnerPointer() on map Bcb $Bcb" if $BObject->{"type"} ne "pin";
672         return if !(my $CObject=CObject $BObject->{"SharedCacheMap"});
673         warn "Double CcSetBcbOwnerPointer() on Bcb $Bcb" if defined $BObject->{"OwnerPointer"};
674         my $val=$CObject->{"pin"}{$BObject->{"FileOffset"}};
675         warn "CcSetBcbOwnerPointer() on unregistered pin Bcb $Bcb" if !$val || $val ne $Bcb;
676         delete $CObject->{"pin"}{$BObject->{"FileOffset"}} if $val && $val eq $Bcb;
677         $BObject->{"OwnerPointer"}=$OwnerPointer;
678 }
679
680 sub IRP_MJ_CLOSE_leave()
681 {
682         my $FileObject=$Object->{"data"}[0]{"FileObject"};
683 #       # IRP_MJ_CLOSE of FileObject w/o CcInitializeCacheMap()?
684 #       return if !$FileObject{$FileObject};
685         return if !(my $FObject=FObject $FileObject);
686         if (eval(my $SectionObjectPointer=$FObject->{"SectionObjectPointer"})) {
687                 return if !(my $SObject=SObject $SectionObjectPointer);
688                 my $SharedCacheMap=$SObject->{"SharedCacheMap"};
689                 if (eval $SharedCacheMap) {
690                         return if !(my $CObject=CObject $SObject->{"SharedCacheMap"});
691                         # SharedCacheMap may still exist for FCB although this FileObject gets destroyed now.
692 #                       warn "SectionObjectPointer $SectionObjectPointer still exists during IRP_MJ_CLOSE"
693 #                                                       ." while SharedCacheMap ".$CObject->{"SharedCacheMap"}." ref_count ".$CObject->{"ref_count"}
694 #                                       if $SectionObjectPointer && $CObject->{"ref_count"};
695                         }
696                 }
697         delete $FileObject{$FileObject};
698 }
699
700
701 local $_;
702 my $hex='0x[\dA-F]+';
703 my %last_irp_mj;
704 my %enter_leave;
705 while (<>) {
706         chomp;
707         s/\r$//;
708         # We may get some foreign garbage without '\n' before our debug data line:
709         # Do not use '\bTraceFS' as there really can be precediny _any_ data.
710         s#^.*?TraceFS[(]($hex/$hex)[)]: ## or do { print "$_\n" if $filter; next; };
711         my($process_thread)=($1);
712
713         $Object=undef();
714         if (/^enter: (\w+)/) {
715                 $Object={};
716                 $Object->{"by"}=$1;
717                 $Object->{"line_enter"}=$.;
718                 $Object->{"process_thread"}=$process_thread;
719                 push @{$enter_leave{$process_thread}},$Object;
720                 }
721         elsif (/^leave: (\w+)/) {
722                 warn "Empty pop stack during 'leave' of $1" if !($Object=pop @{$enter_leave{$process_thread}});
723                 warn "Non-matching popped 'by' ".$Object->{"by"}." ne current 'leave' $1"
724                                 if $Object->{"by"} ne $1;
725                 $Object->{"line_leave"}=$.;
726                 }
727         elsif (my($FileObject,$FileName,$Flags,$SectionObjectPointer,$SharedCacheMap)=
728                         /^FileObject=($hex): FileName=(?:NULL|'(.*)'),Flags=($hex),SectionObjectPointer=($hex),->SharedCacheMap=($hex)/) {
729                 my $aref=$enter_leave{$process_thread};
730                 warn "Empty stack during 'data' line" if !($Object=${$aref}[$#$aref]);
731                 my $data={
732                                 "FileObject"=>$FileObject,
733                                 "FileName"=>$FileName,
734                                 "Flags"=>$Flags,
735                                 "SectionObjectPointer"=>$SectionObjectPointer,
736                                 "SharedCacheMap"=>$SharedCacheMap,
737                                 "line"=>$.,
738                                 };
739                 push @{$Object->{"data"}},$data;
740                 my $isinit={ map(($_=>1),qw(
741                                 CcInitializeCacheMap
742                                 CcUninitializeCacheMap
743                                 IRP_MJ_CREATE
744                                 )) }->{$Object->{"by"}};
745                 check_data $data
746                                 if 1==@{$Object->{"data"}} || !$isinit;
747                 if ($isinit) {
748                         # Prevent 'SharedCacheMap' 0->N change by CcInitializeCacheMap() called inside.
749                         for my $ref (@$aref[0..$#$aref-1]) {
750                                 $ref->{"data"}[0]->{"SharedCacheMap"}=$SharedCacheMap;
751                                 }
752                         }
753                 if (2<=@{$Object->{"data"}}) {
754                         my $data_prev=$Object->{"data"}[$#{$Object->{"data"}}-1];
755                         for my $field (qw(FileObject FileName Flags),($isinit ? () : qw(SharedCacheMap))) {
756                                 next if !defined(my $prev=$data_prev->{$field});
757                                 next if !defined(my $now=$data->{$field});
758                                 my $by=$Object->{"by"};
759                                 if ($field eq "Flags") {
760                                         next if $by eq "IRP_MJ_CREATE" && $field eq "Flags";
761                                         my $FO_CLEANUP_COMPLETE=0x4000;
762                                         $now=tohex(eval($now)&~$FO_CLEANUP_COMPLETE) if $by eq "IRP_MJ_CLEANUP";
763                                         my $FO_FILE_FAST_IO_READ=0x80000;
764                                         $prev=tohex(eval($prev)&~$FO_FILE_FAST_IO_READ) if $by eq "IRP_MJ_CLEANUP";
765                                         $now=tohex(eval($now)&~$FO_FILE_FAST_IO_READ) if $by eq "IRP_MJ_READ" && !(eval($prev)&$FO_FILE_FAST_IO_READ);
766                                         my $FO_FILE_MODIFIED=0x1000;
767                                         $now=tohex(eval($now)&~$FO_FILE_MODIFIED) if $by eq "IRP_MJ_WRITE" && !(eval($prev)&$FO_FILE_MODIFIED);
768                                         my $FO_FILE_SIZE_CHANGED=0x2000;
769                                         $prev=tohex(eval($prev)&~$FO_FILE_MODIFIED)
770                                                         if $by eq "IRP_MJ_SET_INFORMATION" && !(eval($now)&$FO_FILE_MODIFIED);
771                                         $prev=tohex(eval($prev)&~$FO_FILE_SIZE_CHANGED)
772                                                         if $by eq "IRP_MJ_SET_INFORMATION" && !(eval($now)&$FO_FILE_SIZE_CHANGED);
773                                         }
774                                 next if $by eq "IRP_MJ_CLOSE" && $field eq "FileName";
775                                 $prev=~s#\\$## if $by eq "IRP_MJ_CREATE";
776                                 $prev="\\" if $by eq "IRP_MJ_CREATE" && $prev eq "";
777                                 $prev=~s#:.*## if $by eq "IRP_MJ_CREATE" && $prev ne $now;
778                                 next if $field eq "SharedCacheMap" && !SharedCacheMap_valid $prev && !SharedCacheMap_valid $now;
779                                 do { warn "Changed data field $field, prev=".$data_prev->{$field}.", now=".$data->{$field}." by $by";
780 #                                               print STDERR Dumper $data_prev,$data;
781                                                 } if $prev ne $now;
782                                 }
783                         }
784                 next;
785                 }
786
787         if (my($r)=
788                         /^leave: IRP_MJ_\w+: r=($hex)/) {
789                 # Failed requests should make no consequences.
790                 next if eval($r);
791                 }
792
793         if (my($FileObject,$AllocationSize,$FileSize,$ValidDataLength,$PinAccess)=
794                         /^enter: CcInitializeCacheMap: FileObject=($hex),FileSizes,->AllocationSize=($hex),->FileSize=($hex),->ValidDataLength=($hex),PinAccess=([01]),/) {
795                 CcInitializeCacheMap $FileObject,eval($AllocationSize),eval($FileSize),eval($ValidDataLength),eval($PinAccess);
796                 next;
797                 }
798         if (/^leave: CcInitializeCacheMap\b/) {
799                 CcInitializeCacheMap_leave;
800                 next;
801                 }
802
803         if (my($FileObject,$TruncateSize)=
804                         /^enter: CcUninitializeCacheMap: FileObject=($hex),TruncateSize=($hex),/) {
805                 CcUninitializeCacheMap $FileObject,eval($TruncateSize);
806                 next;
807                 }
808         if (my($r)=
809                         /^leave: CcUninitializeCacheMap: r=([01])/) {
810                 CcUninitializeCacheMap_leave $r;
811                 next;
812                 }
813
814         if (my($FileObject,$AllocationSize,$FileSize,$ValidDataLength)=
815                         /^enter: CcSetFileSizes: FileObject=($hex),FileSizes,->AllocationSize=($hex),->FileSize=($hex),->ValidDataLength=($hex)/) {
816                 CcSetFileSizes $FileObject,eval($AllocationSize),eval($FileSize),eval($ValidDataLength);
817                 next;
818                 }
819
820         if (/^leave: IRP_MJ_CREATE\b/) {
821                 IRP_MJ_CREATE_leave;
822                 next;
823                 }
824
825         if (/^leave: IRP_MJ_CLOSE\b/) {
826                 IRP_MJ_CLOSE_leave;
827                 next;
828                 }
829
830         if (my($FileObject,$FileOffset,$Length)=
831                         /^enter: CcMapData: FileObject=($hex),FileOffset=($hex),Length=($hex),Flags=0x1/) {
832                 CcMapData $FileObject,eval($FileOffset),eval($Length);
833                 next;
834                 }
835         if (my($Bcb,$Buffer)=
836                         /^leave: CcMapData: r=1,Bcb=($hex),Buffer=($hex)/) {
837                 CcMapData_leave $Bcb,$Buffer;
838                 next;
839                 }
840
841         if (my($FileObject,$FileOffset,$Length)=
842                         /^enter: CcPinRead: FileObject=($hex),FileOffset=($hex),Length=($hex),Flags=0x1/) {
843                 CcPinRead $FileObject,eval($FileOffset),eval($Length);
844                 next;
845                 }
846         if (my($Bcb,$Buffer)=
847                         /^leave: CcPinRead: r=1,Bcb=($hex),Buffer=($hex)/) {
848                 CcPinRead_leave $Bcb,$Buffer;
849                 next;
850                 }
851
852         if (my($FileObject,$FileOffset,$Length)=
853                         /^enter: CcPreparePinWrite: FileObject=($hex),FileOffset=($hex),Length=($hex),Zero=([01]),Flags=0x1/) {
854                 CcPreparePinWrite $FileObject,eval($FileOffset),eval($Length);
855                 next;
856                 }
857         if (my($Bcb,$Buffer)=
858                         /^leave: CcPreparePinWrite: r=1,Bcb=($hex),Buffer=($hex)/) {
859                 CcPreparePinWrite_leave $Bcb,$Buffer;
860                 next;
861                 }
862
863         if (my($FileObject,$FileOffset,$Length)=
864                         /^enter: CcPinMappedData: FileObject=($hex),FileOffset=($hex),Length=($hex),Flags=0x1/) {
865                 CcPinMappedData $FileObject,eval($FileOffset),eval($Length);
866                 next;
867                 }
868         if (my($Bcb)=
869                         /^leave: CcPinMappedData: r=1,Bcb=($hex)/) {
870                 CcPinMappedData_leave $Bcb;
871                 next;
872                 }
873
874         if (my($BcbVoid,$Lsn)=
875                         /^enter: CcSetDirtyPinnedData: BcbVoid=($hex),Lsn=($hex)/) {
876                 CcSetDirtyPinnedData $BcbVoid,$Lsn;
877                 next;
878                 }
879
880         if (my($Bcb)=
881                         /^enter: CcRemapBcb: Bcb=($hex)/) {
882                 CcRemapBcb $Bcb;
883                 next;
884                 }
885         if (my($r)=
886                         /^leave: CcRemapBcb: r=($hex)/) {
887                 CcRemapBcb_leave $r;
888                 next;
889                 }
890
891         if (my($Bcb)=
892                         /^enter: CcUnpinData: Bcb=($hex)/) {
893                 CcUnpinData $Bcb;
894                 next;
895                 }
896         if (my($Bcb)=
897                         /^enter: CcUnpinDataForThread: Bcb=($hex)/) {
898                 CcUnpinDataForThread $Bcb;
899                 next;
900                 }
901
902         if (my($Bcb,$OwnerPointer)=
903                         /^enter: CcSetBcbOwnerPointer: Bcb=($hex),OwnerPointer=($hex)/) {
904                 CcSetBcbOwnerPointer $Bcb,$OwnerPointer;
905                 next;
906                 }
907
908         print "$_\n" if $filter;
909         }
910 for my $FileObject (keys(%FileObject)) {
911         warn "EXIT: still CcInitializeCacheMap FileObject $FileObject";
912         next if !(my $FObject=FObject $FileObject);
913         }