Fixed and rewritten for better extensibility.
[captive.git] / src / TraceFS / checktrace.pl
index 6439405..f0774c9 100755 (executable)
 
 use strict;
 use warnings;
-use Carp qw(cluck confess);
 use Data::Dumper;
 
 
 my $filter=0;
 $Data::Dumper::Sortkeys=1;
+my $ntfs_blocksize=0x200;
 
-my %init;
+# $Object{"by"}="CcSomeFunction";
+# $Object{"line_enter"}=123;
+# $Object{"line_leave"}=124;
+# $Object{"process_thread"}="0x12345678/0x12345678";
+# $FileObject{$FileObject}{"FileObject"}="0x12345678";
+# $FileObject{$FileObject}{"FileSize"}="0x12345";
+# $FileObject{$FileObject}{"map"}="0x12345678" (Bcb);
+# $FileObject{$FileObject}{"pin"}{"0x1000"}="0x12345678" (Bcb);
+# $FileObject{$FileObject}{"PinAccess"}=0 or 1;
+# $Bcb{$Bcb}{"Bcb"}="0x12345678";
+# $Bcb{$Bcb}{"FileObject"}="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"
+
+my %FileObject;
 my %Bcb;
 
 END {
-       print Data::Dumper->Dump([\%init,\%Bcb],[qw(%init %Bcb)]) if !$filter;
+       print Data::Dumper->Dump([\%FileObject,\%Bcb],[qw(%FileObject %Bcb)]) if !$filter;
        }
 
