Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)
(시리즈 글이 3개 있습니다.)
디버깅 기술: 92. windbg - C# Monitor Lock을 획득하고 있는 스레드 찾는 방법
; https://www.sysnet.pe.kr/2/0/11268

닷넷: 2214. windbg - Monitor.Enter의 thin lock과 fat lock
; https://www.sysnet.pe.kr/2/0/13552

닷넷: 2215. windbg - thin/fat lock 없이 동작하는 Monitor.Wait + Pulse
; https://www.sysnet.pe.kr/2/0/13553




windbg - C# Monitor Lock을 획득하고 있는 스레드 찾는 방법

이에 대해서는 다음의 글에 방법이 잘 나옵니다.

Analyzing Monitor-Based Deadlocks with SOS.dll
; http://blogs.microsoft.co.il/sasha/2009/10/17/analyzing-monitor-based-deadlocks-with-sosdll/

그런데 직접 실습을 해보니 약간 달라진 면이 있어서 기록을 남깁니다.




우선, 재현을 위해 간단한 테스트 프로그램을 다음과 같이 만들어 볼 수 있습니다.

using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            object _objLock1 = new object();
            object _objLock2 = new object();

            Thread t1 = new Thread(threadFunc1);
            t1.IsBackground = true;
            t1.Start(_objLock1);

            Thread t2 = new Thread(threadFunc2);
            t2.IsBackground = true;
            t2.Start(_objLock2);

            lock (_objLock1)
                lock (_objLock2)
                {
                    Console.WriteLine("Enter...");
                    Console.ReadLine();
                    Console.WriteLine("Exit...");
                }
        }

        private static void threadFunc1(object obj)
        {
            lock (obj)
            {
                Console.WriteLine("threadFunc1");
            }
        }

        private static void threadFunc2(object obj)
        {
            lock (obj)
            {
                Console.WriteLine("threadFunc2");
            }
        }
    }
}

결국, Main 스레드가 2개의 lock을 이미 소유한 상태에서 threadFunc1과 threadFunc2의 실행이 hang 상태가 된 것을 표현한 것입니다.

위의 프로그램을 실행하고 작업 관리자를 이용해 덤프를 남긴 후 windbg로 읽어들입니다. 당연히 ".loadby sos clr"로 sos 확장을 로드하고 다음과 같이 현재 열려 있는 lock의 수를 확인할 수 있습니다.

0:000> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner
    3 0000027ecd80ece8            3         1 0000027ecd7b2b00 7744   0   0000027ecf3a3ea8 System.Object
    4 0000027ecd80ed38            3         1 0000027ecd7b2b00 7744   0   0000027ecf3a3ec0 System.Object
-----------------------------
Total           4
CCW             0
RCW             0
ComClassFactory 0
Free            0

보는 바와 같이 총 2개의 lock이 사용 중이고, 현재 그것을 점유하고 있는 "Owing Thread"는 0000027ecd7b2b00으로 나옵니다. 이에 해당하는 스레드 정보는 !threads 명령어로 확인할 수 있습니다.

0:000> !threads
ThreadCount:      4
UnstartedThread:  0
BackgroundThread: 3
PendingThread:    0
DeadThread:       0
Hosted Runtime:   no
                                                                                                        Lock  
       ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1 7744 0000027ecd7b2b00    2a020 Preemptive  0000027ECF3A9FA8:0000027ECF3A9FD0 0000027ecd783180 3     MTA 
   5    2 44c0 0000027ecd7dbe40    2b220 Preemptive  0000000000000000:0000000000000000 0000027ecd783180 0     MTA (Finalizer) 
   6    3 7d7c 0000027ecd80e470  202b220 Preemptive  0000027ECF3A6018:0000027ECF3A7FD0 0000027ecd783180 0     MTA 
   7    4 1338 0000027ecd814970  202b220 Preemptive  0000000000000000:0000000000000000 0000027ecd783180 0     MTA 

0번 스레드인데 당연히 Main 메서드를 실행 중일 테니 굳이 확인할 필요도 없어 보입니다.

나머지 중에 5번 스레드는 Finalizer고 6번과 7번이 우리가 생성했던 스레드였을 텐데 이 중에서 6번을 먼저 살펴보겠습니다.

