use strict;
use warnings;
-use Carp qw(cluck confess);
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->{"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}{"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}{"SharedCacheMap"}="0x12345678";
+# $Bcb{$Bcb}{"type"}="pin" or "map";
+# $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 %init;
+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([\%init,\%Bcb],[qw(%init %Bcb)]) if !$filter;
+ print Data::Dumper->Dump([\%FileObject,\%SectionObjectPointer,\%SharedCacheMap,\%Bcb],
+ [qw(%FileObject %SectionObjectPointer %SharedCacheMap %Bcb)]) if !$filter;
}
+my $Object;
+
+sub tohex($)
+{
+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));
+ $Object->{"ref_count"}=1;
+ $Object->{"AllocationSize"}=tohex($AllocationSize);
+ $Object->{"FileSize"}=tohex($FileSize);
+ $Object->{"ValidDataLength"}=tohex($ValidDataLength);
+ $Object->{"map"}=undef();
+ $Object->{"pin"}={};
+ $Object->{"PinAccess"}=$PinAccess;
+ $Object->{"FileObject"}=$FileObject;
+}
+
+sub CcInitializeCacheMap_leave()
+{
+ 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,
+ };
+
+ CcSetFileSizes($FileObject,map({ eval($Object->{$_}); } qw(AllocationSize FileSize ValidDataLength)));
+ delete $Object->{$_} for (qw(FileObject ValidDataLength));
+}
+
+sub CcUninitializeCacheMap($$)
+{
+my($FileObject,$TruncateSize)=@_;
+
+ $Object->{"FileObject"}=$FileObject;
+}
+
+sub CcUninitializeCacheMap_leave($)
+{
+my($r)=@_;
+
+ 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);
+ 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 $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)
+ if 0!=($AllocationSize%$ntfs_blocksize);
+ # $AllocationSize can be higher
+ warn "FileSize ".tohex($FileSize)." > AllocationSize ".tohex($AllocationSize)
+ if $FileSize>$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};
+ 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 || "<undef>")
+ 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($CObject,@Bcb_list)=@_;
+
+ my $arg=0;
+ my %check=(
+ "map"=>$CObject->{"map"},
+ map(("arg".($arg++)=>$_),@Bcb_list),
+ %{$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 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.
+ 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".($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;
+ }
+ $reversed{$val}=$key;
+ }
+}
+
+# New $BObject will always be forced as the last stored reference.
+sub Bcb_checkref($$)
+{
+my($BObject,$ref)=@_;
+
+ return if !(my $CObject=CObject $BObject->{"SharedCacheMap"});
+ my $type=$BObject->{"type"};
+ my $Bcb=$BObject->{"Bcb"};
+ if ($$ref && $Bcb ne $$ref) {
+ my $BObject2=$Bcb{$$ref};
+ 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"} || "<undef>")
+ ." != old $type $Bcb Buffer ".($BObject2->{"Buffer"} || "<undef>")
+# ." XXX line $. ".Dumper($BObject,$BObject2)
+ if $BObject->{"Buffer"} ne $BObject2->{"Buffer"};
+ }
+ 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 $CObject,($$ref && $$ref eq $Bcb ? () : ($Bcb));
+ $$ref=$Bcb;
+}
+
+sub map_new($;$$)
+{
+my($SharedCacheMap,$FileOffset,$Length)=@_;
+
+ return if !(my $CObject=CObject $SharedCacheMap);
+ if (defined($FileOffset) && defined($Length)) {
+ warn "Mapping data (end ".tohex($FileOffset+$Length).") out of FileSize ".$CObject->{"FileSize"}
+ if $FileOffset+$Length>eval($CObject->{"FileSize"});
+ }
+ $Object->{"SharedCacheMap"}=$CObject->{"SharedCacheMap"};
+ if (defined $FileOffset) {
+ $Object->{"FileOffset"}=tohex($FileOffset);
+ }
+ $Object->{"type"}="map";
+ $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 $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=\$CObject->{"map"};
+ # There may exist some pin bcbs even if we are creating the new map bcb.
+ Bcb_checkref $Object,$ref;
+}
+
+sub CcMapData($$$)
+{
+my($FileObject,$FileOffset,$Length)=@_;
+
+ map_new_from_FileObject $FileObject,$FileOffset,$Length;
+}
+
+sub CcMapData_leave($$)
+{
+my($Bcb,$Buffer)=@_;
+
+ map_new_leave $Bcb,$Buffer;
+}
+
+sub pin_new($$$)
+{
+my($FileObject,$FileOffset,$Length)=@_;
+
+ 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->{"SharedCacheMap"}=$CObject->{"SharedCacheMap"};
+ $Object->{"FileOffset"}=tohex($FileOffset);
+ $Object->{"type"}="pin";
+ $Object->{"ref_count"}=1;
+}
+
+sub pin_new_leave($$)
+{
+my($Bcb,$Buffer)=@_;
+
+ $Object->{"Bcb"}=$Bcb;
+ 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 $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($$$)
+{
+my($FileObject,$FileOffset,$Length)=@_;
+
+ pin_new $FileObject,$FileOffset,$Length;
+}
+
+sub CcPinRead_leave($$)
+{
+my($Bcb,$Buffer)=@_;
+
+ pin_new_leave $Bcb,$Buffer;
+}
+
+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;
+}
+
+sub CcPreparePinWrite_leave($$)
+{
+my($Bcb,$Buffer)=@_;
+
+ pin_new_leave $Bcb,$Buffer;
+ my $BObject=BObject $Bcb;
+ $BObject->{"dirty"}=1;
+}
+
+sub CcPinMappedData($$$)
+{
+my($FileObject,$FileOffset,$Length)=@_;
+
+ pin_new $FileObject,$FileOffset,$Length;
+}
+
+sub CcPinMappedData_leave($)
+{
+my($Bcb)=@_;
+
+ 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;
+
+ pin_new_leave $Bcb,$Buffer;
+}
+
+sub CcSetDirtyPinnedData($$)
+{
+my($Bcb,$Lsn)=@_;
+
+ return if !(my $BObject=BObject $Bcb);
+ # 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($)
+{
+my($Bcb)=@_;
+
+ return if !(my $BObject=BObject $Bcb);
+ map_new $BObject->{"SharedCacheMap"};
+ $Object->{"Buffer"}=tohex(eval($BObject->{"Buffer"})-eval($BObject->{"FileOffset"} || 0));
+}
+
+sub CcRemapBcb_leave($)
+{
+my($r)=@_;
+
+ map_new_leave $r;
+}
+
+sub unpin($)
+{
+my($Bcb)=@_;
+
+ return if !(my $BObject=BObject $Bcb);
+ return if --$BObject->{"ref_count"};
+ 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_BObject $BObject;
+}
+
+sub CcUnpinData($)
+{
+my($Bcb)=@_;
+
+ unpin $Bcb;
+}
+
+sub CcUnpinDataForThread($)
+{
+my($Bcb)=@_;
+
+ unpin $Bcb;
+}
+
+sub CcSetBcbOwnerPointer($$)
+{
+my($Bcb,$OwnerPointer)=@_;
+
+ return if !(my $BObject=BObject $Bcb);
+ warn "CcSetBcbOwnerPointer() on map Bcb $Bcb" if $BObject->{"type"} ne "pin";
+ return if !(my $CObject=CObject $BObject->{"SharedCacheMap"});
+ warn "Double CcSetBcbOwnerPointer() on Bcb $Bcb" if defined $BObject->{"OwnerPointer"};
+ my $val=$CObject->{"pin"}{$BObject->{"FileOffset"}};
+ warn "CcSetBcbOwnerPointer() on unregistered pin Bcb $Bcb" if !$val || $val ne $Bcb;
+ delete $CObject->{"pin"}{$BObject->{"FileOffset"}} if $val && $val eq $Bcb;
+ $BObject->{"OwnerPointer"}=$OwnerPointer;
+ CObject_Buffer_check $CObject;
+}
+
+sub IRP_MJ_CLOSE_leave()
+{
+ my $FileObject=$Object->{"data"}[0]{"FileObject"};
+# # IRP_MJ_CLOSE of FileObject w/o CcInitializeCacheMap()?
+# return if !$FileObject{$FileObject};
+ return if !(my $FObject=FObject $FileObject);
+ 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;
+}
+
+
local $_;
my $hex='0x[\dA-F]+';
-my(@lastmap_CcMapData,@lastmap_CcPinRead,@lastmap_CcPreparePinWrite,@lastmap_CcPinMappedData,@lastmap_CcRemapBcb);
-my $last_irp_mj;
+my %last_irp_mj;
while (<>) {
chomp;
- s#^ *TraceFS[(]($hex)/($hex)[)]: ## or do { print "$_\n" if $filter; next; };
- my($process,$thread)=($1,$2);
+ s/\r$//;
+ # We may get some foreign garbage without '\n' before our debug data line:
+ # 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;
- if (my($FileObject,$AllocationSize,$FileSize,$ValidDataLength)=
- /^enter: CcInitializeCacheMap: FileObject=($hex),FileSizes,->AllocationSize=($hex),->FileSize=($hex),->ValidDataLength=($hex),PinAccess=([01]),/) {
- $AllocationSize=eval($AllocationSize);
- $FileSize=eval($FileSize);
- 0==($AllocationSize%0x200) or die;
- int($AllocationSize/0x200)==int(($FileSize+0x1FF)/0x200) or die;
- $ValidDataLength eq "0x".("F"x8) or eval($ValidDataLength)==$FileSize or die;
- !exists $init{$FileObject} or die;
- $init{$FileObject}={
- "FileObject"=>$FileObject,
- "size"=>$FileSize,
- "unmaps"=>0,
- "maps"=>[],
- "line"=>$.,
- "Bcb_map"=>undef(),
- "Bcb_pin"=>{},
+ $Object=undef();
+ if (/^enter: (\w+)/) {
+ $Object={};
+ $Object->{"by"}=$1;
+ $Object->{"line_enter"}=$.;
+ $Object->{"ProcessThread"}=$ProcessThread;
+ push @{$EnterLeave{$ProcessThread}},$Object;
+ }
+ 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)=
+ /^enter: CcInitializeCacheMap: FileObject=($hex),FileSizes,->AllocationSize=($hex),->FileSize=($hex),->ValidDataLength=($hex),PinAccess=([01]),/) {
+ 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),/) {
- $TruncateSize=eval($TruncateSize);
- next if !exists $init{$FileObject};
- $init{$FileObject}->{"unmaps"}==@{$init{$FileObject}->{"maps"}} or die;
- delete $init{$FileObject};
+ 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)/) {
+ CcSetFileSizes $FileObject,eval($AllocationSize),eval($FileSize),eval($ValidDataLength);
+ 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/) {
- $FileOffset=eval $FileOffset;
- $Length=eval $Length;
- die if !(my $reg=$init{$FileObject});
- die if $FileOffset+$Length>$reg->{"size"};
- my $newmap={
- "FileOffset"=>$FileOffset,
- "Length"=>$Length,
- "init"=>$reg,
- "line"=>$.,
- "by"=>"CcMapData",
- };
- push @{$reg->{"maps"}},$newmap;
- push @lastmap_CcMapData,$newmap;
+ CcMapData $FileObject,eval($FileOffset),eval($Length);
next;
}
if (my($Bcb,$Buffer)=
/^leave: CcMapData: r=1,Bcb=($hex),Buffer=($hex)/) {
- die if !(my $lastmap=pop @lastmap_CcMapData);
- $lastmap->{"Bcb"}=$Bcb;
- $lastmap->{"Buffer"}=$Buffer;
- $lastmap->{"process"}=$process;
- $lastmap->{"thread"}=$thread;
- $Bcb{$Bcb}=$lastmap->{"init"};
- die if $lastmap->{"init"}->{"Bcb_map"} && $lastmap->{"init"}->{"Bcb_map"} ne $Bcb;
- $lastmap->{"init"}->{"Bcb_map"}=$Bcb;
- for my $pinoffs (keys(%{$lastmap->{"init"}->{"Bcb_pin"}})) {
- die $pinoffs if $lastmap->{"init"}->{"Bcb_pin"}->{$pinoffs} eq $Bcb;
- }
+ CcMapData_leave $Bcb,$Buffer;
next;
}
if (my($FileObject,$FileOffset,$Length)=
/^enter: CcPinRead: FileObject=($hex),FileOffset=($hex),Length=($hex),Flags=0x1/) {
- $FileOffset=eval $FileOffset;
- $Length=eval $Length;
- die if !(my $reg=$init{$FileObject});
- die if $FileOffset+$Length>$reg->{"size"};
- my $newmap={
- "FileOffset"=>$FileOffset,
- "Length"=>$Length,
- "init"=>$reg,
- "line"=>$.,
- "by"=>"CcPinRead",
- };
- push @{$reg->{"maps"}},$newmap;
- push @lastmap_CcPinRead,$newmap;
+ CcPinRead $FileObject,eval($FileOffset),eval($Length);
next;
}
if (my($Bcb,$Buffer)=
/^leave: CcPinRead: r=1,Bcb=($hex),Buffer=($hex)/) {
- die if !(my $lastmap=pop @lastmap_CcPinRead);
- $lastmap->{"Bcb"}=$Bcb;
- $lastmap->{"Buffer"}=$Buffer;
- $lastmap->{"process"}=$process;
- $lastmap->{"thread"}=$thread;
- $Bcb{$Bcb}=$lastmap->{"init"};
- my $myoffs=$lastmap->{"FileOffset"}&~0xFFF;
- die if defined $lastmap->{"init"}->{"Bcb_pin"}->{$myoffs} && $lastmap->{"init"}->{"Bcb_pin"}->{$myoffs} ne $Bcb;
- for my $pinoffs (keys(%{$lastmap->{"init"}->{"Bcb_pin"}})) {
- next if $pinoffs==$myoffs;
- die $pinoffs if $lastmap->{"init"}->{"Bcb_pin"}->{$pinoffs} eq $Bcb;
- }
- $lastmap->{"init"}->{"Bcb_pin"}->{$myoffs}=$Bcb;
- die if $lastmap->{"init"}->{"Bcb_map"} && $lastmap->{"init"}->{"Bcb_map"} eq $Bcb;
+ CcPinRead_leave $Bcb,$Buffer;
next;
}
if (my($FileObject,$FileOffset,$Length)=
/^enter: CcPreparePinWrite: FileObject=($hex),FileOffset=($hex),Length=($hex),Zero=([01]),Flags=0x1/) {
- $FileOffset=eval $FileOffset;
- $Length=eval $Length;
- die if !(my $reg=$init{$FileObject});
- die if $FileOffset+$Length>$reg->{"size"};
- my $newmap={
- "FileOffset"=>$FileOffset,
- "Length"=>$Length,
- "init"=>$reg,
- "line"=>$.,
- "by"=>"CcPreparePinWrite",
- };
- push @{$reg->{"maps"}},$newmap;
- push @lastmap_CcPreparePinWrite,$newmap;
+ CcPreparePinWrite $FileObject,eval($FileOffset),eval($Length);
next;
}
if (my($Bcb,$Buffer)=
/^leave: CcPreparePinWrite: r=1,Bcb=($hex),Buffer=($hex)/) {
- die if !(my $lastmap=pop @lastmap_CcPreparePinWrite);
- $lastmap->{"Bcb"}=$Bcb;
- $lastmap->{"Buffer"}=$Buffer;
- $lastmap->{"process"}=$process;
- $lastmap->{"thread"}=$thread;
- $Bcb{$Bcb}=$lastmap->{"init"};
- my $myoffs=$lastmap->{"FileOffset"}&~0xFFF;
- die if defined $lastmap->{"init"}->{"Bcb_pin"}->{$myoffs} && $lastmap->{"init"}->{"Bcb_pin"}->{$myoffs} ne $Bcb;
- for my $pinoffs (keys(%{$lastmap->{"init"}->{"Bcb_pin"}})) {
- next if $pinoffs==$myoffs;
- die $pinoffs if $lastmap->{"init"}->{"Bcb_pin"}->{$pinoffs} eq $Bcb;
- }
- $lastmap->{"init"}->{"Bcb_pin"}->{$myoffs}=$Bcb;
- die if $lastmap->{"init"}->{"Bcb_map"} && $lastmap->{"init"}->{"Bcb_map"} eq $Bcb;
+ CcPreparePinWrite_leave $Bcb,$Buffer;
next;
}
if (my($FileObject,$FileOffset,$Length)=
/^enter: CcPinMappedData: FileObject=($hex),FileOffset=($hex),Length=($hex),Flags=0x1/) {
- $FileOffset=eval $FileOffset;
- $Length=eval $Length;
- die if !(my $reg=$init{$FileObject});
- die if $FileOffset+$Length>$reg->{"size"};
- my $newmap={
- "FileOffset"=>$FileOffset,
- "Length"=>$Length,
- "init"=>$reg,
- "line"=>$.,
- "by"=>"CcPinMappedData",
- };
- push @{$reg->{"maps"}},$newmap;
- push @lastmap_CcPinMappedData,$newmap;
+ CcPinMappedData $FileObject,eval($FileOffset),eval($Length);
next;
}
- if (my($Bcb,$Buffer)=
+ if (my($Bcb)=
/^leave: CcPinMappedData: r=1,Bcb=($hex)/) {
- die if !(my $lastmap=pop @lastmap_CcPinMappedData);
- $lastmap->{"Bcb"}=$Bcb;
- $lastmap->{"process"}=$process;
- $lastmap->{"thread"}=$thread;
- $Bcb{$Bcb}=$lastmap->{"init"};
- my $myoffs=$lastmap->{"FileOffset"}&~0xFFF;
- die if defined $lastmap->{"init"}->{"Bcb_pin"}->{$myoffs} && $lastmap->{"init"}->{"Bcb_pin"}->{$myoffs} ne $Bcb;
- for my $pinoffs (keys(%{$lastmap->{"init"}->{"Bcb_pin"}})) {
- next if $pinoffs==$myoffs;
- die $pinoffs if $lastmap->{"init"}->{"Bcb_pin"}->{$pinoffs} eq $Bcb;
- }
- $lastmap->{"init"}->{"Bcb_pin"}->{$myoffs}=$Bcb;
- die if $lastmap->{"init"}->{"Bcb_map"} && $lastmap->{"init"}->{"Bcb_map"} eq $Bcb;
+ CcPinMappedData_leave $Bcb;
+ next;
+ }
+
+ if (my($BcbVoid,$Lsn)=
+ /^enter: CcSetDirtyPinnedData: BcbVoid=($hex),Lsn=($hex)/) {
+ CcSetDirtyPinnedData $BcbVoid,$Lsn;
+ 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)/) {
- die if !(my $reg=$Bcb{$Bcb});
- my $newmap={
- "remap"=>1,
- "Bcb"=>$Bcb,
- "init"=>$reg,
- "line"=>$.,
- "by"=>"CcRemapBcb of $Bcb",
- };
- push @{$reg->{"maps"}},$newmap;
- push @lastmap_CcRemapBcb,$newmap;
+ CcRemapBcb $Bcb;
+ next;
}
if (my($r)=
/^leave: CcRemapBcb: r=($hex)/) {
- die if !(my $lastmap=pop @lastmap_CcRemapBcb);
- $lastmap->{"process"}=$process;
- $lastmap->{"thread"}=$thread;
- die "CcRemapBcb enterBcb ".$lastmap->{"Bcb"}." != leaveBcb ".$r
- if $lastmap->{"Bcb"} ne $r;
+ CcRemapBcb_leave $r;
next;
}
if (my($Bcb)=
- /^enter: CcUnpinData(?:|ForThread): Bcb=($hex)/) {
- die if !(my $regbcb=$Bcb{$Bcb});
- $regbcb->{"unmaps"}++;
- die if $regbcb->{"unmaps"}>@{$regbcb->{"maps"}};
- if ($regbcb->{"unmaps"}==@{$regbcb->{"maps"}}) {
- warn "Full CcUnPinData for FileObject ".$regbcb->{"FileObject"};
-# $regbcb->{"unmaps"}=0;
-# $regbcb->{"maps"}=[];
- $regbcb->{"unmaps"}++;
- push @{$regbcb->{"maps"}},{
- "unpinned"=>"=========================================",
- "line"=>$.,
- };
- }
- $regbcb->{"Bcb_map"}=undef() if $regbcb->{"Bcb_map"} && $regbcb->{"Bcb_map"} eq $Bcb;
- for my $pinoffs (keys(%{$regbcb->{"Bcb_pin"}})) {
- delete $regbcb->{"Bcb_pin"}->{$pinoffs} if $regbcb->{"Bcb_pin"}->{$pinoffs} eq $Bcb;
- }
+ /^enter: CcUnpinData: Bcb=($hex)/) {
+ CcUnpinData $Bcb;
next;
}
-
- if (my($irp_mj)=
- /^enter: (IRP_MJ_.*)/) {
- $last_irp_mj=$irp_mj;
+ if (my($Bcb)=
+ /^enter: CcUnpinDataForThread: Bcb=($hex)/) {
+ CcUnpinDataForThread $Bcb;
next;
}
- if (my($FileObject)=
- /^debug_irp: IoStackLocation->FileObject=($hex):/) {
- next if $last_irp_mj ne "IRP_MJ_CLOSE";
- warn "IRP_MJ_CLOSE: still mapped $FileObject" if $init{$FileObject}->{"unmaps"}!=@{$init{$FileObject}->{"maps"}};
- delete $init{$FileObject};
+ if (my($Bcb,$OwnerPointer)=
+ /^enter: CcSetBcbOwnerPointer: Bcb=($hex),OwnerPointer=($hex)/) {
+ CcSetBcbOwnerPointer $Bcb,$OwnerPointer;
next;
}
print "$_\n" if $filter;
}
-for my $key (keys(%init)) {
- warn "EXIT: still mapped $key" if $init{$key}->{"unmaps"}!=@{$init{$key}->{"maps"}};
+for my $FileObject (keys(%FileObject)) {
+ warn "EXIT: still CcInitializeCacheMap FileObject $FileObject";
+ next if !(my $FObject=FObject $FileObject);
}