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

이번에는 지난 회에 살펴봤던 3.5. VS.NET 2005를 이용한 미니덤프 파일 분석 (1)에 대해서 좀 더 보충 설명을 해보겠습니다.

사실, 덤프 파일의 디버깅은 대개의 경우 덤프 파일 만을 고객으로부터 받게 될 뿐 별다른 DLL/EXE는 받지 않는 경우가 많습니다. 소스 파일은 말할 필요도 없겠지요.
이런 상황은 예전 Win32 VC++ 상황에서는 무척이나 힘든 디버깅을 해야 했었지만, .NET Managed로 오면서는 그러한 부분들이 많이 편해지게 되었습니다. .NET 자체가 Metadata 및 중간 언어로 되어 있으니 그럴 수밖에 없겠지요.

본론으로 들어가서, 덤프 파일만으로 해당 DLL들을 뽑아내고 소스 파일 까지 생성해서 디버깅에 도움이 될 수 있도록 해보겠습니다.

상황을 바꿔서 여러분들이 특정 프로그램의 문제를 분석하기 위해 덤프 파일을 받은 것으로 가정해 보겠습니다.
물론 프로그램은 지난번 것을 그대로 이어서 설명합니다.

여러분들은 우선, dmp 파일을 더블 클릭해서 VS.NET 2005에서 불러드리게 됩니다.
평상시와 다름없이 "F5"를 눌러 디버깅을 시작하게 되고, sos.dll을 로드한 다음 어느 스레드에서 어떤 문제가 발생했는지 알아낼 것입니다.

!threads
PDB symbol for mscorwks.dll not loaded
ThreadCount: 2
UnstartedThread: 0
BackgroundThread: 1
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
                                      PreEmptive   GC Alloc           Lock
       ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
3764    1  eb4 00187e28     86028 Enabled  00000000:00000000 0014ceb0     0 STA System.IO.FileNotFoundException (013175dc)
1876    2  754 00192e60      b228 Enabled  00000000:00000000 0014ceb0     0 MTA (Finalizer)
 

Finalizer가 표시된 스레드는 GC에 의해서 사용되는 스레드일 뿐이며, 중요한 것은 위에서 FileNotFoundException 예외가 발생한 사용자 STA 스레드입니다.
스레드 예외 오른쪽 괄호에는 해당 예외 객체의 주소가 들어가 있는 것인데요. 다음과 같이 해당 객체의 내용을 살펴볼 수가 있습니다.

!dumpobj 013175dc
Name: System.IO.FileNotFoundException
MethodTable: 79177fc0
EEClass: 79213a28
Size: 84(0x54) bytes
 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
790fa3e0  40000b5        4        System.String  0 instance 00000000 _className
79109208  40000b6        8 ...ection.MethodBase  0 instance 00000000 _exceptionMethod
790fa3e0  40000b7        c        System.String  0 instance 00000000 _exceptionMethodString
790fa3e0  40000b8       10        System.String  0 instance 013176dc _message
79113dfc  40000b9       14 ...tions.IDictionary  0 instance 00000000 _data
790fa9e8  40000ba       18     System.Exception  0 instance 00000000 _innerException
790fa3e0  40000bb       1c        System.String  0 instance 00000000 _helpURL
790f9c18  40000bc       20        System.Object  0 instance 01317a9c _stackTrace
790fa3e0  40000bd       24        System.String  0 instance 00000000 _stackTraceString
790fa3e0  40000be       28        System.String  0 instance 00000000 _remoteStackTraceString
790fed1c  40000bf       34         System.Int32  0 instance        0 _remoteStackIndex
790f9c18  40000c0       2c        System.Object  0 instance 00000000 _dynamicMethods
790fed1c  40000c1       38         System.Int32  0 instance -2147024894 _HResult
790fa3e0  40000c2       30        System.String  0 instance 00000000 _source
790fe160  40000c3       3c        System.IntPtr  0 instance        0 _xptrs
790fed1c  40000c4       40         System.Int32  0 instance -532459699 _xcode
790fa3e0  4001b5f       44        System.String  0 instance 00000000 _maybeFullPath
790fa3e0  4001b7b       48        System.String  0 instance 013060bc _fileName
790fa3e0  4001b7c       4c        System.String  0 instance 00000000 _fusionLog

