Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 11. (Managed) Main Method에 Break Point 걸기 [링크 복사], [링크+제목 복사],
조회: 29921
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 6개 있습니다.)
(시리즈 글이 9개 있습니다.)
디버깅 기술: 11. (Managed) Main Method에 Break Point 걸기
; https://www.sysnet.pe.kr/2/0/469

디버깅 기술: 11.1. (Managed) Main Method에 Break Point 걸기 - 내용 보강
; https://www.sysnet.pe.kr/2/0/470

디버깅 기술: 35. windbg - 분석 예: 시작하자마자 비정상 종료하는 프로세스 - NullReferenceException
; https://www.sysnet.pe.kr/2/0/996

디버깅 기술: 37. .NET 4.0 응용 프로그램의 Main 함수에 BreakPoint 걸기
; https://www.sysnet.pe.kr/2/0/1021

디버깅 기술: 59. NT 서비스가 시작하자마자 디버거를 연결시키는 방법 (1)
; https://www.sysnet.pe.kr/2/0/1586

디버깅 기술: 60. NT 서비스가 시작하자마자 디버거를 연결시키는 방법 (2)
; https://www.sysnet.pe.kr/2/0/1587

디버깅 기술: 61. NT 서비스 시작 단계에서 닷넷 메서드에 BP를 걸어 디버깅하는 방법
; https://www.sysnet.pe.kr/2/0/1598

디버깅 기술: 100. windbg - .NET 4.0 응용 프로그램의 Main 메서드에 Breakpoint 걸기
; https://www.sysnet.pe.kr/2/0/11322

디버깅 기술: 125. WinDbg로 EXE의 EntryPoint에서 BP 거는 방법
; https://www.sysnet.pe.kr/2/0/11859






(Managed) Main Method에 Break Point 걸기


[실습 준비]
1. "Debugging Tools for Windows" 설치.
2. 심벌 환경 설정: 커널 구조체 살펴보기 토픽을 참조. 
그냥 간단하게 다음과 같은 환경변수를 추가해 주셔도 됩니다.

환경변수 이름: _NT_SYMBOL_PATH
환경변수 값  : SRV*c:\Symbol\OSSymbols*http://msdl.microsoft.com/download/symbols;C:\Windows\system32;.




.NET의 JIT 컴파일러는 IL-code를 Natvie-code로 변경하는 작업을, 해당 메서드가 실행되기 바로 직전에 수행합니다. 즉, "클래스" 단위가 아닌 "메서드" 단위로 JIT 컴파일링이 된다는 것인데요. 이에 관해서는 다음의 토픽에서 잘 설명해 주고 있습니다.

Method Calls: Part 1 (Normal Call) 
; http://codebetter.com/blogs/gregyoung/archive/2006/07/20/147512.aspx

위의 토픽에서는 VS.NET 2005 환경에서 SOS.dll만을 가지고 메서드가 JIT 컴파일 되기 전/후의 상황을 Method Description 테이블을 통해서 직접 확인하는 방법을 알려주고 있습니다. 물론, 이것이 현실적으로 직접적인 도움이 되는 지식은 아니지만, 이렇게 눈으로 확인해 봄으로써 .NET에 대한 이해의 깊이를 더해줄 수 있기 때문에, ^^ 시간되시는 분은 따라해 보시기 바랍니다.

결국, 기계가 실행할 수 있는 것은 "Managed"가 아닌 "Unmanaged" 코드임을 알 수 있는데요. 사실, ".NET 어셈블리" 자체도 "Unmanaged 스타트업" 코드가 초기에 실행되어, mscoree.dll에서 제공해 주는 "CLR 호스팅 API"를 사용해서 내부에서 JIT 컴파일러를 통해 적절하게 "Unmanaged" 코드로 변경시켜 주면서 실행해 주는 구조일뿐입니다.

개인적으로, 바로 이 부분에서 의문이 생기더군요. (사실 이것도 그다지 밥벌이하고는 상관없겠지만!) 그렇다면 "Main" 함수에는 어떻게 접근할 수 있을까? 라는 것이었습니다. 이미 .NET 환경이 초기화된 이후에는 sos.dll을 사용해서 적절하게 처리를 할 수 있는 반면 Main 함수 자체는 최초의 실행 코드이기 때문에 그 부분에 BP를 거는 것이 그다지 쉽지는 않을 거란 생각이 들었습니다.

그래도 ^^ 한번 해봐야 겠지요. 그래서 자료를 먼저 찾아보았습니다. 대략 다음과 같은 주옥같은 자료들이 눈에 띄였습니다.

