IT story

프로그램은 릴리스 빌드로만 충돌합니다. 디버그 방법은 무엇입니까?

hot-time 2020. 9. 8. 21:57
반응형

프로그램은 릴리스 빌드로만 충돌합니다. 디버그 방법은 무엇입니까?


여기에 "Schroedinger 's Cat"유형의 문제가 있습니다. 내 프로그램 (실제로는 내 프로그램의 테스트 스위트이지만 그럼에도 불구하고 프로그램)이 충돌하지만 릴리스 모드에서 빌드 된 경우에만 명령 줄에서 시작될 때만 발생합니다. . 원시인 디버깅 (즉, 모든 곳에서 불쾌한 printf () 메시지)을 통해 코드가 충돌하는 테스트 방법을 결정했지만 불행히도 실제 충돌은 일부 소멸자에서 발생하는 것 같습니다. 깨끗하게 실행되는 다른 소멸자.

Visual Studio 내에서이 프로그램을 실행하려고하면 충돌이 발생하지 않습니다. WinDbg.exe에서 시작할 때도 마찬가지입니다. 충돌은 명령 줄에서 시작할 때만 발생합니다. 이것은 Windows Vista, btw에서 발생하며 안타깝게도 지금 테스트 할 XP 시스템에 액세스 할 수 없습니다.

Windows가 스택 추적을 인쇄하도록하거나 프로그램이 깨끗하게 종료 된 것처럼 단순히 종료하는 이외의 다른 것을 인쇄 할 수 있다면 정말 좋을 것 입니다. 여기에서 더 의미있는 정보를 얻고이 버그를 수정하는 방법에 대한 조언이 있습니까?

편집 : 문제는 실제로이 게시물에서 자세히 설명하는 범위를 벗어난 배열로 인해 발생했습니다 . 이 문제를 찾는 데 도움을 주신 모든 분들께 감사드립니다!


C 또는 C ++ 프로그램이 디버거에서 제대로 실행되지만 외부에서 실행하면 실패하는 100 %의 사례에서, 그 원인은 함수 로컬 배열의 끝을지나 쓰기 때문입니다. (디버거는 스택에 더 많이 배치하므로 중요한 내용을 덮어 쓸 가능성이 적습니다.)


일반적으로 변수 초기화로 인해 발생하기 전에 이와 같은 문제가 발생했을 때. 디버그 모드에서 변수와 포인터는 자동으로 0으로 초기화되지만 릴리스 모드에서는 초기화되지 않습니다. 따라서 이와 같은 코드가 있으면

int* p;
....
if (p == 0) { // do stuff }

디버그 모드에서 if의 코드는 실행되지 않지만 릴리스 모드에서 p는 정의되지 않은 값을 포함합니다.이 값은 0 일 가능성이 낮으므로 코드가 자주 실행되어 충돌이 발생합니다.

초기화되지 않은 변수에 대한 코드를 확인합니다. 이것은 배열의 내용에도 적용될 수 있습니다.


지금까지 릴리스 애플리케이션을 디버깅하는 데 사용할 수있는 기술에 대한 진지한 개요를 제공하려는 답변은 없습니다.

  1. 릴리스 및 디버그 빌드는 여러 가지 이유로 다르게 작동합니다. 다음은 훌륭한 개요입니다. 이러한 각 차이점은 디버그 빌드에 존재하지 않는 릴리스 빌드의 버그를 유발할 수 있습니다.

  2. 디버거가 있으면 릴리스 및 디버그 빌드 모두 에서 프로그램의 동작이 변경 될 수 있습니다 . 이 답변을 참조하십시오. 간단히 말해서, 적어도 Visual Studio 디버거는 프로그램에 연결될 때 자동으로 디버그 힙을 사용합니다. 환경 변수 _NO_DEBUG_HEAP를 사용하여 디버그 힙을 끌 수 있습니다. 컴퓨터 속성 또는 Visual Studio의 프로젝트 설정에서 지정할 수 있습니다. 그러면 연결된 디버거로 충돌을 재현 할 수 있습니다.

    여기에서 힙 손상 디버깅에 대해 자세히 알아보십시오.

  3. 이전 솔루션이 작동하지 않으면 처리되지 않은 예외를 포착 하고 충돌이 발생하는 인스턴스에 사후 디버거를 연결해야 합니다. 예를 들어 WinDbg를 사용하여 사용 가능한 사후 디버거 및 MSDN에서의 설치에 대한 세부 정보 를 사용할 수 있습니다.

  4. 예외 처리 코드를 개선 할 수 있으며 프로덕션 애플리케이션 인 경우 다음을 수행해야합니다.

    ㅏ. 다음을 사용하여 사용자 지정 종료 처리기를 설치합니다.std::set_terminate

    이 문제를 로컬로 디버깅하려면 종료 처리기 내에서 무한 루프를 실행하고 일부 텍스트를 콘솔에 출력하여 std::terminate호출되었음을 알릴 수 있습니다 . 그런 다음 디버거를 연결하고 호출 스택을 확인합니다. 또는이 답변에 설명 된대로 스택 추적을 인쇄합니다.

    프로덕션 응용 프로그램에서는 여기에 설명 된대로 문제를 분석 할 수있는 작은 메모리 덤프와 함께 오류 보고서를 집으로 보낼 수 있습니다.

    비. 하드웨어 및 소프트웨어 예외를 모두 포착 할 수있는 Microsoft의 구조적 예외 처리 메커니즘 을 사용합니다. MSDN을 참조하십시오 . SEH를 사용하여 코드의 일부를 보호하고 a)와 동일한 접근 방식을 사용하여 문제를 디버깅 할 수 있습니다. SEH는 프로덕션 앱에서 오류 보고서를 보낼 때 사용할 수있는 발생한 예외에 대한 자세한 정보를 제공합니다.


