Board index » delphi » FastMM detects freed memory block modification revisited

FastMM detects freed memory block modification revisited


2005-11-21 08:54:34 PM
delphi158
[I originally posted this as a reply to a month-old post.
Reposted to raise visibility.]
Pierre le Riche writes:
Quote
>"FastMM has detected an error during a free block scan operation.
>FastMM detected that a block has been modified after being freed.

Most likely there is a something on that frame that modifies it after
the frame has been freed. If you look at the memory dump you should
be able to tell which property it was by counting bytes. The first
four bytes should be the class pointer and the rest should all be
$80. The bytes that aren't $80 are the cause of the error.
What if there are no bytes that are not $80 ? The first non-$80 bytes
in my case appear only after the detected block size of the dump (i.e.
the problematic block is said to be 116 bytes big and the first non-$80
byte is exactly at position 117.
I'm currently testing FastMM 4.48 in a D7-written COM-Addin for MS
Outlook. From my debug messages I can see that all destructors and
constructors get called in the correct order, yet when I run the thing
with FullDebugMode turned on I will reproducably get the above message
about freed memory block modification in a specific situation followed
by an Outlook crash. If I turn off FullDebugMode then everything
appears to work fine.
Any ideas?
BTW: If there had been non-$80 bytes in the dump how exactly would I
have correlated them to specific fields of my class? Counting together
the sizes of all the data fields in that particular class (including
ancestors) I only came up with 87 bytes instead of the detected 116...
Cheers,
--
Oliver
 
 

Re:FastMM detects freed memory block modification revisited

Hi Oliver,
Quote
What if there are no bytes that are not $80 ? The first non-$80 bytes
in my case appear only after the detected block size of the dump (i.e.
the problematic block is said to be 116 bytes big and the first non-$80
byte is exactly at position 117.
Can you perhaps copy-and-paste the report? Perhaps that will give us a
better idea of what is going on.
Quote
by an Outlook crash. If I turn off FullDebugMode then everything
appears to work fine.
That is often the nature of these kinds of bugs... very little symptoms at
the point of trouble, but they usually do catch you later on. If
FullDebugMode reports a problem you can be very sure that there is a
problem.
Quote
Any ideas?
If you send me a test case I will look into it for you.
Quote
have correlated them to specific fields of my class? Counting together
the sizes of all the data fields in that particular class (including
ancestors) I only came up with 87 bytes instead of the detected 116...
Remember to take into account field alignment. By default fields are aligned
to their size. There is also a dword class pointer in front of every object.
Regards,
Pierre
 

Re:FastMM detects freed memory block modification revisited

Pierre le Riche writes:
Quote
Can you perhaps copy-and-paste the report? Perhaps that will give us
a better idea of what is going on.
OK, here you go. Please find the report at the end of this message (by
now the class in question has grown by one interface pointer so its
size is now 120, not the 116 I mentioned earlier in this thread).
Comments:
TOutlookInspector is (indirectly) inherited from TOleServer. It is a
VCL wrapper around the InspectorEvents dispinterface from the
Outlook_TLB.
Quote
>Any ideas?

If you send me a test case I will look into it for you.
I have already tried reproducing this in a simplified test case with
Outlook out of the equation but without success so far.
Basically, what happens is that there is a main controller instance
(the addin in my case) which manages lists of proxy objects (the
TOutlookInspector seen in the mem dump) which in turn hold references
to external objects (Outlook inspectors in this case) and surface the
events of those external objects in a Delphi-friendly way, so the
controller could react to events triggered by the external objects.
When the controller gets notified of the creation of a new external
object it creates a new proxy, passes to it the reference to the
external object, binds to the events surfaced by it and adds it to an
internal TObjectList. When notified about the destruction of the
external object it removes the proxy object from the internal list
again (which implicitly leads to the proxy's destruction because it is
owned by the list object (i.e. AOwnsObjects=True)).
The problem is that because of bugs in the external API (i.e. Outlook)
the notification about the destruction does not always trigger. This is
not such a problem in my particular case as all my code is executed
already in the OnActivate event handler for the external object (via
the event proxy) so I don't really have to hang on to them until they
get destroyed. Thus I simply remove the proxy from my internal list
immediately after my code has executed.
The problem now is that with FullDebugMode turned on FastMM complains
whenever I create a second instance of my event proxy, i.e. even before
that instance gets added to the internal TObjectList.
Quote
Remember to take into account field alignment. By default fields are
aligned to their size. There is also a dword class pointer in front
of every object.
Ah yes, I hadn't thought of the class pointers. I am not sure how to
bring the alignment issue into the equation however...
Would I also have to add the type pointer size for other typed pointers
besides objects (e.g. interfaces, events, strings, ...)? Whichever way
I turn it, I still don't manage to come up with the 120 bytes reported
by FastMM...
Cheers, (memory dump follows below sig)
--
Oliver
--------------------------------2005/11/21
15:49:39--------------------------------
FastMM has detected an error during a GetMem operation. FastMM detected
that a block has been modified after being freed.
The previous block size was: 120
Stack trace of when this block was previously allocated (return
addresses):
2152EEC [system.pas][System][@GetMem][2439]
215403B [system.pas][System][TObject.NewInstance][8360]
2154416 [system.pas][System][@ClassCreate][9019]
21E3E7E
[OutlookEventServers.pas][OutlookEventServers][TOutlookInspector.Create]
[501]
3A568CE3 [ExchEntryPoint@0]
21E49B6
[OutlookEventServers.pas][OutlookEventServers][TOutlookAddin.DoNewInspec
tor][972]
21E3E31
[OutlookEventServers.pas][OutlookEventServers][TOutlookInspectors.Invoke
Event][494]
21CEA82 [OleServer.pas][OleServer][TServerEventDispatch.Invoke][207]
3A60AD29 [MAPIMHeapSize@4]
3A500000 [MAPIMHeapCreate@16]
3A568CF6 [ExchEntryPoint@0]
3A4AF0C0 [OlkGetUIlangID@0]
3A608536 [MAPIMHeapSize@4]
3A473289 [DllGetClassObject]
3A50DB4E [MAPIMHeapCreate@16]
3A7E4F44 [HrDisplayFolderPickerForOutlookToday@0]
3A54C152 [DllCanUnloadNow]
3A54C117 [DllCanUnloadNow]
3A549C78 [DllCanUnloadNow]
3A549A4B [DllCanUnloadNow]
The block was previously used for an object of class: TOutlookInspector
Stack trace of when the block was previously freed (return addresses):
2152F17 [system.pas][System][@FreeMem][2466]
2154059 [system.pas][System][TObject.FreeInstance][8366]
2154461 [system.pas][System][@ClassDestroy][9060]
21E3F39
[OutlookEventServers.pas][OutlookEventServers][TOutlookInspector.Destroy
][511]
215409F [system.pas][System][TObject.Free][8385]
218B3EE [Contnrs.pas][Contnrs][TObjectList.Notify][304]
2174C47 [classes.pas][Classes][TList.Delete][2779]
21E484A
[OutlookEventServers.pas][OutlookEventServers][TOutlookAddin.DoInspector
Close][937]
21E7EB7
[MaskAddin.pas][MaskAddin][TLucatecMaskAddin.DoInspectorActivate][418]
21E40E1
[OutlookEventServers.pas][OutlookEventServers][TOutlookInspector.InvokeE
vent][554]
21CEA82 [OleServer.pas][OleServer][TServerEventDispatch.Invoke][207]
3A60AD29 [MAPIMHeapSize@4]
308E70F7 [_MsoFInsertPx@12]
3091CC10 [_MsoFindFirstFileW@8]
3A45DB18 [DllGetClassObject]
3A608536 [MAPIMHeapSize@4]
3A45D881 [DllGetClassObject]
3A5471B1 [DllCanUnloadNow]
3A4474CD [DllGetClassObject]
3A44720A [DllGetClassObject]
The current stack trace leading to this error (return addresses):
2152EEC [system.pas][System][@GetMem][2439]
215403B [system.pas][System][TObject.NewInstance][8360]
2154416 [system.pas][System][@ClassCreate][9019]
21E3E7E
[OutlookEventServers.pas][OutlookEventServers][TOutlookInspector.Create]
[501]
3A568CE3 [ExchEntryPoint@0]
21E49B6
[OutlookEventServers.pas][OutlookEventServers][TOutlookAddin.DoNewInspec
tor][972]
21E3E31
[OutlookEventServers.pas][OutlookEventServers][TOutlookInspectors.Invoke
Event][494]
21CEA82 [OleServer.pas][OleServer][TServerEventDispatch.Invoke][207]
3A60AD29 [MAPIMHeapSize@4]
3A500000 [MAPIMHeapCreate@16]
3A568CF6 [ExchEntryPoint@0]
3A4AF0C0 [OlkGetUIlangID@0]
3A608536 [MAPIMHeapSize@4]
3A473289 [DllGetClassObject]
3A50DB4E [MAPIMHeapCreate@16]
3A7E4F44 [HrDisplayFolderPickerForOutlookToday@0]
3A54C152 [DllCanUnloadNow]
3A54C117 [DllCanUnloadNow]
3A549C78 [DllCanUnloadNow]
3A549A4B [DllCanUnloadNow]
Current memory dump of 256 bytes starting at pointer address 29F14E8:
DC C8 20 02 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 00 80 80 80
80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
CF EB 60 FD 80 80 80 80
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
? E . ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? . ? ? ?
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
I ? ` ? ? ? ? ?
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . .
 

Re:FastMM detects freed memory block modification revisited

Quote
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 **00** 80 80
80
Ahem ;-)
 

Re:FastMM detects freed memory block modification revisited

Pierre le Riche writes:
Quote
>80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 00 80
>80 80

Ahem ;-)
<Blush />
I guess looking at the character representation instead of the hex
codes would have saved me from that embarassment... ;)
Anyway, this really just brings me back to the question of how exactly
the byte counting technique works... Is there a table overview
somewhere about which types take up how much space exactly and which
parts of a class take up any space in the first place (e.g. what about
advertised implemented interfaces - AFAICT these have an impact on the
reported size as well)?
For example, this is the declaration of the offending class:
TOutlookInspector = class(TOleEventServer)
private
FFirstActivation: Boolean;
FOnActivate: TInspectorEvent;
FOnClose: TInspectorEvent;
FOnDeactivate: TInspectorEvent;
FButtons: TOutlookCommandBarButtons;
function GetButtons: TOutlookCommandBarButtons;
protected
<.snip.>
end;
This is its ancestor:
TOleEventServer = class(TOleServer)
private
FServer: IUnknown;
function GetConnected: Boolean;
public
<.snip.>
end;
(there are no fields outside the private sections in both classes)
And this is the dump:
DC E8 20 02 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 00 80 80 80
80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
So, I need to find out what's at offset 84, right?
Since I started writing this post I have now found the description at
info.borland.com/techpubs/delphi/delphi5/oplg/memory.html but
still haven't been able to add things up to the reported 116 bytes. :(
As I see it there should be:
4 bytes for the VMT pointer
followed by 43 bytes for the data in TComponent
(TObject and TPersistent do not seem to have any private data)
followed by 26 bytes for the data in TOleServer
followed by 4 bytes for the data in my TOleEventServer
followed by 33 bytes for the data in my TOutlookInspector
but that is only 110 bytes instead of 116 so I am obviously still missing
something.
Intermittently TOutlookInspector was implementing an interface with a
single method. When that was still in, the size of the object was
reported as 120 (see previous posts). After I removed the interface
(and the method) from the class declaration the size dropped to 116 so
it seems implemented interfaces are somehow counted in as well?
Help?
--
Oliver
 

Re:FastMM detects freed memory block modification revisited

Pierre le Riche writes:
Quote
TOLEServer.InstanceSize = 80, so you have:

Offset 80: TOleEventServer.FServer (dword)
Offset 84: TOutlookInspector.FFirstActivation (boolean, padded to 4
bytes) Offset 88: TOutlookInspector.FOnActivate (event = 8 bytes)
Offset 96: TOutlookInspector.FOnClose (event = 8 bytes)
Offset 104: TOutlookInspector.FOnDeactivate (event = 8 bytes)
Offset 112: TOutlookInspector.FButtons (dword)

So the total is 116. It appears you are setting FFirstActivation to
false after the object has been freed.
YOU NAILED IT! It gets set to False after the OnActivate event handler
gets called (which is from where I was calling the code that eventually
led to the object's destruction). Now I wonder why this does not cause
an error without FullDebugMode turned on...
anyway: THANKS A BIG FAT LOT! :)
Cheers,
--
Oliver
---- ------------------
JID: XXXX@XXXXX.COM
ICQ: 18777742 (wwp.icq.com/18777742)
 

Re:FastMM detects freed memory block modification revisited

Hi Oliver,
TOLEServer.InstanceSize = 80, so you have:
Offset 80: TOleEventServer.FServer (dword)
Offset 84: TOutlookInspector.FFirstActivation (boolean, padded to 4 bytes)
Offset 88: TOutlookInspector.FOnActivate (event = 8 bytes)
Offset 96: TOutlookInspector.FOnClose (event = 8 bytes)
Offset 104: TOutlookInspector.FOnDeactivate (event = 8 bytes)
Offset 112: TOutlookInspector.FButtons (dword)
So the total is 116. It appears you are setting FFirstActivation to false
after the object has been freed.
Regards,
Pierre