Common reasons for bugs in release version not present in debug mode
Asked Answered
F

18

74

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?

Filia answered 19/11, 2009 at 9:46 Comment(2)
What's up with you people? It is a fantastic question!Candice
Agreed, it's a good question. So many subtleties involved, and they bite you in the behind at the worst possible moment (i.e. when you no longer have full debug support to solve the problem, by definition).Rhythmical
W
36

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.

Waken answered 19/11, 2009 at 9:49 Comment(5)
I always found this behaviour completely backwards. Surely the job of a debug mode is to expose problems, not to hide them?Deadlight
This is strange in C++, but lucking in C# everything is by default NULL initialized.Waken
A small point: normally, variables in debug mode are filled not with null, but with some specific value rare to occur in natural world (e.g. 0xCCCCCCCC for MSVC).Phosphoroscope
Yes, you are right, to expand your answer: priyank.co.in/…Waken
To expand atzz's answer, MSVC fills unitialized stack data with 0xCC, unitialized heap data with 0xCD, and deleted objects with 0xDD. More magic values.Henslowe
S
22

A common pitfall is using an expression with side effect inside an ASSERT.

Syphilology answered 19/11, 2009 at 9:52 Comment(2)
This produces a warning with gcc, but Visual Studio doesn't warn you about it. An example would be: assert(MyObj->LoadFromFile(File));. In release LoadFromFile won't be called at all and you won't be informed at compil time.Blanketyblank
U saved my day thanks: it is really srupid to make such calls inside "assert" unless it is required for some debugging reasonsBajaj
S
12

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.
Shortcoming answered 11/5, 2010 at 13:19 Comment(0)
R
9

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.
Rhythmical answered 19/11, 2009 at 9:52 Comment(3)
"In a garbage-collected language, the collector is usually more aggressive in release mode" That sounds rather absurd. An object is either reachable or not. If the g.c. deletes a reachable objects it’s simply wrong, if it doesn’t delete a non-reachable that shouldn’t cause a bug — the object isn’t reachable anyway.Hardnosed
Absurd or not, it seems to be true. A long time ago, back in the .NET 2.0 days, we had some managed C++ code. We found that in debug mode, "this" seemed to be considered a GC root, but in release, an object could be collected even while running one of its own instance methods, provided the method code made no further references to its own members from that point on. In that scenario, a bit of GC::KeepAlive helped out: msdn.microsoft.com/en-us/library/…Rhythmical
@Hardnosed It's not absurd at all. Debug binary is created for the sole purpose of breaking execution, viewing all in-scope variables, and maintaining a code to binary symmetry. Release is created for speed and/or minimal size. It can leave out entire function calls or variable definitions if it knows it doesn't need them. This creates a very different memory-scape.Finfoot
P
3

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.

Peshawar answered 19/11, 2009 at 9:51 Comment(0)
D
3

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!

Disdain answered 19/11, 2009 at 9:54 Comment(0)
H
3

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!

Holophytic answered 19/11, 2009 at 12:25 Comment(0)
B
2

Sure, for example, if you use constructions like

#if DEBUG

//some code

#endif
Bouie answered 19/11, 2009 at 9:49 Comment(0)
H
1

In a non-void function, all execution paths should end with a return statement.

In debug mode, if you forget to end such a path with a return statement then the function usually returns 0 by default.

However, in release mode your function may return garbage values, which may affect how your program runs.

Highborn answered 19/11, 2009 at 9:46 Comment(0)
T
1

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.

Thibault answered 19/11, 2009 at 9:49 Comment(0)
C
1

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.

Celebrate answered 19/11, 2009 at 9:50 Comment(0)
E
1

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.

Executor answered 19/11, 2009 at 9:51 Comment(0)
A
1

There are compiler optimizations that can break valid code because they are too aggressive.

Try compiling your code with less optimization turned on.

Abdella answered 19/11, 2009 at 10:3 Comment(4)
Never assume it is the compiler's fault. You will be right occasionally but explore all other avenues first. I've only had a compiler be resposible for a bug once in my entire career. I don't use metrowerks compilers anymore.Flair
I haven't yet seen such a case myself. :)Paddlefish
I've seen a fewBracer
@Flair Yup! Exact same experience! Only once, with &*#! CodeWarrior!Micheal
H
1

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.

Humboldt answered 19/11, 2009 at 10:11 Comment(0)
D
0

Another reasons could be DB calls. Are you saving and updating same record multiple times in same thread, sometimes for updating. Its possible the update failed or didnt work as expected because the previous create command was still processing and for update, the db call failed to find any record. this wont happen in debug as debugger makes sure to complete all pending tasks before landing.

Deletion answered 19/11, 2009 at 9:46 Comment(0)
G
0

I remember while ago when we were building dll and pdb in c/c++.

I remember this:

  • Adding log data would sometime make the bug move or disappear or make a totally other error appears (so it was not really an option).
  • Many of these errors where related to char allocation in strcpy and strcat and arrays of char[] etc...
  • We weeded some out by running bounds checker and simply fixing the memory alloc/dealloc issues.
  • Many times, we systematically went through the code and fixed a char allocation.
  • My two cents is that it is related to memory allocation and management and constraints and differences between Debug mode and release mode.

And then kept at going through that cycle.

We sometimes, temporarily swapped release for debug versions of dlls, in order not to hold off production, while working on these bugs.

Geosphere answered 19/11, 2009 at 9:46 Comment(0)
R
0

I just experienced that when I was calling an assembly function that didn't restored the registers' previous values.

In the "Release" configuration, VS was compiling with /O2 which optimizes the code for speed. Thus some local variables where merely mapping to CPU registers (for optimization) which were shared with the aforementioned function leading to serious memory corruption.

Anyhow see if you aren't indirectly messing with CPU registers anywhere in your code.

Reprehend answered 19/11, 2009 at 9:46 Comment(0)
R
0

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!

Retinoscope answered 19/11, 2009 at 10:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.