Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 5.6. VS.NET 2005를 이용한 미니덤프 파일 분석 (1) [링크 복사], [링크+제목 복사]
조회: 27953
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
부모글 보이기/감추기
(연관된 글이 6개 있습니다.)
3.5.1 VS.NET 2005를 이용한 미니덤프 파일 분석 (1)

지난 회에서는 "4. VS.NET 2005 디버그 모드에서의 PDB 파일 사용 차이 (1), (2)"라는 내용을 다뤄봤는데요. 아마도 그 글을 읽으신 분들 중에는 내심으론 현실성이 없다는 생각을 가질 수도 있습니다.
즉, 그렇게 sos까지 이용하면서 원인 파악을 해줄 라이브러리 사용자가 얼마나 있을 것이냐는 생각인데요. ^^ 맞습니다. 그럴 분들이 과연 얼마나 되겠습니까? (국내 개발자들이 점점 더 그렇게 되었으면 좋겠습니다.)

자, 그럼 다소 현실로 돌아와서 이번에는 그렇게 예외가 발생했다고 고객사에서 알려왔을 때, 라이브러리를 실제로 개발한 당사자 측에서는 어떤 대응을 하는 것이 바람직할지 이야기 해보겠습니다.

우선 할 일은 고객으로부터 예외가 발생한 시점의 Dump를 받는 것입니다. 아마도 고객은 지난 회 살펴봤던 아래의 화면에서 더 이상 진전없이 헤매고 있을 것입니다.

PDB 파일 없이 F11을 누른 화면

이때, 여러분들은 고객에게 예외 창에서 "Break" 버튼을 누르라고 시킨 후, "Debug" 메뉴의 "Save Dump As..."를 선택해서 Mini Dump 파일을 생성하라고 요청합니다. 주의해야 할 것은, 이때 반드시 다음 화면과 같이 Heap 정보를 포함시켜야 합니다.

Heap 정보까지 포함한 덤프 파일 생성

Heap 정보를 포함시키지 않으면, VS.NET 2005에서 DUMP 파일을 읽어들여 디버깅 상황을 파헤쳐 갈때 SOS.dll의 일부 확장 명령어들이 오동작을 일으키게 됩니다.
이제, 이렇게 받은 DMP 파일로 어떻게 원인을 파악하는지를 알아보도록 하겠습니다.



지금쯤 여러분들의 메일 함에는 고객이 보내 온 HeapDump.dmp 파일이 있을 것입니다. 이것을 자신의 로컬 하드에 저장한 후, dmp 파일을 더블 클릭을 하면 VS.NET 2005가 뜨면서 덤프 파일이 로드됩니다. 그다음, 여느 응용 프로그램 디버깅 하듯이 "F5" 키를 누릅니다. 그럼, 고객이 예외를 만났던 바로 그때의 상황에서 BP(BreakPoint)가 멈춰 있는 것을 볼 수 있습니다.
이 상태에서는 아무것도 도움이 되는 것이 없습니다. Call Stack 창도, Disassembly 창도 여러분들에게 도움이 되는 정보를 전혀 보여주지 않습니다. 오히려 포기하게 만드는 주요 원인을 제공하지요. ^^; 차라리 고객사에 가서 직접 해당 개발자의 PC 앞에 앉아서 문제 해결을 해주고 오는 편이 더욱 편하다는 생각이 들기 시작할지도 모릅니다. 적어도 여러분들이 이번 토픽을 읽기 전까지는 그랬을 수도 있습니다.

하지만, 여러분들은 제가 쓴 토픽을 이미 읽은 상태이고, 모처럼 얻은 실습의 기회를 놓칠 수 없습니다. 심호흡을 한번 하고, 다시 생각을 가다듬습니다. 첫 번째로 할일은?
우선, "Modules" 창을 통해서 어떤 모듈들이 올라왔는지 확인해 봅니다. 아래는 제 VS.NET 2005에서 확인한 Modules 창입니다.

Modules

딱 보니, BaseLibrary.dll은 자신이 만든 DLL임을 알겠고 WinForm.exe는 고객이 만든 것임을 알 수 있습니다. 둘 다 현재 아무런 "Symbol File"이 매칭되어 있지 않음을 확인할 수 있습니다. 그렇군요. 역시 이번에도 중요한 것은 PDB 파일의 유무입니다.

