1
votes

We have a Delphi app that has been running for a few years and now suddenly we get weird access violations. We used Eurekalog to trace where it comes from and this is even more weird. They are so far all on the Free call of an object, but inside a try except block. One of them is even in 2 try except blocks and still when the Access Violation occurs it jumps totally out of the program and ignores the try excepts, nothing excepts EurekaLog catches it at the end. Really confused on why this is suddenly happening (both instances are old code that has not been touched for years and also other codes changes is not related to it).

A example of the code is

      try
    if Assigned(ClientCommunication) then   begin
      if ClientCommunication.isConnected then  begin
        if ClientCommunication.closeServerConnection then   begin
          try
            ClientCommunication.Free;
            ClientCommunication := nil;
          Except
            on e:Exception do begin
             ClientCommunication := nil; //suppress weird AV error.. get read for new object
            end
          end;

Now the last try except was added later to try and suppress the AV as we just want the object cleared to be restarted if required, but this is mostly called when closing the app. But it still just jumps out of that and I can’t catch it at all.

It works on our developers PCs and not at the client.

1
Smells like heap corruption. The solution is going to involve debugging. Only you can do that. - David Heffernan
Heap corruption is most likely the case, but I can imagine that the .closeServerConnection() method destroys the parent object of the ClientCommunication object (trough a callback or some pointer acrobatics) so any attempt to access it could easily lead to an AV error. In either case you could try to debug the problem by adding a watch to ClientCommunication AND its parent also, then execute the code line-by-line and look where the pointers got released. The error is obviously not in the code shown above. - mg30rg
I did that type of debugging, problem is the parent actually does not get freed anywhere inside the close. It just sends the close command to the server and close the connection. That why it very weird. But if heap corruption then really weird. Will see what I can do, adding FastMM4 now to try and see if it can help somehow give some light on issue. - Johannes
@MartynA Please take a look at this: meta.stackexchange.com/questions/202969/… - David Heffernan
Is this a multi-threaded app? Is ClientCommunication being freed in another thread? What's ClientCommunication.Free look like? Have you seen FreeAndNil? - Marcus Adams

1 Answers

1
votes

If this line: ClientCommunication.Free; is causing an exception. There are a few things you can do here.

FreeAndNil
Replace calls to AObject.free with calls to freeandnil(AObject).
If you just do the free, the old pointer will still have a non-nil reference and Assigned(AObject) will not be able to tell a freed and active object apart.

Beware of clones
Another mistake you may make is that you've cloned the object like so:

Object1:= TObject1.Create;
//.... lots of code
Object2:= Object1;
//.... lots of code
FreeAndNil(Object2);  <<-- freeing the clone-reference is a mistake
//.... lots of code
Object1.Free; <<-- exception: it's already freed

FastMM4 options
Download the latest FastMM4 from http://sourceforge.net/projects/fastmm/
It has a few extra bells and whistles from the one included with Delphi.(*)
Among those whistles is an extra diagnostic mode that makes your program dead slow, but also catches out a lot of those heap corruption bugs you seem to be suffering from.

Open up defines.inc and change this:

{$ifdef DEBUG}
  {.$define EnableMemoryLeakReporting}
  {.$define FullDebugMode}
  {.$define RawStackTraces}
{$endif DEBUG}

Into this

{$ifdef DEBUG}
  {$define EnableMemoryLeakReporting}
  {$define FullDebugMode}
  {$define RawStackTraces}
  {$define CatchUseOfFreedInterfaces}  <<-- very useful
  {$define LogMemoryLeakDetailToFile}
  {$define LogErrorsToFile}
  {$define CheckHeapForCorruption}  <<-- :-) 
{$endif}

There's a nice article here: http://wiert.me/2009/07/29/delphi-fastmm-using-fastmm4-for-debugging-your-memory-allocations-part-1-introduction/
If you don't feel like mucking about with the inc file, there a little utility that will do it for you at: http://jedqc.blogspot.com/2007/07/new-fastmm4-options-interface.html

(*) I think the Delphi one has most of the debug stuff as well, but not 100% sure. Regardless can't hurt to get the latest (greatest) version.

Or buffer overflow
If your ClientCommunication object has some internal structure free will do extra work.

Imagine the following code:

TUnrelatedObject = class
   buffer: array[0..99] of integer;
   procedure DoWork;
end;

TUnrelatedObject.DoWork;
var
  i: integer;
begin
  for i:= 0 to 100 do buffer[i]:= maxint;  <<-- buffer overrun
end;  

Imagine that ClientCommunication is right next to UnrelatedObject in the heap.
The call to DoWork will overwrite data of ClientCommunication as well.
This may or may not trigger an access violation in DoWork. If it doesn't then the error will be very hard to trace, because it will show up in a different and totally unrelated places.

Enable range checking {$R+} in your application.

Warning
Remember to not ship the debug version to your clients.
With all the debugging your program will be dead slow.