# $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";
# $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],
return $FObject;
}
+sub delete_FObject($)
+{
+my($FObject)=@_;
+
+ my $FileObject=$FObject->{"FileObject"};
+ delete $FileObject{$FileObject};
+}
+
sub SObject($)
{
my($SectionObjectPointer)=@_;
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};
}
{
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)=@_;
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"};
my($Bcb,$Buffer)=@_;
pin_new_leave $Bcb,$Buffer;
+ my $BObject=BObject $Bcb;
+ $BObject->{"dirty"}=1;
}
sub CcPinMappedData($$$)
my $BObject2=BObject $Bcb2 if $Bcb2;
pin_new_leave $Bcb,$Buffer;
-# print STDERR "$.:".Dumper($Object);
}
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($)
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($)
# 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,
}
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)/) {
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;