NTSD and SOS basics
; https://docs.microsoft.com/en-us/archive/blogs/thottams/ntsd-and-sos-basics

COM Interop 소개
; https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/january/clr-inside-out-introduction-to-com-interop

Bp at the Main(entry point) function of managed application 
; http://byung.egloos.com/2434057

그럼, 위의 글을 모두 종합해서 ... 저랑 같이 실습을 해볼까요? ^^




0. 테스트 코드

namespace ConsoleApplication1
{
	using System;
	public class Program
	{
		string str;
		public void MyMethod(string arg)
		{
			str = "Member Variable";
		}
		static void Main()
		{
			Program s = new Program();
			s.MyMethod("Hello");
		}
	}
}

1. 디버거 실행
	C:\temp>windbg.exe ConsoleApplication1.exe
	또는
	C:\temp>ntsd.exe ConsoleApplication1.exe	

비스타를 사용하시는 분이라면, "NTSD.exe"의 경우에는 "Run as administrator"를 이용하여 명령어 창을 띄운 후, 그 안에서 실행해 주시면 됩니다. (참고로 여기서는 WinDBG.exe를 기준으로 설명하겠지만, 사실 NTSD.exe와 거의 차이가 없습니다.)

위와 같이 실행했으면 화면에는 다음과 같은 텍스트가 보이게 됩니다.

Microsoft (R) Windows Debugger  Version 6.6.0007.5
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: D:\temp\DumpTest\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
Symbol search path is: SRV*\\localhost\d$\Symbol\OSSymbols*http://msdl.microsoft.com/download/symbols;SRV*\\localhost\d$\Symbol\ProductSymbols;C:\Windows\system32;.
Executable search path is: 
ModLoad: 00920000 00928000   ConsoleApplication1.exe
ModLoad: 77c40000 77d5e000   ntdll.dll
eax=009226fe ebx=7ffd5000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=77ca0f18 esp=001efdb8 ebp=00000000 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000200
ntdll!RtlUserThreadStart:
77ca0f18 89442404        mov     dword ptr [esp+4],eax ss:0023:001efdbc=00000000
 

로드되자마자 BP가 걸려서 응용 프로그램 실행이 멈춰 있습니다. VS.NET 2005와 비교해 본다면 "F11" 키를 눌러서 디버그 실행을 한 것과 같다고 볼 수 있겠습니다.

2. Symbol 폴더 설정
환경변수에 "_NT_SYMBOL_PATH" 값을 설정하신 분은 건너뛰셔도 됩니다. 만약 하지 않은 경우라면 별도의 ".symfix", ".sympath" 등의 명령어를 사용해서 임의로 구성이 가능합니다.

3. sos.dll 로드 확인
".chain" 명령어를 사용해서 sos 확장 DLL이 로드되었는 지 확인합니다. 아마 "WinDbg 6.6.0007.5" 버전을 사용하고 계신 분이라면, 기본적으로 sos.dll이 로드되었을 것이므로 별도로 명령을 주지 않아도 됩니다. 아래는 실제 실행 예입니다.

0:000> .chain
Extension DLL search Path:
    C:\Program Files\De....[중간 생략]...les\Debugging Tools for Windows\
Extension DLL chain:
    C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos: image 2.0.50727.312, API 1.0.0, built Thu Oct 19 12:37:43 2006
        [path: C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll]
    dbghelp: image 6.6.0007.5, API 6.0.6, built Sun Jul 09 05:11:32 2006
        [path: C:\Program Files\Debugging Tools for Windows\dbghelp.dll]
    ext: image 6.6.0007.5, API 1.0.0, built Sun Jul 09 05:10:52 2006
        [path: C:\Program Files\Debugging Tools for Windows\winext\ext.dll]
    exts: image 6.6.0007.5, API 1.0.0, built Sun Jul 09 05:10:48 2006
        [path: C:\Program Files\Debugging Tools for Windows\WINXP\exts.dll]
    uext: image 6.6.0007.5, API 1.0.0, built Sun Jul 09 05:11:02 2006
        [path: C:\Program Files\Debugging Tools for Windows\winext\uext.dll]
    ntsdexts: image 6.0.5457.0, API 1.0.0, built Sun Jul 09 05:29:38 2006
        [path: C:\Program Files\Debugging Tools for Windows\WINXP\ntsdexts.dll]

참고로, windbg.exe와는 달리 ntsd.exe에서는 기본적으로 sos.dll이 로드되어 있지 않았습니다.