그런데, 이걸 어쩝니까? (제가 방법을 모르는 것일 수도 있으나) 현재의 VS.NET 2005에서는 Minidump 파일을 읽어들인 디버깅에서는 .NET 모듈의 PDB 파일을 로드하는 것이 가능하지 않습니다. 기본적으로 VS.NET 2005는 DMP 파일과 동일한 폴더에서 Binary 파일을 찾으려 하고, PDB 파일 역시 동일한 폴더에서 로딩을 합니다. 그런데, .NET 모듈인 경우에는 관련 PDB 파일이 자동으로 매칭이 안될 뿐더러 수동으로 매칭 시키려 해도 다음과 같은 오류를 내고는 더 이상 진행을 하지 않습니다.
(Native Module인 경우에는 정상적으로 PDB 심벌 파일이 로드됩니다.)

"The symbol file WinForm.pdb does not match the module.

이야기의 주제가 "PDB"라는 것이 무색해지는 순간입니다. ^^; 어쩔 수 없습니다. PDB 파일을 로드하지 못하는 이상, 덤프 파일이 가진 정보만으로 문제를 파악해야 합니다.

역시, 이번에도 이전 토픽에서 살펴본 "Sos.dll"을 사용해서 문제를 해결해 나갈 수 있습니다. 방법은 아래와 같습니다.



1. 사용자가 예외 상황 발생 후의 덤프를 보냈으므로, 현재 발생한 예외 정보를 출력시킵니다.

!printexception
PDB symbol for mscorwks.dll not loaded
Exception object: 012e8878
Exception type: System.IO.FileNotFoundException
Message: Could not find file 'C:\test_nothing.txt'.
InnerException: 
StackTrace (generated):
    SP       IP       Function
    0012EA58 796538B2 System.IO.__Error.WinIOError(Int32, System.String)
    0012EAB8 7936F43B System.IO.FileStream.Init(System.String, System.IO.FileMode, System.IO.FileAccess, Int32, Boolean, System.IO.FileShare, Int32, System.IO.FileOptions, SECURITY_ATTRIBUTES, System.String, Boolean)
    0012EBAC 793723BF System.IO.FileStream..ctor(System.String, System.IO.FileMode, System.IO.FileAccess, System.IO.FileShare)
    0012EBD0 7946D097 System.IO.File.Open(System.String, System.IO.FileMode)
    0012EBE0 012402E6 BaseLibrary.ThrowClass.TestMethod()
    0012EBF0 01240272 WinForm.Form1.Form1_Load(System.Object, System.EventArgs)
    0012EC40 7B065B27 System.Windows.Forms.Form.OnLoad(System.EventArgs)
    0012EC78 7B060BFE System.Windows.Forms.Form.OnCreateControl()
    0012EC80 7B06FB22 System.Windows.Forms.Control.CreateControl(Boolean)
    0012ECBC 7B06FB57 System.Windows.Forms.Control.CreateControl()
    0012ECCC 7B06BD92 System.Windows.Forms.Control.WmShowWindow(System.Windows.Forms.Message ByRef)
    0012ED08 7B072E3B System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
    0012ED6C 7B07F795 System.Windows.Forms.ScrollableControl.WndProc(System.Windows.Forms.Message ByRef)
    0012ED74 7B07F743 System.Windows.Forms.ContainerControl.WndProc(System.Windows.Forms.Message ByRef)
    0012ED78 7B067086 System.Windows.Forms.Form.WmShowWindow(System.Windows.Forms.Message ByRef)
    0012ED84 7B063F40 System.Windows.Forms.Form.WndProc(System.Windows.Forms.Message ByRef)
    0012ED94 7B07A72D System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
    0012ED98 7B07A706 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
    0012EDAC 7B08B845 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr, Int32, IntPtr, IntPtr)
    00000000 00000001 System.Windows.Forms.SafeNativeMethods.ShowWindow(System.Runtime.InteropServices.HandleRef, Int32)
    0012F2B0 7B073135 System.Windows.Forms.Control.SetVisibleCore(Boolean)
    0012F358 7B06382B System.Windows.Forms.Form.SetVisibleCore(Boolean)
    0012F398 7B070336 System.Windows.Forms.Control.set_Visible(Boolean)
    0012F39C 7B08429E System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
    0012F408 7B08416B System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
    0012F438 7B0C69FE System.Windows.Forms.Application.Run(System.Windows.Forms.Form)
    0012F448 012400BA WinForm.Program.Main()

StackTraceString: 
HResult: 80070002

