WinDbg + SOS로 크래시 덤프 읽기 ── 수집 이후 실무 분석 입문

· · WinDbg, SOS, 크래시 덤프, .NET, CSharp, 디버깅, PDB, 불량 조사, 기술 상담

이전 글 「Windows 크래시 덤프 수집 입문」에서는 WER LocalDumps・ProcDump・MiniDumpWriteDump를 이용해 덤프를 “채집”하는 부분까지를 정리했습니다. 다만 덤프는 채집한 것만으로는 아무것도 알려주지 않습니다. 실제로 손을 움직여서 “어느 스레드가” “왜” 떨어졌는지, 혹은 “무엇이” 메모리를 붙잡고 있는지를 읽어내야 비로소 조사의 재료가 됩니다.

이 글에서는 수집편의 후속편으로, 채집한 덤프를 WinDbg와 SOS 확장으로 실제로 읽는 절차에 집중해서 씁니다. 설치와 심볼 설정, .NET 앱에서 필수가 되는 SOS 확장의 로딩, !clrstack이나 !dumpheap -stat 같은 대표적인 명령으로 “무엇을 보고, 어떻게 판단할 것인가”, 네이티브 크래시의 !analyze -v, 그리고 WinDbg를 쓰지 않는 dotnet-dump analyze와의 구분까지를 다룹니다.

1. 먼저 결론

  • 덤프 분석의 주역은 WinDbg(현재 버전. 이전 명칭 WinDbg Preview)입니다. winget install Microsoft.WinDbg 또는 Microsoft Store에서 얻을 수 있으며, Windows 10 Anniversary Update(1607) 이후 / Windows 11의 x64・ARM64에서 동작합니다.1
  • 심볼(PDB)이 로드되지 않은 상태여도 !clrstack!dumpheap -stat!gcroot 등은 CLR의 메타데이터・힙 데이터로부터 그대로 동작합니다. 잃게 되는 것은 매니지드 소스 파일명・줄 번호와, 네이티브 프레임의 심볼 이름입니다. 다만 소스 줄까지 추적하고 싶다면 이야기가 다르므로, _NT_SYMBOL_PATH에 Microsoft의 공개 심볼 서버와 자사 PDB의 위치를 모두 넣는 것이 정석입니다.2
  • .NET(Framework / Core / 5+) 앱의 덤프에서는, SOS 확장을 불러와야 비로소 매니지드 정보가 보입니다. 네이티브의 k(스택 표시)만으로는 C# 코드를 추적할 수 없습니다.3
  • 대표적인 조사 유형은 3가지입니다. 예외로 떨어졌다면 !clrstack!pe, 메모리가 계속 늘어난다면 !dumpheap -stat!gcroot, 네이티브 크래시라면 !analyze -v부터 시작합니다.
  • WinDbg를 쓰지 않는 선택지로 dotnet-dump analyze가 있습니다. SOS 명령의 대부분을 그대로 쓸 수 있지만, 네이티브 스택 프레임은 다룰 수 없습니다. 손에 있는 것이 네이티브 DLL이나 COM이 관여하지 않는 매니지드 전용 조사라면, 이쪽이 도입이 가볍습니다.4
  • 여기서 쓰는 절차는 “읽는 법”이며 “채집하는 법”이 아닙니다. 덤프 획득 방법(WER / ProcDump / MiniDumpWriteDump)은 수집편을, 크래시 시점에 로그와 맞춰보는 설계는 「크래시 시 로그와 덤프를 남기는 설계」를 참고하시기 바랍니다.

2. WinDbg를 설치하고 심볼을 통과시키기

2.1 설치

현재의 WinDbg는 다음 중 하나로 설치할 수 있습니다.1

winget install Microsoft.WinDbg