+my $Object;
+
+sub tohex($)
+{
+my($num)=@_;
+
+       return sprintf("0x%X",$num);
+}
+
+sub CcInitializeCacheMap($$$$$)
+{
+my($FileObject,$AllocationSize,$FileSize,$ValidDataLength,$PinAccess)=@_;
+
+       $ValidDataLength=$FileSize if $ValidDataLength==eval("0x".("F"x8));
+       warn "FileObject $FileObject registered twice" if $FileObject{$FileObject};
+       $FileObject{$FileObject}=$Object;
+       $Object->{"FileObject"}=$FileObject;
+       $Object->{"FileSize"}=tohex($FileSize);
+       $Object->{"map"}=undef();
+       $Object->{"pin"}={};
+       $Object->{"PinAccess"}=$PinAccess;
+       CcSetFileSizes($FileObject,$AllocationSize,$FileSize,$ValidDataLength);
+}
+
+sub FObject($)
+{
+my($FileObject)=@_;
+
+       my $FObject=$FileObject{$FileObject};
+       warn "Non-existent FileObject $FileObject" if !$FObject;
+       return $FObject;
+}
+
+sub delete_FObject($)
+{
+my($FObject)=@_;
+
+       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 (keys(%{$FObject->{"pin"}}));
+       delete $FileObject{$FileObject};
+}
+
+sub CcUninitializeCacheMap($$)
+{
+my($FileObject,$TruncateSize)=@_;
+
+       # CcUninitializeCacheMap() w/o CcInitializeCacheMap() is allowed:
+       return if !$FileObject{$FileObject};
+       return if !(my $FObject=FObject $FileObject);
+       delete_FObject $FObject;
+}
+
+sub CcSetFileSizes($$$$)
+{
+my($FileObject,$AllocationSize,$FileSize,$ValidDataLength)=@_;
+
+       return if !(my $FObject=FObject $FileObject);
+       # $ValidDataLength can be > $FObject->{"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;
+       $FObject->{"FileSize"}=tohex($FileSize);
+}
+
+sub BObject($)
+{
+my($Bcb)=@_;
+
+       my $BObject=$Bcb{$Bcb};
+       warn "Non-existent Bcb $Bcb" if !$BObject;
+       return $BObject;
+}
+
+sub Bcb_conflict($;@)
+{
+my($FObject,@Bcb_list)=@_;
+
+       my $arg=0;
+       my %check=(
+               "map"=>$FObject->{"map"},
+               map(("arg".($arg++)=>$_),@Bcb_list),
+               %{$FObject->{"pin"}},
+               );
+       my %reversed;
+       my $BufferBase; # relativized to FileOffset 0
+       while (my($key,$val)=each(%check)) {
+               next if !defined $val;
+               warn "Conflicting Bcb $val of keys $key and ".$reversed{$val}." of FileObject ".$FObject->{"FileObject"}
+                               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 $Buffer=eval $BObject->{"Buffer"};
+                       $Buffer-=eval($BObject->{"FileOffset"}) if exists $BObject->{"FileOffset"};
+                       warn "Non-matching Buffer base ".tohex($Buffer)." for Bcb $val"
+                                       if defined($BufferBase) && $Buffer!=$BufferBase;
+                       $BufferBase=$Buffer;
+                       }
+               $reversed{$val}=$key;
+               }
+}
+
+# New $BObject will always be forced as the last stored reference.
+sub Bcb_checkref($$)
+{
+my($BObject,$ref)=@_;
+
+       return if !(my $FObject=FObject $BObject->{"FileObject"});
+       my $type=$BObject->{"type"};
+       my $Bcb=$BObject->{"Bcb"};
+       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"}
+                               if $BObject->{"type"} ne $BObject2->{"type"};
+               if (!defined $BObject->{"Buffer"}) {
+                       warn "HERE";
+                       }
+               warn "new $type $Bcb Buffer ".$BObject->{"Buffer"}." != old $type $$ref Buffer ".$BObject2->{"Buffer"}
+                               if $BObject->{"Buffer"} ne $BObject2->{"Buffer"};
+               }
+       if ($$ref && $$ref eq $Bcb) {
+               $BObject->{"ref_count"}+=$Bcb{$$ref}->{"ref_count"};
+               $$ref=undef();
+               }
+       $Bcb{$Bcb}=$BObject;    # &Bcb_conflict needs this reference
+       Bcb_conflict $FObject,$Bcb;
+       $$ref=$Bcb;
+}
+
+sub map_new($;$$)
+{
+my($FileObject,$FileOffset,$Length)=@_;
+
+       return if !(my $FObject=FObject $FileObject);
+       if (defined($FileOffset) && defined($Length)) {
+               warn "Mapping data (end ".tohex($FileOffset+$Length).") out of FileSize ".$FObject->{"FileSize"}
+                               if $FileOffset+$Length>eval($FObject->{"FileSize"});
+               }
+       $Object->{"FileObject"}=$FileObject;
+       if (defined $FileOffset) {
+               $Object->{"FileOffset"}=tohex($FileOffset);
+               }
+       $Object->{"type"}="map";
+       $Object->{"ref_count"}=1;
+}
+
+sub map_new_leave($;$)
+{
+my($Bcb,$Buffer)=@_;
+
+       $Object->{"Bcb"}=$Bcb;
+       return if !(my $FObject=FObject $Object->{"FileObject"});
+
+       if (defined $Buffer) {
+               $Object->{"Buffer"}=tohex(eval($Buffer)-(eval($Object->{"FileOffset"}) || 0));
+               }
+       delete $Object->{"FileOffset"};
+
+       my $ref=\$FObject->{"map"};
+       Bcb_checkref $Object,$ref;
+}
+
+sub CcMapData($$$)
+{
+my($FileObject,$FileOffset,$Length)=@_;
+
+       map_new $FileObject,$FileOffset,$Length;
+}
+
+sub CcMapData_leave($$)
+{
+my($Bcb,$Buffer)=@_;
+
+       map_new_leave $Bcb,$Buffer;
+}
+
+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"});
+       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->{"FileOffset"}=tohex($FileOffset);
+       $Object->{"type"}="pin";
+       $Object->{"ref_count"}=1;
+}
+
+sub pin_new_leave($$)
+{
+my($Bcb,$Buffer)=@_;
+
+       $Object->{"Bcb"}=$Bcb;
+       return if !(my $FObject=FObject $Object->{"FileObject"});
+       $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"}};
+       Bcb_checkref $Object,$ref;
+}
+
+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)=@_;
+
+       pin_new $FileObject,$FileOffset,$Length;
+}
+
+sub CcPreparePinWrite_leave($$)
+{
+my($Bcb,$Buffer)=@_;
+
+       pin_new_leave $Bcb,$Buffer;
+}
+
+sub CcPinMappedData($$$)
+{
+my($FileObject,$FileOffset,$Length)=@_;
+
+       pin_new $FileObject,$FileOffset,$Length;
+}
+
+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"}));
+       pin_new_leave $Bcb,$Buffer;
+}
+
+sub CcRemapBcb($)
+{
+my($Bcb)=@_;
+
+       return if !(my $BObject=BObject $Bcb);
+       map_new $BObject->{"FileObject"};
+       $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"};
+       return if !(my $FObject=FObject $BObject->{"FileObject"});
+       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 || "<undef>")
+                               if !$$ref || $$ref ne $Bcb;
+               if ($$ref && $$ref eq $Bcb) {
+                       $$ref=undef();
+                       delete $FObject->{"pin"}{$BObject->{"FileOffset"}} if $BObject->{"type"} eq "pin";
+                       }
+               }
+       delete $Bcb{$Bcb};
+}
+
+sub CcUnpinData($)
+{
+my($Bcb)=@_;
+
+       unpin $Bcb;
+}
+
+sub CcUnpinDataForThread($)
+{
+my($Bcb)=@_;
+
+       unpin $Bcb;
+}
+
+sub IRP_MJ_CLOSE($)
+{
+my($FileObject)=@_;
+
+       return if !$FileObject{$FileObject};
+       return if !(my $FObject=FObject $FileObject);
+       warn "CcUnpinData() not called for FileObject $FileObject before IRP_MJ_CLOSE";
+       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;
+my %enter_leave;
 while (<>) {
        chomp;
-       s#^ *TraceFS[(]($hex)/($hex)[)]: ## or do { print "$_\n" if $filter; next; };
-       my($process,$thread)=($1,$2);
+       s/\r$//;
+       s#^ *TraceFS[(]($hex/$hex)[)]: ## or do { print "$_\n" if $filter; next; };
+       my($process_thread)=($1);
 
-       if (my($FileObject,$AllocationSize,$FileSize,$ValidDataLength)=
+       $Object=undef();
+       if (/^enter: (\w+)/) {
+               $Object={};
+               $Object->{"by"}=$1;
+               $Object->{"line_enter"}=$.;
+               $Object->{"process_thread"}=$process_thread;
+               push @{$enter_leave{$process_thread}},$Object;
+               }
+       if (/^leave: (\w+)/) {
+               warn "Empty pop stack during 'leave' of $1" if !($Object=pop @{$enter_leave{$process_thread}});
+               warn "Non-matching popped 'by' ".$Object->{"by"}." ne current 'leave' $1"
+                               if $Object->{"by"} ne $1;
+               $Object->{"line_leave"}=$.;
+               }
+
+       if (my($FileObject,$AllocationSize,$FileSize,$ValidDataLength,$PinAccess)=
                        /^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"=>{},
-                       };
+               CcInitializeCacheMap $FileObject,eval($AllocationSize),eval($FileSize),eval($ValidDataLength),eval($PinAccess);
                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($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 (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($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($Bcb)=
+                       /^enter: CcUnpinDataForThread: Bcb=($hex)/) {
+               CcUnpinDataForThread $Bcb;
                next;
                }
 
        if (my($irp_mj)=
-                       /^enter: (IRP_MJ_.*)/) {
-               $last_irp_mj=$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)=
-                       /^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};
+                       /^FileObject=($hex):/) {
+               my $aref=$last_irp_mj{$process_thread};
+               my $irp_mj_last=${$aref}[$#$aref];
+               next if $irp_mj_last ne "IRP_MJ_CLOSE";
+               IRP_MJ_CLOSE $FileObject;
                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);
+       delete_FObject $FObject;
        }