4. sos 확장 API 사용을 위한 프로그램 부분 실행
sos.dll에서 제공되는 확장 명령어를 사용하려면 mscorwks.dll이 로드되어야 합니다. 실제로, mscorwks.dll이 로드되기 전에 sos 명령어를 사용하게 되면 다음과 같은 오류를 보게 됩니다.

0:000> !clrstack
Failed to find runtime DLL (mscorwks.dll), 0x80004005
Extension commands need mscorwks.dll in order to have something to do.

mscorwks.dll을 로드하는 시점까지 프로그램을 실행시키는 방법은 "sxe ld mscorwks" 명령어를 사용하는 방법이 있습니다.
출력 결과는 아래와 같습니다.

0:000> sxe ld mscorwks
0:000> g
ModLoad: 79000000 79045000   C:\Windows\system32\mscoree.dll
ModLoad: 76720000 767f8000   C:\Windows\system32\KERNEL32.dll
(154c.b70): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=0026f53c edx=77ca0f34 esi=fffffffe edi=77d05d14
eip=77c82ea8 esp=0026f554 ebp=0026f584 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
77c82ea8 cc              int     3
0:000> g
ModLoad: 77b80000 77c3f000   C:\Windows\system32\ADVAPI32.dll
ModLoad: 77db0000 77e73000   C:\Windows\system32\RPCRT4.dll
... [중간 생략]...
ModLoad: 76690000 7670d000   C:\Windows\system32\USP10.dll
ModLoad: 752a0000 75434000   C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.6000.16386_none_5d07289e07e1d100\comctl32.dll
ModLoad: 79e70000 7a3d6000   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
eax=0026eb90 ebx=00000000 ecx=0026eb88 edx=00343bc2 esi=7ffdf000 edi=20000000
eip=77ca0f34 esp=0026ebcc ebp=0026ec10 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
77ca0f34 c3              ret

하지만, 아직 CLR에 "ConsoleApplication1"의 IL들이 로드조차 되지 않아서 그런지 sos.dll에서 제공되는 "!bpmd" 메서드를 사용해도 정상적으로 동작되지 않았습니다. 그래서 저 같은 경우에는 일단 "unmanaged" 방식으로 돌아와서 디버깅 방법을 찾기 시작했습니다. 우선, 테스트 소스에서 Main 함수에 예외를 발생하도록 일부러 다음과 같이 수정했습니다.

static void Main()
{
  throw new ApplicationException();
  Program s = new Program();
  s.MyMethod("Hello");
}

빌드하고, windbg.exe로 불러들여서 예외발생까지 코드를 실행시킨 후 "k" 명령어를 통해서 다음과 같이 호출 스택을 얻었습니다.

0:000> g
Tue Feb 27 23:25:40.120 2007 (GMT+9): ModLoad: 77b80000 77c3f000   C:\Windows\system32\ADVAPI32.dll
...[중간 생략]...
Tue Feb 27 23:25:40.300 2007 (GMT+9): ModLoad: 79e70000 7a3d6000   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
ChildEBP RetAddr  Args to Child              
0026ebc8 77c9fb70 77c6e334 00000054 ffffffff ntdll!KiFastSystemCallRet
...[중간 생략]...
0026fa28 00000000 0034271e 7ffd3000 00000000 ntdll!_RtlUserThreadStart+0x23
Tue Feb 27 23:25:40.498 2007 (GMT+9): ModLoad: 78130000 781cb000   C:\Windows\WinSxS\x86_microsoft.vc80
....[중간 생략]...
Tue Feb 27 23:25:40.618 2007 (GMT+9): ModLoad: 79060000 790b3000   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorjit.dll
Tue Feb 27 23:25:40.668 2007 (GMT+9): (12a8.f40): CLR exception - code e0434f4d (first chance)
Tue Feb 27 23:25:40.683 2007 (GMT+9): (12a8.f40): CLR exception - code e0434f4d (!!! second chance !!!)
eax=0026ed20 ebx=004b5108 ecx=00000001 edx=00000000 esi=0026eda8 edi=e0434f4d
eip=7673b09e esp=0026ed20 ebp=0026ed70 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
KERNEL32!RaiseException+0x58:
7673b09e c9              leave