Microsoft Store를 통해서도 같은 엔진이 설치되며, 명령・확장・워크플로가 공통입니다. 설치 후에는 자동으로 업데이트되므로(Store・직접 설치의 경우는 백그라운드에서, winget의 경우는 winget upgrade Microsoft.WinDbg로) 버전 차이에 따른 동작 차이에는 크게 신경 쓸 필요가 없습니다.1

2.2 덤프 열기

windbg -z C:\CrashDumps\MyApp\MyApp_20260702_101500.dmp

-z는 덤프 파일을 지정해서 시작하는 옵션입니다. GUI에서 “File > Open Dump File”로도 같은 작업을 할 수 있습니다.

2.3 심볼 경로 설정하기

Windows 디버거가 심볼 파일(PDB)을 찾는 위치는 _NT_SYMBOL_PATH 환경 변수, 또는 세션 안에서의 .sympath 명령으로 지정합니다.2 실무에서는 Microsoft의 공개 심볼 서버와 자사 PDB의 위치를 모두 넣는 것이 기본형입니다.

.symfix C:\Symbols\Microsoft
.sympath+ C:\Symbols\MyApp
.reload
  • .symfix는 Microsoft의 공개 심볼 서버(https://msdl.microsoft.com/download/symbols)로의 경로를, 지정한 로컬 캐시와 함께 설정하는 단축 명령입니다. OS 표준 DLL의 심볼은 여기서 자동으로 다운로드됩니다.5
  • .sympath+는 기존 경로에 자사 PDB의 위치를 덧붙입니다. 자사 코드의 PDB는 스스로 준비해야 하며, Microsoft의 심볼 서버에는 올라가 있지 않습니다.
  • .reload로 다시 로드해서, 모듈 목록의 심볼 상태를 확인합니다.

환경 변수로 영구히 설정하는 경우는 다음과 같은 형태입니다. CI나 빌드 서버에서의 자동 분석에는 이쪽이 적합합니다.

set _NT_SYMBOL_PATH=srv*C:\Symbols\Microsoft*https://msdl.microsoft.com/download/symbols;C:\Symbols\MyApp

심볼이 올바르게 읽히고 있는지는, lm(loaded modules) 명령으로 모듈 목록을 출력해서 대상 모듈이 pdb symbols로 되어 있는지로 확인할 수 있습니다. deferred인 채로 있다면, 아직 심볼이 해결되지 않은 상태입니다.

3. SOS 확장 불러오기

.NET 앱의 덤프는, 네이티브 WinDbg 명령만으로는 “매니지드 힙의 내용” “C#의 스택 프레임” “예외 객체의 내용”이 보이지 않습니다. 이 부분을 채워 주는 것이 SOS(Son of Strike) 확장입니다. 힙 조사, 힙 손상 감지, 런타임 내부의 데이터 타입 표시, 실행 중인 매니지드 코드의 상태 파악까지를 SOS 명령을 통해 수행합니다.3

3.1 런타임에 따른 차이

대상 앱이 .NET Framework인지 .NET (Core) / .NET 5+인지에 따라, 로드해야 할 런타임과 SOS의 출처가 달라집니다.

대상 런타임 본체 로딩 명령
.NET Framework clr.dll .loadby sos clr
.NET Core / .NET 5+ coreclr.dll .loadby sos coreclr

.loadby는 지정한 모듈(clr이나 coreclr)이 있는 디렉터리에서, 같은 위치에 있는 확장 DLL(sos.dll)을 찾아 불러오는 명령입니다. 전체 경로를 입력하지 않고도, 덤프가 채집된 환경에 대응하는 버전의 SOS를 확실히 잡아낼 수 있는 것이 장점입니다.6

버전 10.0.18317.1001 이후의 WinDbg・cdb에서는, 대상 프로세스가 coreclr.dll(또는 Linux/macOS의 libcoreclr.so)을 로드하고 있는 것을 감지하면, .NET용 확장을 Microsoft Extension Gallery에서 자동으로 불러옵니다.6 자동 로딩이 잘 되지 않는 경우나, 오래된 버전의 디버거를 쓰고 있는 경우에 위의 .loadby를 수동으로 입력한다는 위치입니다.

3.2 SOS를 찾을 수 없는 경우

자동 로딩이 되지 않는 환경에서는, dotnet-sos 도구로 로컬에 설치할 수 있습니다.

dotnet tool install --global dotnet-sos
dotnet-sos install

설치 후에는 WinDbg에서 다음과 같이 수동으로도 불러올 수 있습니다(오래된 디버거에서는 이 방법이 필요할 수 있습니다).7

.load %USERPROFILE%\.dotnet\sos\sos.dll

3.3 로딩이 되었는지 확인하기

!sos.help

또는 대상이 Core 계열이라면 !Threads를, Framework 계열이라면 !sosstatus를 시도해서, 오류가 나지 않고 뭔가 정보가 돌아오면 로딩은 성공한 것입니다. 여기서 명령이 Unable to find module 같은 오류로 실패한다면, 대부분 심볼 경로나 런타임의 불일치(덤프를 채집한 환경과 손에 있는 런타임의 비트 수・버전 차이 등)가 원인입니다. 다음 장의 명령 이전에, 여기서 막히는 경우도 실무에서 드물지 않습니다.

4. 예외와 스택 읽기 ── !clrstack과 !pe

처리되지 않은 예외로 크래시한 덤프의, 첫 번째 수순입니다.

!threads

먼저 !Threads(lldb 환경에서는 clrthreads 별칭)로 매니지드 스레드의 목록과, 각 스레드의 Exception 열을 확인합니다.8 예외를 가진 스레드가 있다면, 그 스레드로 전환합니다.

~5s
!clrstack

!CLRStack은 매니지드 코드만의 스택 트레이스를 표시합니다.9 인수나 변수까지 보고 싶은 경우는 -a(-l-p를 합친 단축 옵션)를 붙입니다.

!clrstack -a
  • 자사 코드의 메서드가 나열되어 있다면, 그대로 “어디서” “어떤 호출 경로로” 떨어졌는지를 읽을 수 있습니다. 소스 파일명・줄 번호까지 나오는 것은, 심볼이 올바르게 읽혀 있기(2장) 때문입니다. CLRStack은 CLR의 메타데이터에서 매니지드 프레임을 직접 나열하므로, 심볼의 유무는 프레임이 표시되는지 여부에는 영향을 주지 않습니다. 심볼이 부족한 경우 잃게 되는 것은 소스 파일명・줄 번호뿐이며, 프레임 자체가 생략되는 일은 없습니다.9
  • 자사 코드의 프레임이 하나도 보이지 않는 경우는 심볼 부족이 아니라 다음을 의심하시기 바랍니다. 선택한 스레드가 예외를 가지지 않은 다른 스레드였다(스레드 선택 실수), 네이티브 쪽에서만 떨어져서 매니지드 프레임이 애초에 존재하지 않는다, 혹은 덤프의 종류(Mini 등)가 그 시점의 스택 정보를 충분히 포함하지 않는다 같은 원인입니다.

다음으로 예외 객체 자체를 살펴봅니다.

!pe

!PrintException(약칭 !pe)은 지정하지 않으면 현재 스레드에서 마지막으로 던져진 예외를 표시합니다. 타입 이름, 메시지, 내부 예외(-nested로 표시), 스택 트레이스 문자열까지 얻을 수 있습니다.10 System.NullReferenceException처럼 타입 이름만으로는 아무것도 알려주지 않는 예외일수록, !clrstack -a로 보이는 로컬 변수의 값과 맞춰보는 작업이 필요해집니다.

5. 힙과 누수 추적하기 ── !dumpheap -stat과 !gcroot

“메모리가 조금씩 늘어나서, 몇 시간~며칠 후에 떨어진다”는 유형의 조사에서 중심이 되는 명령입니다. GC 대기인지 진짜 누수인지를 구분하는 앞단의 이야기는 「.NET에서 GC 대기와 메모리 누수를 구분하기」에서 자세히 다뤘습니다. 이 글은 그 후속편으로서, 덤프를 1장 읽고 “무엇이 붙잡고 있는지”까지 파고드는 부분에 해당합니다.

!dumpheap -stat

-stat 옵션은 매니지드 힙의 통계 요약만을 표시합니다. 타입별 건수와 총 크기가 많은 순서에 가깝게 나열되므로, 먼저 “양으로 밀어붙이고 있는 타입”을 특정합니다.11 실무에서 흔히 보이는 타입은 다음 2가지 계열입니다.

  • 업무 클래스 자체가 늘어나고 있다(예: MyApp.Models.Customer가 수십만 건) ── 어딘가에 계속 유지되고 있는 강한 참조가 있다
  • System.String이나 배열만 극단적으로 많다 ── 위쪽에 보이는 업무 클래스의 내부 데이터가 원인인 경우가 많으며, 개별 인스턴스를 하나하나 보기 전에 먼저 업무 클래스 쪽을 의심하는 것이 더 빠른 경우가 많습니다

대상을 좁혔다면, 개별 인스턴스의 주소를 얻습니다.

!dumpheap -type MyApp.Models.Customer

그리고 왜 그 객체가 GC로 회수되지 않고 남아 있는지를 조사합니다.

!gcroot 000001a2b3c4d5e0

!GCRoot는 매니지드 힙과 핸들 테이블 전체를 검색해서, 지정한 객체에 도달하는 루트(스택 위의 변수, 정적 필드, GC 핸들 등)를 찾아냅니다.12 출력에 캐시용 정적 필드나 이벤트 핸들러의 구독이 보인다면, 거기가 해제 누락의 유력한 범인입니다. 다음과 같은 “캐시에 넣은 채로 해제하지 않는” 코드가 전형적인 예입니다.

public static class CustomerCache
{
    // 해제할 경로가 없어 계속 늘어나기만 하는 정적 딕셔너리
    private static readonly Dictionary<int, Customer> _cache = new();

    public static void Add(Customer c) => _cache[c.Id] = c;
}

!gcroot의 출력에 CustomerCache 같은 정적 컨테이너가 나타난다면, 코드 쪽에서는 유효 기간이나 상한 건수, 혹은 WeakReference로의 전환을 검토할 재료가 됩니다.11

6. 네이티브 크래시의 자동 분석 ── !analyze -v

C++ DLL, COM, 벤더 SDK가 관여하는 네이티브 크래시(액세스 위반 등)에서는, 먼저 이 명령부터 시작합니다.

!analyze -v

!analyze는 크래시・예외의 자동 분석을 수행하는 확장 명령이며, -v를 붙이면 상세 표시가 됩니다.13 출력 중 특히 봐야 할 항목은 다음 3가지입니다.

  • EXCEPTION_CODE / BUGCHECK_STR: 어떤 종류의 이상인지(액세스 위반, 스택 오버플로 등)
  • FAULTING_IP / FOLLOWUP_IP: 실제로 떨어진 명령어 주소와, 대응하는 모듈・함수 이름
  • MODULE_NAME / IMAGE_NAME: 떨어진 위치가 자사 모듈인지, 서드파티 DLL인지

자사 모듈이 아니라 벤더의 DLL 안에서 떨어진 경우, 거기서부터 더 추적하려면 벤더의 PDB가 필요합니다(대부분 구할 수 없습니다). 실무에서는 “호출한 쪽(자사 코드가 마지막에 넘긴 인수)까지 거슬러 올라가서, 넘긴 값이 이상하지 않았는지”를 의심하는 것이 현실적인 결론입니다. !analyze -vSTACK_TEXT 필드로, 자사 코드에서 무엇을 호출한 직후에 떨어졌는지를 확인하시기 바랍니다.

!analyze는 예외가 일어난 덤프 이외에도 쓸 수 있습니다. 행(hang)을 의심하는 경우는, 대상 스레드를 선택한 상태에서 다음을 실행하면 스레드 간의 블로킹 관계를 분석해 줍니다.

!analyze -hang

7. WinDbg를 쓰지 않는 선택지 ── dotnet-dump analyze

.NET Core / .NET 5+의 매니지드 코드만 조사하고 싶다면(네이티브 DLL이나 COM이 관여하지 않는다면), WinDbg보다 가벼운 dotnet-dump도 선택지입니다.

dotnet tool install --global dotnet-dump
dotnet-dump analyze C:\CrashDumps\MyApp\MyApp_20260702_101500.dmp

analyze 하위 명령은 SOS가 미리 설치된 대화형 세션을 열어서, clrstack, dumpheap, gcroot 등 여기까지 소개한 명령의 대부분을 ! 접두사 없이 그대로 쓸 수 있게 해 줍니다.4

구분해서 쓰는 기준은 다음과 같습니다.

관점 WinDbg + SOS dotnet-dump analyze
네이티브 스택 프레임 보인다 보이지 않는다(매니지드만)4
!analyze -v에 의한 네이티브 자동 분석 쓸 수 있다 쓸 수 없다
Linux 덤프 Windows 위의 WinDbg로 분석 가능(x64 덤프에는 x64판, Arm64 덤프에는 x64판, x86 덤프에는 x86판 사용)14 지원(같은 플랫폼의 비트 수 도구 사용)14
macOS 덤프 지원하지 않음(WinDbg의 Linux 덤프 지원은 macOS를 포함하지 않음) 지원(.NET 5 이후)4
도입의 가벼움 설치 프로그램 또는 winget dotnet global tool 1개의 명령
크로스 플랫폼 CI에 편입 손이 많이 간다 하기 쉽다

“COM이나 P/Invoke, 네이티브 DLL이 관여할 가능성이 있다”면 WinDbg, “순수한 매니지드 코드의 메모리 누수 조사이고, CI나 여러 플랫폼에서도 돌리고 싶다”면 dotnet-dump analyze라는 것이 실무상의 기준입니다. 둘 다 SOS의 명령 체계를 공유하므로, 한쪽에서 익힌 명령은 다른 쪽에서도 거의 그대로 쓸 수 있습니다. macOS에서 채집한 덤프를 다루는 경우는 WinDbg는 선택지에 들지 않으므로 dotnet-dump(또는 LLDB) 중 하나를 쓸 수밖에 없다는 점에 주의하시기 바랍니다.

8. 심볼을 읽지 못하면 아무것도 시작되지 않는다

여기까지의 절차 중 일부는, 심볼(PDB)이 올바르게 로드되지 않아도 어느 정도는 기능합니다. 4장에서 썼듯이 !clrstack!dumpheap -stat!gcroot는 CLR의 메타데이터나 힙 데이터를 직접 읽으므로, PDB가 없어도 프레임이나 타입 정보 자체는 표시됩니다. PDB가 없어서 잃게 되는 것은 매니지드 코드의 소스 파일명・줄 번호와, 네이티브 프레임・네이티브 모듈의 심볼 이름(함수 이름 대신 주소만 표시됨)입니다. 덤프와 실행 파일밖에 손에 없다, 같은 “일단 무슨 일이 일어났는지만이라도 파악하고 싶다”는 상황에서는, PDB를 찾다가 막히기 전에 !threads!clrstack부터 시작해도 문제없습니다. 다만 소스 줄까지 추적해서 원인 위치를 특정하고 싶다면 이야기가 다릅니다. 2장에서 .sympath+에 자사 PDB의 경로를 추가했지만, 실무에서는 “배포한 EXE/DLL에 대응하는 PDB가 어디 있는지 모른다”는 이유만으로 소스 줄 특정에 도달하지 못하고 조사가 멈추는 경우가, 수집편에서 쓴 이야기 이상으로 빈번하게 일어납니다.

PDB 자체가 무엇을 가지고 무엇을 가지지 않는지, Portable PDB, 그리고 Source Link(어셈블리 안에 소스 관리 메타데이터를 넣어, 디버거가 대응하는 커밋 시점의 소스를 직접 가져올 수 있게 하는 방식)에 대해서는 「PDB란 무엇인가」에 한 곳에 정리해 두었습니다.15 덤프 분석을 지속적인 운영에 편입시키려면, 빌드별로 PDB를 보관하고, Source Link를 활성화해 두는 것이 채집 설정 자체와 마찬가지로 중요한 준비입니다. 여기를 게을리하면, !clrstack의 출력에서 소스 줄이 전혀 나오지 않아, 주소와 타입 이름만을 실마리로 손으로 더듬어야 하는 처지가 됩니다.

9. 실례 ── 핸들 누수 조사에서의 덤프 분석

이전에 쓴 「산업용 카메라 장기 가동 크래시 조사 - 핸들 누수편」은, 장시간 운전 후에 갑자기 떨어지는 산업용 카메라 제어 앱의 조사에서, 주범이 메모리 누수가 아니라 핸들 누수였던 사례입니다. 이런 종류의 조사에서 덤프가 효과를 발휘하는 것은 다음과 같은 조합일 때입니다.

  1. !dumpheap -stat으로 매니지드 힙 쪽은 정상(타입별 건수・크기가 계속 늘어나지 않고 있음)이라는 것을 확인한다
  2. 그런데도 프로세스의 핸들 수만 계속 늘어난다면, 누수되고 있는 것은 매니지드 객체가 아니라 OS 핸들(파일, 이벤트, 카메라 SDK가 내부에서 확보하는 핸들 등)이라고 구분할 수 있다
  3. SafeHandle을 가진 매니지드 래퍼 객체가 남아 있다면, !gcroot로 그 래퍼의 GC 루트를 추적해서 “해제되어야 하는데 참조가 남아 있는 위치”를 특정한다

!dumpheap -stat은 “매니지드 힙의 증가인지 아닌지”를 판정하는 분기점으로 기능하고, 그것이 아니라는 것을 알게 된 시점에서 Application Verifier 같은 네이티브 경계의 이상 감지 도구로 조사의 축이 옮겨갑니다. 이 이상계 테스트 기반을 만드는 방법은 「Application Verifier로 만드는 Windows 이상계 테스트 기반」에서 다루고 있습니다. 덤프 분석은 “지금 일어나고 있는 상태”를, Application Verifier는 “이상을 앞당겨 재현시키는” 역할 분담을 하고 있으며, 양쪽을 함께 쓰는 것이 장시간 운전 계열 장애 조사의 정석입니다.

10. 정리

크래시 덤프는 “채집하는” 것보다 “읽는” 쪽이 익숙해지는 데 시간이 걸리지만, 유형은 그렇게 많지 않습니다.

  1. WinDbg를 설치하고, 심볼 경로에 Microsoft의 공개 심볼 서버와 자사 PDB를 모두 넣는다(2장)
  2. .NET 앱이라면 SOS 확장을 불러온다(.loadby sos clr / .loadby sos coreclr, 또는 자동 로딩. 3장)
  3. 예외가 원인이라면 !clrstack!pe, 메모리 증가라면 !dumpheap -stat!gcroot, 네이티브 크래시라면 !analyze -v부터 시작한다(4~6장)
  4. 용도가 순수하게 매니지드 조사라면, 가벼운 dotnet-dump analyze도 검토한다(7장)

그리고 이 모든 것의 토대가 되는 것이 PDB와 심볼의 관리입니다. 덤프 수집 설정을 넣는 시점에, 빌드별 PDB 보관과 Source Link 활성화도 함께 정해 두면, 실제로 장애가 일어났을 때의 조사 시간이 크게 달라집니다. 자체적으로 분석하기 어렵거나 시간을 낼 수 없는 경우는, 덤프와 로그를 일괄로 보내 주시면 저희 쪽에서 분석해 드리는 것도 가능합니다.

관련 기사

관련 상담 분야

합동회사 코무라소프트는 크래시 덤프와 로그를 조합한 버그 원인 조사, 장기 가동 후에만 발생하는 장애의 구분, 덤프・PDB의 보관이나 분석 체계 자체의 설계 상담을 다루고 있습니다.

참고 링크

  1. Microsoft Learn, Install the Windows debugger. WinDbg의 winget / Microsoft Store를 통한 설치 방법, 지원 OS(Windows 10 1607 이후・Windows 11)와 아키텍처(x64・ARM64), 자동 업데이트 동작에 대해.  2 3

  2. Microsoft Learn, Symbol path for Windows debuggers. _NT_SYMBOL_PATH 환경 변수에 의한 심볼 경로 설정 방법과, .symfix 명령으로 공개 심볼 서버로의 기본 경로를 설정할 수 있다는 점에 대해.  2

  3. Microsoft Learn, SOS debugging extension. SOS 확장이 매니지드 힙 정보 수집, 힙 손상 감지, 런타임 내부 데이터 타입 표시에 쓸 수 있다는 점, WinDbg에서의 문법이 ![command]라는 점에 대해.  2

  4. Microsoft Learn, Dump collection and analysis utility (dotnet-dump). dotnet-dump analyze가 SOS 명령을 그대로 쓸 수 있는 대화형 세션을 제공하는 한편, 네이티브 디버거가 아니기 때문에 네이티브 스택 프레임을 표시할 수 없다는 점, macOS 지원은 .NET 5 이후라는 점에 대해.  2 3 4

  5. Microsoft Learn, Microsoft public symbol server. srv*DownstreamStore*https://msdl.microsoft.com/download/symbols 형식의 심볼 경로 문법과, .symfix에 의한 로컬 캐시가 포함된 설정에 대해. 

  6. Microsoft Learn, Debugging Managed Code Using the Windows Debugger. .NET Framework의 런타임이 clr.dll, .NET Core/.NET 5+의 런타임이 coreclr.dll이라는 점, .loadby에 의한 모듈 인근으로부터의 확장 로딩, WinDbg 10.0.18317.1001 이후의 자동 로딩에 대해.  2

  7. Microsoft Learn, SOS installer (dotnet-sos). dotnet-sos install에 의한 로컬 SOS 확장 설치와, 오래된 버전의 디버거에서의 수동 로딩 명령에 대해. 

  8. Microsoft Learn, SOS debugging extension - Commands. Threads(lldb 환경에서의 별칭 clrthreads) 명령이 각 스레드의 ID・도메인・마지막으로 던져진 예외 등을 목록으로 표시한다는 점에 대해. 

  9. Microsoft Learn, SOS debugging extension - Commands. CLRStack 명령이 매니지드 코드만의 스택 트레이스를 표시하고, -a 옵션으로 로컬 변수와 인수를 모두 표시할 수 있다는 점, 심볼(SYMOPT_LOAD_LINES)은 소스 파일명・줄 번호의 표시 여부에만 영향을 주며 프레임 자체의 표시와는 무관하다는 점에 대해.  2

  10. Microsoft Learn, SOS debugging extension - Commands. PrintException(pe) 명령이 주소를 생략하면 현재 스레드에서 마지막으로 던져진 예외를 표시하고, -nested로 중첩된 예외도 표시할 수 있다는 점에 대해. 

  11. Microsoft Learn, Debug a memory leak in .NETDump collection and analysis utility (dotnet-dump) - Analyze memory leaks and allocations. dumpheap -stat에 의한 타입별 건수・총 크기의 통계 표시와, 이를 기점으로 한 조사 진행 방법에 대해.  2

  12. Microsoft Learn, SOS debugging extension - Commands. GCRoot 명령이 매니지드 힙과 핸들 테이블 전체를 검색해서, 지정한 객체로의 참조(루트)를 찾아낸다는 점에 대해. 

  13. Microsoft Learn, Using the !analyze Extension!analyze (WinDbg). !analyze -v에 의한 크래시・예외의 자동 분석과, 출력에 포함되는 FAULTING_IPMODULE_NAME 등 필드의 의미, 행(hang) 조사용 !analyze -hang에 대해. 

  14. Microsoft Learn, Debug Linux dumps. Linux 덤프를 Windows 위에서 WinDbg 또는 dotnet-dump로 분석할 수 있다는 점, 채집 환경(x64/Arm64/x86)의 비트 수에 맞는 버전의 도구를 써야 한다는 점에 대해.  2

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

자주 묻는 질문

이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.

.NET 앱의 크래시 덤프는 어느 명령부터 읽기 시작해야 하나요?
조사의 유형은 3가지입니다. 처리되지 않은 예외로 떨어졌다면 !threads로 예외를 가진 스레드를 찾고, !clrstack으로 매니지드 스택을 표시한 뒤 !pe로 예외 객체의 타입・메시지・내부 예외를 확인합니다. 메모리가 계속 늘어나는 조사라면 !dumpheap -stat으로 양이 많은 타입을 찾고, !gcroot로 그 객체를 붙잡고 있는 루트(정적 필드나 이벤트 핸들러의 구독)를 찾아냅니다. 네이티브 크래시라면 !analyze -v의 자동 분석부터 시작합니다.
WinDbg에서 SOS 확장은 어떻게 불러오나요?
.NET Framework라면 .loadby sos clr, .NET Core/.NET 5+라면 .loadby sos coreclr입니다. 버전 10.0.18317.1001 이후의 WinDbg는 대상 프로세스가 coreclr.dll을 로드한 것을 감지하면 SOS를 자동으로 불러옵니다. 자동 로딩이 되지 않는 환경에서는 dotnet-sos 도구로 로컬에 설치하고 수동으로 .load할 수도 있습니다. 로딩이 되었는지는 !sos.help나 !Threads가 오류 없이 반환되는지로 확인합니다.
PDB(심볼)가 없어도 덤프 분석을 할 수 있나요?
어느 정도는 가능합니다. !clrstack・!dumpheap -stat・!gcroot는 CLR의 메타데이터나 힙 데이터를 직접 읽으므로, PDB가 없어도 프레임이나 타입 정보는 표시됩니다. 잃게 되는 것은 매니지드 코드의 소스 파일명・줄 번호와, 네이티브 프레임의 심볼 이름입니다. 소스 줄까지 추적해서 원인 위치를 특정하고 싶다면, Microsoft의 공개 심볼 서버와 자사 PDB의 위치를 모두 심볼 경로에 넣어야 합니다. 빌드별 PDB 보관과 Source Link 활성화가 운영상 중요합니다.
dotnet-dump analyze와 WinDbg는 어떻게 구분해서 쓰나요?
COM이나 P/Invoke, 네이티브 DLL이 관여할 가능성이 있다면 WinDbg, 순수한 매니지드 코드 조사라면 dotnet-dump analyze가 기준입니다. dotnet-dump는 dotnet global tool 1개의 명령으로 설치할 수 있고, clrstack이나 dumpheap 등 SOS 명령의 대부분을 그대로 쓸 수 있지만, 네이티브 스택 프레임은 다루지 못하고 !analyze -v도 쓸 수 없습니다. macOS에서 채집한 덤프는 WinDbg로는 분석할 수 없으므로 dotnet-dump 또는 LLDB밖에 선택지가 없습니다. 둘 다 SOS의 명령 체계를 공유하므로, 익힌 명령은 거의 상호 호환됩니다.

저자 프로필

기사 저자의 프로필 페이지입니다.

Go Komura

합동회사 코무라소프트 대표

Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.

블로그 목록으로 돌아가기