+Tracking of LogHandle.
[captive.git] / src / TraceFS / checktrace.pl
index 2949af2..5e7b258 100755 (executable)
@@ -31,7 +31,7 @@ my $ntfs_blocksize=0x200;
 # $Object->{"by"}="CcSomeFunction";
 # $Object->{"line_enter"}=123;
 # $Object->{"line_leave"}=124;
-# $Object->{"process_thread"}="0x12345678/0x12345678";
+# $Object->{"ProcessThread"}="0x12345678/0x12345678";
 # $Object->{"data"}[dataline]{"FileObject"}="0x12345678";
 # $Object->{"data"}[dataline]{"FileName"}="\filename" or undef() if NULL;
 # $Object->{"data"}[dataline]{"Flags"}="0x40100";
@@ -47,19 +47,35 @@ my $ntfs_blocksize=0x200;
 # $SharedCacheMap{$SharedCacheMap}{"FileSize"}="0x12345";
 # $SharedCacheMap{$SharedCacheMap}{"ref_count"}=1;
 # $SharedCacheMap{$SharedCacheMap}{"map"}="0x12345678" (Bcb);
-# $SharedCacheMap{$SharedCacheMap}{"pin"}{"0x1000"}="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
+# $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 %FileObject;
+my %LogHandle;
 my %SectionObjectPointer;
 my %SharedCacheMap;
 my %Bcb;
+my %MdlChain;
+my %LastLeave; # $ProcessThread=>[$Object,$Object,...]
+my $LastLeave; # ref copy for the current $ProcessThread
+my $ProcessThread;
+my %EnterLeave;
 
 END {
        print Data::Dumper->Dump([\%FileObject,\%SectionObjectPointer,\%SharedCacheMap,\%Bcb],
@@ -87,6 +103,14 @@ my($FileObject)=@_;
        return $FObject;
 }
 
+sub delete_FObject($)
+{
+my($FObject)=@_;
+
+       my $FileObject=$FObject->{"FileObject"};
+       delete $FileObject{$FileObject};
+}
+
 sub SObject($)
 {
 my($SectionObjectPointer)=@_;
@@ -119,6 +143,11 @@ 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
+               }
        delete $SharedCacheMap{$SharedCacheMap};
 }
 
@@ -384,11 +413,53 @@ sub BObject($)
 {
 my($Bcb)=@_;
 
+       cluck if !defined $Bcb;
        my $BObject=$Bcb{$Bcb};
        warn "Non-existent Bcb $Bcb" if !$BObject;
        return $BObject;
 }
 
+sub delete_BObject($)
+{
+my($BObject)=@_;
+
+       my $Bcb=$BObject->{"Bcb"};
+       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";
+                       }
+               }
+       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};
+}
+
+sub MObject($)
+{
+my($MdlChain)=@_;
+
+       cluck if !defined $MdlChain;
+       my $MObject=$MdlChain{$MdlChain};
+       warn "Non-existent MdlChain $MdlChain" if !$MObject;
+       return $MObject;
+}
+
 sub Bcb_conflict($;@)
 {
 my($CObject,@Bcb_list)=@_;
@@ -429,11 +500,15 @@ 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;
+               warn "INTERNAL: Trailing ref to Bcb $$ref" if $$ref;
+               }
        if ($$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 type ".$BObject->{"type"}." != old type $type $$ref type ".$BObject2->{"type"}
                                if $BObject->{"type"} ne $BObject2->{"type"};
                warn "new $type $Bcb Buffer ".$BObject->{"Buffer"}." != old $type $$ref Buffer ".$BObject2->{"Buffer"}
                                if $BObject->{"Buffer"} ne $BObject2->{"Buffer"};
@@ -562,6 +637,8 @@ sub CcPreparePinWrite_leave($$)
 my($Bcb,$Buffer)=@_;
 
        pin_new_leave $Bcb,$Buffer;
+       my $BObject=BObject $Bcb;
+       $BObject->{"dirty"}=1;
 }
 
 sub CcPinMappedData($$$)
@@ -585,7 +662,6 @@ my($Bcb)=@_;
        my $BObject2=BObject $Bcb2 if $Bcb2;
 
        pin_new_leave $Bcb,$Buffer;
-#      print STDERR "$.:".Dumper($Object);
 }
 
 sub CcSetDirtyPinnedData($$)