주의해야 할 사항 :

배열 오버런-Visual Studio 디버거가 충돌을 중지 할 수있는 패딩을 삽입합니다.

경쟁 조건-여러 스레드가 관련된 경우 경쟁 조건이 많은 경우 응용 프로그램이 직접 실행될 때만 나타납니다.

연결-올바른 라이브러리를 가져 오는 릴리스 빌드입니다.

시도 할 사항 :

Minidump-정말 사용하기 쉬울 것입니다 (msdn에서 찾아보기 만하면됩니다). 각 스레드에 대한 전체 크래시 덤프를 제공합니다. 출력을 Visual Studio에로드하기 만하면 충돌시 디버깅하는 것과 같습니다.


WinDbg를 사후 디버거로 설정할 수 있습니다. 그러면 디버거가 시작되고 충돌이 발생할 때 프로세스에 연결됩니다. 사후 디버깅을 위해 WinDbg를 설치하려면 / I 옵션을 사용하십시오 ( 대문자로 표시됨).

windbg /I

자세한 내용은 여기를 참조 하세요 .

원인에 관해서는 다른 답변에서 알 수 있듯이 아마도 단일화 된 변수 일 것입니다.


After many hours of debugging, I finally found the cause of the problem, which was indeed caused by a buffer overflow, caused a single byte difference:

char *end = static_cast<char*>(attr->data) + attr->dataSize;

This is a fencepost error (off-by-one error) and was fixed by:

char *end = static_cast<char*>(attr->data) + attr->dataSize - 1;

The weird thing was, I put several calls to _CrtCheckMemory() around various parts of my code, and they always returned 1. I was able to find the source of the problem by placing "return false;" calls in the test case, and then eventually determining through trial-and-error where the fault was.

Thanks everybody for your comments -- I learned a lot about windbg.exe today! :)


Even though you have built your exe as a release one, you can still generate PDB (Program database) files that will allow you to stack trace, and do a limited amount of variable inspection. In your build settings there is an option to create the PDB files. Turn this on and relink. Then try running from the IDE first to see if you get the crash. If so, then great - you're all set to look at things. If not, then when running from the command line you can do one of two things:

  1. Run EXE, and before the crash do an Attach To Process (Tools menu on Visual Studio).
  2. After the crash, select the option to launch debugger.

When asked to point to PDB files, browse to find them. If the PDB's were put in the same output folder as your EXE or DLL's they will probably be picked up automatically.

The PDB's provide a link to the source with enough symbol information to make it possible to see stack traces, variables etc. You can inspect the values as normal, but do be aware that you can get false readings as the optimisation pass may mean things only appear in registers, or things happen in a different order than you expect.

NB: I'm assuming a Windows/Visual Studio environment here.