PDB 파일이 올라왔다면 위의 부분에서 파일 정보와 소스 코드 라인 정보를 얻을 수 있었겠지만, 지금으로선 그와 같은 정보를 얻을 수 없습니다.
(아쉽게도, 이것은 가정일 뿐 .NET 2.0의 sos.dll은 PDB 파일 유무에 상관없이 소스 및 라인 정보를 보여주지 않습니다.)
우리가 알 수 있는 정보는 사용자가 작성한 WinForm.Form1.Form1_Load에서 우리가 개발한 BaseLibrary.ThrowClass.TestMethod를 호출해서 예외가 발생했다는 것 정도입니다.

2. 이젠 TestMethod의 "IP" 컬럼값인 "012402E6"에 대해서 역어셈블을 합니다.

!u 012402E6
Normal JIT generated code
BaseLibrary.ThrowClass.TestMethod()
Begin 012402c0, size 36
012402C0 57               push        edi
012402C1 56               push        esi
012402C2 50               push        eax
012402C3 890C24           mov         dword ptr [esp],ecx
012402C6 833D7C64950000   cmp         dword ptr ds:[0095647Ch],0
012402CD 7405             je          012402D4
012402CF E82A20E578       call        7A0922FE (JitHelp: CORINFO_HELP_DBG_IS_JUST_MY_CODE)
012402D4 33FF             xor         edi,edi
012402D6 8B0D9C6B2E02     mov         ecx,dword ptr ds:[022E6B9Ch]
012402DC BA03000000       mov         edx,3
012402E1 E886CD2278       call        7946D06C (System.IO.File.Open(System.String, System.IO.FileMode), mdToken: 060033db)
>>> 012402E6 CC               int         3
012402E7 F08BFE           lock mov    edi,esi
012402EA 8BCF             mov         ecx,edi
012402EC 8B01             mov         eax,dword ptr [ecx]
012402EE FF5070           call        dword ptr [eax+70h]
012402F1 90               nop
012402F2 59               pop         ecx
012402F3 5E               pop         esi
012402F4 5F               pop         edi
012402F5 C3               ret

실행이 어느 지점에서 멈췄는 지를 ">>>" 기호를 통해서 알 수 있습니다. 즉 call 명령어에서 예외가 발생했음을 알 수 있고, 이에 대해 File.Open에 들어간 인자값을 알아내야 합니다. 이전 토픽과는 달리, 이때의 ecx 값은 신뢰할 수 없는 상태입니다. 왜냐하면 mov ecx, dword ptr ds:[022E6B9Ch]를 한 이후, 내부의 File.Open 처리 및 예외 발생으로 인해 ecx 레지스터는 달리 쓰여졌을 수 있기 때문입니다. 그러니 애당초 기대를 하지 않는 것이 좋겠고요. 이렇게 되면, ds:[022E6B9Ch] 값을 Memory 창을 이용해서 직접 구해야 합니다.

3. 메모리 창을 통해서 ds:[022E6B9Ch] 주소 영역을 확인한 결과는 다음과 같습니다. (대개의 경우, ds 세그먼트 값은 무시하셔도 좋습니다. ds 세그먼트는 0~4GB 영역의 범위를 가지고 있으며 Base Address는 0 이기 때문입니다. 커널 드라이버 개발자가 아니라면 굳이 ds 값을 확인할 필요는 없습니다.)

Memory

Little Endian 형식으로 저장된 DWORD 값이므로, 실제 값은 "0x012e747c"입니다.

4. 지금쯤 다음 단계를 짐작하셔야 될텐데요. ^^ 바로 DumpObj 명령으로 위의 주소값에 해당하는 클래스 인스턴스를 검사해 보는 것입니다.

!DumpObj 012e747c
Name: System.String
MethodTable: 790fa3e0
EEClass: 790fa340
Size: 58(0x3a) bytes
 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: C:\\test_nothing.txt
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
790fed1c  4000096        4         System.Int32  0 instance       21 m_arrayLength
790fed1c  4000097        8         System.Int32  0 instance       20 m_stringLength
790fbefc  4000098        c          System.Char  0 instance       43 m_firstChar
790fa3e0  4000099       10        System.String  0   shared   static Empty
    >> Domain:Value  0014ceb0:790d6584 <<
79124670  400009a       14        System.Char[]  0   shared   static WhitespaceChars
    >> Domain:Value  0014ceb0:012e13dc <<

해당 참조형은 System.String 형으로, 그 값은 "C:\\test_nothing.txt"인 것을 확인할 수 있습니다.



