Common reasons for bugs in release version not present in debug mode

31,003

Solution 1

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.

Solution 2

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

Solution 3

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.

Solution 4

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.

Solution 5

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.

Share:
31,003
Benny
Author by

Benny

i love c# and doing programming on .net

Updated on July 05, 2022

Comments

  • Benny
    Benny almost 2 years

    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?

  • walkytalky
    walkytalky over 14 years
    I always found this behaviour completely backwards. Surely the job of a debug mode is to expose problems, not to hide them?
  • J.Joe
    J.Joe over 14 years
    This is strange in C++, but lucking in C# everything is by default NULL initialized.
  • atzz
    atzz over 14 years
    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).
  • deft_code
    deft_code over 14 years
    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.
  • Stefan
    Stefan over 14 years
    I haven't yet seen such a case myself. :)
  • J.Joe
    J.Joe over 14 years
    Yes, you are right, to expand your answer: priyank.co.in/…
  • BlueRaja - Danny Pflughoeft
    BlueRaja - Danny Pflughoeft about 14 years
    I've seen a few
  • Benlitz
    Benlitz almost 12 years
    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.
  • rustyx
    rustyx about 8 years
    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.
  • idmean
    idmean about 7 years
    "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.
  • stusmith
    stusmith about 7 years
    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/…
  • diox8tony
    diox8tony almost 7 years
    @idmean 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.
  • Tyson Jacobs
    Tyson Jacobs almost 6 years
    @deft_code Yup! Exact same experience! Only once, with &*#! CodeWarrior!
  • Adel Ben Hamadi
    Adel Ben Hamadi about 3 years
    U saved my day thanks: it is really srupid to make such calls inside "assert" unless it is required for some debugging reasons