5
votes

Even if it sounds strange, I believe that everybody encountered this kind of issue when working on big applications with lots of custom components. An AV is generated somewhere but the application is continuing with the execution and the error is raised some when later. I'm not talking about multi thread application. Just about generic single thread application.

I'm struggling to find an error which is raised randomly and even I'm using MadExcept/AQT, debugging dcus and breakpoints, but I can not find exact the steps when it is raised, and from where it gets raised. Error is propagating, and it is raising on an TWinControl destroy(the Delphi standard TPageControl) sometimes, other times when opening an dataset(which is opened and close several times before with the same SQL). So the stack gets corrupted and I can not use it in this case.

I know the question is too localized, but I'm asking you what other alternatives exists to track such kind of errors.

5
Thank you for the -1. Now, can you tell me a tool you have used when dealing with such a kind of situation?RBA
I can only give one advice, add logging to your application. At least it will give you a hint what the user was doing before the AV occurred. (neutered the -1)whosrdaddy
I have tried with logs, try/except/finally but without success. Error gets propagated and raised randomly.RBA
maybe it is use-after-free override FreeMem so it would wipe memory area with fixed pattern like $DEADBEEF or with Nth return-address (may hint where it was prematurely deleted, but hardly so due to classes inheritance. Then call inherited FreeMem. Hopefully that would turn you bug into die-early category. Also Windows DEP might be of some help, disabling code execution on anything but real code segments.Arioch 'The
Did you also do FastMM logging? - Could point out memleaks etc.Jan Doggen

5 Answers

7
votes

I'd start with using some other memory managers to see if you can force the usage pattern of your application into breaking sooner.

FastMM can be run with FullDebugMode (be sure to put the according DLL with your application EXE). A few important things it can do:

  • fill memory with patterns (like Arioch 'The suggested)
  • check for modifications to blocks after they have been freed
  • log memory leaks upon application termination
  • callback upon certain events so you can log stuff

In the past I've written 2 blog posts about FastMM, I know I should write more about it and other memory managers.

Edit: Nice article about other memory managers Memory Manager Investigations by SO user Eric Grange.

For documentation purposes some other memory managers (most of them don't apply to your case, but I have not yet found a post including them all):

In multi-threaded environments, these can be nice:

In a EXE/DLL setup, you might want to try FastShareMem.

In his ScaleMM repository, André Mussche did test quite a few other memory managers.

NB: +1; good question, and I should find some time to write a follow up blog post on Delphi memory managers(:

3
votes

Logging to files can change the results, so I recommend the GExperts debugger which writes messages to an output window in real-time. Write a wrapper function so that you can dump to a file if you want, control what gets logged (loglevel filters), etc..

Put logging in the Initialization/Finalization of all units. I've found that "out of the blue" errors often happen during finalization. ex: you close a database connection, but that object has already been freed by something else. D'oh!

Also put logging in event handlers of any components, as well as the destructors of those components. This will show you if you're getting events firing after the components have been destroyed.

As a last resort, you may have to "gut" the program, removing all code that you wrote, in "chunks". Save the code somewhere, delete event handlers on form1, compile and test. If the error goes away, put back the event handlers and delete half of them, re-test. Continue until you narrow it down sufficiently, and the culprit should be identified by process of elimination.

2
votes

I believe this is more of a general question than Delphi question. But some useful techniques include:

  • Excessive logging to a file, so that you can study it later - with all the values
  • Assertions everywhere. You can disable them so they add no cost on release
  • Disabling parts of the application until the error stops appearing, or
  • Disabling everything except for some basic shell and then enabling components one by one until AVs return.
  • Tracking memory leaks with ReportMemoryLeaksOnShutdown := true.

When you find a component which seems to trigger the error, go into it and repeat the procedure with disabling everything inside it and enabling parts step by step.

2
votes

Two things have become my best friends, particularly, when developing non visual stuff and not only in Delphi:

  • Unit tests and logging

I use (DUnit) for unit tests but also to perform system and stress tests. I use AQT to assess the coverage of my unit tests and memory leaks detection. The idea is that I don't need upfront to write tests for every single method, but try to write a test that covers as much as possible. When there is a particular problem, then I write a test to reproduces the problem then I know when my fix works. Another advantage particularly, with big applications, is I can run the tests after making changes and verify that everything is still working as expected.

The other tool that I use is SmartInspect, because it logs entering and leaving methods, among other things, and has a Wizard to instrument existing code (add enter leave logging) and a console to filter the logs.

2
votes

It's strange, that nobody mentioned debugging memory manager called SafeMM. It can catch AV exactly where it occurs.