성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>PDB 파일을 연동해 소스 코드 라인 정보를 알아내는 방법</h1> <p> PDB 파일을 다뤘던 것이 벌써 8년 가까이 되어 가는군요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PDB 이야기 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/309'>http://www.sysnet.pe.kr/2/0/309</a> </pre> <br /> PDB의 중요성은 그걸로 설명되었으니, 이제 우리가 PDB 파일과 연동해 소스 코드 라인 정보를 알아내는 방법을 한번 살펴보겠습니다. 일단, 전체 소스 코드를 나열하면 다음과 같습니다. (그냥 전형적인 패턴이므로 이대로 쓰시면 됩니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > #include "stdafx.h" <span style='color: blue; font-weight: bold'>#include <DbgHelp.h></span> #pragma comment(lib, "dbghelp.lib") <span style='color: blue; font-weight: bold'>void test() { printf("test\n"); } </span> int _tmain(int argc, _TCHAR* argv[]) { test(); DWORD error; <span style='color: blue; font-weight: bold'>SymSetOptions(SYMOPT_LOAD_LINES);</span> HANDLE thisProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId()); // 또는 thisProcess = GetCurrentProcess(); if (!<span style='color: blue; font-weight: bold'>SymInitialize(thisProcess, NULL, TRUE)</span>) { error = GetLastError(); printf("SymInitialize ERROR : %d\n", error); return FALSE; } DWORD dwDisplacement = 0; <span style='color: blue; font-weight: bold'>DWORD64 dwAddress = (DWORD64)test;</span> BOOL result = FALSE; { IMAGEHLP_SYMBOL64 *pSym = NULL; char buffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_SYM_NAME*sizeof(TCHAR)] = { 0 }; pSym = (IMAGEHLP_SYMBOL64 *)buffer; pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); pSym->MaxNameLength = MAX_PATH; DWORD64 dwDisplacement64 = 0; result = <span style='color: blue; font-weight: bold'>SymGetSymFromAddr64</span>(thisProcess, dwAddress, &dwDisplacement64, pSym); if (result == TRUE) { printf("\n%s\n", pSym->Name); } } { IMAGEHLP_LINE64 line64 = { 0 }; line64.SizeOfStruct = sizeof(IMAGEHLP_LINE64); dwDisplacement = 0; if (<span style='color: blue; font-weight: bold'>SymGetLineFromAddr64</span>(thisProcess, dwAddress, &dwDisplacement, &line64)) { printf("%s (%d)", line64.FileName, line64.LineNumber); } else { DWORD error = GetLastError(); _tprintf(TEXT("SymGetLineFromAddr64 ERROR: %d\n"), error); } } <span style='color: blue; font-weight: bold'>SymCleanup(thisProcess);</span> return 0; } // 출력 결과 /* test @ILT+340(?test@@YAXXZ) SymGetLineFromAddr64 ERROR: 487 */ </pre> <br /> 보시는 바와 같이 SymGetSymFromAddr64는 정상적으로 동작한 반면, SymGetLineFromAddr64는 오류 반환값이 487 (Attempt to access invalid address.)로 오동작했습니다. 검색해 보면 다음의 글이 나오는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > How to get function's name from function's pointer in C? (Windows) ; <a target='tab' href='http://ivbel.blogspot.kr/2012/02/how-to-get-functions-name-from.html'>http://ivbel.blogspot.kr/2012/02/how-to-get-functions-name-from.html</a> </pre> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> 3. SymGetSymFromAddr64 succeed but SymGetLineFromAddr64 fails, last error is 487. That was the most tricky for me: documentation says nothing about it. It seems that you have to enable profile information (Project->Properties, Configuration Properties, Linker, Advanced, make sure that Profile is "Enable ..."). However I found it by playing with options. I didn't find any explanation in the documentation. <br /> </div><br /> <br /> 해결책이 약간은 실망스럽습니다. 즉, 다음과 같이 "Profile" 옵션을 켜서 빌드해야 하는 것입니다.<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='get_symbol_line_1.png' src='/SysWebRes/bbs/get_symbol_line_1.png' /><br /> <br /> 물론, 우리가 만든 DLL/EXE는 저 옵션을 켜면 되지만 범용 라이브러리를 개발하는 입장에서는 저 방법은 그다지 쓸모가 없습니다. 그런데... 이상하군요. 마이크로소프트는 저 옵션이 켜져 있지 않아도 소스 코드 라인을 가져오는데... 그 차이점이 궁금하군요. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 혹시 닷넷에서도 dbghelp.dll의 함수들이 사용될 수 있을까요? 다음과 같이 PInvoke로 구현해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; namespace ConsoleApplication2 { // <a target='tab' href='http://stackoverflow.com/questions/12656737/how-to-obtain-the-dll-list-of-a-specified-process-and-loop-through-it-to-check-i'>http://stackoverflow.com/questions/12656737/how-to-obtain-the-dll-list-of-a-specified-process-and-loop-through-it-to-check-i</a> class Program { [Flags] public enum SymOpt : uint { CASE_INSENSITIVE = 0x00000001, UNDNAME = 0x00000002, DEFERRED_LOADS = 0x00000004, NO_CPP = 0x00000008, LOAD_LINES = 0x00000010, OMAP_FIND_NEAREST = 0x00000020, LOAD_ANYTHING = 0x00000040, IGNORE_CVREC = 0x00000080, NO_UNQUALIFIED_LOADS = 0x00000100, FAIL_CRITICAL_ERRORS = 0x00000200, EXACT_SYMBOLS = 0x00000400, ALLOW_ABSOLUTE_SYMBOLS = 0x00000800, IGNORE_NT_SYMPATH = 0x00001000, INCLUDE_32BIT_MODULES = 0x00002000, PUBLICS_ONLY = 0x00004000, NO_PUBLICS = 0x00008000, AUTO_PUBLICS = 0x00010000, NO_IMAGE_SEARCH = 0x00020000, SECURE = 0x00040000, SYMOPT_DEBUG = 0x80000000 }; [StructLayout(LayoutKind.Sequential)] public struct _IMAGEHLP_LINE64 { public uint SizeOfStruct; public uint Key; public uint LineNumber; public IntPtr FileName; public ulong Address; }; [DllImport("dbghelp.dll", SetLastError = true)] public static extern bool SymInitialize(IntPtr hProcess, string UserSearchPath, bool fInvadeProcess); [DllImport("dbghelp.dll", SetLastError = true)] public static extern uint SymSetOptions(SymOpt SymOptions); [DllImport("dbghelp.dll", SetLastError = true)] public static extern bool SymGetLineFromAddr64(IntPtr hProcess, ulong dwAddr, ref uint pdwDisplacement, ref _IMAGEHLP_LINE64 Line); static IntPtr Function() { MethodInfo mi = typeof(Program).GetMethod("Function", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); return mi.MethodHandle.GetFunctionPointer(); } static void Main(string[] args) { IntPtr funcAddress = Function(); SymSetOptions(SymOpt.LOAD_LINES | SymOpt.DEFERRED_LOADS | SymOpt.SYMOPT_DEBUG); IntPtr processHandle = Process.GetCurrentProcess().Handle; int error; if (SymInitialize(processHandle, null, true) == false) { error = Marshal.GetLastWin32Error(); Console.WriteLine("SymInitialize returned error : " + error); return; } uint dwDisplacement = 0; _IMAGEHLP_LINE64 line = new _IMAGEHLP_LINE64(); ulong dwAddress = (ulong)funcAddress.ToInt64(); if (SymGetLineFromAddr64(processHandle, dwAddress, ref dwDisplacement, ref line) == false) { error = Marshal.GetLastWin32Error(); Console.WriteLine("SymGetLineFromAddr64 returned error : " + error); return; } Console.WriteLine(line.LineNumber); } } } </pre> <br /> SymGetLineFromAddr64의 오류가 126으로 "The specified module could not be found." 의미가 됩니다. 왜냐하면 JIT 컴파일된 코드들이므로 Heap에 위치해 있을 테고 그 부분은 Module과는 상관없는 영역이기 때문입니다.<br /> <br /> 사실, 닷넷에서 dbghelp 라이브러리를 이용해 구할 수 있다고 해도 별 의미가 없습니다. 왜냐하면 더 좋은 StackFrame 클래스가 있기 때문입니다. 그냥 간단하게 다음과 같이 구할 수 있으므로!<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > StackFrame st = new StackFrame(0, true); Console.WriteLine(st.GetFileName() + ":" + st.GetFileLineNumber()); </pre> <br /> 확실히 닷넷이 편합니다. ^^<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=888&boardid=331301885'>첨부 파일은 위의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1960
(왼쪽의 숫자를 입력해야 합니다.)