0:000> ~6s
ntdll!NtWaitForMultipleObjects+0x14:
00007fff`499f5ef4 c3              ret

0:006> !clrstack
OS Thread Id: 0x7d7c (6)
        Child SP               IP Call Site
000000e3b82ff0f8 00007fff499f5ef4 [GCFrame: 000000e3b82ff0f8] 
000000e3b82ff238 00007fff499f5ef4 [GCFrame: 000000e3b82ff238] 
000000e3b82ff278 00007fff499f5ef4 [HelperMethodFrame_1OBJ: 000000e3b82ff278] System.Threading.Monitor.Enter(System.Object)
000000e3b82ff370 00007ffed33f0775 ConsoleApp1.Program.threadFunc1(System.Object) [E:\ConsoleApp1\ConsoleApp1\Program.cs @ 32]
000000e3b82ff3c0 00007fff2f06ca72 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000e3b82ff490 00007fff2f06c904 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000e3b82ff4c0 00007fff2f06c8c2 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
000000e3b82ff510 00007fff2fa40ffc System.Threading.ThreadHelper.ThreadStart(System.Object)
000000e3b82ff768 00007fff329f6753 [GCFrame: 000000e3b82ff768] 
000000e3b82ffab8 00007fff329f6753 [DebuggerU2MCatchHandlerFrame: 000000e3b82ffab8] 

콜스택 상단에 Monitor.Enter가 있으므로 현재 lock을 대기 중임을 알 수 있습니다. 그렇다면 어떤 object를 대상으로 lock을 건 것일까요? 이를 위해 native 레벨의 callstack을 확인해 보면 단서를 찾을 수 있습니다.

0:006> kv
 # Child-SP          RetAddr           : Args to Child                                                           : Call Site
00 000000e3`b82feb38 00007fff`4677dd20 : 00000099`00000000 00000000`00000010 00000002`00000002 00007fff`00000010 : ntdll!NtWaitForMultipleObjects+0x14
01 000000e3`b82feb40 00007fff`32bb401e : ffffffff`fffffffe 00007fff`00000000 000000e3`b82ff0f8 00000000`00000000 : KERNELBASE!WaitForMultipleObjectsEx+0xf0
02 000000e3`b82fee40 00007fff`32bb3eb0 : 00000000`00000001 00000000`00000000 0000027e`cd80e470 00000000`ffffffff : clr!WaitForMultipleObjectsEx_SO_TOLERANT+0x62
03 000000e3`b82feea0 00007fff`32bb3ca9 : 0000027e`cd80e470 00000000`00000001 00000000`00000000 00007ffe`00000000 : clr!Thread::DoAppropriateWaitWorker+0x1e4
04 000000e3`b82fefa0 00007fff`32bb42b9 : 00000000`00000000 00007fff`00000001 0000027e`cd80e470 00007fff`329ff1af : clr!Thread::DoAppropriateWait+0x7d
05 000000e3`b82ff020 00007fff`32bb458e : 0000027e`cd80ece8 000000e3`b82ff3b0 00000000`00000000 0000027e`cd80e470 : clr!CLREventBase::WaitEx+0xc4
06 000000e3`b82ff0b0 00007fff`32bb44a2 : 0000027e`cd80ece8 0000027e`cd80ece8 00007ffe`00000000 0000027e`cf3a3ea8 : clr!AwareLock::EnterEpilogHelper+0xca
07 000000e3`b82ff170 00007fff`32bb41b2 : 0000027e`cd80e470 0000027e`cd80e470 00000000`00000000 0000027e`cd80ece8 : clr!AwareLock::EnterEpilog+0x62
08 000000e3`b82ff1d0 00007ffe`d33f0775 : 0000027e`cf3a3fa0 000000e3`b82ff3a8 0000027e`cf3a4080 00000000`00000000 : clr!JITutil_MonEnterWorker+0xe2
09 000000e3`b82ff370 00007fff`2f06ca72 : 0000027e`cf3a3ea8 0000027e`cf3a3ea8 000000e3`b82ff3f0 00000000`00000000 : 0x00007ffe`d33f0775
0a 000000e3`b82ff3c0 00007fff`2f06c904 : 0000027e`cf3a4080 0000027e`cf3a3fa0 0000027e`cf3a3f78 00000000`00000000 : mscorlib_ni+0x4dca72
0b 000000e3`b82ff490 00007fff`2f06c8c2 : 0000027e`cf3a3f78 00000000`00000000 00000000`cf3a3ea8 00000000`00000000 : mscorlib_ni+0x4dc904
0c 000000e3`b82ff4c0 00007fff`2fa40ffc : 000000e3`b82ff668 000000e3`b82ff668 000000e3`b82ff570 00000000`00000000 : mscorlib_ni+0x4dc8c2
0d 000000e3`b82ff510 00007fff`329f6753 : 0000027e`cf3a3fe0 0000027e`cf3a3ea8 00000000`00000000 00000000`00000000 : mscorlib_ni+0xeb0ffc
0e 000000e3`b82ff550 00007fff`329f6649 : 00000000`00000001 00007fff`32b3f8e3 000000e3`b82ff828 00000000`00000000 : clr!CallDescrWorkerInternal+0x83
0f 000000e3`b82ff590 00007fff`329f732d : 00000000`00000002 000000e3`b82ff748 000000e3`b82ff768 000000e3`b82ff828 : clr!CallDescrWorkerWithHandler+0x4e
10 000000e3`b82ff5d0 00007fff`32b096e9 : 000000e3`b82ffbe0 000000e3`b82ffb20 00007fff`2f28e720 000000e3`b82ffb20 : clr!MethodDescCallSite::CallTargetWorker+0xf8
11 000000e3`b82ff6d0 00007fff`329f7c9d : 00000000`00001f80 00007fff`32b095e0 000000e3`b82ffb20 ffffffff`ffffffff : clr!ThreadNative::KickOffThread_Worker+0x109
12 000000e3`b82ff930 00007fff`329f7c18 : 000000e3`b82ffb20 00007fff`329f6051 0000027e`00000000 00000000`00000018 : clr!Frame::Push+0x59
13 000000e3`b82ff970 00007fff`329f7b56 : 00000000`00000001 00000000`00000000 00000000`00000000 00000000`00000000 : clr!MarshalInfo::SetupArgumentSizes+0x234
14 000000e3`b82ffa70 00007fff`329f7cdf : ffffffff`ffffffff 0000027e`cd80e470 0000027e`cd80e470 0000027e`cd806190 : clr!MarshalInfo::SetupArgumentSizes+0x15d
15 000000e3`b82ffb00 00007fff`32b095cb : 0000027e`cd80e470 000000e3`00000001 0000027e`cd806190 00000000`00000003 : clr!MarshalInfo::SetupArgumentSizes+0xe3
16 000000e3`b82ffb60 00007fff`32b31c3f : 0000027e`cd806190 0000027e`cd80e470 000000e3`b82ffbb8 00000000`00000000 : clr!ThreadNative::KickOffThread+0xdb
17 000000e3`b82ffc30 00007fff`47152774 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : clr!Thread::intermediateThreadProc+0x86
18 000000e3`b82ffd70 00007fff`499c0d51 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0x14
19 000000e3`b82ffda0 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21

콜스택에 보면 clr!CLREventBase::WaitEx 호출이 보이는데 그것의 첫 번째 인자(0000027e`cd80ece8) 값이 바로 syncblock 값과 일치하게 나옵니다.