0:000> k
ChildEBP RetAddr  
0026ed70 79f9691a KERNEL32!RaiseException+0x58
0026edd0 7a098e88 mscorwks!RaiseTheExceptionInternalOnly+0x226
0026ee94 003e00a7 mscorwks!JIT_Throw+0xfc
WARNING: Frame IP not in any known module. Following frames may be wrong.
0026eeb0 79e826bd 0x3e00a7
0026ef30 79e8451b mscorwks!CallDescrWorkerWithHandler+0xa3
0026f06c 79e84403 mscorwks!MethodDesc::CallDescr+0x19c
0026f084 79e843e0 mscorwks!MethodDesc::CallTargetWorker+0x20
0026f098 79ef3e20 mscorwks!MethodDescCallSite::CallWithValueTypes+0x18
0026f1fc 79ef3c19 mscorwks!ClassLoader::RunMain+0x220
0026f464 79ef3aee mscorwks!Assembly::ExecuteMainMethod+0xa6
0026f934 79ef3737 mscorwks!SystemDomain::ExecuteMainMethod+0x398
0026f984 79ef18d1 mscorwks!ExecuteEXE+0x59
0026f9cc 79003aa0 mscorwks!_CorExeMain+0x11b
0026f9dc 76763833 mscoree!_CorExeMain+0x2c
0026f9e8 77c7a9bd KERNEL32!BaseThreadInitThunk+0xe
0026fa28 00000000 ntdll!_RtlUserThreadStart+0x23

대강 봐도, "mscorwks!ClassLoader::RunMain"이 Main 메서드를 실행해 주는 함수라는 것을 쉽게 짐작할 수 있습니다.

이제, mscorwks.dll 로드를 굳이 명시적으로 확인할 필요없이 다음과 같이 직접 unmanaged API 함수에 BP를 걸어 놓는 것으로 문제를 해결할 수 있습니다.

0:000> bp mscorwks!ClassLoader::RunMain
Bp expression 'mscorwks!ClassLoader::RunMain' could not be resolved, adding deferred bp

현재는 mscorwks모듈이 로드되어 있지 않기 때문에 RunMain기호를 찾을 수 없지만, 이후에 혹시나 발견이 되면 설정해 주겠다고 친절하게(?) 메시지를 보여주고 있습니다. 이제 거의 다 되었습니다. "g" 명령어로 실행시키면 다음과 같이 RunMain 함수에서 실행이 멈추는 것을 확인할 수 있습니다.

0:000> g
Tue Feb 27 23:36:53.110 2007 (GMT+9): ModLoad: 77b80000 77c3f000   C:\Windows\system32\ADVAPI32.dll
....[중간 생략]...
Tue Feb 27 23:36:55.763 2007 (GMT+9): ModLoad: 79e70000 7a3d6000   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
ChildEBP RetAddr  Args to Child              
0030ef14 77c9fb70 77c6e334 00000054 ffffffff ntdll!KiFastSystemCallRet
....[중간 생략]...
mscorlib\7fe79782947b85d961fd55cb5e02a129\mscorlib.ni.dll
Tue Feb 27 23:37:01.632 2007 (GMT+9): Breakpoint 0 hit
eax=0030f584 ebx=00000000 ecx=1e3ea55d edx=80000001 esi=007c3000 edi=00000000
eip=79ef3c78 esp=0030f54c ebp=0030f59c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
mscorwks!ClassLoader::RunMain:
79ef3c78 55              push    ebp

5. Main 함수에 BP 설정
자, 다 되었습니다. 이제 sos.dll에서 제공되는 확장 명령어를 이용해서 Managed 함수에 쉽게 BP를 설정할 수 있습니다. 여기서는 Main 함수가 목표였기 때문에 다음과 같이 설정하시면 됩니다.

0:000> !bpmd ConsoleApplication1 ConsoleApplication1.Program.Main
Found 1 methods...
MethodDesc = 007c3000
Adding pending breakpoints...

0:000> g
Tue Feb 27 23:42:31.292 2007 (GMT+9): ModLoad: 79060000 790b3000   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorjit.dll
Tue Feb 27 23:42:31.313 2007 (GMT+9): (f68.16c0): CLR notification exception - code e0444143 (first chance)
JITTED ConsoleApplication1!ConsoleApplication1.Program.Main()
Setting breakpoint: bp 00D30070 [ConsoleApplication1.Program.Main()]
Tue Feb 27 23:42:31.413 2007 (GMT+9): Breakpoint 1 hit
eax=007c3000 ebx=0030f21c ecx=ffffffff edx=ffffffff esi=00135108 edi=00000000
eip=00d30070 esp=0030f1f4 ebp=0030f200 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
00d30070 83ec08          sub     esp,8

어떠세요? 재미있지요? ^^

[첨부된 파일은 실제로 위의 실습을 한 WinDBG.exe 실행 화면의 텍스트를 캡쳐한 것입니다.]



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

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/23/2021]

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

비밀번호

댓글 작성자
 



