+Check neccessarities of IRP_MJ_READ.
[captive.git] / src / TraceFS / checktrace.pl
index 5e7b258..c6d6a3b 100755 (executable)
@@ -43,7 +43,7 @@ my $ntfs_blocksize=0x200;
 # $SectionObjectPointer{$SectionObjectPointer}{"SharedCacheMap"}="0x12345678";
 # $SharedCacheMap{$SharedCacheMap}{"SharedCacheMap"}="0x12345678";
 # $SharedCacheMap{$SharedCacheMap}{"SectionObjectPointer"}="0x12345678";
-# $SharedCacheMap{$SharedCacheMap}{"Allocation"}="0x12345";
+# $SharedCacheMap{$SharedCacheMap}{"AllocationSize"}="0x12345";
 # $SharedCacheMap{$SharedCacheMap}{"FileSize"}="0x12345";
 # $SharedCacheMap{$SharedCacheMap}{"ref_count"}=1;
 # $SharedCacheMap{$SharedCacheMap}{"map"}="0x12345678" (Bcb);
@@ -51,6 +51,7 @@ my $ntfs_blocksize=0x200;
 # $SharedCacheMap{$SharedCacheMap}{"PinAccess"}=0 or 1;
 # $SharedCacheMap{$SharedCacheMap}{"LogHandle"}="0x12345678" optional;
 # $SharedCacheMap{$SharedCacheMap}{"AcquireForLazyWrite"}=0;   # count
+# $SharedCacheMap{$SharedCacheMap}{"in_memory"}{"0x4000"}=1;   # mapped page?
 # $LogHandle{$LogHandle}{"LogHandle"}="0x12345678";
 # $Bcb{$Bcb}{"Bcb"}="0x12345678";
 # $Bcb{$Bcb}{"SharedCacheMap"}="0x12345678";
@@ -73,9 +74,10 @@ my %SharedCacheMap;
 my %Bcb;
 my %MdlChain;
 my %LastLeave; # $ProcessThread=>[$Object,$Object,...]
-my $LastLeave; # ref copy for the current $ProcessThread
+my $LastLeave; # ref copy of the last item for the current $ProcessThread
 my $ProcessThread;
 my %EnterLeave;
+my $EnterLeave;        # ref copy of the list for the current $ProcessThread
 
 END {
        print Data::Dumper->Dump([\%FileObject,\%SectionObjectPointer,\%SharedCacheMap,\%Bcb],
@@ -436,6 +438,14 @@ my($BObject)=@_;
                                        if $Bcb{$pin}->{"by"} eq "CcPinMappedData";
                        }
                }
