X-Git-Url: http://git.jankratochvil.net/?p=captive.git;a=blobdiff_plain;f=src%2FTraceFS%2Fchecktrace.pl;h=61c63e89abaef4f84d7ba0767dbf82fead92ff8d;hp=1e397a5545c58a68792b62c72fa285d84396bc1e;hb=HEAD;hpb=d670ebc7ec19fd1399ce032fd15067c5d9bad383 diff --git a/src/TraceFS/checktrace.pl b/src/TraceFS/checktrace.pl index 1e397a5..61c63e8 100755 --- a/src/TraceFS/checktrace.pl +++ b/src/TraceFS/checktrace.pl @@ -21,34 +21,68 @@ use strict; use warnings; use Data::Dumper; +use Carp qw(cluck confess); my $filter=0; $Data::Dumper::Sortkeys=1; my $ntfs_blocksize=0x200; -# $Object{"by"}="CcSomeFunction"; -# $Object{"line_enter"}=123; -# $Object{"line_leave"}=124; -# $Object{"process_thread"}="0x12345678/0x12345678"; +# $Object->{"by"}="CcSomeFunction"; +# $Object->{"line_enter"}=123; +# $Object->{"line_leave"}=124; +# $Object->{"ProcessThread"}="0x12345678/0x12345678"; +# $Object->{"data"}[dataline]{"FileObject"}="0x12345678"; +# $Object->{"data"}[dataline]{"FileName"}="\filename" or undef() if NULL; +# $Object->{"data"}[dataline]{"Flags"}="0x40100"; +# $Object->{"data"}[dataline]{"SectionObjectPointer"}="0x12345678"; +# $Object->{"data"}[dataline]{"SharedCacheMap"}="0x12345678"; # $FileObject{$FileObject}{"FileObject"}="0x12345678"; -# $FileObject{$FileObject}{"Allocation"}="0x12345"; -# $FileObject{$FileObject}{"FileSize"}="0x12345"; -# $FileObject{$FileObject}{"map"}="0x12345678" (Bcb); -# $FileObject{$FileObject}{"pin"}{"0x1000"}="0x12345678" (Bcb); -# $FileObject{$FileObject}{"PinAccess"}=0 or 1; +# $FileObject{$FileObject}{"SectionObjectPointer"}="0x12345678"; +# $SectionObjectPointer{$SectionObjectPointer}{"SectionObjectPointer"}="0x12345678"; +# $SectionObjectPointer{$SectionObjectPointer}{"SharedCacheMap"}="0x12345678"; +# $SharedCacheMap{$SharedCacheMap}{"SharedCacheMap"}="0x12345678"; +# $SharedCacheMap{$SharedCacheMap}{"SectionObjectPointer"}="0x12345678"; +# $SharedCacheMap{$SharedCacheMap}{"AllocationSize"}="0x12345"; +# $SharedCacheMap{$SharedCacheMap}{"FileSize"}="0x12345"; +# $SharedCacheMap{$SharedCacheMap}{"ref_count"}=1; +# $SharedCacheMap{$SharedCacheMap}{"map"}="0x12345678" (Bcb); +# $SharedCacheMap{$SharedCacheMap}{"pin"}{"0x1000"}="0x12345678" (Bcb) if !Bcb->{"OwnerPointer"}; +# $SharedCacheMap{$SharedCacheMap}{"PinAccess"}=0 or 1; +# $SharedCacheMap{$SharedCacheMap}{"LogHandle"}="0x12345678" optional; +# $SharedCacheMap{$SharedCacheMap}{"AcquireForLazyWrite"}=0; # count +# $SharedCacheMap{$SharedCacheMap}{"in_memory"}{"0x4000"}=1; # mapped page? +# $SharedCacheMap{$SharedCacheMap}{"Buffer"}="0x12345678"; +# $LogHandle{$LogHandle}{"LogHandle"}="0x12345678"; # $Bcb{$Bcb}{"Bcb"}="0x12345678"; -# $Bcb{$Bcb}{"FileObject"}="0x12345678"; +# $Bcb{$Bcb}{"SharedCacheMap"}="0x12345678"; # $Bcb{$Bcb}{"type"}="pin" or "map"; -# $Bcb{$Bcb}{"ref_count"}=1 +# $Bcb{$Bcb}{"ref_count"}=1; # $Bcb{$Bcb}{"FileOffset"}="0x1000" if {"type"} eq "pin"; # $Bcb{$Bcb}{"Buffer"}="0x12345678"; # PAGE_SIZE-aligned for "pin", FileOffset_0-aligned for "map" +# $Bcb{$Bcb}{"OwnerPointer"}="0x12345678" optional; +# $Bcb{$Bcb}{"Lsn"}="0x12345678" optional; +# $Bcb{$Bcb}{"dirty"}=1 optional; +# $MdlChain{$MdlChain}{"MdlChain"}="0x12345678"; +# $MdlChain{$MdlChain}{"FileObject"}="0x12345678"; +# $MdlChain{$MdlChain}{"FileOffset"}="0x5000"; +# $MdlChain{$MdlChain}{"Length"}="0x9000"; my %FileObject; +my %LogHandle; +my %SectionObjectPointer; +my %SharedCacheMap; my %Bcb; +my %MdlChain; +my %LastLeave; # $ProcessThread=>[$Object,$Object,...] +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,\%Bcb],[qw(%FileObject %Bcb)]) if !$filter; + print Data::Dumper->Dump([\%FileObject,\%SectionObjectPointer,\%SharedCacheMap,\%Bcb], + [qw(%FileObject %SectionObjectPointer %SharedCacheMap %Bcb)]) if !$filter; } my $Object; @@ -60,69 +94,301 @@ my($num)=@_; return sprintf("0x%X",$num); } +sub FObject($) +{ +my($FileObject)=@_; + + my $FObject=$FileObject{$FileObject}; + if (!$FObject) { + my($package,$filename,$line,$subroutine)=caller 0; + warn "Non-existent FileObject $FileObject by line $line"; + } + return $FObject; +} + +sub delete_FObject($) +{ +my($FObject)=@_; + + my $FileObject=$FObject->{"FileObject"}; + delete $FileObject{$FileObject}; +} + +sub SObject($) +{ +my($SectionObjectPointer)=@_; + + my $SObject=$SectionObjectPointer{$SectionObjectPointer}; + if (!$SObject) { + my($package,$filename,$line,$subroutine)=caller 0; + warn "Non-existent SectionObjectPointer $SectionObjectPointer by line $line" + } + return $SObject; +} + +sub SObject_from_FileObject($) +{ +my($FileObject)=@_; + + return if !(my $FObject=FObject $FileObject); + my $SObject=SObject $FObject->{"SectionObjectPointer"}; + if (!$SObject) { + my($package,$filename,$line,$subroutine)=caller 0; + warn "by line $line"; + } + return $SObject; +} + +sub delete_CObject($) +{ +my($CObject)=@_; + + my $SharedCacheMap=$CObject->{"SharedCacheMap"}; + do { warn "Trailing map $_ of SharedCacheMap $SharedCacheMap during its deletion" if $_; } for ($CObject->{"map"}); + do { warn "Trailing pin $_ of SharedCacheMap $SharedCacheMap during its deletion" if $_; } for (values(%{$CObject->{"pin"}})); + if (my $LogHandle=$CObject->{"LogHandle"}) { + do { warn "INTERNAL: Missing LogHandle $LogHandle for SharedCacheMap $SharedCacheMap"; return; } + if !(my $LObject=$LogHandle{$LogHandle}); + # Do not delete $LogHandle as it may be used by many SharedCacheMap-s + } + warn "ref_count=".$CObject->{"ref_count"}." of SharedCacheMap $SharedCacheMap during its deletion" + if $CObject->{"ref_count"}; + delete $SharedCacheMap{$SharedCacheMap}; +} + +sub CObject($) +{ +my($SharedCacheMap)=@_; + + my $CObject=$SharedCacheMap{$SharedCacheMap}; + if (!$CObject) { + my($package,$filename,$line,$subroutine)=caller 0; + warn "Non-existent SharedCacheMap $SharedCacheMap by line $line"; + } + return $CObject; +} + +sub CObject_from_FileObject($) +{ +my($FileObject)=@_; + + return if !(my $SObject=SObject_from_FileObject $FileObject); + return if !(my $CObject=CObject $SObject->{"SharedCacheMap"}); + return $CObject; +} + +sub SharedCacheMap_valid($) +{ +my($SharedCacheMap)=@_; + + cluck if !defined $SharedCacheMap; + return 0 if "0x".("F"x8) eq $SharedCacheMap; + return 0 if !eval $SharedCacheMap; + return 1; +} + +sub check_data($) +{ +my($data)=@_; + + if (!eval $data->{"SectionObjectPointer"}) { + return if $Object->{"by"} eq "IRP_MJ_CREATE"; # SectionObjectPointer is not yet initialized + warn "Existing FileObject ".$data->{"FileObject"}." but no SectionObjectPointer found" + if $FileObject{$data->{"FileObject"}} && eval($FileObject{$data->{"FileObject"}}{"SectionObjectPointer"}); + return; + } + my $SectionObjectPointer=$data->{"SectionObjectPointer"}; + if (!SharedCacheMap_valid $data->{"SharedCacheMap"} && $SectionObjectPointer{$SectionObjectPointer}) { + return if !(my $SObject=SObject $SectionObjectPointer); + my $SharedCacheMap=$SObject->{"SharedCacheMap"}; + return if !eval $SharedCacheMap; + my $CObject=CObject $SharedCacheMap; + warn "Existing SectionObjectPointer ".$data->{"SectionObjectPointer"}." but no SharedCacheMap found," + ." ref_count of SharedCacheMap is ".$CObject->{"ref_count"} + if $CObject->{"ref_count"}; +# if $SectionObjectPointer{$data->{"SectionObjectPointer"}}; + # SharedCacheMap was droppped by async task as it had ref_count==0. + delete_CObject $CObject; + $SObject->{"SharedCacheMap"}=tohex(0); + # FileObject is still valid! + return; + } + return if !$FileObject{$data->{"FileObject"}}; + return if !(my $FObject=FObject $data->{"FileObject"}); + $SectionObjectPointer=$FObject->{"SectionObjectPointer"}; + return if !(my $SObject=SObject $SectionObjectPointer); + warn "FileObject ".$FObject->{"FileObject"} + ." expected SectionObjectPointer $SectionObjectPointer" + ." but found SectionObjectPointer ".$data->{"SectionObjectPointer"} + if $SectionObjectPointer ne $data->{"SectionObjectPointer"}; + my $SharedCacheMap=$SObject->{"SharedCacheMap"}; + warn "FileObject ".$FObject->{"FileObject"}." SectionObjectPointer ".$SObject->{"SectionObjectPointer"} + ." expected SharedCacheMap $SharedCacheMap" + ." but found SharedCacheMap ".$data->{"SharedCacheMap"} + if $SharedCacheMap ne $data->{"SharedCacheMap"}; + warn "INTERNAL: SharedCacheMap $SharedCacheMap of FileObject ".$FObject->{"FileObject"}." got destroyed" + if !$SharedCacheMap{$SharedCacheMap}; +} + sub CcInitializeCacheMap($$$$$) { my($FileObject,$AllocationSize,$FileSize,$ValidDataLength,$PinAccess)=@_; $ValidDataLength=$FileSize if $ValidDataLength==eval("0x".("F"x8)); - if ($FileObject{$FileObject}) { -# warn "FileObject $FileObject registered twice"; - return if !(my $FObject=FObject($FileObject)); - delete_FObject($FObject); - } - $FileObject{$FileObject}=$Object; - $Object->{"FileObject"}=$FileObject; + $Object->{"ref_count"}=1; $Object->{"AllocationSize"}=tohex($AllocationSize); $Object->{"FileSize"}=tohex($FileSize); + $Object->{"ValidDataLength"}=tohex($ValidDataLength); $Object->{"map"}=undef(); $Object->{"pin"}={}; $Object->{"PinAccess"}=$PinAccess; - CcSetFileSizes($FileObject,$AllocationSize,$FileSize,$ValidDataLength); + $Object->{"FileObject"}=$FileObject; } -sub FObject($) +sub CcInitializeCacheMap_leave() { -my($FileObject)=@_; + my $SharedCacheMap=$Object->{"data"}[1]{"SharedCacheMap"}; + $Object->{"SharedCacheMap"}=$SharedCacheMap; + my $old=$SharedCacheMap{$SharedCacheMap}; + if (!SharedCacheMap_valid $Object->{"data"}[0]{"SharedCacheMap"} && $old) { + # SharedCacheMap got deleted in the meantime + delete_CObject CObject $SharedCacheMap; + # Either it got deleted of some foreign SectionObjectPointer + # or of the current one: + if (my $SObject=$SectionObjectPointer{$Object->{"data"}[0]{"SectionObjectPointer"}}) { + $SObject->{"SharedCacheMap"}=tohex(0); + } + $old=undef(); + } + if (!$old != !SharedCacheMap_valid $Object->{"data"}[0]{"SharedCacheMap"}) { + warn "Expecting old SharedCacheMap validity ".(!!$old) + ." but found old SharedCacheMap ".$Object->{"data"}[0]{"SharedCacheMap"}; + } + warn "New SharedCacheMap ".$Object->{"data"}[1]{"SharedCacheMap"}." is not valid" + if !SharedCacheMap_valid $Object->{"data"}[1]{"SharedCacheMap"}; + if (SharedCacheMap_valid $Object->{"data"}[0]{"SharedCacheMap"}) { + warn "Existing SharedCacheMap changed" + ." from ".$Object->{"data"}[0]{"SharedCacheMap"}." to ".$Object->{"data"}[1]{"SharedCacheMap"} + if $Object->{"data"}[0]{"SharedCacheMap"} ne $Object->{"data"}[1]{"SharedCacheMap"}; + } + if ($old) { + for my $field (qw(AllocationSize FileSize PinAccess)) { + warn "SharedCacheMap $SharedCacheMap old instance $field ".$old->{$field} + ." != new instance $field ".$Object->{$field} + if $old->{$field} ne $Object->{$field}; + } + do { warn "Existing map Bcb $_ during CcInitializeCacheMap()" if $_; } for ($old->{"map"}); + do { warn "Existing pin Bcb $_ during CcInitializeCacheMap()" if $_; } for (values(%{$old->{"pin"}})); + $Object->{"ref_count"}+=$old->{"ref_count"}; + } + $SharedCacheMap{$SharedCacheMap}=$Object; + + warn "Changed SectionObjectPointer inside CcInitializeCacheMap()" + ." from ".$Object->{"data"}[0]{"SectionObjectPointer"}." to ".$Object->{"data"}[1]{"SectionObjectPointer"} + if $Object->{"data"}[0]{"SectionObjectPointer"} ne $Object->{"data"}[1]{"SectionObjectPointer"}; + my $SectionObjectPointer=$Object->{"data"}[1]{"SectionObjectPointer"}; + + my $FileObject=$Object->{"FileObject"}; + if (my $FObject=$FileObject{$FileObject}) { + if (my $SObject=$SectionObjectPointer{$FObject->{"SectionObjectPointer"}}) { + warn "Changed SectionObjectPointer of FileObject $FileObject" + ." from ".$FObject->{"SectionObjectPointer"}." to ".$SectionObjectPointer + if $FObject->{"SectionObjectPointer"} ne $SectionObjectPointer; + } + # Otherwise SectionObjectPointer could be deleted and rebuilt async in the meantime. + } + $FileObject{$FileObject}={ + "FileObject"=>$FileObject, + "SectionObjectPointer"=>$SectionObjectPointer, + }; + + if (my $SObject=$SectionObjectPointer{$SectionObjectPointer}) { + warn "Changed SharedCacheMap of SectionObjectPointer $SectionObjectPointer" + ." from ".$SObject->{"SharedCacheMap"}." to ".$SharedCacheMap + if $SObject->{"SharedCacheMap"} ne $SharedCacheMap && eval($SObject->{"SharedCacheMap"}); + } + $SectionObjectPointer{$SectionObjectPointer}={ + "SectionObjectPointer"=>$SectionObjectPointer, + "SharedCacheMap"=>$SharedCacheMap, + }; - my $FObject=$FileObject{$FileObject}; - warn "Non-existent FileObject $FileObject" if !$FObject; - return $FObject; + CcSetFileSizes($FileObject,map({ eval($Object->{$_}); } qw(AllocationSize FileSize ValidDataLength))); + delete $Object->{$_} for (qw(FileObject ValidDataLength)); } -sub delete_FObject($) +sub CcUninitializeCacheMap($$) { -my($FObject)=@_; +my($FileObject,$TruncateSize)=@_; - my $FileObject=$FObject->{"FileObject"}; - do { warn "Trailing map $_ of FileObject $FileObject during its deletion" if $_; } for ($FObject->{"map"}); - do { warn "Trailing pin $_ of FileObject $FileObject during its deletion" if $_; } for (values(%{$FObject->{"pin"}})); - delete $FileObject{$FileObject}; + $Object->{"FileObject"}=$FileObject; } -sub CcUninitializeCacheMap($$) +sub CcUninitializeCacheMap_leave($) { -my($FileObject,$TruncateSize)=@_; +my($r)=@_; - # CcUninitializeCacheMap() w/o CcInitializeCacheMap() is allowed: - return if !$FileObject{$FileObject}; + my $FileObject=$Object->{"FileObject"}; + # 'r' means function success. + # r=0 either if no CcInitializeCacheMap() was called at all + # or if Cc was unable to detach SharedCacheMap and it remains valid + # (FIXME: Do we SharedCacheMap->ref_count-- on in such case?). + my $SectionObjectPointer=$FileObject{$FileObject}->{"SectionObjectPointer"} if $FileObject{$FileObject}; + my $SharedCacheMap=$SectionObjectPointer{$SectionObjectPointer}->{"SharedCacheMap"} + if $SectionObjectPointer && $SectionObjectPointer{$SectionObjectPointer}; + warn "Unexpected 'r' result $r for CcUninitializeCacheMap of FileObject $FileObject" + if !(eval($SharedCacheMap) && !SharedCacheMap_valid($Object->{"data"}[1]{"SharedCacheMap"})) != !$r; + if (!eval $SharedCacheMap) { + for my $SharedCacheMap ($Object->{"data"}[0]{"SharedCacheMap"},$Object->{"data"}[1]{"SharedCacheMap"}) { + warn "Not expecting valid SharedCacheMap $SharedCacheMap" + if SharedCacheMap_valid $SharedCacheMap; + } + return; + } + for my $SharedCacheMap ($Object->{"data"}[0]{"SharedCacheMap"}) { + warn "Expecting valid SharedCacheMap $SharedCacheMap" + if !SharedCacheMap_valid $SharedCacheMap; + } return if !(my $FObject=FObject $FileObject); - delete_FObject $FObject; + return if !(my $SObject=SObject $FObject->{"SectionObjectPointer"}); + return if !(my $CObject=CObject $SObject->{"SharedCacheMap"}); + if (--$CObject->{"ref_count"}) { + for my $SharedCacheMap ($Object->{"data"}[1]{"SharedCacheMap"}) { + warn "Expecting still valid SharedCacheMap $SharedCacheMap after CcUninitializeCacheMap()" + ." with ref_count=".$CObject->{"ref_count"} + if !SharedCacheMap_valid $SharedCacheMap; + } + return; + } + if (!SharedCacheMap_valid $Object->{"data"}[1]{"SharedCacheMap"}) { + delete_CObject $CObject; + $SObject->{"SharedCacheMap"}=tohex(0); + # FileObject is still valid! + } + else { + # FIXME: Do we SharedCacheMap->ref_count-- on in such case? + } } sub CcSetFileSizes($$$$) { my($FileObject,$AllocationSize,$FileSize,$ValidDataLength)=@_; - return if !(my $FObject=FObject $FileObject); - if ($AllocationSize!=eval($FObject->{"AllocationSize"})) { - do { warn "Existing map $_ of FileObject $FileObject during CcSetAllocationSizes()," - ." AllocationSize=".$FObject->{"AllocationSize"} if $_; } - for ($FObject->{"map"}); - do { warn "Existing pin $_ of FileObject $FileObject during CcSetAllocationSizes()," - ." AllocationSize=".$FObject->{"AllocationSize"} if $_; } - for (values(%{$FObject->{"pin"}})); - } - # $ValidDataLength can be > $FObject->{"FileSize"}; + return if !(my $CObject=CObject_from_FileObject $FileObject); + my $SharedCacheMap=$CObject->{"SharedCacheMap"}; + if ($AllocationSize!=eval($CObject->{"AllocationSize"})) { + do { warn "Existing map $_ of FileObject $FileObject SharedCacheMap $SharedCacheMap during CcSetAllocationSizes()," + ." AllocationSize=".$CObject->{"AllocationSize"} if $_; } + for ($CObject->{"map"}); + do { warn "Existing pin $_ of FileObject $FileObject SharedCacheMap $SharedCacheMap during CcSetAllocationSizes()," + ." AllocationSize=".$CObject->{"AllocationSize"} if $_; } + for (values(%{$CObject->{"pin"}})); + # Do not: delete $CObject->{"in_memory"}; + # as its keep is required by W32. Squeeze it instead: + $CObject->{"in_memory"}={ map({ + (eval($_)<$AllocationSize ? ($_=>1) : ()); + } keys(%{$CObject->{"in_memory"}})) }; + } + # $ValidDataLength can be > $CObject->{"FileSize"}; warn "ValidDataLength ".tohex($ValidDataLength)." > FileSize ".tohex($FileSize) if $ValidDataLength>$FileSize; warn "0 != AllocationSize ".tohex($AllocationSize)." % ntfs_blocksize ".tohex($ntfs_blocksize) @@ -130,43 +396,156 @@ my($FileObject,$AllocationSize,$FileSize,$ValidDataLength)=@_; # $AllocationSize can be higher warn "FileSize ".tohex($FileSize)." > AllocationSize ".tohex($AllocationSize) if $FileSize>$AllocationSize; - $FObject->{"FileSize"}=tohex($FileSize); - $FObject->{"AllocationSize"}=tohex($AllocationSize); + $CObject->{"FileSize"}=tohex($FileSize); + $CObject->{"AllocationSize"}=tohex($AllocationSize); + delete $CObject->{"Buffer"} if !eval $AllocationSize; +} + +sub IRP_MJ_CREATE_leave() +{ + do { warn "Non-NULL SectionObjectPointer $_ not expected" if eval($_); } for ($Object->{"data"}[0]{"SectionObjectPointer"}); + my $FileObject=$Object->{"data"}[0]{"FileObject"}; + warn "Existing FileObject $FileObject not expected" if $FileObject{$FileObject}; + my $SectionObjectPointer=$Object->{"data"}[1]{"SectionObjectPointer"}; + # We want to track even FileObject without SectionObjectPointer yet. +# if ($SectionObjectPointer && $SectionObjectPointer{$SectionObjectPointer}) + { + $FileObject{$FileObject}={ + "FileObject"=>$FileObject, + "SectionObjectPointer"=>$SectionObjectPointer, + }; + } + if (eval $SectionObjectPointer) { + my $SharedCacheMap=$Object->{"data"}[1]{"SharedCacheMap"}; + if (my $SObject=$SectionObjectPointer{$SectionObjectPointer}) { + warn "Changed SharedCacheMap from stored ".$SObject->{"SharedCacheMap"}." to ".$SharedCacheMap + if $SObject->{"SharedCacheMap"} ne $SharedCacheMap && $Object->{"by"} ne "IRP_MJ_CREATE"; + } + $SectionObjectPointer{$SectionObjectPointer}={ + "SectionObjectPointer"=>$SectionObjectPointer, + "SharedCacheMap"=>$SharedCacheMap, + }; + } } sub BObject($) { my($Bcb)=@_; + cluck if !defined $Bcb; my $BObject=$Bcb{$Bcb}; - warn "Non-existent Bcb $Bcb" if !$BObject; + if (!$BObject) { + my($package,$filename,$line,$subroutine)=caller 0; + warn "Non-existent Bcb $Bcb by line $line" + } return $BObject; } +sub delete_BObject($) +{ +my($BObject)=@_; + + my $Bcb=$BObject->{"Bcb"}; +# warn "XXX delete_BObject 0x811799B8 line $. BObject=".Dumper $BObject if $Bcb eq "0x811799B8"; + warn "Deleting ref_count=".$BObject->{"ref_count"}." Bcb $Bcb" if $BObject->{"ref_count"}; + # Do not: warn "Deleting dirty Bcb $Bcb" if $BObject->{"dirty"}; + # as it is valid to allow sanity check below. + warn "Deleting dirty Bcb $Bcb" if $BObject->{"dirty"} && $BObject->{"ref_count"}; + return if !(my $CObject=CObject $BObject->{"SharedCacheMap"}); + if ($BObject->{"type"} eq "map") { + for my $pin (values(%{$CObject->{"pin"}})) { + next if !defined $pin; + warn "unpin map but CcPinMappedData pin $pin still exists" + 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 || "") + if !defined($BObject->{"OwnerPointer"}) && !($$ref && $$ref eq $Bcb) + && !($BObject->{"ref_count"}==0 && $BObject->{"dirty"}); + if ($$ref && $$ref eq $Bcb) { + $$ref=undef(); + # Do not: delete $CObject->{"pin"}{$BObject->{"FileOffset"}} if $BObject->{"type"} eq "pin"; + # as it would destroy $$ref slot in &Bcb_checkref '($$ref && $Bcb ne $$ref)' codepath. + } + } + delete $Bcb{$Bcb}; +# warn "XXX delete_BObject 0x811799B8 line $. CObject=".Dumper $CObject if $Bcb eq "0x811799B8"; + CObject_Buffer_check($CObject); +} + +sub MObject($) +{ +my($MdlChain)=@_; + + cluck if !defined $MdlChain; + my $MObject=$MdlChain{$MdlChain}; + warn "Non-existent MdlChain $MdlChain" if !$MObject; + return $MObject; +} + +sub CObject_Buffer_check($) +{ +my($CObject)=@_; + + my $any; + for my $BObject ($CObject->{"map"},values(%{$CObject->{"pin"}})) { + # There may exist OwnerPointer-ed or dirty&unreffed standalone Bcbs bound to this SharedCacheMap + # but these are not important for Buffer reset. + next if !$BObject; + $any=1; + last; + } + if (!$any) { + delete $CObject->{"Buffer"}; + } +} + sub Bcb_conflict($;@) { -my($FObject,@Bcb_list)=@_; +my($CObject,@Bcb_list)=@_; my $arg=0; my %check=( - "map"=>$FObject->{"map"}, + "map"=>$CObject->{"map"}, map(("arg".($arg++)=>$_),@Bcb_list), - %{$FObject->{"pin"}}, + %{$CObject->{"pin"}}, ); my %reversed; my $BufferBase; # relativized to FileOffset 0 my $BufferBase_val; + if ($CObject->{"Buffer"}) { + $BufferBase=eval $CObject->{"Buffer"}; + $BufferBase_val="SharedCacheMap Buffer"; + } while (my($key,$val)=each(%check)) { next if !defined $val; - warn "Conflicting Bcb $val of keys $key and ".$reversed{$val}." of FileObject ".$FObject->{"FileObject"} + warn "Conflicting Bcb $val of keys $key and ".$reversed{$val}." of SharedCacheMap ".$CObject->{"SharedCacheMap"} if $reversed{$val}; # Buffer base should match even between 'map's and 'pin's # as the data are always mapped only once. - if (my $BObject=BObject $val) { + my $BObject=$Bcb{$val}; + warn "Non-existent key=$key Bcb $val" +# ." XXX line $. CObject=".Dumper $CObject + if !$BObject; + if ($BObject) { my $Buffer=eval $BObject->{"Buffer"}; $Buffer-=eval($BObject->{"FileOffset"}) if exists $BObject->{"FileOffset"}; + warn "INTERNAL: Non page aligned Buffer ".tohex($Buffer)." of Bcb $val" + if $Buffer & 0xFFF; warn "Non-matching Bcb ".$BObject->{"type"}." $val Buffer base ".tohex($Buffer) - ." with Bcb ".$Bcb{$BufferBase_val}->{"type"}." $BufferBase_val BufferBase ".tohex($BufferBase) + ." with".($BufferBase_val=~/^SharedCacheMap / ? "" : " Bcb ".$Bcb{$BufferBase_val}->{"type"}) + ." $BufferBase_val BufferBase ".tohex($BufferBase)."; SharedCacheMap=".$CObject->{"SharedCacheMap"} +# ." XXX line $. ".Dumper($CObject,\%Bcb) if defined($BufferBase) && $Buffer!=$BufferBase; $BufferBase=$Buffer; $BufferBase_val=$val; @@ -180,37 +559,43 @@ sub Bcb_checkref($$) { my($BObject,$ref)=@_; - return if !(my $FObject=FObject $BObject->{"FileObject"}); + return if !(my $CObject=CObject $BObject->{"SharedCacheMap"}); my $type=$BObject->{"type"}; my $Bcb=$BObject->{"Bcb"}; - if ($$ref) { + if ($$ref && $Bcb ne $$ref) { my $BObject2=$Bcb{$$ref}; - warn "new $type Bcb $Bcb != old $type Bcb $$ref" - if $Bcb ne $$ref; - warn "new $type $Bcb type ".$BObject->{"type"}." != old $type $$ref type ".$BObject2->{"type"} + warn "new $type Bcb $Bcb != old ".$BObject2->{"type"}." Bcb $$ref"; + delete_BObject $BObject2; + } + if ($Bcb{$Bcb}) { + my $BObject2=$Bcb{$Bcb}; + warn "new $type $Bcb type ".$BObject->{"type"}." != old type $type $Bcb type ".$BObject2->{"type"} if $BObject->{"type"} ne $BObject2->{"type"}; - warn "new $type $Bcb Buffer ".$BObject->{"Buffer"}." != old $type $$ref Buffer ".$BObject2->{"Buffer"} + warn "new $type $Bcb Buffer ".($BObject->{"Buffer"} || "") + ." != old $type $Bcb Buffer ".($BObject2->{"Buffer"} || "") +# ." XXX line $. ".Dumper($BObject,$BObject2) if $BObject->{"Buffer"} ne $BObject2->{"Buffer"}; } - if ($$ref && $$ref eq $Bcb) { - $BObject->{"ref_count"}+=$Bcb{$$ref}->{"ref_count"}; - $$ref=undef(); + if ($Bcb{$Bcb}) { + warn "Equal Bcb?" if $Bcb{$Bcb} eq $BObject; + $Bcb{$Bcb}->{"ref_count"}+=$BObject->{"ref_count"}; + $BObject=$Bcb{$Bcb}; } $Bcb{$Bcb}=$BObject; # &Bcb_conflict needs this reference - Bcb_conflict $FObject,$Bcb; + Bcb_conflict $CObject,($$ref && $$ref eq $Bcb ? () : ($Bcb)); $$ref=$Bcb; } sub map_new($;$$) { -my($FileObject,$FileOffset,$Length)=@_; +my($SharedCacheMap,$FileOffset,$Length)=@_; - return if !(my $FObject=FObject $FileObject); + return if !(my $CObject=CObject $SharedCacheMap); if (defined($FileOffset) && defined($Length)) { - warn "Mapping data (end ".tohex($FileOffset+$Length).") out of FileSize ".$FObject->{"FileSize"} - if $FileOffset+$Length>eval($FObject->{"FileSize"}); + warn "Mapping data (end ".tohex($FileOffset+$Length).") out of FileSize ".$CObject->{"FileSize"} + if $FileOffset+$Length>eval($CObject->{"FileSize"}); } - $Object->{"FileObject"}=$FileObject; + $Object->{"SharedCacheMap"}=$CObject->{"SharedCacheMap"}; if (defined $FileOffset) { $Object->{"FileOffset"}=tohex($FileOffset); } @@ -218,19 +603,30 @@ my($FileObject,$FileOffset,$Length)=@_; $Object->{"ref_count"}=1; } +sub map_new_from_FileObject($;$$) +{ +my($FileObject,$FileOffset,$Length)=@_; + + return if !(my $CObject=CObject_from_FileObject $FileObject); + map_new $CObject->{"SharedCacheMap"},$FileOffset,$Length; +} + sub map_new_leave($;$) { my($Bcb,$Buffer)=@_; $Object->{"Bcb"}=$Bcb; - return if !(my $FObject=FObject $Object->{"FileObject"}); + return if !(my $CObject=CObject $Object->{"SharedCacheMap"}); if (defined $Buffer) { $Object->{"Buffer"}=tohex(eval($Buffer)-(eval($Object->{"FileOffset"}) || 0)); } delete $Object->{"FileOffset"}; + $CObject->{"Buffer"}=$Object->{"Buffer"} if !$CObject->{"Buffer"}; + warn "Unknown Buffer during map_new_leave" if !$Object->{"Buffer"}; - my $ref=\$FObject->{"map"}; + my $ref=\$CObject->{"map"}; + # There may exist some pin bcbs even if we are creating the new map bcb. Bcb_checkref $Object,$ref; } @@ -238,7 +634,7 @@ sub CcMapData($$$) { my($FileObject,$FileOffset,$Length)=@_; - map_new $FileObject,$FileOffset,$Length; + map_new_from_FileObject $FileObject,$FileOffset,$Length; } sub CcMapData_leave($$) @@ -252,14 +648,14 @@ sub pin_new($$$) { my($FileObject,$FileOffset,$Length)=@_; - return if !(my $FObject=FObject $FileObject); - warn "Pinning of non-PinAccess FileObject $FileObject" if !$FObject->{"PinAccess"}; - warn "Mapping data (end ".tohex($FileOffset+$Length).") out of FileSize ".$FObject->{"FileSize"} - if $FileOffset+$Length>eval($FObject->{"FileSize"}); + return if !(my $CObject=CObject_from_FileObject $FileObject); + warn "Pinning of non-PinAccess FileObject $FileObject" if !$CObject->{"PinAccess"}; + warn "Mapping data (end ".tohex($FileOffset+$Length).") out of FileSize ".$CObject->{"FileSize"} + if $FileOffset+$Length>eval($CObject->{"FileSize"}); warn "Pinning Length ".tohex($Length)." > 0x1000" if $Length>0x1000; warn "Pinning across file page (start=".tohex($FileOffset).",end-1=".tohex($FileOffset+$Length-1).")" if ($FileOffset&~0xFFF)!=(($FileOffset+$Length-1)&~0xFFF); - $Object->{"FileObject"}=$FileObject; + $Object->{"SharedCacheMap"}=$CObject->{"SharedCacheMap"}; $Object->{"FileOffset"}=tohex($FileOffset); $Object->{"type"}="pin"; $Object->{"ref_count"}=1; @@ -270,14 +666,23 @@ sub pin_new_leave($$) my($Bcb,$Buffer)=@_; $Object->{"Bcb"}=$Bcb; - return if !(my $FObject=FObject $Object->{"FileObject"}); + return if !(my $CObject=CObject $Object->{"SharedCacheMap"}); $Object->{"Buffer"}=tohex(eval($Buffer)-(eval($Object->{"FileOffset"})&0xFFF)); my $shift=eval($Object->{"FileOffset"})&0xFFF; $Object->{"FileOffset"}=tohex(eval($Object->{"FileOffset"})-$shift); $Object->{"Buffer"}=tohex(eval($Buffer)-$shift); - my $ref=\$FObject->{"pin"}{$Object->{"FileOffset"}}; + my $Buffer_base=tohex(eval($Object->{"Buffer"})-eval($Object->{"FileOffset"})); + $CObject->{"Buffer"}=$Buffer_base if !$CObject->{"Buffer"}; + + 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; +# warn "XXX pin_new_leave line $. BObject=".Dumper $Object if $Bcb eq "0x811799B8"; } sub CcPinRead($$$) @@ -298,6 +703,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; } @@ -306,6 +717,8 @@ sub CcPreparePinWrite_leave($$) my($Bcb,$Buffer)=@_; pin_new_leave $Bcb,$Buffer; + my $BObject=BObject $Bcb; + $BObject->{"dirty"}=1; } sub CcPinMappedData($$$) @@ -319,28 +732,19 @@ sub CcPinMappedData_leave($) { my($Bcb)=@_; - return if !(my $FObject=FObject $Object->{"FileObject"}); - do { warn "CcPinMappedData() with Bcb $Bcb on non-CcMapData()ed FileObject ".$Object->{"FileObject"}; return; } - if !(my $mapBcb=$FObject->{"map"}); - return if !(my $BmapObject=BObject $mapBcb); - my $Buffer=tohex(eval($BmapObject->{"Buffer"})+eval($Object->{"FileOffset"})); - - my $Bcb2=$FObject->{"pin"}{tohex(eval($Object->{"FileOffset"})&~0xFFF)}; - my $BObject2=BObject $Bcb2 if $Bcb2; - if ($BObject2 && $BObject2->{"CcPinMappedData_double"} - && eval($BObject2->{"CcPinMappedData_double"})==eval($Object->{"FileOffset"})) { # unaligned yet - my $BmapBuffer=eval($BmapObject->{"Buffer"})+(eval($Object->{"FileOffset"})&~0xFFF); - warn "CcPinMappedData-double cludge non-matching new Bcb $Bcb != old Bcb ".$BObject2->{"Bcb"} - if $Bcb ne $BObject2->{"Bcb"}; - warn "CcPinMappedData-double cludge non-matching Buffer new Bcb $Bcb Buffer $BmapBuffer" - ." != old Bcb ".$BObject2->{"Bcb"}." Buffer ".$BObject2->{"Buffer"} - if eval($BmapBuffer)!=eval($BObject2->{"Buffer"}); - return; - } + return if !(my $CObject=CObject $Object->{"SharedCacheMap"}); + # Do not: do { warn "CcPinMappedData() with Bcb $Bcb on non-CcMapData()ed SharedCacheMap ".$CObject->{"SharedCacheMap"}; return; } + # if !$CObject->{"map"}; + # as the only requirement of CcPinMappedData() is to have all the pages already 'in_memory'. + my $Buffer=$CObject->{"Buffer"}; + warn "SharedCacheMap ".$CObject->{"SharedCacheMap"}." Buffer not known during CcPinMappedData()" + if !$Buffer; + $Buffer=tohex(eval($Buffer)+eval($Object->{"FileOffset"})) if $Buffer; + +# my $Bcb2=$CObject->{"pin"}{tohex(eval($Object->{"FileOffset"})&~0xFFF)}; +# my $BObject2=BObject $Bcb2 if $Bcb2; - $Object->{"CcPinMappedData_double"}=$Object->{"FileOffset"}; # unaligned yet pin_new_leave $Bcb,$Buffer; -# print STDERR "$.:".Dumper($Object); } sub CcSetDirtyPinnedData($$) @@ -348,7 +752,289 @@ sub CcSetDirtyPinnedData($$) my($Bcb,$Lsn)=@_; return if !(my $BObject=BObject $Bcb); - delete $BObject->{"CcPinMappedData_double"}; + # Do not: warn "Lsn already set for Bcb $Bcb as ".$BObject->{"Lsn"}." while current Lsn=$Lsn" if $BObject->{"Lsn"}; + # as it is permitted. + warn "Lsn goes backward for Bcb $Bcb old Lsn ".$BObject->{"Lsn"}." to a new Lsn=$Lsn" + if $BObject->{"Lsn"} && eval($BObject->{"Lsn"})>eval($Lsn); + $BObject->{"Lsn"}=$Lsn if $Lsn ne "0x".("F"x8); + $BObject->{"dirty"}=1; + return if !(my $CObject=CObject $BObject->{"SharedCacheMap"}); +} + +sub FlushToLsnRoutine($$) +{ +my($LogHandle,$Lsn)=@_; + + $Object->{"LogHandle"}=$LogHandle; + $Object->{"Lsn"}=$Lsn; + + my $obj=${$EnterLeave}[$#$EnterLeave-1]; + warn "FLUSH" if $obj->{"by"} eq "CcFlushCache"; +} + +my $LogHandle_static; +sub CcSetLogHandleForFile($$$) +{ +my($FileObject,$LogHandle,$FlushToLsnRoutine)=@_; + + return if !(my $CObject=CObject_from_FileObject $FileObject); + warn "LogHandle ".$CObject->{"LogHandle"}." already exists for SharedCacheMap ".$CObject->{"SharedCacheMap"} + if $CObject->{"LogHandle"}; + return if !eval $LogHandle; # $LogHandle may be "0x0" + # ntfs.sys uses single LogHandle for its whole session: + warn "Non-unique LogHandle $LogHandle while last LogHandle was $LogHandle_static" + if $LogHandle_static && $LogHandle ne $LogHandle_static; + $CObject->{"LogHandle"}=$LogHandle; + if (!$LogHandle{$LogHandle}) { + $LogHandle{$LogHandle}={ + "LogHandle"=>$LogHandle, + }; + } +} + +sub IRP_MJ_WRITE_leave_page($$) +{ +my($ByteOffset,$Lsn_check)=@_; + + my $SharedCacheMap=$Object->{"data"}[0]{"SharedCacheMap"}; + return if !(my $CObject=CObject $SharedCacheMap); + my $FlushToLsnRoutine=$LastLeave if $LastLeave->{"by"} eq "FlushToLsnRoutine"; + # Do not: my $Bcb=$CObject->{"pin"}{$ByteOffset}; + # as Bcbs with $BObject->{"OwnerPointer"} are no longer stored in $CObject->{"pin"}. + my @Bcbs; + for my $Bcb (keys(%Bcb)) { + my $BObject=BObject $Bcb; + if (1 + && $BObject->{"type"} eq "pin" + && $BObject->{"SharedCacheMap"} eq $SharedCacheMap + && $BObject->{"FileOffset"} eq $ByteOffset) { + push @Bcbs,$Bcb; + } + } + if (!@Bcbs) { + do { + 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 && !(${$EnterLeave}[$#$EnterLeave]->{"by"}=~/^(?:FlushToLsnRoutine\b|IRP_MJ_)/); + 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; + my $Bcb=$Bcbs[0]; + return if !(my $BObject=BObject $Bcb); + warn "IRP_MJ_WRITE on non-dirty Bcb $Bcb" if !$BObject->{"dirty"}; + if ($FlushToLsnRoutine) { + push @$Lsn_check,{ + "Bcb"=>$Bcb, + "Bcb_Lsn",$BObject->{"Lsn"}, + } if $Lsn_check; + } + else { + warn "Missing preceding FlushToLsnRoutine during IRP_MJ_WRITE of Bcb $Bcb with Lsn ".$BObject->{"Lsn"} + if $BObject->{"Lsn"}; + } + warn "IRP_MJ_WRITE with FlushToLsnRoutine although not in AcquireForLazyWrite or CcFlushCache" + if $FlushToLsnRoutine && !((1==@$EnterLeave && ${$EnterLeave}[0]->{"by"} eq "CcFlushCache") + || (2==@$EnterLeave && ${$EnterLeave}[0]->{"by"}=~/^IRP_MJ_/ + && ${$EnterLeave}[1]->{"by"} eq "CcFlushCache")) + && !($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"}=~/^IRP_MJ_/ + && ${$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"}; +} + +sub IRP_MJ_WRITE_leave() +{ + return if !(my $CObject=CObject $Object->{"data"}[0]{"SharedCacheMap"}); + # toplevel IRP_MJ_WRITE has no requirements + return if 0==@$EnterLeave + # We do not need any outer function, just 'AcquireForLazyWrite' is enough + # for flushing Cache Manager buffers by some its LazyWriter task. + && !$CObject->{"AcquireForLazyWrite"}; + do { warn "Length $_ not divisible by 0x1000" if eval($_)%0x1000; } for ($Object->{"WRITE"}{"Length"}); + my @Lsn_check; + for my $reloffs (0..(eval($Object->{"WRITE"}{"Length"})/0x1000)-1) { + IRP_MJ_WRITE_leave_page tohex(eval($Object->{"WRITE"}{"ByteOffset"})+0x1000*$reloffs),\@Lsn_check; + } + + if ($LastLeave->{"by"} eq "FlushToLsnRoutine" && (my $FlushToLsnRoutine=$LastLeave)) { + my $Lsn_max; + for (@Lsn_check) { + my $Lsn=eval $_->{"Bcb_Lsn"}; + $Lsn_max=$Lsn if !defined($Lsn_max) || $Lsn_max<$Lsn; + } + warn "FlushToLsnRoutine of line_enter ".$FlushToLsnRoutine->{"line_enter"} + ." got Lsn ".$FlushToLsnRoutine->{"Lsn"}." although Bcbs have " + .join(",",map({ "(".$_->{"Bcb"}.":".$_->{"Bcb_Lsn"}.")"; } @Lsn_check)) + if tohex($Lsn_max) ne $FlushToLsnRoutine->{"Lsn"}; + } +} + +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"}})); + # Primary goal of this function: + delete $CObject->{"in_memory"}; + # Really needed: + delete $CObject->{"Buffer"}; +} + +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"}=~/^IRP_MJ_/)); + 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)=@_; + + $Object->{"FileObject"}=$FileObject; + warn "FileOffset $FileOffset not divisible by 0x1000" if eval($FileOffset)%0x1000; + $Object->{"FileOffset"}=$FileOffset; + warn "Length $Length not divisible by 0x1000" if eval($Length)%0x1000; + $Object->{"Length"}=$Length; +} + +sub CcPrepareMdlWrite_leave($$$) +{ +my($MdlChain,$Status,$Information)=@_; + + do { warn "Unexpected Status $Status"; return; } if eval $Status; + warn "Unexpected Information $Information" if $Information ne $Object->{"Length"}; + warn "MdlChain $MdlChain already exists" if $MdlChain{$MdlChain}; + $MdlChain{$MdlChain}=$Object; +} + +sub CcMdlWriteComplete($$$) +{ +my($FileObject,$FileOffset,$MdlChain)=@_; + + return if !(my $MObject=MObject $MdlChain); + warn "CcMdlWriteComplete() parameter FileObject $FileObject" + ." not matching MdlChain $MdlChain FileObject ".$MObject->{"FileObject"} + if $FileObject ne $MObject->{"FileObject"}; + warn "CcMdlWriteComplete() parameter FileOffset $FileOffset" + ." not matching MdlChain $MdlChain FileOffset ".$MObject->{"FileOffset"} + if $FileOffset ne $MObject->{"FileOffset"}; + # Propose MdlChain to a simulated Bcb. + # We must split it by pages as pin can be just 0x1000 sized. + return if !(my $CObject=CObject_from_FileObject $MObject->{"FileObject"}); + for my $reloffs (0..eval($MObject->{"Length"})/0x1000-1) { + my $BObject={ %$MObject }; + $BObject->{"Bcb"}="MdlChain $MdlChain reloffs $reloffs"; + $BObject->{"FileOffset"}=tohex(eval($MObject->{"FileOffset"})+$reloffs*0x1000); + $BObject->{"SharedCacheMap"}=$CObject->{"SharedCacheMap"}; + $BObject->{"type"}="pin"; + $BObject->{"ref_count"}=0; + $BObject->{"dirty"}=1; + warn "Bcb ".$BObject->{"Bcb"}." already exists" if $Bcb{$BObject->{"Bcb"}}; + $Bcb{$BObject->{"Bcb"}}=$BObject; + } + delete $MdlChain{$MdlChain}; +} + +sub CcMdlWriteAbort($$) +{ +my($FileObject,$MdlChain)=@_; + + warn "CcMdlWriteAbort() not handled"; +} + +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); + $CObject->{"AcquireForLazyWrite"}--; } sub CcRemapBcb($) @@ -356,7 +1042,7 @@ sub CcRemapBcb($) my($Bcb)=@_; return if !(my $BObject=BObject $Bcb); - map_new $BObject->{"FileObject"}; + map_new $BObject->{"SharedCacheMap"}; $Object->{"Buffer"}=tohex(eval($BObject->{"Buffer"})-eval($BObject->{"FileOffset"} || 0)); } @@ -372,25 +1058,20 @@ sub unpin($) my($Bcb)=@_; return if !(my $BObject=BObject $Bcb); - delete $BObject->{"CcPinMappedData_double"}; return if --$BObject->{"ref_count"}; - return if !(my $FObject=FObject $BObject->{"FileObject"}); - if ($BObject->{"type"} eq "map") { - for my $pin (values(%{$FObject->{"pin"}})) { - warn "unpin map but CcPinMappedData pin $pin still exists" - if $Bcb{$pin}->{"by"} eq "CcPinMappedData"; - } - } - for my $ref ($BObject->{"type"} eq "map" ? \$FObject->{"map"} : \$FObject->{"pin"}{$BObject->{"FileOffset"}}) { - warn "Final unpin but ".$BObject->{"type"}." Bcb $Bcb not registered" - ." in FileObject ".$FObject->{"FileObject"}." ref ".($$ref || "") - if !defined($BObject->{"OwnerPointer"}) && !($$ref && $$ref eq $Bcb); - if ($$ref && $$ref eq $Bcb) { - $$ref=undef(); - delete $FObject->{"pin"}{$BObject->{"FileOffset"}} if $BObject->{"type"} eq "pin"; - } + if ($BObject->{"dirty"}) { + # last unpin of unreferenced dirty Bcb will no longer allow reincarnation + # of the same Bcb to the pin map of its SharedCacheMap. + return if !(my $CObject=CObject $BObject->{"SharedCacheMap"}); + warn "unpin() of pin Bcb $Bcb but it is already not registered in SharedCacheMap ".$BObject->{"SharedCacheMap"}." pin map" + if (!$CObject->{"pin"}{$BObject->{"FileOffset"}} || $CObject->{"pin"}{$BObject->{"FileOffset"}} ne $Bcb) + && !$BObject->{"OwnerPointer"}; + delete $CObject->{"pin"}{$BObject->{"FileOffset"}} + if $CObject->{"pin"}{$BObject->{"FileOffset"}} && ($CObject->{"pin"}{$BObject->{"FileOffset"}} eq $Bcb); + CObject_Buffer_check $CObject; + return; } - delete $Bcb{$Bcb}; + delete_BObject $BObject; } sub CcUnpinData($) @@ -413,21 +1094,32 @@ my($Bcb,$OwnerPointer)=@_; return if !(my $BObject=BObject $Bcb); warn "CcSetBcbOwnerPointer() on map Bcb $Bcb" if $BObject->{"type"} ne "pin"; - return if !(my $FObject=FObject $BObject->{"FileObject"}); + return if !(my $CObject=CObject $BObject->{"SharedCacheMap"}); warn "Double CcSetBcbOwnerPointer() on Bcb $Bcb" if defined $BObject->{"OwnerPointer"}; - my $val=$FObject->{"pin"}{$BObject->{"FileOffset"}}; + my $val=$CObject->{"pin"}{$BObject->{"FileOffset"}}; warn "CcSetBcbOwnerPointer() on unregistered pin Bcb $Bcb" if !$val || $val ne $Bcb; - delete $FObject->{"pin"}{$BObject->{"FileOffset"}} if $val && $val eq $Bcb; + delete $CObject->{"pin"}{$BObject->{"FileOffset"}} if $val && $val eq $Bcb; $BObject->{"OwnerPointer"}=$OwnerPointer; + CObject_Buffer_check $CObject; } -sub IRP_MJ_CLOSE($) +sub IRP_MJ_CLOSE_leave() { -my($FileObject)=@_; - - return if !$FileObject{$FileObject}; + my $FileObject=$Object->{"data"}[0]{"FileObject"}; +# # IRP_MJ_CLOSE of FileObject w/o CcInitializeCacheMap()? +# return if !$FileObject{$FileObject}; return if !(my $FObject=FObject $FileObject); - warn "CcUnpinData() not called for FileObject $FileObject before IRP_MJ_CLOSE"; + if (eval(my $SectionObjectPointer=$FObject->{"SectionObjectPointer"})) { + return if !(my $SObject=SObject $SectionObjectPointer); + my $SharedCacheMap=$SObject->{"SharedCacheMap"}; + if (eval $SharedCacheMap) { + return if !(my $CObject=CObject $SObject->{"SharedCacheMap"}); + # SharedCacheMap may still exist for FCB although this FileObject gets destroyed now. +# warn "SectionObjectPointer $SectionObjectPointer still exists during IRP_MJ_CLOSE" +# ." while SharedCacheMap ".$CObject->{"SharedCacheMap"}." ref_count ".$CObject->{"ref_count"} +# if $SectionObjectPointer && $CObject->{"ref_count"}; + } + } delete_FObject $FObject; } @@ -435,27 +1127,106 @@ my($FileObject)=@_; local $_; my $hex='0x[\dA-F]+'; my %last_irp_mj; -my %enter_leave; while (<>) { chomp; s/\r$//; # We may get some foreign garbage without '\n' before our debug data line: - s#^.*?\bTraceFS[(]($hex/$hex)[)]: ## or do { print "$_\n" if $filter; next; }; - my($process_thread)=($1); + # Do not use '\bTraceFS' as there really can be precediny _any_ data. + s#^.*?TraceFS[(]($hex/$hex)[)]: ## or do { print "$_\n" if $filter; next; }; + $ProcessThread=$1; $Object=undef(); if (/^enter: (\w+)/) { $Object={}; $Object->{"by"}=$1; $Object->{"line_enter"}=$.; - $Object->{"process_thread"}=$process_thread; - push @{$enter_leave{$process_thread}},$Object; + $Object->{"ProcessThread"}=$ProcessThread; + push @{$EnterLeave{$ProcessThread}},$Object; } - if (/^leave: (\w+)/) { - warn "Empty pop stack during 'leave' of $1" if !($Object=pop @{$enter_leave{$process_thread}}); + elsif (/^leave: (\w+)/) { + warn "Empty pop stack during 'leave' of $1" if !($Object=pop @{$EnterLeave{$ProcessThread}}); warn "Non-matching popped 'by' ".$Object->{"by"}." ne current 'leave' $1" if $Object->{"by"} ne $1; $Object->{"line_leave"}=$.; + push @{$LastLeave{$ProcessThread}},$Object; + } + elsif (my($FileObject,$FileName,$Flags,$SectionObjectPointer,$SharedCacheMap)= + /^FileObject=($hex): FileName=(?:NULL|'(.*)'),(?:ref=[+-]\d+,)?Flags=($hex),SectionObjectPointer=($hex),->SharedCacheMap=($hex)/) { + my $aref=$EnterLeave{$ProcessThread}; + warn "Empty stack during 'data' line" if !($Object=${$aref}[$#$aref]); + my $data={ + "FileObject"=>$FileObject, + "FileName"=>$FileName, + "Flags"=>$Flags, + "SectionObjectPointer"=>$SectionObjectPointer, + "SharedCacheMap"=>$SharedCacheMap, + "line"=>$., + }; + push @{$Object->{"data"}},$data; + my $isinit={ map(($_=>1),qw( + CcInitializeCacheMap + CcUninitializeCacheMap + IRP_MJ_CREATE + )) }->{$Object->{"by"}}; + check_data $data + if 1==@{$Object->{"data"}} || !$isinit; + if ($isinit) { + # Prevent 'SharedCacheMap' 0->N change by CcInitializeCacheMap() called inside. + for my $ref (@$aref[0..$#$aref-1]) { + $ref->{"data"}[0]->{"SharedCacheMap"}=$SharedCacheMap; + } + } + if (2<=@{$Object->{"data"}}) { + my $data_prev=$Object->{"data"}[$#{$Object->{"data"}}-1]; + for my $field (qw(FileObject FileName Flags),($isinit ? () : qw(SharedCacheMap))) { + next if !defined(my $prev=$data_prev->{$field}); + next if !defined(my $now=$data->{$field}); + my $by=$Object->{"by"}; + if ($field eq "Flags") { + next if $by eq "IRP_MJ_CREATE" && $field eq "Flags"; + my $FO_CLEANUP_COMPLETE=0x4000; + $now=tohex(eval($now)&~$FO_CLEANUP_COMPLETE) if $by eq "IRP_MJ_CLEANUP"; + my $FO_FILE_FAST_IO_READ=0x80000; + $prev=tohex(eval($prev)&~$FO_FILE_FAST_IO_READ) if $by eq "IRP_MJ_CLEANUP"; + $now=tohex(eval($now)&~$FO_FILE_FAST_IO_READ) if $by eq "IRP_MJ_READ" && !(eval($prev)&$FO_FILE_FAST_IO_READ); + my $FO_FILE_MODIFIED=0x1000; + $now=tohex(eval($now)&~$FO_FILE_MODIFIED) if $by eq "IRP_MJ_WRITE" && !(eval($prev)&$FO_FILE_MODIFIED); + my $FO_FILE_SIZE_CHANGED=0x2000; + $prev=tohex(eval($prev)&~$FO_FILE_MODIFIED) + if $by eq "IRP_MJ_SET_INFORMATION" && !(eval($now)&$FO_FILE_MODIFIED); + $prev=tohex(eval($prev)&~$FO_FILE_SIZE_CHANGED) + if $by eq "IRP_MJ_SET_INFORMATION" && !(eval($now)&$FO_FILE_SIZE_CHANGED); + } + next if $by eq "IRP_MJ_CLOSE" && $field eq "FileName"; + $prev=~s#\\$## if $by eq "IRP_MJ_CREATE"; + $prev="\\" if $by eq "IRP_MJ_CREATE" && $prev eq ""; + $prev=~s#:.*## if $by eq "IRP_MJ_CREATE" && $prev ne $now; + next if $field eq "SharedCacheMap" && !SharedCacheMap_valid $prev && !SharedCacheMap_valid $now; + do { warn "Changed data field $field, prev=".$data_prev->{$field}.", now=".$data->{$field}." by $by"; +# print STDERR Dumper $data_prev,$data; + } if $prev ne $now; + } + } + next; + } + 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->{$op}={ + "ByteOffset"=>$ByteOffset, + "Length"=>$Length, + }; + next; + } + + $LastLeave=${$LastLeave{$ProcessThread}}[$#{$LastLeave{$ProcessThread}}-1]; + $EnterLeave=$EnterLeave{$ProcessThread}; + + if (my($r)= + /^leave: IRP_MJ_\w+: r=($hex)/) { + # Failed requests should make no consequences. + next if eval($r); } if (my($FileObject,$AllocationSize,$FileSize,$ValidDataLength,$PinAccess)= @@ -463,11 +1234,21 @@ while (<>) { CcInitializeCacheMap $FileObject,eval($AllocationSize),eval($FileSize),eval($ValidDataLength),eval($PinAccess); next; } + if (/^leave: CcInitializeCacheMap\b/) { + CcInitializeCacheMap_leave; + next; + } + if (my($FileObject,$TruncateSize)= /^enter: CcUninitializeCacheMap: FileObject=($hex),TruncateSize=($hex),/) { CcUninitializeCacheMap $FileObject,eval($TruncateSize); next; } + if (my($r)= + /^leave: CcUninitializeCacheMap: r=([01])/) { + CcUninitializeCacheMap_leave $r; + next; + } if (my($FileObject,$AllocationSize,$FileSize,$ValidDataLength)= /^enter: CcSetFileSizes: FileObject=($hex),FileSizes,->AllocationSize=($hex),->FileSize=($hex),->ValidDataLength=($hex)/) { @@ -475,6 +1256,16 @@ while (<>) { next; } + if (/^leave: IRP_MJ_CREATE\b/) { + IRP_MJ_CREATE_leave; + next; + } + + if (/^leave: IRP_MJ_CLOSE\b/) { + IRP_MJ_CLOSE_leave; + next; + } + if (my($FileObject,$FileOffset,$Length)= /^enter: CcMapData: FileObject=($hex),FileOffset=($hex),Length=($hex),Flags=0x1/) { CcMapData $FileObject,eval($FileOffset),eval($Length); @@ -525,6 +1316,78 @@ while (<>) { next; } + if (my($LogHandle,$Lsn)= + /^enter: FlushToLsnRoutine: LogHandle=($hex),Lsn=($hex)/) { + FlushToLsnRoutine $LogHandle,$Lsn; + 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; + } + + if (/^leave: ReleaseFromLazyWrite\b/) { + ReleaseFromLazyWrite_leave; + } + + if (my($FileObject,$LogHandle,$FlushToLsnRoutine)= + /^enter: CcSetLogHandleForFile: FileObject=($hex),LogHandle=($hex),FlushToLsnRoutine=($hex)/) { + CcSetLogHandleForFile $FileObject,$LogHandle,$FlushToLsnRoutine; + next; + } + + if (my($FileObject,$FileOffset,$Length)= + /^enter: CcPrepareMdlWrite: FileObject=($hex),FileOffset=($hex),Length=($hex)/) { + CcPrepareMdlWrite $FileObject,$FileOffset,$Length; + next; + } + if (my($MdlChain,$Status,$Information)= + /^leave: CcPrepareMdlWrite: MdlChain=($hex),IoStatus->Status=($hex),IoStatus->Information=($hex)/) { + CcPrepareMdlWrite_leave $MdlChain,$Status,$Information; + next; + } + + if (my($FileObject,$FileOffset,$MdlChain)= + /^enter: CcMdlWriteComplete: FileObject=($hex),FileOffset=($hex),MdlChain=($hex)/) { + CcMdlWriteComplete $FileObject,$FileOffset,$MdlChain; + next; + } + + if (my($FileObject,$MdlChain)= + /^enter: CcMdlWriteAbort: FileObject=($hex),MdlChain=($hex)/) { + CcMdlWriteAbort $FileObject,$MdlChain; + next; + } + if (my($Bcb)= /^enter: CcRemapBcb: Bcb=($hex)/) { CcRemapBcb $Bcb; @@ -553,32 +1416,9 @@ while (<>) { next; } - if (my($irp_mj)= - /^enter: (IRP_MJ_\w+)/) { - push @{$last_irp_mj{$process_thread}},$irp_mj; - next; - } - if (my($irp_mj)= - /^leave: (IRP_MJ_\w+)/) { - my $irp_mj_last=pop @{$last_irp_mj{$process_thread}}; - warn "Non-matching popped IRP name irp_mj $irp_mj ne irp_mj_last $irp_mj_last" - if $irp_mj ne $irp_mj_last; - next; - } - - if (my($FileObject)= - /^FileObject=($hex):/) { - my $aref=$last_irp_mj{$process_thread}; - my $irp_mj_last=${$aref}[$#$aref]; - next if !$irp_mj_last || $irp_mj_last ne "IRP_MJ_CLOSE"; - IRP_MJ_CLOSE $FileObject; - next; - } - print "$_\n" if $filter; } for my $FileObject (keys(%FileObject)) { warn "EXIT: still CcInitializeCacheMap FileObject $FileObject"; next if !(my $FObject=FObject $FileObject); - delete_FObject $FObject; }