What are the typical reasons for bugs and abnormal program behavior that manifest themselves only in release compilation mode but which do not occur when in debug mode?
You'd need to give a lot more information, but yes, it's possible. It depends what your debug version does. You may well have logging or extra checks in that that don't get compiled into a release version. These debug only code paths may have unintended side effects which change state or affect variables in strange ways. Debug builds usually run slower, so this may affect threading and hide race conditions. The same for straight forward optimisations from a release compile, it's possible (although unlikely these days) that a release compile may short circuit something as an optimisation.
Sure, for example, if you use constructions like
#if DEBUG
//some code
#endif
Many times, in debug mode in C++ all variables are null initialized, whereas the same does not happen in release mode unless explicitly stated.
Check for any debug macros and uninitialized variables
Does your program uses threading, then optimization can also cause some issues in release mode.
Also check for all exceptions, for example not directly related to release mode but sometime we just ignore some critical exceptions, like mem access violation in VC++, but the same can be a issue at least in other OS like Linux, Solaris. Ideally your program should not catch such critical exceptions like accessing a NULL pointer.
Without more details, I will assume that "not OK" means that it either does not compile or throws some sort of error at runtime. Check if you have code that relies on the compilation version, either via #if DEBUG
statements or via methods marked with the Conditional
attribute.
Yes!, if you have conditional compilation, there may be timing bugs (optimised release code verse, non-optimised debug code), memory re-use vs. debug heap.
In .NET, even if you don't use conditional compilation like #if DEBUG
, the compiler is still alot more liberal with optimisations in release mode than it is in debug mode, which can lead to release only bugs as well.
Other differences might be:
- In a garbage-collected language, the collector is usually more aggressive in release mode;
- Layout of memory may often be different;
- Memory may be initialized differently (eg could be zeroed in debug mode, or re-used more aggressively in release);
- Locals may be promoted to register values in release, which can cause issues with floating point values.
A common pitfall is using an expression with side effect inside an ASSERT.
It can, especially if you are in the C realm.
One cause could be that the DEBUG version may add code to check for stray pointers and somehow protect your code from crashing (or behave incorrectly). If this is the case you should carefully check warnings and other messages you get from your compiler.
Another cause could be optimization (which is normally on for release versions and off for debug). The code and data layout may have been optimized and while your debugging program just was, for example, accessing unused memory, the release version is now trying to access reserved memory or even pointing to code!
EDIT: I see other mentioned it: of course you might have entire code sections that are conditionally excluded if not compiling in DEBUG mode. If that's the case, I hope that is really debugging code and not something vital for the correctness of the program itself!
It's possible. If it happens and no conditional compilation is involved, than you can be pretty sure that your program is wrong, and is working in debug mode only because of fortuitous memory initializations or even layout in memory!
There are compiler optimizations that can break valid code because they are too aggressive.
Try compiling your code with less optimization turned on.
That is possible, if you have conditional compilation so that the debug code and release code are different, and there is a bug in the code that is only use in the release mode.
Other than that, it's not possible. There are difference in how debug code and release code are compiled, and differences in how code is executed if run under a debugger or not, but if any of those differences cause anything other than a performance difference, the problem was there all along.
In the debug version the error might not be occuring (because the timing or memory allocation is different), but that doesn't mean that the error is not there. There may also be other factors that are not related to the debug mode that changes the timing of the code, causing the error to occur or not, but it all boils down to the fact that if the code was correct, the error would not occur in any of the situations.
So, no, the debug version is not OK just because you can run it without getting an error. If an error occurs when you run it in release mode, it's not because of the release mode, it's because the error was there from the start.
The CRT library functions behave differently in debug vs release (/MD vs /MDd).
For example, the debug versions often prefill buffers you pass to the indicated length to verify your claim. Examples include strcpy_s
, StringCchCopy
, etc. Even if the strings terminate earlier, your szDest better be n bytes long!
I've been bitten by a number of bugs in the past that have been fine in Debug builds but crash in Release builds. There are many underlying causes (including of course those that have already been summarised in this thread) and I've been caught out by all of the following:
- Member variables or member functions in an
#ifdef _DEBUG
, so that a class is a different size in a debug build. Sometimes#ifndef NDEBUG
is used in a release build - Similarly, there's a different
#ifdef
which happens to be only present in one of the two builds - The debug version uses debug versions of the system libraries, especially the heap and memory allocation functions
- Inlined functions in a release build
- Order of inclusion of header files. This shouldn't cause problems, but if you have something like a
#pragma pack
that hasn't been reset then this can lead to nasty problems. Similar problems can also occur using precompiled headers and forced includes - Caches: you may have code such as caches that only gets used in release builds, or cache size limits that are different
- Project configurations: the debug and release configurations may have different build settings (this is likely to happen when using an IDE)
- Race conditions, timing issues and miscellanous side-effects occurring as a result of debug only code
Some tips that I've accumulated over the years for getting to the bottom of debug/release bugs:
- Try to reproduce anomalous behaviour in a debug build if you can, and even better, write a unit test to capture it
- Think about what differs between the two: compiler settings, caches, debug-only code. Try to minimise those differences temporarily
- Create a release build with optimisations switched off (so you're more likely to get useful data in the debugger), or an optimised debug build. By minimising the changes between debug and release, you're more likely to be able to isolate which difference is causing the bug.