Is it possible that FastMM is wrongly reporting an access violation?
Asked Answered
L

3

5

I have a complex application that works as expected when compiled normally, but closing a form generates an access violation when compiled using FastMM 4.97 (the latest). The AV occurs when the message dispatching attempts to handle a message meant for a button on the form that has already been destroyed. I also have Eurekalog 6.1.0.1 (the latest) enabled for the project. It doesn't trap any run-time exception when this same code is executed with FastMM's FullDebug mode disabled.

Is it possible that in some situations FastMM can alter the behaviour of the EXE such that it causes, or falsely reports access violations?

Here is the FastMM error report:

--------------------------------2011/3/21 13:30:17--------------------------------
FastMM has detected an attempt to call a virtual method on a freed object. An access violation will now be raised in order to abort the current operation.

Freed object class: TftGenericButton80

Virtual method: Offset +80

Virtual method address: 4A1FF0

The allocation number was: 5628628

The object was allocated by thread 0xE80, and the stack trace (return addresses) at the time was:
403110 [System][@GetMem]
404F03 [System][TObject.NewInstance]
42E85D [FastMM4][CreateComponent]
42EAD9 [FastMM4][TReader.ReadComponent]
42FEE1 [Classes][TReader.ReadValue]
42ED86 [FastMM4][TReader.ReadDataInner]
42ECC5 [FastMM4][TReader.ReadData]
433802 [Classes][TComponent.ReadState]
4A21CE [Controls][TControl.ReadState]
4A5742 [Controls][TWinControl.ReadState]
48BCA0 [Forms][TCustomForm.ReadState]

The object was subsequently freed by thread 0xE80, and the stack trace (return addresses) at the time was:
40313B [System][@FreeMem]
404F21 [System][TObject.FreeInstance]
405339 [System][@ClassDestroy]
8AFBEF [..\..\AdvShapeButton.pas][AdvShapeButton][TAdvCustomShapeButton.Destroy][1422]
4A5601 [Controls][TWinControl.Destroy]
48A9DD [Forms][TScrollingWinControl.Destroy]
48B9D8 [Forms][TCustomForm.Destroy]
48B9F2 [Forms][TCustomForm.Destroy]
404F67 [System][TObject.Free]
A9C42C [..\..\fmWaitingList.pas][fmWaitingList][TfrmWaitingList.OnTanWaitingListItem][130]
A9D41B [fmWaitingListItem.pas][fmWaitingListItem][TfrmWaitingListItem.DoOnTanItem][142]

The current thread ID is 0xE80, and the stack trace (return addresses) leading to this error is:
8B2630 [..\..\AdvShapeButton.pas][AdvShapeButton][TAdvCustomShapeButton.Click][3042]
4A4817 [Controls][TControl.WMLButtonUp]
4A4227 [Controls][TControl.WndProc]
7E42B401 [CallNextHookEx]
7E42B401 [CallNextHookEx]
7E42B372 [MoveWindow]
7E42B317 [MoveWindow]
7E42B326 [MoveWindow]
7E42B326 [MoveWindow]
7E4278D0 [GetWindowTextLengthW]
7E4278E0 [GetWindowTextLengthW]

Current memory dump of 256 bytes starting at pointer address 7FEB5F00:
90 8C AD 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 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 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 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
  Œ  ­  .  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
Lamarlamarck answered 21/3, 2011 at 19:31 Comment(4)
You said this occurrs when closing a form. Bear in mind that closing a form behaves differently depending on a fair number of other factors: What kind of form is it? Modal/Non-modal? MDI/SDI? What do you do with CloseAction? Answers to some of these questions may make it easier to help you.Gameto
CallNextHookEx in last call stack looks very suspicious to me. It's part of calling hooks' chain. Looks like there was some hook installed and it may call your code in not appropriate time. Of course, this may be just false-positive entry.Medicare
Do any of your forms implement any Interfaces? What notifications, callbacks, and links do you have to that form? Does that form free itself on close?Grande
The log is incomplete, probably because project options is not correctly configured for Debug mode.Articulation
L
13

It's not reporting an AV; it's creating a fake AV exception in order to bring your process to a screeching halt, because the road it's on--accessing a deleted object--frequently leads to data corruption, which is even worse.

The problem isn't an access violation; the problem is something is trying to call the Click method on a button that's already been freed. Just from looking at this stack trace, it looks like you or someone else has a hook installed that's holding a reference to the button and not updating itself appropriately once the form is destroyed. That's where I'd start looking.

Leg answered 21/3, 2011 at 19:40 Comment(3)
Thanks for the comments. Is there anyway to determine what hooks are in place and where they might have originated? I am embedding TForms into a TScrollBox. One way to remove the embedded for is to click on a button on the form itself. That's when I'm getting this problem. Should I use something other than a TForm?Lamarlamarck
@User: I really can't answer that very well without looking at your code.Leg
You could say that the Access Violation isn't really Entirely faked, it's possible that you could get a real access violation doing that kind of access to freed memory, in the future. fastMM is just making something silent, deadly, and irregular, a little more predictable, and obvious. It's just raising a probability 1.0E-09 event to 1.0 probability.Grande
G
7

A little tricky without being able to see your code. But from the stack traces it looks to me as if you might be destroying a TAdvCustomShapeButton instance in the middle of processing messages destined for that object.

A good place to start checking should be the method currently hooked to the TfrmWaitingList.OnTanWaitingListItem event.

You said this occurrs when closing a form.
I'm assuming your form is destroyed (not simply hidden) when closed, so it would in turn destroy all objects owned by the form.

How is the form being closed? You may want to consider Form.Release instead.

Gameto answered 21/3, 2011 at 20:42 Comment(1)
I have changed the code to call Form.Release instead of Form.Free and FastMM in Full Debug mode no longer gives me an EAbstractError in the TCustomForm.DoClose() method in other areas of the application, and in the specific case I reported here, the error no longer occurs! Thanks Mason...while I was aware of Release I normally just use Free.Lamarlamarck
H
3

I have found FastMM to be exceedingly reliable in this regard.

If you run in release mode you'll have no problems with calling methods on freed objects 99.9% of the time. The 0.1% of the time will invariably occur only on your most valuable customers machine!

So, I'd bet money that this is a problem. It's actually quite easy to track down because FastMM gives you all the call stacks that show exactly how it occurred. You just need to follow through the details carefully.


Now, you may well ask, how can I call a method on an object that has been freed? Well, when you free an object, you return the memory to the memory manager. But the memory manager typically holds on to that memory and waits for an opportune moment to re-use it. Crucially it doesn't immediately return it to the system because doing so is expensive (it takes a significant amount of time).

This is what makes the memory manager fast, but it also leads to errors such as calling methods on free objects being masked. If the memory had been returned to the system then such an action would result in a real access violation. This is what I mean by saying the most of the time, with the memory manager in release mode, such a bug will not surface.

This is one of the best features of FastMM in my view and heeding this warning will save you pain in the future. Trying to track down such a problem in the field is exceedingly difficult. Fixing it with the information FastMM has provided is usually simple.

Harry answered 21/3, 2011 at 19:39 Comment(1)
Well, the main reason for not returning to the system is that it memmanagers usually allocates from the system in big blocks, and then does the suballoc itself. So unless a deallocated object is not the last in the block, the block can't be returned. Large allocations are usually directly allocated via the OS tho.Boltrope

© 2022 - 2024 — McMap. All rights reserved.