성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<span> <br /> <br /> <div class='mainCenterTitle'>(Managed) Main Method에 Break Point 걸기</div><br /> <br /> <pre class='code'> [실습 준비] 1. "<a target='_tab' href='http://www.microsoft.com/whdc/devtools/debugging/default.mspx'>Debugging Tools for Windows</a>" 설치. 2. 심벌 환경 설정: <a target='_tab' href='/2/0/263'>커널 구조체 살펴보기</a> 토픽을 참조. 그냥 간단하게 다음과 같은 환경변수를 추가해 주셔도 됩니다. 환경변수 이름: _NT_SYMBOL_PATH 환경변수 값 : SRV*c:\Symbol\OSSymbols*http://msdl.microsoft.com/download/symbols;C:\Windows\system32;. </pre> <br /> <hr style='width: 50%' /><br /> <br /> .NET의 JIT 컴파일러는 IL-code를 Natvie-code로 변경하는 작업을, 해당 메서드가 실행되기 바로 직전에 수행합니다. 즉, "클래스" 단위가 아닌 "메서드" 단위로 JIT 컴파일링이 된다는 것인데요. 이에 관해서는 다음의 토픽에서 잘 설명해 주고 있습니다.<br /> <br /> <pre class='code'> Method Calls: Part 1 (Normal Call) ; <a target='_tab' href='http://codebetter.com/blogs/gregyoung/archive/2006/07/20/147512.aspx'>http://codebetter.com/blogs/gregyoung/archive/2006/07/20/147512.aspx</a> </pre> <br /> 위의 토픽에서는 VS.NET 2005 환경에서 SOS.dll만을 가지고 메서드가 JIT 컴파일 되기 전/후의 상황을 Method Description 테이블을 통해서 직접 확인하는 방법을 알려주고 있습니다. 물론, 이것이 현실적으로 직접적인 도움이 되는 지식은 아니지만, 이렇게 눈으로 확인해 봄으로써 .NET에 대한 이해의 깊이를 더해줄 수 있기 때문에, ^^ 시간되시는 분은 따라해 보시기 바랍니다. <br /> <br /> 결국, 기계가 실행할 수 있는 것은 "Managed"가 아닌 "Unmanaged" 코드임을 알 수 있는데요. 사실, ".NET 어셈블리" 자체도 "Unmanaged 스타트업" 코드가 초기에 실행되어, mscoree.dll에서 제공해 주는 "CLR 호스팅 API"를 사용해서 내부에서 JIT 컴파일러를 통해 적절하게 "Unmanaged" 코드로 변경시켜 주면서 실행해 주는 구조일뿐입니다.<br /> <br /> 개인적으로, 바로 이 부분에서 의문이 생기더군요. (사실 이것도 그다지 밥벌이하고는 상관없겠지만!) 그렇다면 "Main" 함수에는 어떻게 접근할 수 있을까? 라는 것이었습니다. 이미 .NET 환경이 초기화된 이후에는 sos.dll을 사용해서 적절하게 처리를 할 수 있는 반면 Main 함수 자체는 최초의 실행 코드이기 때문에 그 부분에 BP를 거는 것이 그다지 쉽지는 않을 거란 생각이 들었습니다.<br /> <br /> 그래도 ^^ 한번 해봐야 겠지요. 그래서 자료를 먼저 찾아보았습니다. 대략 다음과 같은 주옥같은 자료들이 눈에 띄였습니다.<br /> <br /> <pre class='code'> NTSD and SOS basics ; <a target='_tab' href='https://docs.microsoft.com/en-us/archive/blogs/thottams/ntsd-and-sos-basics'>https://docs.microsoft.com/en-us/archive/blogs/thottams/ntsd-and-sos-basics</a> COM Interop 소개 ; <a target='_tab' href='https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/january/clr-inside-out-introduction-to-com-interop'>https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/january/clr-inside-out-introduction-to-com-interop</a> Bp at the Main(entry point) function of managed application ; <a target='_tab' href='http://byung.egloos.com/2434057'>http://byung.egloos.com/2434057</a> </pre> <br /> 그럼, 위의 글을 모두 종합해서 ... 저랑 같이 실습을 해볼까요? ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 0. 테스트 코드<br /> <br /> <pre class='code'> 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"); } } } </pre> <br /> <span class='subLastTitle'>1. 디버거 실행</span> <br /> <pre class='code'> C:\temp>windbg.exe ConsoleApplication1.exe 또는 C:\temp>ntsd.exe ConsoleApplication1.exe </pre> <br /> 비스타를 사용하시는 분이라면, "NTSD.exe"의 경우에는 "Run as administrator"를 이용하여 명령어 창을 띄운 후, 그 안에서 실행해 주시면 됩니다. (참고로 여기서는 WinDBG.exe를 기준으로 설명하겠지만, 사실 NTSD.exe와 거의 차이가 없습니다.)<br /> <br /> 위와 같이 실행했으면 화면에는 다음과 같은 텍스트가 보이게 됩니다.<br /> <br /> <pre class='code'> 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 <b>ntdll!RtlUserThreadStart</b>: 77ca0f18 89442404 mov dword ptr [esp+4],eax ss:0023:001efdbc=00000000 </pre> <br /> 로드되자마자 BP가 걸려서 응용 프로그램 실행이 멈춰 있습니다. VS.NET 2005와 비교해 본다면 "F11" 키를 눌러서 디버그 실행을 한 것과 같다고 볼 수 있겠습니다.<br /> <br /> <span class='subLastTitle'>2. Symbol 폴더 설정</span> <br /> 환경변수에 "_NT_SYMBOL_PATH" 값을 설정하신 분은 건너뛰셔도 됩니다. 만약 하지 않은 경우라면 별도의 ".symfix", ".sympath" 등의 명령어를 사용해서 임의로 구성이 가능합니다.<br /> <br /> <span class='subLastTitle'>3. sos.dll 로드 확인</span> <br /> ".chain" 명령어를 사용해서 sos 확장 DLL이 로드되었는 지 확인합니다. 아마 "WinDbg 6.6.0007.5" 버전을 사용하고 계신 분이라면, 기본적으로 sos.dll이 로드되었을 것이므로 별도로 명령을 주지 않아도 됩니다. 아래는 실제 실행 예입니다.<br /> <br /> <pre class='code'> 0:000> <b>.chain</b> Extension DLL search Path: C:\Program Files\De....[중간 생략]...les\Debugging Tools for Windows\ Extension DLL chain: <b>C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos</b>: image 2.0.50727.312, API 1.0.0, built Thu Oct 19 12:37:43 2006 <b>[path: C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll]</b> 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] </pre> <br /> 참고로, windbg.exe와는 달리 ntsd.exe에서는 기본적으로 sos.dll이 로드되어 있지 않았습니다.<br /> <br /> <span class='subLastTitle'>4. sos 확장 API 사용을 위한 프로그램 부분 실행</span> <br /> sos.dll에서 제공되는 확장 명령어를 사용하려면 mscorwks.dll이 로드되어야 합니다. 실제로, mscorwks.dll이 로드되기 전에 sos 명령어를 사용하게 되면 다음과 같은 오류를 보게 됩니다.<br /> <br /> <pre class='code'> 0:000> <b>!clrstack</b> <b>Failed to find runtime DLL (mscorwks.dll)</b>, 0x80004005 Extension commands need mscorwks.dll in order to have something to do. </pre> <br /> mscorwks.dll을 로드하는 시점까지 프로그램을 실행시키는 방법은 "sxe ld mscorwks" 명령어를 사용하는 방법이 있습니다.<br /> 출력 결과는 아래와 같습니다.<br /> <br /> <pre class='code'> 0:000> <b>sxe ld mscorwks</b> 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 <b>C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll</b> 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 </pre> <br /> 하지만, 아직 CLR에 "ConsoleApplication1"의 IL들이 로드조차 되지 않아서 그런지 sos.dll에서 제공되는 "!bpmd" 메서드를 사용해도 정상적으로 동작되지 않았습니다. 그래서 저 같은 경우에는 일단 "unmanaged" 방식으로 돌아와서 디버깅 방법을 찾기 시작했습니다. 우선, 테스트 소스에서 Main 함수에 예외를 발생하도록 일부러 다음과 같이 수정했습니다.<br /> <br /> <pre class="code"> static void Main() { throw new ApplicationException(); Program s = new Program(); s.MyMethod("Hello"); } </pre> <br /> 빌드하고, windbg.exe로 불러들여서 예외발생까지 코드를 실행시킨 후 "k" 명령어를 통해서 다음과 같이 호출 스택을 얻었습니다.<br /> <br /> <pre class='code'> <b>0:000> g</b> 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 <b>KERNEL32!RaiseException+0x58:</b> 7673b09e c9 leave <b>0:000> k</b> 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 <b>0026f1fc 79ef3c19 mscorwks!ClassLoader::RunMain+0x220</b> 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 </pre> <br /> 대강 봐도, "mscorwks!ClassLoader::RunMain"이 Main 메서드를 실행해 주는 함수라는 것을 쉽게 짐작할 수 있습니다.<br /> <br /> 이제, mscorwks.dll 로드를 굳이 명시적으로 확인할 필요없이 다음과 같이 직접 unmanaged API 함수에 BP를 걸어 놓는 것으로 문제를 해결할 수 있습니다.<br /> <br /> <pre class='code'> 0:000> <b>bp mscorwks!ClassLoader::RunMain</b> Bp expression 'mscorwks!ClassLoader::RunMain' could not be resolved, adding deferred bp </pre> <br /> 현재는 mscorwks모듈이 로드되어 있지 않기 때문에 RunMain기호를 찾을 수 없지만, 이후에 혹시나 발견이 되면 설정해 주겠다고 친절하게(?) 메시지를 보여주고 있습니다. 이제 거의 다 되었습니다. "g" 명령어로 실행시키면 다음과 같이 RunMain 함수에서 실행이 멈추는 것을 확인할 수 있습니다.<br /> <br /> <pre class='code'> 0:000> <b>g</b> 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): <b>Breakpoint 0 hit</b> 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 <b>mscorwks!ClassLoader::RunMain:</b> 79ef3c78 55 push ebp </pre> <br /> <span class='subLastTitle'>5. Main 함수에 BP 설정</span> <br /> 자, 다 되었습니다. 이제 sos.dll에서 제공되는 확장 명령어를 이용해서 Managed 함수에 쉽게 BP를 설정할 수 있습니다. 여기서는 Main 함수가 목표였기 때문에 다음과 같이 설정하시면 됩니다.<br /> <br /> <pre class='code'> 0:000> <b>!bpmd ConsoleApplication1 ConsoleApplication1.Program.Main</b> Found 1 methods... MethodDesc = 007c3000 Adding pending breakpoints... 0:000> <b>g</b> 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) <b>JITTED ConsoleApplication1!ConsoleApplication1.Program.Main()</b> Setting breakpoint: <b>bp 00D30070 [ConsoleApplication1.Program.Main()]</b> 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 </pre> <br /> 어떠세요? 재미있지요? ^^<br /> <br /> [첨부된 파일은 실제로 위의 실습을 한 WinDBG.exe 실행 화면의 텍스트를 캡쳐한 것입니다.]<br /> <br /><br /><hr /><span style='color: Maroon'>[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </span>
첨부파일
스팸 방지용 인증 번호
8893
(왼쪽의 숫자를 입력해야 합니다.)