성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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'>linux - lldb를 이용한 .NET Core 응용 프로그램의 메모리 덤프 분석 방법</h1> <p> 리눅스에서 닷넷 프로세스의 메모리 덤프를 떴으면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET Core 응용 프로그램을 위한 메모리 덤프 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/12078'>http://www.sysnet.pe.kr/2/0/12078</a> </pre> <br /> 이제 분석을 해야 하는데요. 리눅스 용 덤프 파일은, 리눅스 운영체제 상에서 lldb를 이용해 분석할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Debugging .NET Core on Linux with LLDB ; <a target='tab' href='https://www.raydbg.com/2018/Debugging-Net-Core-on-Linux-with-LLDB/'>https://www.raydbg.com/2018/Debugging-Net-Core-on-Linux-with-LLDB/</a> </pre> <br /> lldb + sos 확장 명령어 관련한 기본적인 사용법은 이미 아래의 글을 통해 설명해 두었고.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Linux - lldb 환경에서 sos 확장 명령어를 이용한 닷넷 프로세스 디버깅 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/12075'>http://www.sysnet.pe.kr/2/0/12075</a> Linux - lldb 환경에서 sos 확장 명령어를 이용한 닷넷 프로세스 디버깅 - 배포 방법에 따른 차이 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/12076'>http://www.sysnet.pe.kr/2/0/12076</a> </pre> <br /> <hr style='width: 50%' /><br /> <a name='setclrpath'></a> <br /> 유형을 나눠서, 우선 덤프 파일을 뜬 그 PC에서 분석을 해보겠습니다. lldb 실행 후,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [Centos 8] $ <span style='color: blue; font-weight: bold'>/usr/local/bin/lldb-3.9.1 </span> [Ubuntu 18.04] $ <span style='color: blue; font-weight: bold'>lldb-3.9</span> </pre> <br /> libsosplugin.so 플러그인을 로드하는 것과 setclrpath를 지정하는 것은 지난 글(<a target='tab' href='https://www.sysnet.pe.kr/2/0/12075'>1</a>, <a target='tab' href='https://www.sysnet.pe.kr/2/0/12076'>2</a>)에서 설명한 규칙과 동일합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > (lldb) <span style='color: blue; font-weight: bold'>plugin load /usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.7/libsosplugin.so</span> (lldb) <span style='color: blue; font-weight: bold'>setclrpath /home/tusr/corecorelin</span> Set load path for sos/dac/dbi to '/home/tusr/corecorelin/' </pre> <br /> 단지, 실행 중인 프로세스를 attach하는 것이 아닌, 덤프 파일을 여는 것이기 때문에 "target create"로 명령어만 바꿔 실행하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > (lldb) <span style='color: blue; font-weight: bold'>target create -c dotnet_time_2019-12-09_16:44:23.55130</span> </pre> <br /> <hr style='width: 50%' /><br /> <br /> 하지만 현실적으로 봤을 때, 일반적으로 덤프 파일은 운영 서버에서 뜨고 개발자 PC로 복사한 후 분석하는 식이기 때문에 .NET Framework 때와 마찬가지로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > windbg의 mscordacwks DLL 로드 문제 - 세 번째 이야기 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11231'>http://www.sysnet.pe.kr/2/0/11231</a> </pre> <br /> 덤프 파일을 뜬 그 환경에 있는 libmscordaccore.so, libsos.so 파일을 함께 복사해야 합니다. 또한, 리눅스의 경우 libsos.so와 lldb의 교각 역할을 하는 libsosplugin.so도 필요하기 때문에 이것도 복사해야 하는데 (<a target='tab' href='https://www.sysnet.pe.kr/2/0/12076#libsosplugin'>지난 글에 설명한 이유로 인해</a>) 안전하게 .NET SDK가 설치된 폴더의 것들도 함께 복사하는 것이 좋습니다. (혹은 해당 버전의 .NET SDK를 lldb 분석을 수행할 PC에 설치합니다.)<br /> <br /> 따라서, 기본적인 덤프 분석을 위해서는 다음의 파일들이 있어야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 덤프 파일 (예: dotnet_time_2019-12-09_16:44:23.55130) libmscordaccore.so libsosplugin.so libsos.so </pre> <br /> 다른 컴퓨터에서 분석한다는 점으로 인해 또 하나 고려해야 할 사항이 있는데요, 같은 PC에서 분석하는 거라면 덤프 파일 로드를 lldb의 "target create"로 하게 되지만, 이것을 다른 PC에서 수행하는 경우라면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > $ <span style='color: blue; font-weight: bold'>/usr/local/bin/lldb-3.9.1</span> (lldb) <span style='color: blue; font-weight: bold'>target create -c test.dmp</span> Core file '/home/tusr/temp/test.dmp' (x86_64) was loaded. (lldb) <span style='color: blue; font-weight: bold'>plugin load libsosplugin.so</span> (lldb) <span style='color: blue; font-weight: bold'>setclrpath .</span> Set load path for sos/dac/dbi to './' </pre> <br /> sos 확장 명령어 수행 시 다음과 같은 식의 오류가 발생할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > (lldb) <span style='color: blue; font-weight: bold'>clrstack</span> Failed to load data access DLL, 0x80004005 Can not load or initialize libmscordaccore.so. The target runtime may not be initialized. ClrStack failed </pre> <br /> 말 그대로 현재 로드된 덤프 파일로부터는 .NET 런타임을 알 수 없다는 건데 실제로 이미지 목록을 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > (lldb) <span style='color: blue; font-weight: bold'>image list</span> [ 0] 50089E85-080B-EEA9-7A2C-01E8CBBB7723-31C8B461 /usr/share/dotnet/dotnet </pre> <br /> 정상적으로 모듈 정보를 구하지 못하고 있습니다. 비교를 위해 덤프를 뜬 PC에서 "image list" 명령을 수행하면 해당 프로세스 공간에 로드된 모듈 정보를 이렇게 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > (lldb) <span style='color: blue; font-weight: bold'>image list</span> [ 0] 46BD32F2-D61A-2A48-2BDB-4A2B0787F8DF-39ECF703 /usr/share/dotnet/dotnet [ 1] D1AA3951-3462-B997-B990-85F995A37B71-714B8370 0x00007ffca1366000 [vdso] (0x00007ffca1366000) [ 2] D1AA3951-3462-B997-B990-85F995A37B71-714B8370 0x00007ffca1366000 linux-vdso.so.1 (0x00007ffca1366000) ...[생략]... [ 73] 4B80C543-356E-E0AF-9039-EFE7C9EA1CC1-F74C426A /usr/lib/x86_64-linux-gnu/libhx509.so.5 [ 74] A609DB07-7BDC-3B54-A8C6-4BBCF82C2B7B-D1CC8B98 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0 [ 75] 810686AF-0D5F-D350-A4FB-1CC4B5AFF44A-05C102CB /lib/x86_64-linux-gnu/libcrypt.so.1 /usr/lib/debug/lib/x86_64-linux-gnu/libcrypt-2.27.so </pre> <br /> 이 문제를 해결하려면, 덤프를 뜬 PC에서 해당 프로세스의 실행 파일도 함께 복사해 와야 합니다. 그리고 그 프로세스 이미지 파일을 "target create" 명령어에 전달해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > $ <span style='color: blue; font-weight: bold'>/usr/local/bin/lldb-3.9.1</span> (lldb) <span style='color: blue; font-weight: bold'>target create "dotnet" --core "test.dmp"</span> Core file '/home/tusr/temp/test.dmp' (x86_64) was loaded. </pre> <br /> 정상적으로 lldb가 덤프 파일을 로드했다면 "image list" 명령어도 잘 동작하고, 이후의 libsosplugin.so, setclrpath 명령어와 함께 일련의 sos 확장 명령어가 정상적으로 실행이 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > (lldb) <span style='color: blue; font-weight: bold'>plugin load libsosplugin.so</span> (lldb) <span style='color: blue; font-weight: bold'>setclrpath .</span> Set load path for sos/dac/dbi to './' (lldb) <span style='color: blue; font-weight: bold'>clrstack</span> OS Thread Id: 0x2691 (1) Child SP IP Call Site 00007FFCA12E0F20 00007faf7ee479f3 [GCFrame: 00007ffca12e0f20] 00007FFCA12E1010 00007faf7ee479f3 [HelperMethodFrame_1OBJ: 00007ffca12e1010] System.Threading.Monitor.ObjWait(Boolean, Int32, System.Object) 00007FFCA12E1140 00007FAF0628646D Error: Fail to initialize CoreCLR 80004005 System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken) 00007FFCA12E11D0 00007FAF06285E4C System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken) 00007FFCA12E1230 00007FAF06285BEC System.Threading.Tasks.Task.InternalWait(Int32, System.Threading.CancellationToken) 00007FFCA12E12F0 00007FAF062859D9 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task) 00007FFCA12E1320 00007FAF0625721D Microsoft.AspNetCore.Hosting.WebHostExtensions.Run(Microsoft.AspNetCore.Hosting.IWebHost) 00007FFCA12E1340 00007FAF043504A0 CoreCoreLin.Program.Main(System.String[]) 00007FFCA12E1630 00007faf7d47a307 [GCFrame: 00007ffca12e1630] 00007FFCA12E1A40 00007faf7d47a307 [GCFrame: 00007ffca12e1a40] </pre> <br /> <hr style='width: 50%' /><br /> <a name='need_files'></a> <br /> 정리해 보면, 다른 PC에서 덤프 뜬 것을 분석하고 싶다면 안전하게 다음과 같은 파일들을 함께 복사해 와야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 1) dump file 2) libsosplugin.so - dotnet ...: /usr/share/dotnet/shared/Microsoft.NETCore.App/...[version].../libsosplugin.so - standalone: /...배포 폴더.../libsosplugin.so 3) libmscordaccore.so - /...배포 폴더.../libmscordaccore.so 4) libsos.so - /...배포 폴더.../libsos.so 5) executable binary - dotnet으로 실행한 경우: /usr/share/dotnet/dotnet - standalone인 경우: /...[배포 폴더].../...[실행 파일]... 6) libcoreclr.so - /...배포 폴더.../libcoreclr.so </pre> <br /> ^^; 좀 복잡한데요. 가능하면 이런 파일들을 그냥 압축해 주는 shell 스크립트나 프로그램을 하나 만들어 두는 것이 속 편할 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 현재(2019-12-17) procdump로 뜬 덤프 파일에 대해 lldb의 "target create" 명령 시 아무런 동작을 하지 않습니다. 이에 관해서는 .NET Core 2.1 관련 이슈가 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > LLDB hangs in Linux when opening a dotnet core 2.1 dump #79 ; <a target='tab' href='https://github.com/dotnet/diagnostics/issues/79'>https://github.com/dotnet/diagnostics/issues/79</a> </pre> <br /> 이 때문에라도 procdump보다는 createdump를 사용하는 것이 좋습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > $ <span style='color: blue; font-weight: bold'>sudo $(find /usr/share/dotnet -name createdump) -u 19234</span> Writing full dump to file /tmp/coredump.27581 Written 22551396352 bytes (5505712 pages) to core file </pre> <br /> 위의 스크립트에서 좀 더 하드 코딩을 없애고 싶다면!<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > $ readlink -f $(which dotnet) /usr/share/dotnet/dotnet $ dirname $(readlink -f $(which dotnet)) /usr/share/dotnet $ find $(dirname $(readlink -f $(which dotnet))) -name createdump /usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.5/createdump $ <span style='color: blue; font-weight: bold'>$(find $(dirname $(readlink -f $(which dotnet))) -name createdump) -u 19234</span> </pre> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, 간혹 (저처럼 ^^) 덤프 파일 로드하는 것을 깜빡 잊고 sos 확장 명령어를 수행했다가,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > $ <span style='color: blue; font-weight: bold'>lldb-3.9</span> (lldb) <span style='color: blue; font-weight: bold'>plugin load /usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.7/libsosplugin.so</span> (lldb) <span style='color: blue; font-weight: bold'>setclrpath /home/tusr/corecorelin</span> Set load path for sos/dac/dbi to '/home/tusr/corecorelin/' (lldb) <span style='color: blue; font-weight: bold'>clrstack</span> Failed to load data access DLL, 0x80004005 Can not load or initialize libmscordaccore.so. The target runtime may not be initialized. ClrStack failed </pre> <br /> 0x80004005가 왜 발생하는 지 이해가 안 돼 헤맬 수도 있을 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <a name='windbg'></a> <br /> 마지막으로, (Windows 10 SDK에 포함된 최신 버전의) windbg는 리눅스에서 생성한 덤프 파일을 열면 다음과 같은 오류가 발생하지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Could not find the c:\temp\dotnet_time_2019-12-06_16_23_19.29310 Dump File, Win32 error 0n87 The parameter is incorrect. </pre> <br /> Windbg Preview 버전으로는 이렇게 열립니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Microsoft (R) Windows Debugger Version 10.0.19494.1001 AMD64 Copyright (c) Microsoft Corporation. All rights reserved. Loading Dump File [c:\temp\dotnet_time_2019-12-06_16_23_19.29310] 64-bit machine not using 64-bit API ************* Path validation summary ************** Response Time (ms) Location Deferred srv* OK c:\symbols2 Deferred SRV*c:\Symbols*https://msdl.microsoft.com/download/symbols Symbol search path is: srv*;c:\symbols2;SRV*c:\Symbols*https://msdl.microsoft.com/download/symbols Executable search path is: Generic Unix Version 0 UP Free x64 Machine Name: System Uptime: not available Process Uptime: not available ................................................................ ................................................................ 00007f85`18d679f3 ?? ??? </pre> <br /> 심지어 lm과 같은 명령어도 통하는 걸로 봐서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>lm</span> start end module name 00000000`00600000 00000000`00611000 dotnet (deferred) 00007f7f`e3fe4000 00007f7f`e3fee000 libcrypt_2_27 (deferred) 00007f7f`e421c000 00007f7f`e4324000 libsqlite3_so_0_8 (deferred) 00007f7f`e4525000 00007f7f`e456e000 libhx509_so_5_0 (deferred) ...[생략]... 00007f85`16f20000 00007f85`16f2d000 libunwind_so_8_0 (deferred) 00007f85`1713b000 00007f85`17143000 librt_2_27 (deferred) 00007f85`17143000 00007f85`1782b000 libcoreclr (deferred) 00007f85`17a5c000 00007f85`17b26000 libhostpolicy (deferred) 00007f85`17d26000 00007f85`17dcf000 libhostfxr (deferred) 00007f85`17fcf000 00007f85`181bc000 libc_2_27 (deferred) 00007f85`183c0000 00007f85`183d8000 libgcc_s_so (deferred) 00007f85`185d8000 00007f85`18776000 libm_2_27 (deferred) 00007f85`18976000 00007f85`18b53000 libstdc___so_6_0 (deferred) 00007f85`18d56000 00007f85`18d5a000 libdl_2_27 (deferred) 00007f85`18f5a000 00007f85`18f75000 libpthread_2_27 (deferred) 00007f85`18f79000 00007f85`18fa2000 ld_2_27 (deferred) </pre> <br /> 아마도 windbg 나름대로 리눅스 용 덤프 분석도 지원하는 방향으로 노력하고 있는 듯합니다. 위에서 특이한 점이 있다면, lldb의 경우 "target create"로 덤프 파일을 로드 시 반드시 대상 프로세스 이미지를 인자로 전달해야 했지만, windbg의 경우 lm 명령어는 그런 이미지가 없이도 로드된 모듈 목록을 잘 해석하고 있습니다.<br /> <br /> 하지만, 닷넷 개발자에게 있어 가장 중요한 <a target='tab' href='http://www.sysnet.pe.kr/2/0/11839'>sos.dll이나 mscordaccore.dll 등의 확장 모듈</a>들은 모두 유닉스 ELF 포맷으로 빌드된 것이기 때문에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > libmscordaccore.so libsos.so </pre> <br /> 당연히 로드가 되지 않습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>.load c:\temp\libsos.so</span> The call to LoadLibrary(f:\ts\libsos.so) failed, Win32 error 0n193 "%1 is not a valid Win32 application." Please check your debugger configuration and/or network access. 0:000> <span style='color: blue; font-weight: bold'>.cordll -lp c:\temp</span> CLR DLL status: No load attempts </pre> <br /> 참고로, 비주얼 스튜디오 역시 리눅스 용 덤프 파일은 "Debugging older format crashdumps is not supported"라는 식의 오류를 내며 지원하지 않습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1053
(왼쪽의 숫자를 입력해야 합니다.)