이렇게 해서 고객이 보내온 덤프 파일을 통해서 우리의 문제를 확인할 수 있었습니다. 이제 여러분은 고객에게 새로 패치된 모듈을 보내주면서, 개발자가 실수로 남겨놓은 하드 코딩된 문자열이 그 원인이었다는 말은 빼고 더 이상 그런 문제가 없을 거라는 말로 마무리 지을 수 있게 됩니다.
지난 번의 "3.4. VS.NET 2005 디버그 모드에서의 PDB 파일 사용 차이" 토픽에서 살펴보았던 그런 개발자가 고객사에 있다면 여러분의 실수가 드러났을 수도 있겠지만. ^^

마지막으로 언급할 것이 있다면 ^^ 물론, 모든 문제들이 이렇게 간단하지는 않습니다. 다양한 문제 상황에 대해서 경험을 쌓아야만 하고 특정 상황에 대해서 어떻게 문제를 풀어나가야 할지 감각을 키우셔야 합니다.




[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]
[연관 글]






[최초 등록일: ]
[최종 수정일: 7/1/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13608정성태4/26/202425닷넷: 2249. C# - 부모의 필드/프로퍼티에 대해 서로 다른 자식 클래스 간에 Reflection 접근이 동작할까요?파일 다운로드1
13607정성태4/25/2024207닷넷: 2248. C# - 인터페이스 타입의 다중 포인터를 인자로 갖는 C/C++ 함수 연동
13606정성태4/24/2024220닷넷: 2247. C# - tensorflow 연동 (MNIST 예제)파일 다운로드1
13605정성태4/23/2024495닷넷: 2246. C# - Python.NET을 이용한 파이썬 소스코드 연동파일 다운로드1
13604정성태4/22/2024563오류 유형: 901. Visual Studio - Unable to set the next statement. Set next statement cannot be used in '[Exception]' call stack frames.
13603정성태4/21/2024759닷넷: 2245. C# - IronPython을 이용한 파이썬 소스코드 연동파일 다운로드1
13602정성태4/20/2024831닷넷: 2244. C# - PCM 오디오 데이터를 연속(Streaming) 재생 (Windows Multimedia)파일 다운로드1
13601정성태4/19/2024876닷넷: 2243. C# - PCM 사운드 재생(NAudio)파일 다운로드1
13600정성태4/18/2024904닷넷: 2242. C# - 관리 스레드와 비관리 스레드
13599정성태4/17/2024878닷넷: 2241. C# - WAV 파일의 PCM 사운드 재생(Windows Multimedia)파일 다운로드1
13598정성태4/16/2024907닷넷: 2240. C# - WAV 파일 포맷 + LIST 헤더파일 다운로드2
13597정성태4/15/2024891닷넷: 2239. C# - WAV 파일의 PCM 데이터 생성 및 출력파일 다운로드1
13596정성태4/14/20241077닷넷: 2238. C# - WAV 기본 파일 포맷파일 다운로드1
13595정성태4/13/20241058닷넷: 2237. C# - Audio 장치 열기 (Windows Multimedia, NAudio)파일 다운로드1
13594정성태4/12/20241072닷넷: 2236. C# - Audio 장치 열람 (Windows Multimedia, NAudio)파일 다운로드1
13593정성태4/8/20241088닷넷: 2235. MSBuild - AccelerateBuildsInVisualStudio 옵션
13592정성태4/2/20241226C/C++: 165. CLion으로 만든 Rust Win32 DLL을 C#과 연동
13591정성태4/2/20241201닷넷: 2234. C# - WPF 응용 프로그램에 Blazor App 통합파일 다운로드1
13590정성태3/31/20241081Linux: 70. Python - uwsgi 응용 프로그램이 k8s 환경에서 OOM 발생하는 문제
13589정성태3/29/20241158닷넷: 2233. C# - 프로세스 CPU 사용량을 나타내는 성능 카운터와 Win32 API파일 다운로드1
13588정성태3/28/20241272닷넷: 2232. C# - Unity + 닷넷 App(WinForms/WPF) 간의 Named Pipe 통신 [2]파일 다운로드1
13587정성태3/27/20241172오류 유형: 900. Windows Update 오류 - 8024402C, 80070643
13586정성태3/27/20241343Windows: 263. Windows - 복구 파티션(Recovery Partition) 용량을 늘리는 방법
13585정성태3/26/20241145Windows: 262. PerformanceCounter의 InstanceName에 pid를 추가한 "Process V2"
13584정성태3/26/20241273개발 환경 구성: 708. Unity3D - C# Windows Forms / WPF Application에 통합하는 방법파일 다운로드1
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...