+       else {
+               warn "unpin of pin Bcb $Bcb of SharedCacheMap ".$CObject->{"SharedCacheMap"}
+                                               ." although FileOffset ".$BObject->{"FileOffset"}." not in_memory"
+                               if !($CObject->{"in_memory"}{$BObject->{"FileOffset"}});
+               # Do not: delete $CObject->{"in_memory"}{$BObject->{"FileOffset"}};
+               # as Cache Manager is not forced to drop it.
+#              warn "UNMARK: SharedCacheMap ".$CObject->{"SharedCacheMap"}." FileOffset ".$BObject->{"FileOffset"};
+               }
        for my $ref ($BObject->{"type"} eq "map" ? \$CObject->{"map"} : \$CObject->{"pin"}{$BObject->{"FileOffset"}}) {
                warn "Final unpin but ".$BObject->{"type"}." Bcb $Bcb not registered"
                                                ." in SharedCacheMap ".$CObject->{"SharedCacheMap"}." ref ".($$ref || "<undef>")
@@ -606,6 +616,10 @@ my($Bcb,$Buffer)=@_;
        $Object->{"FileOffset"}=tohex(eval($Object->{"FileOffset"})-$shift);
        $Object->{"Buffer"}=tohex(eval($Buffer)-$shift);
 
+       warn "pin_new_leave() while FileOffset ".$Object->{"FileOffset"}." not in_memory"
+                                       ." of SharedCacheMap ".$CObject->{"SharedCacheMap"}
+                       if !$CObject->{"in_memory"}{$Object->{"FileOffset"}};
+
        my $ref=\$CObject->{"pin"}{$Object->{"FileOffset"}};
        # There may not exist map bcb even if we are creating the new pin bcb.
        Bcb_checkref $Object,$ref;
@@ -629,6 +643,12 @@ sub CcPreparePinWrite($$$)
 {
 my($FileObject,$FileOffset,$Length)=@_;
 
+       return if !(my $CObject=CObject_from_FileObject $FileObject);
+       # Full pages do not need to be read:
+       if (!($FileOffset&0xFFF)) {
+               $CObject->{"in_memory"}{tohex $FileOffset}=1;
+               }
+
        pin_new $FileObject,$FileOffset,$Length;
 }
 
@@ -727,19 +747,18 @@ my($ByteOffset,$Lsn_check)=@_;
                }
        if (!@Bcbs) {
                do {
-                               warn "Non-Bcb IRP_MJ_WRITE ByteOffset=$ByteOffset but some functions"
-                                               ." (".join(",",map({ $_->{"line_enter"}.":".$_->{"by"}; } @{$EnterLeave{$ProcessThread}})).")"
-                                               ." are nested:";
+                               warn "Non-Bcb IRP_MJ_WRITE ByteOffset=$ByteOffset as non-toplevel function"
+                                               ." (".join(",",map({ $_->{"line_enter"}.":".$_->{"by"}; } @$EnterLeave)).")";
 #                              warn Dumper $CObject;
                                # Direct IRP_MJ_WRITE can be from callbacked 'FlushToLsnRoutine'.
                                # It can occur even from other callbacks ('DirtyPageRoutine' etc.)
                                # but it was not needed here yet.
-                               } if @{$EnterLeave{$ProcessThread}}
-                                               && !(${$EnterLeave{$ProcessThread}}[$#{$EnterLeave{$ProcessThread}}]->{"by"} eq "FlushToLsnRoutine");
+                               } if @$EnterLeave && !(${$EnterLeave}[$#$EnterLeave]->{"by"} eq "FlushToLsnRoutine");
                warn "Non-Bcb IRP_MJ_WRITE ByteOffset=$ByteOffset but FlushToLsnRoutine was preceding"
                                if $FlushToLsnRoutine;
                return;
                }
+       $CObject->{"in_memory"}{$ByteOffset}=1;
        warn "Ambiguous matching Bcbs ".join(",",@Bcbs)
                                        ." to SharedCacheMap $SharedCacheMap WRITE ByteOffset $ByteOffset"
                        if @Bcbs>=2;
@@ -758,6 +777,21 @@ my($ByteOffset,$Lsn_check)=@_;
                }
        warn "IRP_MJ_WRITE with FlushToLsnRoutine although not in AcquireForLazyWrite"
                        if $FlushToLsnRoutine && !($CObject->{"AcquireForLazyWrite"}>=1);
+       warn "IRP_MJ_WRITE not the toplevel function"
+                                               ." (".join(",",map({ $_->{"line_enter"}.":".$_->{"by"}; } @$EnterLeave)).")"
+                       if !(0==@$EnterLeave
+                        || (1==@$EnterLeave && ${$EnterLeave}[0]->{"by"} eq "CcFlushCache")
+                        || (2==@$EnterLeave && ${$EnterLeave}[0]->{"by"} eq "IRP_MJ_FILE_SYSTEM_CONTROL"
+                                            && ${$EnterLeave}[1]->{"by"} eq "CcFlushCache"));
+       my $CcFlushCache=${$EnterLeave}[$#$EnterLeave];
+       if ($CcFlushCache && $CcFlushCache->{"by"} eq "CcFlushCache") {
+               $CcFlushCache->{"CcFlushCached"}++;
+               if ($CcFlushCache->{"FileOffset"} ne "0x".("F"x8) || $CcFlushCache->{"Length"} ne "0x0") {
+                       warn "IRP_MJ_WRITE outside of range of active CcFlushCache()"
+                                       if eval($ByteOffset)< eval($CcFlushCache->{"FileOffset"})
+                                       || eval($ByteOffset)>=eval($CcFlushCache->{"FileOffset"})+eval($CcFlushCache->{"Length"});
+                       }
+               }
        # Keep $BObject->{"dirty"} there for &delete_BObject sanity checks.
        delete_BObject $BObject if $BObject->{"dirty"} && !$BObject->{"ref_count"};
 }
@@ -783,6 +817,73 @@ sub IRP_MJ_WRITE_leave()
                }
 }
 
