성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<br /> 7. Microsoft의 PDB 파일 관리<br /> <br /> 지금까지의 글들을 읽어오시면서, PDB 파일의 유무로 인해 얼마나 디버깅이 쉽게 되어질 수 있음을 아시게 되었을 것입니다. (.NET 2.0의 sos가 좀 실망스럽긴 하지만.) 그렇다면, 이제 여러분들도 PDB 파일을 관리해야 할 필요가 생기셨을 텐데요.<br /> 과연 어떻게 관리해야 잘되었다고 할 수 있을까요? <br /> <br /> 사실, 이 문제는 무엇보다도 Microsoft에서 더욱 고민을 했을 것입니다. 여러 운영체제와 그에 따른 방대한 모듈들이, 수많은 클라이언트들의 서로 다른 하드웨어에서 운영되다 보니 Debugging에 관해서는 MS만큼 뼈저리게 느낀 업체도 없을 것이기 때문입니다.<br /> <br /> MS는 이에 대해서 "Symbol Server"를 도입함으로써 훌륭하게 해결해 주고 있는데요. 그럼 어떤 것인지 직접 실습을 해보면서 설명을 해보겠습니다.<br /> <br /> 우선, PDB 파일 관리는 VS.NET 2005에서 하는 방법과 "_NT_SYMBOL_PATH" 환경 변수를 통해서 하는 방법이 있습니다. 개인적으로 WinDBG에서도 함께 사용할 수 있는 "_NT_SYMBOL_PATH" 환경 변수를 더 권장합니다. 환경 변수로 하면 얻어지는 또 다른 이점으로는 Symbol 파일들에 대한 캐시 폴더를 별도로 설정할 수 있다는 장점도 있습니다.<br /> <br /> VS.NET 2005의 경우 다음과 같이 "Tools" / "Options" 메뉴를 통해 지정할 수 있습니다. (보시는 것처럼, 모든 심벌 서버에 대해서 단일한 클라이언트 캐시 폴더로만 받을 수 있습니다.) <br /> <br /> <img alt="VS.NET 2005에서 Symbol Server 설정" src="/SysWebRes/bbs/pdb_7_1_debug_step_into.png" /><br /> <br /> 아래의 방법은 "_NT_SYMBOL_PATH" 환경 변수를 설정하는 방법을 보여주고 있습니다.<br /> <br /> <img alt="_NT_SYMBOL_PATH 설정" src="/SysWebRes/bbs/pdb_7_2_debug_step_into.png" /><br /> <br /> 저 같은 경우, 환경 변수 값을 아래와 같이 설정하고 있습니다. (일단은 Microsoft 관련 Symbol server 설정만 주목하십시오.)<br /> <br /> <pre class="code"> <b>SRV*\\localhost\d$\Symbol\OSSymbols*http://msdl.microsoft.com/download/symbols</b>;D:\Symbol\ProductSymbols;C:\Windows\system32;D:\Symbol\TestSymbols; </pre> <br /> 찬찬히 하나씩 뜯어 볼까요? ^^<br /> <br /> <pre class="code"> 1. SRV*\\localhost\d$\Symbol\OSSymbols*http://msdl.microsoft.com/download/symbols; 2. D:\Symbol\ProductSymbols; 3. C:\Windows\system32; 4. D:\Symbol\TestSymbols; </pre> <br /> 1번 항목을 통해서, Microsoft의 Symbol 파일에 대해서 로컬 PC의 "D:\Symbol\OSSymbols" 폴더로 다운로드하도록 합니다. 이 폴더는 상당히 커질 수 있는데요. native dll뿐만 아니라, .NET Framework BCL의 모든 PDB 파일들이 저장되게 됩니다.<br /> 물론, 경로에 보시는 것처럼 SRV* 다음에는 UNC 경로를 통해 네트워크 드라이브를 지정할 수 있으며 팀 간에 서로 공유할 수 있습니다. 공유라는 것이 좋긴 하지만, 개인적으로는 UNC 경로라는 것으로 인해 항상 그 컴퓨터를 접근할 수 있어야 하는데 그렇지 못하기 때문에 그냥 로컬 PC에 저장하는 것을 더 선호합니다. (만약 제가 주로 작업하는 컴퓨터가 노트북이 아닌 붙박이 데스크톱이었다면 공유 폴더를 지정했을 것입니다.)<br /> <br /> 2번 항목은, 제가 작업하는 회사 제품들에 대한 PDB 파일들을 보관하는 폴더입니다. <br /> <br /> 3번 항목은, system32 폴더에 기본적으로 설치되는 PDB 파일들을 이용할 수 있게 하도록 지정한 것입니다.<br /> <br /> 4번 항목은, 테스트 용도의 프로젝트에 대한 PDB 파일을 임시로 저장해 놓을 목적으로 만든 것입니다.<br /> <br /> 또 다른 팁을 하나 알려드리자면, 저 같은 경우 다음과 같이 환경 변수를 2개를 만들어서 경우에 따라 번갈아 가면서 설정을 합니다.<br /> <br /> <pre class="code"> 1번 유형. _NT_SYMBOL2_PATH : D:\Symbol\OSSymbols;D:\Symbol\ProductSymbols;C:\Windows\system32;D:\Symbol\TestSymbols; 2번 유형. _NT_SYMBOL_PATH : SRV*\\localhost\d$\Symbol\OSSymbols*http://msdl.microsoft.com/download/symbols;D:\Symbol\ProductSymbols;C:\Windows\system32;D:\Symbol\TestSymbols; </pre> <br /> 네트워크가 끊겨진 상태 또는 PDB 파일이 많이 누락된 프로젝트를 디버깅할 때는 1번 유형으로 설정해 놓고요. 새로운 유형의 프로젝트로 인해 PDB 파일을 다운로드하는 처음 시도에서는 2번 유형으로 해놓습니다.<br /> 예를 들어, Firefox 브라우저에 올라가는 DLL을 디버깅할 때가 있었는데요. Firefox 자체의 PDB 파일들이 없음으로 인해서 로드되는 Firefox 모듈들마다 "<a target='_blank' href='http://msdl.microsoft.com/download/symbols'>http://msdl.microsoft.com/download/symbols</a>"에서 다운로드하는 시도로 인해 디버깅 시작이 느려지는 문제가 발생을 했었습니다. 따라서 이런 경우에는 1번 유형으로 설정을 해놓으시면 빠른 디버깅 시작을 할 수 있게 되지요. ^^<br /> <br /> 자, 이제 값 설정하는 방법을 알았으니 여러분들만의 경로를 적절하게 설정하십시오. 반드시 설정해야만 이후의 실습을 테스트해 보실 수 있습니다. 두 번 설정하지 않기 위해 가능한 VS.NET 2005 설정보다는 "_NT_SYMBOL_PATH"에다 지정하기를 권장합니다.<br /> <br /> <hr /> <br /> 그럼, 실습 삼아서 지금까지 다뤄왔던 BaseLibrary를 참조하고 있던 WinFormApp를 로드해 봅니다. "F5" 를 눌러서 디버깅을 시작하면 평소보다 느린 디버깅 화면 전환을 경험하실 수가 있습니다. 기다리기 지루하신 분은 위에서 설정했던 "\\localhost\d$\Symbol\OSSymbols" 폴더를 살펴보시기 바랍니다. WinFormApp 프로젝트를 시작하기 위해서 필요한 DLL들이 Microsoft가 제공하는 Symbol Server로부터 다운로드와 함께 캐시되고 있는 것을 확인할 수 있습니다.<br /> 물론, VS.NET 2005 디버거에서는 이 PDB 파일들을 디버깅 시에 이용하게 되는데요. Symbol 파일들이 정상적으로 올라오고 있는지 확인하기 위해서 다음과 같이 "Modules" 창을 띄워 봅니다.<br /> <br /> <img alt="PDB 파일들이 로드된 상태" src="/SysWebRes/bbs/pdb_7_3_debug_step_into.png" /><br /> <br /> 이 화면을 보면,,, 사실 박수를 치고픈 마음이 안 생기시나요? ^^ 저 같은 경우에는 그랬습니다. 레드몬드에 있는 이들에게 마음껏 박수를 쳐주고 싶었습니다. 그들의 노고에 감사할 따름입니다. <br /> <br /> "Symbol Status"에 보면, "Symbols loaded (source information stripped)." 상태가 있는가 하면, 단순히 "Symbols loaded." 상태도 있는데요. 그 둘 간의 차이는 VC++을 하신 분들이라면 아실 수 있을 것입니다. VS.NET 2003/2005에서는 VC++ 프로젝트에 대해 소스 파일 및 라인 정보 등의 사적인(private) 정보를 제외시킨 PDB 파일을 지원하는 데 바로 거기에서 오는 차이입니다.<br /> 나중에 설명할 기회가 있겠지만, Microsoft는 "Code Premium"에 가입한 사람들을 대상으로 소스 정보 및 라인 정보까지 포함한 "Full PDB" 파일까지도 사용할 수 있도록 하는 방법을 제공합니다. (물론, 이걸 이용해서 CCP 사이트에 있는 Microsoft 모듈의 소스 파일까지 F11 디버깅이 가능하게 됩니다.)<br /> <br /> <hr /> <br /> VS.NET 2005뿐만 아니라, WinDBG에서도 심벌을 로드하는 화면을 확인해 보겠습니다. WinDBG를 실행시키고, "File" / "Open Executable..."을 선택해서 WinForm.exe를 로드합니다. 다음은 처음 로드된 모습을 보여주고 있습니다.<br /> <br /> <img alt="WinDBG" src="/SysWebRes/bbs/pdb_7_4_debug_step_into.png" /><br /> <br /> 이 상태에서 하단의 command 창에, "LM" 명령을 입력하면 다음과 같은 결과를 볼 수 있습니다.<br /> <br /> <pre class="code"> <b>0:000> lm</b> start end module name 00400000 00408000 WinForm (deferred) 77e40000 77f42000 KERNEL32 (deferred) 79000000 79045000 mscoree (deferred) 7c800000 7c8c0000 ntdll (pdb symbols) D:\Symbol\OSSymbols\ntdll.pdb\DCE823FCF71A4BF5AA489994520EA18F2\ntdll.pdb </pre> <br /> WinForm.exe가 Loader에 의해서 로드되자마자 정지된 상태이므로 관련 DLL들이 몇개 되지 않는 것을 볼 수 있습니다. 그중에서 이미 ntdll.pdb는 기본적으로 올라와 있는 데요. 이처럼, WinDBG는 기본적으로 PDB 심벌 파일들을 로드하지는 않습니다. 개발자가 임의로 로드될 심벌들을 지정할 수 있는데요. 저 같은 경우에는 다음과 같이 전체 PDB를 로드하는 명령어를 즐겨 사용합니다.<br /> <br /> <pre class="code"> <b>0:000> .reload -f</b> Reloading current modules .*** WARNING: Unable to verify checksum for WinForm.exe ... </pre> <br /> 경고 하나가 있긴 한데요. 역시 VC++ 하신 분들은 아실 텐데, 별로 중요한 것이 아니니 설명은 생략하겠습니다. 아무 이상이 없다고만 알고 넘어가겠습니다. 실제로 원하는 PDB 파일들이 올라왔는지 확인하기 위해서 다음과 같이 다시 "lm" 명령을 통해서 확인해 봅니다.<br /> <br /> <pre class="code"> <b>0:000> lm</b> start end module name 00400000 00408000 WinForm C (private pdb symbols) D:\temp2\PdbSample\WinFormApp\WinForm\bin\Debug\WinForm.pdb 77e40000 77f42000 KERNEL32 (pdb symbols) D:\Symbol\OSSymbols\kernel32.pdb\75CFE96517E5450DA600C870E95399FF2\kernel32.pdb 79000000 79045000 mscoree (pdb symbols) D:\Symbol\OSSymbols\mscoree.pdb\0D7C30DDE4864A76BCE4B0CB18E63C4E2\mscoree.pdb 7c800000 7c8c0000 ntdll (pdb symbols) D:\Symbol\OSSymbols\ntdll.pdb\DCE823FCF71A4BF5AA489994520EA18F2\ntdll.pdb </pre> <br /> 확인이 잘 되었습니다. ^^ 잠깐 설명드리지만, 위에서 "private pdb symbols"는 사적인 정보까지 포함한 Full PDB 유형이라고 볼 수 있겠고, "pdb symbols"는 그런 정보들이 누락된 PDB 유형일 것입니다.<br /> 그리고, "WinForm C ( private pdb symbols )"에서 중간에 "C"는 Checksum을 찾을 수 없다는 의미합니다. VC++에서는 Checksum을 dll/exe에 기록하는 컴파일 옵션이 제공이 되는데요. .NET 같은 경우에는 런타임 시에 컴파일되는 유형이죠. 아마도 그래서 이 Checksum을 기록하는 컴파일 옵션이 .NET 프로젝트에는 없는 것 같습니다.<br /> <br /> <hr /> <br /> 사실, PDB 파일 로드 유무는 Call Stack과 Disassembly 창에서 가장 큰 차이를 느낄 수 있습니다. 비록 Managed 환경에서는 중간 언어의 장점으로 인해 그런 부분들이 크게 두각을 나타내지는 못하지만, Win32 VC++ 같은 개발 환경에서는 그 차이가 확실하게 나타나게 됩니다.<br /> <br /> 일례로 간단하게 VC++ 8.0에서 MFC Dialog-Based 프로젝트를 기본 생성한 다음, ::OnInitDialog 메서드에서 다음과 같이 "MessageBox"를 띄우는 것을 예로 들어보겠습니다.<br /> <br /> <pre class='code'> 01: BOOL CTestDlg::OnInitDialog() 02: { ............. [중간 생략] ... 08: SetIcon(m_hIcon, FALSE); // Set small icon 09: 10: ::MessageBox( NULL, L"Hello World!", L"Title", MB_OK ); 11: 12: return TRUE; // return TRUE unless you set the focus to a control 13: } </pre> <br /> 우선, Symbol Server가 지정이 되지 않은 상태로 "라인 10"에 BP를 설정해서 "F5" 키로 디버깅을 시작합니다.<br /> 10번째 줄에서 디버깅이 멈췄으면 마우스 오른쪽 버튼을 이용해서 "Go To Disassembly" 창으로 이동합니다. 그럼, 다음과 같은 정보가 Disassembly 창에 보이게 됩니다.<br /> <br /> <pre class='code'> 96: 97: ::MessageBox( NULL, L"Hello World!", L"Title", MB_OK ); 004128C2 8B F4 mov esi,esp 004128C4 6A 00 push 0 004128C6 68 54 B9 41 00 push offset string L"Title" (41B954h) 004128CB 68 60 B9 41 00 push offset string L"Hello World!" (41B960h) 004128D0 6A 00 push 0 004128D2 FF 15 D8 0A 42 00 call dword ptr [__imp__MessageBoxW@16 (420AD8h)] </pre> <br /> "F11" 키로 "call dword ptr [__imp__MessageBoxW@16 (420AD8h)]" 지점까지 계속 추적해 들어갑니다. "call"이 실행된 이후에는 다음과 같은 정보가 나옵니다.<br /> <br /> <pre class='code'> 773B197B 8B FF mov edi,edi 773B197D 55 push ebp 773B197E 8B EC mov ebp,esp 773B1980 83 3D 18 21 3E 77 00 cmp dword ptr ds:[773E2118h],0 773B1987 0F 85 52 45 02 00 jne 773D5EDF 773B198D 6A 00 push 0 773B198F FF 75 14 push dword ptr [ebp+14h] 773B1992 FF 75 10 push dword ptr [ebp+10h] 773B1995 FF 75 0C push dword ptr [ebp+0Ch] 773B1998 FF 75 08 push dword ptr [ebp+8] 773B199B E8 09 00 00 00 call 773B19A9 773B19A0 5D pop ebp 773B19A1 C2 10 00 ret 10h </pre> <br /> 이제, "Call stack" 창을 띄워서 보게 되면 다음과 같이 되어 있는 것을 확인할 수 있습니다.<br /> <br /> <img alt="MS 관련 PDB 로딩이 되어 있지 않은 상태의 Call stack 창" src="/SysWebRes/bbs/pdb_7_5_debug_step_into.png" /><br /> <br /> 그동안 특별히 심벌 서버 설정을 해보지 않은 분들이라면 평소에 보아왔던 지극히 당연한 화면입니다. ^^ 이제 이것이 PDB 파일 로드로 인해 어떻게 바뀌어지는 한번 확인해 볼까요! ^^<br /> 이제, 위에서 설명했던 내용으로 "_NT_SYMBOL_PATH" 환경 설정 변수에 다음과 같은 내용을 기록합니다.<br /> <br /> <pre class="code"> <b>SRV*\\localhost\d$\Symbol\OSSymbols*http://msdl.microsoft.com/download/symbols</b> </pre> <br /> 그다음, 이미 실행되어 있던 VS.NET 2005 IDE를 종료하고 다시 실행한 후, 위에서 했던 MFC 프로젝트를 다시 로딩합니다. 절차는 MessageBox 안에 들어간 곳까지 다시 되풀이 합니다.<br /> MessageBox 호출 전까지의 내용은 동일한데요. "F11" 키로 "call dword ptr [__imp__MessageBoxW@16 (420AD8h)]" 안으로 추적해 들어가는 순간부터 상황이 바뀌게 됩니다. 즉, PDB 파일이 있는 지금은 다음과 같은 disassembly 내용이 화면에 보여지게 됩니다.<br /> <br /> <pre class='code'> _MessageBoxW@16: 773B197B 8B FF mov edi,edi 773B197D 55 push ebp 773B197E 8B EC mov ebp,esp 773B1980 83 3D 18 21 3E 77 00 cmp dword ptr [_gfEMIEnable (773E2118h)],0 773B1987 0F 85 52 45 02 00 jne _MessageBoxW@16+0Eh (773D5EDFh) 773B198D 6A 00 push 0 773B198F FF 75 14 push dword ptr [ebp+14h] 773B1992 FF 75 10 push dword ptr [ebp+10h] 773B1995 FF 75 0C push dword ptr [ebp+0Ch] 773B1998 FF 75 08 push dword ptr [ebp+8] 773B199B E8 09 00 00 00 call _MessageBoxExW@20 (773B19A9h) 773B19A0 5D pop ebp 773B19A1 C2 10 00 ret 10h </pre> <br /> 훨씬 더 자세한 정보를 볼 수가 있지요. Call stack 창도 알기 쉽게 다음과 같은 정보를 보여주고 있습니다.<br /> <br /> <img alt='MS 관련 PDB 로딩이 되어 있는 상태의 Call stack 창' src='/SysWebRes/bbs/pdb_7_6_debug_step_into.png' /><br /> <br /> 바꿔 말하면, 여러분들이 스스로 개발한 모듈의 PDB 파일을 유지하고 있다면 디버깅 시에 위와 같은 정보를 얻을 수 있다는 것을 의미합니다.<br /> <br /> 여기서 마치고, 다음에는 자사 제품의 PDB 파일을 보관하는 Symbol Server를 구성하는 방법을 알아보겠습니다.<br /> <br /> <br /><br /><hr /><span style='color: Maroon'>[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</a>
첨부파일
스팸 방지용 인증 번호
1586
(왼쪽의 숫자를 입력해야 합니다.)