2008-06-09 08시21분
kevin25
2011-04-18 11시17분
.NET 4.0 응용 프로그램의 Main 함수에 BreakPoint 걸기
; http://www.sysnet.pe.kr/2/0/1021
정성태
2019-04-06 12시10분
0:000> sxe ld mscorwks.dll

0:000> g

0:000> bp mscorwks!ClassLoader::RunMain

0:000> g

0:000> !bpmd ConsoleApp1 ConsoleApp1.Program.Main

0:000> g

0:000> !clrstack
정성태

... 76  77  78  79  [80]  81  82  83  84  85  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
11936정성태6/10/201918401Math: 58. C# - 최소 자승법의 1차, 2차 수렴 그래프 변화 확인 [2]파일 다운로드1
11935정성태6/9/201919974.NET Framework: 843. C# - PLplot 출력을 파일이 아닌 Window 화면으로 변경
11934정성태6/7/201921315VC++: 133. typedef struct와 타입 전방 선언으로 인한 C2371 오류파일 다운로드1
11933정성태6/7/201919626VC++: 132. enum 정의를 C++11의 enum class로 바꿀 때 유의할 사항파일 다운로드1
11932정성태6/7/201918808오류 유형: 544. C++ - fatal error C1017: invalid integer constant expression파일 다운로드1
11931정성태6/6/201919317개발 환경 구성: 441. C# - CairoSharp/GtkSharp 사용을 위한 프로젝트 구성 방법
11930정성태6/5/201919858.NET Framework: 842. .NET Reflection을 대체할 System.Reflection.Metadata 소개 [1]
11929정성태6/5/201919430.NET Framework: 841. Windows Forms/C# - 클립보드에 RTF 텍스트를 복사 및 확인하는 방법 [1]
11928정성태6/5/201918189오류 유형: 543. PowerShell 확장 설치 시 "Catalog file '[...].cat' is not found in the contents of the module" 오류 발생
11927정성태6/5/201919430스크립트: 15. PowerShell ISE의 스크립트를 복사 후 PPT/Word에 붙여 넣으면 한글이 깨지는 문제 [1]
11926정성태6/4/201919939오류 유형: 542. Visual Studio - pointer to incomplete class type is not allowed
11925정성태6/4/201919806VC++: 131. Visual C++ - uuid 확장 속성과 __uuidof 확장 연산자파일 다운로드1
11924정성태5/30/201921461Math: 57. C# - 해석학적 방법을 이용한 최소 자승법 [1]파일 다운로드1
11923정성태5/30/201921076Math: 56. C# - 그래프 그리기로 알아보는 경사 하강법의 최소/최댓값 구하기파일 다운로드1
11922정성태5/29/201918545.NET Framework: 840. ML.NET 데이터 정규화파일 다운로드1
11921정성태5/28/201924406Math: 55. C# - 다항식을 위한 최소 자승법(Least Squares Method)파일 다운로드1
11920정성태5/28/201916063.NET Framework: 839. C# - PLplot 색상 제어
11919정성태5/27/201920319Math: 54. C# - 최소 자승법의 1차 함수에 대한 매개변수를 단순 for 문으로 구하는 방법 [1]파일 다운로드1
11918정성태5/25/201921155Math: 53. C# - 행렬식을 이용한 최소 자승법(LSM: Least Square Method)파일 다운로드1
11917정성태5/24/201922157Math: 52. MathNet을 이용한 간단한 통계 정보 처리 - 분산/표준편차파일 다운로드1
11916정성태5/24/201919961Math: 51. MathNET + OxyPlot을 이용한 간단한 통계 정보 처리 - Histogram파일 다운로드1
11915정성태5/24/201923084Linux: 11. 리눅스의 환경 변수 관련 함수 정리 - putenv, setenv, unsetenv
11914정성태5/24/201922120Linux: 10. 윈도우의 GetTickCount와 리눅스의 clock_gettime파일 다운로드1
11913정성태5/23/201918768.NET Framework: 838. C# - 숫자형 타입의 bit(2진) 문자열, 16진수 문자열 구하는 방법파일 다운로드1
11912정성태5/23/201918727VS.NET IDE: 137. Visual Studio 2019 버전 16.1부터 리눅스 C/C++ 프로젝트에 추가된 WSL 지원
11911정성태5/23/201917503VS.NET IDE: 136. Visual Studio 2019 - 리눅스 C/C++ 프로젝트에 인텔리센스가 동작하지 않는 경우
... 76  77  78  79  [80]  81  82  83  84  85  86  87  88  89  90  ...