0:000> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner
    3 0000027ecd80ece8            3         1 0000027ecd7b2b00 7744   0   0000027ecf3a3ea8 System.Object
    4 0000027ecd80ed38            3         1 0000027ecd7b2b00 7744   0   0000027ecf3a3ec0 System.Object

즉, 6번 스레드는 0000027ecd80ece8에 해당하는 lock을 대기하고 있는 중이며 그 lock을 현재 소유하고 있는 스레드는 0000027ecd7b2b00인 0번 스레드임이 밝혀집니다. 이 정도면... ^^ 실전에서 충분히 추적할만한 단서가 되겠지요.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 8/14/2017]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 16  17  18  19  20  21  22  23  24  25  26  27  28  [29]  30  ...
NoWriterDateCnt.TitleFile(s)
12919정성태1/13/20226526Windows: 199. Host Network Service (HNS)에 의해서 점유되는 포트
12918정성태1/13/20226739Linux: 47. WSL - shell script에서 설정한 환경 변수가 스크립트 실행 후 반영되지 않는 문제
12917정성태1/12/20225929오류 유형: 785. C# - The type or namespace name '...' could not be found (are you missing a using directive or an assembly reference?)
12916정성태1/12/20225697오류 유형: 784. TFS - One or more source control bindings for this solution are not valid and are listed below.
12915정성태1/11/20225998오류 유형: 783. Visual Studio - We didn't find any interpreters
12914정성태1/11/20228014VS.NET IDE: 172. 비주얼 스튜디오 2022의 파이선 개발 환경 지원
12913정성태1/11/20228522.NET Framework: 1133. C# - byte * (바이트 포인터)를 FileStream으로 쓰는 방법 [1]
12912정성태1/11/20229179개발 환경 구성: 623. ffmpeg.exe를 사용해 비디오 파일의 이미지를 PGM(Portable Gray Map) 파일 포맷으로 출력하는 방법 [1]
12911정성태1/11/20226403VS.NET IDE: 171. 비주얼 스튜디오 - 더 이상 만들 수 없는 "ASP.NET Core 3.1 Web Application (.NET Framework)" 프로젝트
12910정성태1/10/20226898제니퍼 .NET: 30. 제니퍼 닷넷 적용 사례 (8) - CPU high와 DB 쿼리 성능에 문제가 함께 있는 사이트
12909정성태1/10/20228274오류 유형: 782. Visual Studio 2022 설치 시 "Couldn't install Microsoft.VisualCpp.Redist.14.Latest"
12908정성태1/10/20226100.NET Framework: 1132. C# - ref/out 매개변수의 IL 코드 처리
12907정성태1/9/20226632오류 유형: 781. (youtube-dl.exe) 실행 시 "This app can't run on your PC" / "Access is denied." 오류 발생
12906정성태1/9/20227288.NET Framework: 1131. C# - 네임스페이스까지 동일한 타입을 2개의 DLL에서 제공하는 경우 충돌을 우회하는 방법 [1]파일 다운로드1
12905정성태1/8/20226942오류 유형: 780. Could not load file or assembly 'Microsoft.VisualStudio.TextTemplating.VSHost.15.0, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies.
12904정성태1/8/20228914개발 환경 구성: 623. Visual Studio 2022 빌드 환경을 위한 github Actions 설정 [1]
12903정성태1/7/20227520.NET Framework: 1130. C# - ELEMENT_TYPE_INTERNAL 유형의 사용 예
12902정성태1/7/20227563오류 유형: 779. SQL 서버 로그인 에러 - provider: Shared Memory Provider, error: 0 - No process is on the other end of the pipe.
12901정성태1/5/20227586오류 유형: 778. C# - .NET 5+에서 warning CA1416: This call site is reachable on all platforms. '...' is only supported on: 'windows' 경고 발생
12900정성태1/5/20229272개발 환경 구성: 622. vcpkg로 ffmpeg를 빌드하는 경우 생성될 구성 요소 제어하는 방법
12899정성태1/3/20228763개발 환경 구성: 621. windbg에서 python 스크립트 실행하는 방법 - pykd (2)
12898정성태1/2/20229343.NET Framework: 1129. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 인코딩 예제(encode_video.c) [1]파일 다운로드1
12897정성태1/2/20228178.NET Framework: 1128. C# - 화면 캡처한 이미지를 ffmpeg(FFmpeg.AutoGen)로 동영상 처리 [4]파일 다운로드1
12896정성태1/1/202211131.NET Framework: 1127. C# - FFmpeg.AutoGen 라이브러리를 이용한 기본 프로젝트 구성파일 다운로드1
12895정성태12/31/20219555.NET Framework: 1126. C# - snagit처럼 화면 캡처를 연속으로 수행해 동영상 제작 [1]파일 다운로드1
12894정성태12/30/20217522.NET Framework: 1125. C# - DefaultObjectPool<T>의 IDisposable 개체에 대한 풀링 문제 [3]파일 다운로드1
... 16  17  18  19  20  21  22  23  24  25  26  27  28  [29]  30  ...