예외 클래스 내부의 멤버 변수들에 대한 값을 열람할 수 있는데요. Value 형식은 그 값을 그대로 확인할 수 있지만 나머지 참조형의 경우에는 역시 "!dumpobj" 명령어를 통해서 세부적인 값을 확인할 수 있습니다. 사실 대부분의 경우에, 위의 예외 클래스에서 제공하는 정보만으로 예외의 원인을 알 수 있는데, 그렇지 못한 경우에는 더 오류 추적을 해봐야 합니다.

우리가 지금 원하는 것은 예외가 발생된 사용자 메서드의 위치를 알고 싶은 것이므로, 다음과 같이 printexception 명령어를 통해서 확인을 합니다.

!printexception 013175dc
Exception object: 013175dc
Exception type: System.IO.FileNotFoundException
Message: Could not find file 'C:\test_nothing.txt'.
InnerException: 
StackTrace (generated):
    SP       IP       Function
    0012EA20 796538B2 System.IO.__Error.WinIOError(Int32, System.String)
    0012EA80 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)
    0012EB74 793723BF System.IO.FileStream..ctor(System.String, System.IO.FileMode, System.IO.FileAccess, System.IO.FileShare)
    0012EB98 7946D097 System.IO.File.Open(System.String, System.IO.FileMode)
    0012EBA8 0124030F BaseLibrary.ThrowClass.TestMethod()
    0012EBF0 01240270 WinForm.Form1.Form1_Load(System.Object, System.EventArgs)
    0012EC40 7B065B27 System.Windows.Forms.Form.OnLoad(System.EventArgs)
    0012EC78 7B060BFE System.Windows.Forms.Form.OnCreateControl()
... [중간 생략] ...
    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

예외가 발생한 사용자 모듈은 BaseLibrary.ThrowClass.TestMethod임을 알 수 있는데요.
해당 메서드의 IP(Instruction Pointer) 값을 확인해서 Method Descriptor를 다음과 같이 알아낼 수 있습니다.

!ip2md 0124030F
MethodDesc: 00956690
Method Name: BaseLibrary.ThrowClass.TestMethod()
Class: 012614ec
MethodTable: 009566a0
mdToken: 06000001
Module: 009562c8
IsJitted: yes
m_CodeOrIL: 012402d8

사실 ^^ MethodDesc 보다는 Module 값이 더 궁금한 것이었는데 위와 같이 친절하게 ip2md에서 알려주고 있습니다.
이제 해당 모듈의 시작 주소를 알아내야 하는데, 다음과 같은 방법으로 알아낼 수 있습니다.

!dumpmodule 009562c8
Name: D:\temp2\PdbSample\WinFormApp\WinForm\bin\Debug\BaseLibrary.dll
Attributes: PEFile 
Assembly: 001dad00
LoaderHeap: 00000000
TypeDefToMethodTableMap: 01260330
TypeRefToMethodTableMap: 01260338
MethodDefToDescMap: 0126039c
FieldDefToDescMap: 012603a8
MemberRefToDescMap: 012603ac
FileReferencesMap: 01260400
AssemblyReferencesMap: 01260404
MetaData start address: 03c7207c (1848 bytes)

휴... 시작 주소까지 무사히 알아내었으니, 이젠 해당 주소부터 시작하는 모듈을 추출해서 로컬에 저장해 보겠습니다.

!savemodule 03c7207c C:\mymodule.dll
3 sections in file
section 0 - VA=2000, VASize=884, FileAddr=1000, FileSize=1000
section 1 - VA=4000, VASize=360, FileAddr=2000, FileSize=1000
section 2 - VA=6000, VASize=c, FileAddr=3000, FileSize=1000

