views:

5785

answers:

9

I am debugging a (native) multi-threaded C++ application under VS2008. On seemingly random occasions, I get a "Windows has triggered a break point..." error with a note that this might be due to a corruption in the heap. These errors won't always crash the application right away, although it is likely to crash short after.

The big problem with these errors is that they pop up only after the corruption has actually taken place, which makes them very hard to track and debug, especially on a multi-threaded app.

So the questions are these:

What sort of things can cause these errors?

How do I debug them?

Tips, tools, methods, enlightments... are welcomed.

+4  A: 

What sort of things can cause these errors?

Doing naughty things with memory, e.g. writing after the end of a buffer, or writing to a buffer after it's been freed back to the heap.

How do I debug them?

Use an instrument which adds automated bounds-checking to your executable: i.e. valgrind on Unix, or a tool like BoundsChecker (Wikipedia suggests also Purify and Insure++) on Windows.

Beware that these will slow your application, so they may be unusable if yours is a soft-real-time application.

Another possible debugging aid/tool might be MicroQuill's HeapAgent.

ChrisW
Rebuilding the application with debugging runtime (/MDd or /MTd flag) would be my first step. These perform additional checks at malloc and free, and are often quit effective at narrowing down the location of the bug(s).
Employed Russian
MicroQuill's HeapAgent: There's not much written or heard about it, but for heap corruption, it should be on your list.
Samrat Patil
+1  A: 

What type of allocation functions are you using? I recently hit a similar error using the Heap* style allocation functions.

It turned out that I was mistakenly creating the heap with the HEAP_NO_SERIALIZE option. This essentially makes the Heap functions run without thread safety. It's a performance improvement if used properly but shouldn't ever be used if you are using HeapAlloc in a multi-threaded program [1]. I only mention this because your post mentions you have a multi-threaded app. If you are using HEAP_NO_SERIALIZE anywhere, delete that and it will likely fix your problem.

[1] There are certain situations where this is legal, but it requires you to serialize calls to Heap* and is typically not the case for multi-threaded programs.

JaredPar
Yes: look at the application's compiler/build options, and ensure it's being built to linking against a "multi-threaded" version of the C run-time library.
ChrisW
@ChrisW for the HeapAlloc style APIs this is different. It's actually a parameter that can be changed at heap creation time, not link time.
JaredPar
Oh. It didn't occur to me that the OP might be talking about that heap, and not about the heap in the CRT.
ChrisW
@ChrisW, the question is rather vague but I just hit the problem I detailed ~1 week ago so it's fresh on my mind.
JaredPar
A: 

In addition to looking for tools, consider looking for a likely culprit. Is there any component you're using, perhaps not written by you, which may not have been designed and tested to run in a multithreaded environment? Or simply one which you do not know has run in such an environment.

The last time it happened to me, it was a native package which had been successfully used from batch jobs for years. But it was the first time at this company that it had been used from a .NET web service (which is multithreaded). That was it - they had lied about the code being thread safe.

John Saunders
+3  A: 

The best tool I found useful and worked every time is code review (with good code reviewers).

Other than code review, I'd first try Page Heap. Page Heap takes a few seconds to set up and with luck it might pinpoint your problem.

If no luck with Page Heap, download Debugging Tools for Windows from Microsoft and learn to use the WinDbg. Sorry couldn't give you more specific help, but debuging multi-threaded heap corruption is more an art than science. Google for "WinDbg heap corruption" and you should find many articles on the subject.

Shing Yip
+5  A: 

You can detect a lot of heap corruption problems by enabling Page Heap for your application . To do this you need to use gflags.exe that comes as a part of Debugging Tools For Windows

Run Gflags.exe and in the Image file options for your executable, check "Enable Page Heap" option.

Now restart your exe and attach to a debugger. With Page Heap enabled, the application will break into debugger whenever any heap corruption occurs.

Canopus
+1,Its easy and better way to narrow down the issue!
aJ
+12  A: 

Application Verifier combined with Debugging Tools for Windows is an amazing setup. (Found out about Application Verifier when researching an earlier question about a heap corruption issue.) I've used BoundsChecker and Insure++ (mentioned in other answers) in the past too, although I was surprised how much functionality was in Application Verifier.

electricfence, dmalloc, valgrind, and so forth are all worth mentioning, but most of these are much easier to get running under *nix than Windows. Valgrind is ridiculously flexible: I've debugged large server software with many heap issues using it.

When all else fails, you can provide your own global operator new/delete and malloc/calloc/realloc overloads -- how to do so will vary a bit depending on compiler and platform -- and this will be a bit of an investment -- but it may pay off over the long run. The desirable feature list should look familiar from dmalloc and electricfence, and the surprisingly excellent book Writing Solid Code:

  • sentry values: allow a little more space before and after each alloc, respecting maximum alignment requirement; fill with magic numbers (helps catch buffer overflows and underflows, and the occasional "wild" pointer)
  • alloc fill: fill new allocations with a magic non-0 value -- Visual C++ will already do this for you in Debug builds (helps catch use of uninitialized vars)
  • free fill: fill in freed memory with a magic non-0 value, designed to trigger a segfault if it's dereferenced in most cases (helps catch dangling pointers)
  • delayed free: don't return freed memory to the heap for a while, keep it free filled but not available (helps catch more dangling pointers, catches proximate double-frees)
  • tracking: being able to record where an allocation was made can sometimes be useful

Note that in our local homebrew system (for an embedded target) we keep the tracking separate from most of the other stuff, because the run-time overhead is much higher.

leander
One tiny thing worth noting about Application Verifier: you must register Application Verifier's symbols ahead of the microsoft symbol server symbols in your symbol search path, if you use that... Took me a bit of searching to figure out why !avrf wasn't finding the symbols it needed.
leander
Application Verifier was a great deal of help, and combined with some guessing, I was able to solve the problem! Thanks a lot, and for everyone else too, for bringing up helpful points.
+2  A: 

One quick tip, that I got from Detecting access to freed memory is this:

If you want to locate the error quickly, without checking every statement that accesses the memory block, you can set the memory pointer to an invalid value after freeing the block:

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif
StackedCrooked
+1  A: 

If these errors occur randomly, there is high probability that you encountered data-races. Please, check: do you modify shared memory pointers from different threads? Intel Thread Checker may help to detect such issues in multithreaded program.

Vova