I’m working on a multithreaded C++ application that is corrupting the heap. The usual tools to locate this corruption seem to be inapplicable. Old builds (18 months old) of the source code exhibit the same behaviour as the most recent release, so this has been around for a long time and just wasn’t noticed; on the downside, source deltas can’t be used to identify when the bug was introduced – there are a lot of code changes in the repository.
The prompt for crashing behaviuor is to generate throughput in this system – socket transfer of data which is munged into an internal representation. I have a set of test data that will periodically cause the app to exception (various places, various causes – including heap alloc failing, thus: heap corruption).
The behaviour seems related to CPU power or memory bandwidth; the more of each the machine has, the easier it is to crash. Disabling a hyper-threading core or a dual-core core reduces the rate of (but does not eliminate) corruption. This suggests a timing related issue.
Now here’s the rub:
When it’s run under a lightweight debug environment (say
Visual Studio 98 / AKA MSVC6) the heap corruption is reasonably easy to reproduce – ten or fifteen minutes pass before something fails horrendously and exceptions, like an
alloc; when running under a sophisticated debug environment (Rational Purify,
VS2008/MSVC9 or even Microsoft Application Verifier) the system becomes memory-speed bound and doesn’t crash (Memory-bound: CPU is not getting above
50%, disk light is not on, the program’s going as fast it can, box consuming
1.3G of 2G of RAM). So, I’ve got a choice between being able to reproduce the problem (but not identify the cause) or being able to idenify the cause or a problem I can’t reproduce.
My current best guesses as to where to next is:
- Get an insanely grunty box (to replace the current dev box: 2Gb RAM in an
E6550 Core2 Duo); this will make it possible to repro the crash causing mis-behaviour when running under a powerful debug environment; or
- Rewrite operators
VirtualProtectto mark memory as read-only as soon as it’s done with. Run under
MSVC6and have the OS catch the bad-guy who’s writing to freed memory. Yes, this is a sign of desperation: who the hell rewrites
delete?! I wonder if this is going to make it as slow as under Purify et al.
And, no: Shipping with Purify instrumentation built in is not an option.
A colleague just walked past and asked “Stack Overflow? Are we getting stack overflows now?!?”
And now, the question: How do I locate the heap corruptor?
delete seems to have gotten a long way towards solving the problem. Instead of 15mins, the app now goes about two hours before crashing. Not there yet. Any further suggestions? The heap corruption persists.
Update: a release build under Visual Studio 2008 seems dramatically better; current suspicion rests on the
STL implementation that ships with
- Reproduce the problem.
Dr Watsonwill produce a dump that might be helpful in further analysis.
I’ll take a note of that, but I’m concerned that Dr Watson will only be tripped up after the fact, not when the heap is getting stomped on.
Another try might be using
WinDebugas a debugging tool which is quite powerful being at the same time also lightweight.
Got that going at the moment, again: not much help until something goes wrong. I want to catch the vandal in the act.
Maybe these tools will allow you at least to narrow the problem to certain component.
I don’t hold much hope, but desperate times call for…
And are you sure that all the components of the project have correct runtime library settings (
C/C++ tab, Code Generation category in VS 6.0 project settings)?
No I’m not, and I’ll spend a couple of hours tomorrow going through the workspace (58 projects in it) and checking they’re all compiling and linking with the appropriate flags.
Update: This took 30 seconds. Select all projects in the
Settings dialog, unselect until you find the project(s) that don’t have the right settings (they all had the right settings).
My first choice would be a dedicated heap tool such as pageheap.exe.
Rewriting new and delete might be useful, but that doesn’t catch the allocs committed by lower-level code. If this is what you want, better to Detour the
low-level alloc APIs using Microsoft Detours.
Also sanity checks such as: verify your run-time libraries match (release vs. debug, multi-threaded vs. single-threaded, dll vs. static lib), look for bad deletes (eg, delete where delete  should have been used), make sure you’re not mixing and matching your allocs.
Also try selectively turning off threads and see when/if the problem goes away.
What does the call stack etc look like at the time of the first exception?