Crashes like this are almost always caused because an IDE will usually set the contents of uninitialized variable to zeros, null or some other such 'sensible' value, whereas when running natively you'll get whatever random rubbish that the system picks up.

Your error is therefore almost certainly that you are using something like you are using a pointer before it has been properly initialized and you're getting away with it in the IDE because it doesn't point anywhere dangerous - or the value is handled by your error checking - but in release mode it does something nasty.


In order to have a crash dump that you can analyze:

  1. Generate pdb files for your code.
  2. You rebase to have your exe and dlls loaded in the same address.
  3. Enable post mortem debugger such as Dr. Watson
  4. Check the crash failures address using a tool such as crash finder.

You should also check out the tools in Debugging tools for windows. You can monitor the application and see all the first chance exceptions that were prior to your second chance exception.

Hope it helps...


Once i had a problem when app behaved similarily to yours. It turned out to be a nasty buffer overrun in sprintf. Naturally, it worked when run with a debugger attached. What i did, was to install an unhandled exception filter (SetUnhandledExceptionFilter) in which i simply blocked infinitely (using WaitForSingleObject on a bogus handle with a timeout value of INFINITE).

So you could something along the lines of:

long __stdcall MyFilter(EXCEPTION_POINTERS *)
{
    HANDLE hEvt=::CreateEventW(0,1,0,0);
    if(hEvt)
    {
        if(WAIT_FAILED==::WaitForSingleObject(hEvt, INFINITE))
        {
            //log failure
        }
    }

}
// somewhere in your wmain/WinMain:
SetUnhandledExceptionFilter(MyFilter);

I then attached the debugger after the bug had manifested itself (gui program stopped responding).

Then you can either take a dump and work with it later:

.dump /ma path_to_dump_file

Or debug it right away. The simplest way is to track where processor context has been saved by the runtime exception handling machinery:

s-d esp Range 1003f

Command will search stack address space for CONTEXT record(s) provided the length of search. I usually use something like 'l?10000'. Note, do not use unsually large numbers as the record you're after usually near to the unhanded exception filter frame. 1003f is the combination of flags (i believe it corresponds to CONTEXT_FULL) used to capture the processor state. Your search would look similar to this:

0:000> s-d esp l1000 1003f
0012c160 0001003f 00000000 00000000 00000000 ?...............

Once you get results back, use the address in the cxr command:

.cxr 0012c160

This will take you to this new CONTEXT, exactly at the time of crash (you will get exactly the stack trace at the time your app crashed). Additionally, use:

.exr -1

to find out exactly which exception had occurred.

Hope it helps.


Sometimes this happens because you have wrapped important operation inside "assert" macro. As you may know, "assert" evaluates expressions only on debug mode.


With regard to your problems getting diagnostic information, have you tried using adplus.vbs as an alternative to WinDbg.exe? To attach to a running process, use

adplus.vbs -crash -p <process_id>

Or to start the application in the event that the crash happens quickly:

adplus.vbs -crash -sc your_app.exe

Full info on adplus.vbs can be found at: http://support.microsoft.com/kb/286350


Ntdll.dll with debugger attached

One little know difference between launching a program from the IDE or WinDbg as opposed to launching it from command line / desktop is that when launching with a debugger attached (i.e. IDE or WinDbg) ntdll.dll uses a different heap implementation which performs some little validation on the memory allocation/freeing.

You may read some relevant information in unexpected user breakpoint in ntdll.dll. One tool which might be able to help you identifying the problem is PageHeap.exe.

Crash analysis

You did not write what is the "crash" you are experiencing. Once the program crashes and offers you to send the error information to the Microsoft, you should be able to click on the technical information and to check at least the exception code, and with some effort you can even perform post-mortem analysis (see Heisenbug: WinApi program crashes on some computers) for instructions)


Vista SP1 actually has a really nice crash dump generator built into the system. Unfortunately, it isn't turned on by default!

See this article: http://msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx

The benefit of this approach is that no extra software needs to be installed on the affected system. Grip it and rip it, baby!


As my experience, that are most being memory corruption issues.

For example :

char a[8];
memset(&a[0], 0, 16);

: /*use array a doing some thing */

it is very possible to be normal in debug mode when one runs the code.

But in release, that would/might be crash.