+sub IRP_MJ_READ_leave()
+{
+       # toplevel IRP_MJ_READ has no requirements
+       return if (0==@$EnterLeave);
+       my @stack=map({ $_->{"by"}=~/^IRP_MJ_/ ? () : $_ } @$EnterLeave);
+       my $opObject=$stack[0] if 1==@stack;
+       warn "IRP_MJ_READ not the expected function stack"
+                                               ." (".join(",",map({ $_->{"line_enter"}.":".$_->{"by"}; } @$EnterLeave)).")"
+                       if !($opObject->{"by"} eq "CcMapData"
+                         || $opObject->{"by"} eq "CcCopyRead"
+                         || $opObject->{"by"} eq "CcMdlRead"
+                         || $opObject->{"by"} eq "CcPinRead");
+       if ($opObject->{"by"} eq "CcMdlRead") {
+               do { warn "Length $_ not divisible by 0x1000" if eval($_)%0x1000; } for ($Object->{"READ"}{"Length"});
+               }
+       else {
+               do { warn "Length $_ not 0x1000" if eval($_)!=0x1000; } for ($Object->{"READ"}{"Length"});
+               }
+       my $SharedCacheMap=$Object->{"data"}[0]{"SharedCacheMap"};
+       return if !(my $CObject=CObject $SharedCacheMap);
+       for my $reloffs (0..eval($Object->{"READ"}{"Length"})/0x1000-1) {
+               my $ByteOffset=tohex(eval($Object->{"READ"}{"ByteOffset"})+$reloffs*0x1000);
+               # Do not: warn "Reading ByteOffset $ByteOffset into SharedCacheMap $SharedCacheMap twice"
+               #             if $CObject->{"in_memory"}{$ByteOffset};
+               # as it may be still cached there as Cache Manager is not forced to drop it.
+               $CObject->{"in_memory"}{$ByteOffset}=1;
+#              warn "MARK: SharedCacheMap ".$CObject->{"SharedCacheMap"}." FileOffset $ByteOffset";
+               }
+}
+
+sub CcPurgeCacheSection($$$$$)
+{
+my($SectionObjectPointer,$SharedCacheMap,$FileOffset,$Length,$UninitializeCacheMaps)=@_;
+
+       return if !(my $CObject=CObject $SharedCacheMap);
+       warn "Unexpected UninitializeCacheMaps $UninitializeCacheMaps" if $UninitializeCacheMaps ne "0";
+       my $all=($FileOffset eq "0x".("F"x8) && !eval $Length);
+       warn "Not yet implemented ranged CcPurgeCacheSection()" if !$all;
+       do { warn "Existing map Bcb $_ during CcPurgeCacheSection()" if $_; } for ($CObject->{"map"});
+       do { warn "Existing pin Bcb $_ during CcPurgeCacheSection()" if $_; } for (values(%{$CObject->{"pin"}}));
+       delete $CObject->{"in_memory"};
+}
+
+sub CcFlushCache($$$$)
+{
+my($SectionObjectPointer,$SharedCacheMap,$FileOffset,$Length)=@_;
+
+       $Object->{"CcFlushCached"}=0;
+       $Object->{"FileOffset"}=$FileOffset;
+       $Object->{"Length"}=$Length;
+}
+
+sub CcFlushCache_leave($$)
+{
+my($Status,$Information)=@_;
+
+       warn "CcFlushCache() not the toplevel function"
+                                               ." (".join(",",map({ $_->{"line_enter"}.":".$_->{"by"}; } @$EnterLeave)).")"
+                       if !(0==@$EnterLeave
+                        || (1==@$EnterLeave && ${$EnterLeave}[0]->{"by"} eq "IRP_MJ_FILE_SYSTEM_CONTROL"));
+       if ($Status ne "0x".("F"x8) || $Information ne "0x".("F"x8)) {
+               warn "Unexpected Status $Status" if eval $Status;
+               warn "Unexpected Information $Information while CcFlushCached=".$Object->{"CcFlushCached"}
+                               if eval($Information)!=eval($Object->{"CcFlushCached"})*0x1000;
+               }
+}
+
 sub CcPrepareMdlWrite($$$)
 {
 my($FileObject,$FileOffset,$Length)=@_;
@@ -844,12 +945,14 @@ sub AcquireForLazyWrite_leave($)
 my($r)=@_;
 
        warn "Unexpected 'r' $r" if $r ne "1";
+       warn "AcquireForLazyWrite() not the toplevel function" if @$EnterLeave;
        return if !(my $CObject=CObject $Object->{"data"}[0]{"SharedCacheMap"});
        $CObject->{"AcquireForLazyWrite"}++;
 }
 
 sub ReleaseFromLazyWrite_leave()
 {
+       warn "ReleaseFromLazyWrite() not the toplevel function" if @$EnterLeave;
        return if !(my $CObject=CObject $Object->{"data"}[0]{"SharedCacheMap"});
        warn "Invalid 'AcquireForLazyWrite' value ".$CObject->{"AcquireForLazyWrite"}
                        if !($CObject->{"AcquireForLazyWrite"}>=1);
@@ -1026,11 +1129,11 @@ while (<>) {
                        }
                next;
                }