DLL 을 추출했으면 당연히 소스도 얻을 수 있겠지요. ^^ 어렵게 ILDASM을 쓸 필요도 없이 우리에게 널리 알려진 .NET Relector를 통해서 소스를 얻어낼 수 있습니다.

여기까지 해보니, 이전에 살펴본 "3.5. VS.NET 2005를 이용한 미니덤프 파일 분석 (1)" 때보다도 상황이 훨씬 더 좋아보입니다.
문제가 발생한 부분의 소스 코드까지 얻었으니 문제 접근이 더욱 용이해졌습니다.

이제, 실제 소스와 함께 실질적으로 예외가 발생한 부분을 맞추기 위해서 IP 레지스터가 가리키는 값을 기반으로 역어셈블을 해봐야 합니다.

!u 0124030F
Normal JIT generated code
BaseLibrary.ThrowClass.TestMethod()
Begin 012402d8, size 4c
012402D8 55               push        ebp
012402D9 8BEC             mov         ebp,esp
012402DB 57               push        edi
012402DC 56               push        esi
012402DD 53               push        ebx
012402DE 83EC34           sub         esp,34h
012402E1 33C0             xor         eax,eax
012402E3 8945F0           mov         dword ptr [ebp-10h],eax
012402E6 33C0             xor         eax,eax
012402E8 8945E4           mov         dword ptr [ebp-1Ch],eax
012402EB 894DC4           mov         dword ptr [ebp-3Ch],ecx
012402EE 833D7C64950000   cmp         dword ptr ds:[0095647Ch],0
012402F5 7405             je          012402FC
012402F7 E80220E578       call        7A0922FE (JitHelp: CORINFO_HELP_DBG_IS_JUST_MY_CODE)
012402FC 33FF             xor         edi,edi
012402FE 90               nop
012402FF 8B0D9C6B2E02     mov         ecx,dword ptr ds:[022E6B9Ch]
01240305 BA03000000       mov         edx,3
0124030A E85DCD2278       call        7946D06C (System.IO.File.Open(System.String, System.IO.FileMode), mdToken: 060033db)
>>> 0124030F 8BF0             mov         esi,eax
01240311 8BFE             mov         edi,esi
01240313 8BCF             mov         ecx,edi
01240315 8B01             mov         eax,dword ptr [ecx]
01240317 FF5070           call        dword ptr [eax+70h]
0124031A 90               nop
0124031B 90               nop
0124031C 8D65F4           lea         esp,[ebp-0Ch]
0124031F 5B               pop         ebx
01240320 5E               pop         esi
01240321 5F               pop         edi
01240322 5D               pop         ebp
01240323 C3               ret

위의 굵은 글씨 부분이 눈에 익으실 것입니다. 바로 ">>>" 부분이 오류가 난 다음 라인입니다. 그러니, 그 이전의 call 명령어를 .NET Reflector를 통해 본 소스와 맞춰 보면 실제로 어느 라인에서 오류가 발생했는 지를 알 수 있습니다.
또한, 이전에 설명한 방법을 사용하시면 인자값까지 확인할 수 있으니... 이렇게 되면 거의 예외 상황의 근거는 확인이 끝난 것이 됩니다.

어떠세요? 좀 재미있으셨나요? ^^




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






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

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

비밀번호

댓글 작성자
 



2014-07-07 12시53분
정성태