For me, to rummage where the memory is out of bound is too toilsome.

Use some tools like Visual Leak Detector (windows) or valgrind (linux) are more wise choise.


A great way to debug an error like this is to enable optimizations for your debug build.


I've seen a lot of right answers. However, there is none that helped me. In my case, there was a wrong usage of the SSE instructions with the unaligned memory. Take a look at your math library (if you use one), and try to disable SIMD support, recompile and reproduce the crash.

Example:

A project includes mathfu, and uses the classes with STL vector: std::vector< mathfu::vec2 >. Such usage will probably cause a crash at the time of the construction of mathfu::vec2 item since the STL default allocator does not guarantee required 16-byte alignment. In this case to prove the idea, one can define #define MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT 1 before each include of the mathfu, recompile in Release configuration and check again.

The Debug and RelWithDebInfo configurations worked well for my project, but not the Release one. The reason behind this behavior is probably because debugger processes allocation/deallocation requests and does some memory bookkeeping to check and verify the accesses to the memory.

I experienced the situation in Visual Studio 2015 and 2017 environments.


Something similar happend to me once with GCC. It turned out to be a too aggressive optimization that was enabled only when creating the final release and not during the development process.

Well, to tell the truth it was my fault, not gcc's, as I didn't noticed that my code was relying on the fact that that particular optimization wouldn't have been done.

It took me a lot of time to trace it and I only came to it because I asked on a newsgroup and somebody made me think about it. So, let me return the favour just in case this is happening to you as well.


I've found this this article useful for your scenario. ISTR the compiler options were a little out of date. Look around your Visual Studio project options to see how to generate pdb files for your release build, etc.


It's suspicious that it would happen outside the debugger and not inside; running in the debugger does not normally change the application behavior. I would check the environment differences between the console and the IDE. Also, obviously, compile release without optimizations and with debug information, and see if that affects the behavior. Finally, check out the post-mortem debugging tools other people have suggested here, usually you can get some clue from them.


Debugging release builds can be a pain due to optimizations changing the order in which lines of your code appear to be executed. It can really get confusing!

One technique to at least narrow down the problem is to use MessageBox() to display quick statements stating what part of the program your code has got to ("Starting Foo()", "Starting Foo2()"); start putting them at the top of functions in the area of your code that you suspect (what were you doing at the time when it crashed?). When you can tell which function, change the message boxes to blocks of code or even individual lines within that function until you narrow it down to a few lines. Then you can start printing out the value of variables to see what state they are in at the point of crashing.


Try using _CrtCheckMemory() to see what state the allocated memory is in . If everything goes well , _CrtCheckMemory returns TRUE , else FALSE .


You might run your software with Global Flags enabled (Look in Debugging Tools for Windows). It will very often help to nail the problem.


Make your program generate a mini dump when the exception occurs, then open it up in a debugger (for example, in WinDbg). The key functions to look at: MiniDumpWriteDump, SetUnhandledExceptionFilter


Here's a case I had that somebody might find instructive. It only crashed in release in Qt Creator - not in debug. I was using .ini files (as I prefer apps that can be copied to other drives, vs. ones that lose their settings if the Registry gets corrupted). This applies to any apps that store their settings under the apps' directory tree. If the debug and release builds are under different directories, you can have a setting that's different between them, too. I had preference checked in one that wasn't checked in the other. It turned out to be the source of my crash. Good thing I found it.

I hate to say it, but I only diagnosed the crash in MS Visual Studio Community Edition; after having VS installed, letting my app crash in Qt Creator, and choosing to open it in Visual Studio's debugger. While my Qt app had no symbol info, it turns out that the Qt libraries had some. It led me to the offending line; since I could see what method was being called. (Still, I think Qt is a convenient, powerful, & cross-platform LGPL framework.)


I had this error and vs crashed even when trying to !clean! my project. So I deleted the obj files manually from the Release directory, and after that it built just fine.


I agree with Rolf. Because reproducibility is so important, you shouldn't have a non-debug mode. All your builds should be debuggable. Having two targets to debug more than doubles your debugging load. Just ship the "debug mode" version, unless it is unusable. In which case, make it usable.

참고URL : https://stackoverflow.com/questions/186237/program-only-crashes-as-release-build-how-to-debug

반응형