-       elsif (my($ByteOffset,$Length)=
-                       /^WRITE: ByteOffset=($hex),Length=($hex)/) {
+       elsif (my($op,$ByteOffset,$Length)=
+                       /^(READ|WRITE): ByteOffset=($hex),Length=($hex)/) {
                my $aref=$EnterLeave{$ProcessThread};
                warn "Empty stack during 'data' line" if !($Object=${$aref}[$#$aref]);
-               $Object->{"WRITE"}={
+               $Object->{$op}={
                        "ByteOffset"=>$ByteOffset,
                        "Length"=>$Length,
                        };
@@ -1038,6 +1141,7 @@ while (<>) {
                }
 
        $LastLeave=${$LastLeave{$ProcessThread}}[$#{$LastLeave{$ProcessThread}}-1];
+       $EnterLeave=$EnterLeave{$ProcessThread};
 
        if (my($r)=
                        /^leave: IRP_MJ_\w+: r=($hex)/) {
@@ -1138,11 +1242,34 @@ while (<>) {
                next;
                }
 
+       if (/^leave: IRP_MJ_READ\b/) {
+               IRP_MJ_READ_leave;
+               next;
+               }
+
+       if (my($SectionObjectPointer,$SharedCacheMap,$FileOffset,$Length,$UninitializeCacheMaps)=
+               /^enter: CcPurgeCacheSection: SectionObjectPointer=($hex),->SharedCacheMap=($hex),FileOffset=($hex),Length=($hex),UninitializeCacheMaps=([01])/) {
+               CcPurgeCacheSection $SectionObjectPointer,$SharedCacheMap,$FileOffset,$Length,$UninitializeCacheMaps;
+               next;
+               }
+
        if (/^leave: IRP_MJ_WRITE\b/) {
                IRP_MJ_WRITE_leave;
                next;
                }
 
+       if (my($SectionObjectPointer,$SharedCacheMap,$FileOffset,$Length)=
+                       /^enter: CcFlushCache: SectionObjectPointer=($hex),->SharedCacheMap=($hex),FileOffset=($hex),Length=($hex)/) {
+               CcFlushCache $SectionObjectPointer,$SharedCacheMap,$FileOffset,$Length;
+               next;
+               }
+
+       if (my($Status,$Information)=
+                       /^leave: CcFlushCache: IoStatus->Status=($hex),IoStatus->Information=($hex)/) {
+               CcFlushCache_leave $Status,$Information;
+               next;
+               }
+
        if (my($r)=
                        /^leave: AcquireForLazyWrite: r=([01])/) {
                AcquireForLazyWrite_leave $r;