... 91  92  93  94  95  96  97  98  99  100  [101]  102  103  104  105  ...
NoWriterDateCnt.TitleFile(s)
11407정성태12/18/201724220.NET Framework: 712. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 + OpenCV [1]파일 다운로드1
11406정성태12/17/201746615.NET Framework: 711. C# - OpenCvSharp의 Mat 데이터 조작 방법 [5]파일 다운로드1
11405정성태12/17/201742614.NET Framework: 710. C# - OpenCvSharp을 이용한 Webcam 영상 처리 + Direct2D [1]파일 다운로드1
11404정성태12/16/201729905.NET Framework: 709. C# - OpenCvSharp을 이용한 동영상(avi, mp4, ...) 처리 + Direct2D [7]파일 다운로드1
11403정성태12/16/201732516.NET Framework: 708. C# - OpenCvSharp을 이용한 동영상(avi, mp4, ...) 처리 [3]파일 다운로드1
11402정성태12/15/201737171.NET Framework: 707. OpenCV 응용 프로그램을 C#으로 구현 - OpenCvSharp [2]파일 다운로드1
11401정성태12/15/201726106.NET Framework: 706. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 [2]파일 다운로드1
11400정성태12/14/201728971.NET Framework: 705. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 [9]파일 다운로드1
11399정성태12/13/201717598.NET Framework: 704. Win32 API의 UnionRect를 닷넷 BCL의 Rectangle.Union으로 바꿀 때 주의 사항
11398정성태12/13/201717801오류 유형: 442. ASP.NET Core Web Application (on .NET Framework) 프로젝트에서 외부 라이브러리 동적 로드 시 런타임 버전 문제파일 다운로드1
11397정성태12/12/201720395.NET Framework: 703. 양자 컴퓨팅을 위한 마이크로소프트의 Q# 언어
11396정성태12/8/201742767개발 환경 구성: 343. Visual Studio - 리눅스 용 프로젝트의 인텔리센스를 위한 헤더 파일 처리 방법 [3]
11395정성태12/8/201718621오류 유형: 441. 이벤트 로그 - Time Provider NtpClient: No valid response has been received from domain controller
11394정성태12/8/201718251개발 환경 구성: 342. 비주얼 스튜디오에서 실행하던 ASP.NET Core (.NET Framework) 응용 프로그램을 명령행에서 실행하는 방법
11393정성태12/7/201722808Windows: 145. 윈도우 10 빌드 17046부터 WSL에서 백그라운드 작업 지원 [5]
11392정성태12/7/201718064개발 환경 구성: 341. openSUSE에 닷넷 코어 설치
11391정성태12/7/201720914개발 환경 구성: 340. WSL을 이용해 윈도우 PC 1대에서 openSUSE 응용 프로그램을 Visual Studio로 개발하는 방법 [1]
11390정성태12/7/201729568개발 환경 구성: 339. WSL을 이용해 윈도우 PC 1대에서 Linux 응용 프로그램을 Visual Studio로 개발하는 방법 [6]
11389정성태12/7/201718236오류 유형: 440. .NET Core 오류 - 0x80131620 Unable to load DLL 'libuv'
11388정성태12/6/201721874개발 환경 구성: 338. WSL 또는 Ubuntu에 닷넷 코어 설치 [3]
11387정성태12/6/201722234오류 유형: 439. 이벤트 로그 - Data Sharing Service 서비스의 %%3239247874 오류 메시지
11386정성태12/5/201717804오류 유형: 438. Hyper-V - '...' failed to add device 'Virtual CD/DVD Disk'
11385정성태12/5/201730918VC++: 121. DXGI를 이용한 윈도우 화면 캡처 소스 코드(Visual C++) [16]파일 다운로드1
11384정성태12/5/201720203오류 유형: 437. Visual C++ - Cannot open include file: 'SDKDDKVer.h'
11383정성태12/4/201723304디버깅 기술: 110. 비동기 코드 실행 중 예외로 인한 ASP.NET 프로세스 비정상 종료 현상 [1]
11382정성태12/4/201721884오류 유형: 436. System.Data.SqlClient.SqlException (0x80131904): Connection Timeout Expired 예외 발생 시 "[Pre-Login] initialization=48; handshake=1944;" 값의 의미
... 91  92  93  94  95  96  97  98  99  100  [101]  102  103  104  105  ...