@@ -593,6 +669,191 @@ 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 $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 but some functions"
+                                               ." (".join(",",map({ $_->{"line_enter"}.":".$_->{"by"}; } @{$EnterLeave{$ProcessThread}})).")"
+                                               ." are nested:";
+#                              warn Dumper $CObject;
+                               # Direct IRP_MJ_WRITE can be from callbacked 'FlushToLsnRoutine'.
+                               # It can occur even from other callbacks ('DirtyPageRoutine' etc.)
+                               # but it was not needed here yet.
+                               } if @{$EnterLeave{$ProcessThread}}
+                                               && !(${$EnterLeave{$ProcessThread}}[$#{$EnterLeave{$ProcessThread}}]->{"by"} eq "FlushToLsnRoutine");
+               warn "Non-Bcb IRP_MJ_WRITE ByteOffset=$ByteOffset but FlushToLsnRoutine was preceding"
+                               if $FlushToLsnRoutine;
+               return;
+               }
+       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"
+                       if $FlushToLsnRoutine && !($CObject->{"AcquireForLazyWrite"}>=1);
+       # Keep $BObject->{"dirty"} there for &delete_BObject sanity checks.
+       delete_BObject $BObject if $BObject->{"dirty"} && !$BObject->{"ref_count"};
+}
+
+sub IRP_MJ_WRITE_leave()
+{
+       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 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";
+       return if !(my $CObject=CObject $Object->{"data"}[0]{"SharedCacheMap"});
+       $CObject->{"AcquireForLazyWrite"}++;
+}
+
+sub ReleaseFromLazyWrite_leave()
+{
+       return if !(my $CObject=CObject $Object->{"data"}[0]{"SharedCacheMap"});
+       warn "Invalid 'AcquireForLazyWrite' value ".$CObject->{"AcquireForLazyWrite"}
+                       if !($CObject->{"AcquireForLazyWrite"}>=1);
+       $CObject->{"AcquireForLazyWrite"}--;
 }
 
 sub CcRemapBcb($)
@@ -617,23 +878,18 @@ my($Bcb)=@_;
 
        return if !(my $BObject=BObject $Bcb);
        return if --$BObject->{"ref_count"};
-       return if !(my $CObject=CObject $BObject->{"SharedCacheMap"});
-       if ($BObject->{"type"} eq "map") {
-               for my $pin (values(%{$CObject->{"pin"}})) {
-                       warn "unpin map but CcPinMappedData pin $pin still exists"
-                                       if $Bcb{$pin}->{"by"} eq "CcPinMappedData";
-                       }
-               }
-       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);
-               if ($$ref && $$ref eq $Bcb) {
-                       $$ref=undef();
-                       delete $CObject->{"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);
+               return;
                }
-       delete $Bcb{$Bcb};
+       delete_BObject $BObject;
 }
 
 sub CcUnpinData($)
@@ -681,39 +937,39 @@ sub IRP_MJ_CLOSE_leave()
 #                                      if $SectionObjectPointer && $CObject->{"ref_count"};
                        }
                }
-       delete $FileObject{$FileObject};
+       delete_FObject $FObject;
 }
 
 
 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:
        # Do not use '\bTraceFS' as there really can be precediny _any_ data.
        s#^.*?TraceFS[(]($hex/$hex)[)]: ## or do { print "$_\n" if $filter; next; };
-       my($process_thread)=($1);
+       $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;
                }
        elsif (/^leave: (\w+)/) {
-               warn "Empty pop stack during 'leave' of $1" if !($Object=pop @{$enter_leave{$process_thread}});
+               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|'(.*)'),Flags=($hex),SectionObjectPointer=($hex),->SharedCacheMap=($hex)/) {
-               my $aref=$enter_leave{$process_thread};
+               my $aref=$EnterLeave{$ProcessThread};
                warn "Empty stack during 'data' line" if !($Object=${$aref}[$#$aref]);
                my $data={
                                "FileObject"=>$FileObject,
@@ -770,6 +1026,18 @@ while (<>) {
                        }
                next;
                }
+       elsif (my($ByteOffset,$Length)=
+                       /^WRITE: ByteOffset=($hex),Length=($hex)/) {
+               my $aref=$EnterLeave{$ProcessThread};
+               warn "Empty stack during 'data' line" if !($Object=${$aref}[$#$aref]);
+               $Object->{"WRITE"}={
+                       "ByteOffset"=>$ByteOffset,
+                       "Length"=>$Length,
+                       };
+               next;
+               }
+
+       $LastLeave=${$LastLeave{$ProcessThread}}[$#{$LastLeave{$ProcessThread}}-1];
 
        if (my($r)=
                        /^leave: IRP_MJ_\w+: r=($hex)/) {
@@ -864,6 +1132,55 @@ while (<>) {
                next;
                }
 
+       if (my($LogHandle,$Lsn)=
+                       /^enter: FlushToLsnRoutine: LogHandle=($hex),Lsn=($hex)/) {
+               FlushToLsnRoutine $LogHandle,$Lsn;
+               next;
+               }
+
+       if (/^leave: IRP_MJ_WRITE\b/) {
+               IRP_MJ_WRITE_leave;
+               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;