views:

367

answers:

13

Possible Duplicate:
Common reasons for bugs in release version not present in debug mode

Sometimes I encouter such strange situations that the program run incorrectly while running normally and it will pop-up the termination dialog,but correctly while debugging.This do make me frustrated when I want to use debugger to find the bug inside my code.

Have you ever met this kind of situation and why?

Update:

To prove there are logic reasons that will led such a frustrating situation:

I think one big possibility is heap access volidation.I once wrote a function that allocate a small buffer,but later I step out the boudary.It will run correctly within gdb,cdb,etc(I do not know why,but it do run correctly);but terminate abnormally while running normally.

I am using C++.

I do not think my problem duplicate the above one.

That one is comparision between release mode and debug mode,but mine is between debugging and not debugging,which have a word heisenbug,as many other noted.

thanks.

+1  A: 

When using a debugger, sometimes memory gets initialized (e.g. zero'ed) whereas without a debugging session, memory can be random. This could explain the behavior you are seeing.

jldupont
We just switched our builds from debug to release and had to go find all of the uninitialized variables - in debug mode, they are all nicely initialized to 0. Release mode, they could be anything.
mskfisher
gcc has `-Wuninitialized` etc., and there are several lint like tools which will just tell you where they are, rather than you having to find them.
Pete Kirkham
+15  A: 

You have a heisenbug.

Debugger might be initializing values

Some environments initialize variables and/or memory to known values like zero in debug builds but not release builds.

Release might be built with optimizations

Modern compilers are good, but it could hypothetically happen that optimized code functions differently than non-optimized code. Edit: These days, compiler bugs are rare. If you find yourself thinking you have one, exhaust all other ideas first.

There can be other reasons for heisenbugs.

JeffH
+1 for link to Heisenbug
APC
Also (as mentioned in that link), stack-smashing is a common cause
BlueRaja - Danny Pflughoeft
Optimizations, correct optimization that is, can trigger timing issues in multi threaded applications. The fact that a piece of code runs just a bit faster can hit a race condition.
David Rodríguez - dribeas
A: 

Which programming language are you using. Certain languages, such as C++, behave slightly differently between release and debug builds. In the case of C++, this means that when you declare a var, such as int i;, in debug builds it will be initialised to 0, while in release builds it may take any value (whatever was stored in its memory location before).

Aistina
+2  A: 

You have dialogs, so there may be threads in your application. If there is threads, there is a possibility of race conditions.

Let say your main thread initialize a structure that another thread uses. When you run your program inside the debugger the initializing thread may be scheduled before the other thread while in your real-life situation the thread that use the structure is scheduled before the other thread actually initialize it.

Ben
+1  A: 

In addition to what JeffH said, you have to consider if the deploying computer (or server) has the same environment/libraries/whatever_related_to_the_program.

Sometimes it's very difficult to debug correctly if you debug with other conditions.

Giovanni

Giovanni Di Milia
A: 

One real-world example of heisenbug from Raymand Zhang.

/*-------------------------------------------------------------- 
GdPage.cpp : a real example to illustrate Heisenberg Effect
   related with guard page by Raymond Zhang, Oct. 2008
--------------------------------------------------------------*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
  LPVOID lpvAddr;               // address of the test memory
  lpvAddr = VirtualAlloc(NULL, 0x4096,
                         MEM_RESERVE | MEM_COMMIT,
                         PAGE_READONLY | PAGE_GUARD);
  if(lpvAddr == NULL) 
  {
    printf("VirtualAlloc failed with %ld\n", GetLastError());
    return -1;
  }

  return *(long *)lpvAddr;  
}

The program would terminate abnormally whether compile with Debug or Release,because by specifying the PAGE_GUARD flag would cause the:

Pages in the region become guard pages. Any attempt to read from or write to a guard page causes the system to raise a STATUS_GUARD_PAGE exception and turn off the guard page status. Guard pages thus act as a one-shot access alarm.

So you'd get STATUS_GUARD_PAGE while trying to access *lpvAddr.But if you use debugger load the program and watch *lpvAddv or step the last statement return *(long *)lpvAddr assembly by assembly,the debugger would forsee the guard page to determine the value of *lpvAddr.So the debugger would have cleared the guard alarm for us before we access *lpvAddr.

Jichao
Can you explain what happens when compiling this in Debug and Release ? Is this related to one of the flags passed to VirtualAlloc ?
BlueTrin
A: 

One big reason is that debug code may define the _DEBUG macro that one may use in the code to add extra stuff in debug builds.

For multithreaded code, optimization may affect ordering which may influence race conditions.

I do not know if debug code adds code on the stack to mark stack frames. Any extra stuff on the stack may hide the effects of buffer overruns.

Try using the same command options as your release build and just add the -g (or equivalent debug flag). gcc allows the debug option together with the optimization options.

doron
A: 

Also, debuggers might add some padding around allocated memory changing the behaviour. This has caught me out a number of times, so you need to be aware of it. Getting the same memory behaviour in debug is important.

For MSVC, this can be disabled with the env-var _NO_DEBUG_HEAP=1. (The debug heap is slow, so this helps if your debug runs are hideously slow too..).

Another method to get the same is to start the process outside the debugger, so you get a normal startup, then wait on first line in main and attach the debugger to process. That should work for "any" system. provided that you don't crash before main. (You could wait on a ctor on a statically pre-mani constructed object then...)

But I've no experience with gcc/gdb in this matter, but things might be similar there... (Comments welcome.)

Marcus Lindblom
A: 

Simple answer: Because the program is wrong.

David Rodríguez - dribeas
+2  A: 

Here's a common gotcha that can lead to a Heisenbug (love that name!):

    // Sanity check - this should never fail
    ASSERT( ReleaseResources() == SUCCESS);

In a debug build, this will work as expected, but the ASSERT macro's argument is ignored in a release build. By ignored, I mean that not only won't the result be reported, but the expression won't be evaluated at all (i.e. ReleaseResources() won't be called).

This is a common mistake, and it's why the Windows SDK defines a VERIFY() macro in addition to the ASSERT() macro. They both generate an assertion dialog at runtime in a debug build if the argument evaluates to false. Their behavior is different for a release build, however. Here's the difference:

    ASSERT( foo() == true );  // Confirm that call to foo() was successful
    VERIFY( bar() == true );  // Confirm that call to bar() was successful

In a debug build, the above two macros behave identically. In a release build, however, they are essentially equivalent to:

    ;       // Confirm that call to foo() was successful
    bar();  // Confirm that call to bar() was successful



By the way, if your environment defines an ASSERT() macro, but not a VERIFY() macro, you can easily define your own:

    #ifdef _DEBUG
       // DEBUG build: Define VERIFY simply as ASSERT
    #  define VERIFY(expr)  ASSERT(expr)
    #else
       // RELEASE build: Define VERIFY as the expression, without any checking
    #  define VERIFY(expr)  ((void)(expr))
    #endif

Hope that helps.

Scott Smith
A: 

maybe program is wrong.

ezygamelink
+2  A: 

Apparently stackoverflow won't let me post a response which contains only a single word :)

VALGRIND

Einstein
A: 

If your logic depends on data from the system clock, you could see serious probe effects. If you break into the debugger, you will obviously effect the values returned from clock functions such as timeGetTime(). The same is true if your program takes longer to execute. As other people have said, debug builds insert NOOPs. Also, simply running under the debugger (without hitting breakpoints) might slow things down.

An example of where this might happen is a real-time physics simulation with a variable time step, based off elapsed system time. This is why there are articles like this: http://gafferongames.com/game-physics/fix-your-timestep/

Merlyn Morgan-Graham