2009. 9. 25. 15:11

Crash 발생시 스스로 미니 덤프를 생성하는 코드 공유와 간략한 덤프 분석 방법


다음과 같은 코드가 있을때,


(*p) = 10; 코드 실행시 NULL point assignment로 인해 Crash가 발생하게 됩니다.
그때, 시스템에 포스트모텀 디버거가 등록되어 있다면, 그것이 실행되어 Crash에 대한 덤프파일을 만들게 됩니다.

대표적인 포스트모텀 디버거는 닥터왓슨(drwtsn32.exe)이며, WinDbg도 여기에 해당됩니다.
(단, 닥터왓슨은 Vista 이상의 OS에서는 기본적으로 포함되지 않아 보입니다.)

임의의 응용프로그램에서 Crash가 발생했을때, 자동으로 그 덤프를 만들기 위해서는 다음과 같은 등록과정이 필요합니다.
  • drwtsn32.exe -i

  • windbg.exe -I

  • Visual Studio -> 메뉴 -> Tools -> Option -> Debug -> Just-In-Time debugging 체크 (JIT)

즉, 이렇게 등록이 되면, 그 이후부터 Crash가 발생했을 경우마다 다음과 같은 동작을 하게 됩니다.

  • drwtsn32.exe
    drwtsn32.exe의 설정 옵션중 아래 경로에서 덤프파일 확인 가능 (크래시 덤프 위치)

  • windbg.exe
    windbg.exe의 command로 .dump /f 옵션으로 덤프파일 생성가능
  • Visual Studio
    자체적으로 디버깅 가능

위에도 언급하였지만, Vista 이상에서는 닥터 왓슨을 기본적으로 사용할 수 없으며, Crash 발생시 "Problem Reports and Solutions"로 연결됩니다. 따라서, 비스타 이상에서 윈도우 기본 설정으로 자동으로 포스트 모텀 덤프파일을 남길 수 없으며 (제가 알기론 현재까지 그렇습니다. 혹시 있나요?), 아래와 같이 "덤프 파일 만들기"를 실행해야 합니다. 즉, 사용자의 개입을 통해서만 덤프를 남길수 있습니다.

1) Vista에서 오류 발생


2) 위 창을 닫지 않는 상태에서, 작업 관리자 실행

3) "덤프 파일 만들기" 실행하여 덤프 파일 생성

여기까지 일단 포스트 모텀 디버거의 설명을 끝내구요.
만약, 이러한 포스트 모텀 디버거가 등록되지 않는 경우에는, 오류에 대한 덤프파일 수집이 불가능하게 됩니다.
따라서, 그런 경우에 대비하기 위해서는, .exe에서 자신이 Crash가 발생했을때 덤프파일을 직접 만들줄 알아야 합니다. 물론, 최소한 drwtsn32.exe가 지원되지 않는 Vista 이상에서는 더욱더 필요할 지 모릅니다.

이제, 공유 코드를 설명하도록 하겠습니다.
(사용전, selfdump.h의 주석을 필히 탐독하시길 바랍니다.)

사용예는,

#include "selfdump.h"

winmain()
{
 ...
 // 대략 초기에...
 CSelfDump::RegisterExectionFilter(); // <- 코드로 실행
 ...
}
혹은
CApp theApp;
...
CSelfDump g_cSelfDump(); // <- 전역 변수로 실행
와 같이 하면 됩니다. 매우 간단합니다.

위를 호출하면, 디폴트로 동작하게 되며, 상세한 옵션은 다음과 같습니다.
(옵션은 RegisterExectionFilter() 혹은 CSelfDump 생성자의 파라미터에 넣으시면 됩니다.)

  • lpszDumpPreFixName
    덤프 파일 이름을 지정한다. 지정되지 않는 경우는 실행 .exe 파일의 이름이 된다.
    ex) abc로 지정되는 경우, abc_yyyymmddmmssmmm.dmp로 덤프 파일 이름이 결정된다.
  • nMiniDumpType
    덤프 파일의 타입을 지정한다. 기본값은 MiniDumpNormal 이다.
    타입은 http://msdn.microsoft.com/en-us/library/ms680519(VS.85).aspx 에서 참고하시오.
  • dwDumpCapacityBytes
    PreFixName으로 만들어진 덤프 파일들이 유지될 파일 크기. 기본값은 1,048,576(1MB)이다.
    만약, 크기가 넘어가는 경우, 가장 옛날에 만들어졌던 파일이 삭제되는 논리로, 크기를 유지한다.
  • lpszDumpPath
    덤프 파일을 생성할 Path. 기본값은 .exe Path 하부의 temp 폴더이다.
    Vista이상에서, .exe의 권한이 낮은 경우를 대비하려면, temp 폴더에는 everyone 계정으로 Full Access가 필요한다.
    ex) 기본값인 경우, C:\ttt\abc.exe는 C:\ttt\temp\abc_~~~~.dmp로 저장된다.
  • pfnCallback / pfnUserData
    덤프파일을 만들기 전에 호출받을 수 있는 callback을 등록. 기본값은 NULL.
    callback 호출받아 UI를 띄워 줄 수 있다. FALSE를 리턴하면 덤프를 남기지 않는다.

그렇다면, 위의 내용을 토대로 응용해 보도록 하겠습니다.

1) 아래와 같이, release에서 debug info를 추가하여 pdb 파일을 만들 수 있도록 빌드 설정을 변경합니다.





2) 아래와 같은 코드를 만듭니다.



3) 아래와 같이 Temp 폴더를 만들고 .exe를 실행합니다.


4) 실행후, 포스트모텀 디버거가 연결되어 있다면, 해당 디버거가 실행됩니다. Temp 폴더에 들어가면 포스트모텀 디버거와 상관없이 만들어진 덤프파일과 로그가 포함되어 있습니다.


5) CrashDmp.log는 다음과 같이 기록됩니다.
[20090925_143205640]
Prefix=CrashIt
DumpPath=D:\XXX\CrashIt\Release\Temp\CrashIt_20090925143205.dmp
ProcessId=756
CrashModulePath=D:\XXX\CrashIt\Release\CrashIt.exe
SuccessWriteDump=yes

6) 덤프 파일을 windbg로 분석해 봅니다.
6-1) windbg 실행
6-2) windbg에 위 CrashIt_~~~.dmp 파일을 끌어 Load 시킴
6-3) 메뉴 -> Symbol File Path에 3)의 폴더 경로를 넣음. 즉, .pdb 경로를 대입
6-4) windbg의 command 창에 .reload -i를 실행
6-5) windbg의 command 창에 !analyze -v를 실행
6-6) 다음과 같은 결과 보고
...
 FAULTING_SOURCE_CODE: 
     4: #include "stdafx.h"
     5: #include "SelfDump.h"
     6:
     7: VOID Crash(VOID)
>    8: {
     9:  INT *p = NULL;
    10:
    11:  // ********
    12:  // crash it
    13:  // ********
...


즉, (*p) = 10; 부근에서 오류가 발생했음을 확인할 수 있습니다.
(뭐... 한 두라인 정도 차이가 있네요. 아마도 심볼 연결이 정확하진 않아서 그런지... 모르겠습니다. ;ㅁ;)