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

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  37  38  [39]  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12644정성태5/14/20219119오류 유형: 719. 윈도우 - 제어판의 "프로그램 및 기능" / "Windows 기능 켜기/끄기" 오류 0x800736B3
12643정성태5/14/20218255오류 유형: 718. 서버 유형의 COM+ 사용 시 0x80080005(Server execution failed) 오류 발생
12642정성태5/14/20219174오류 유형: 717. The 'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine.
12641정성태5/13/20218902디버깅 기술: 179. 윈도우용 .NET Core 3 이상에서 Windbg의 sos 사용법
12640정성태5/13/202111803오류 유형: 716. RDP 연결 - Because of a protocol error (code: 0x112f), the remote session will be disconnected. [1]
12639정성태5/12/20218695오류 유형: 715. Arduino: Open Serial Monitor - The module '...\detection.node' was compiled against a different Node.js version using NODE_MODULE_VERSION
12638정성태5/12/20219597사물인터넷: 63. NodeMCU v1 ESP8266 - 펌웨어 내 파일 시스템(SPIFFS, LittleFS) 및 EEPROM 활용
12637정성태5/10/20219288사물인터넷: 62. NodeMCU v1 ESP8266 보드의 A0 핀에 다중 아날로그 센서 연결 [1]
12636정성태5/10/20219437사물인터넷: 61. NodeMCU v1 ESP8266 보드의 A0 핀 사용법 - FSR-402 아날로그 압력 센서 연동파일 다운로드1
12635정성태5/9/20218758기타: 81. OpenTabletDriver를 (관리자 권한으로 실행하지 않고도) 관리자 권한의 프로그램에서 동작하게 만드는 방법
12634정성태5/9/20217868개발 환경 구성: 572. .NET에서의 신뢰도 등급 조정 - 외부 Manifest 파일을 두는 방법파일 다운로드1
12633정성태5/7/20219318개발 환경 구성: 571. UAC - 관리자 권한 없이 UIPI 제약을 없애는 방법
12632정성태5/7/20219464기타: 80. (WACOM도 지원하는) Tablet 공통 디바이스 드라이버 - OpenTabletDriver
12631정성태5/5/20219397사물인터넷: 60. ThingSpeak 사물인터넷 플랫폼에 ESP8266 NodeMCU v1 + 조도 센서 장비 연동파일 다운로드1
12630정성태5/5/20219744사물인터넷: 59. NodeMCU v1 ESP8266 보드의 A0 핀 사용법 - CdS Cell(GL3526) 조도 센서 연동파일 다운로드1
12629정성태5/5/202111495.NET Framework: 1057. C# - CoAP 서버 및 클라이언트 제작 (UDP 소켓 통신) [1]파일 다운로드1
12628정성태5/4/20219424Linux: 39. Eclipse 원격 디버깅 - Cannot run program "gdb": Launching failed
12627정성태5/4/202110138Linux: 38. 라즈베리 파이 제로 용 프로그램 개발을 위한 Eclipse C/C++ 윈도우 환경 설정
12626정성태5/3/202110157.NET Framework: 1056. C# - Thread.Suspend 호출 시 응용 프로그램 hang 현상 (2)파일 다운로드1
12625정성태5/3/20219113오류 유형: 714. error CS5001: Program does not contain a static 'Main' method suitable for an entry point
12624정성태5/2/202112834.NET Framework: 1055. C# - struct/class가 스택/힙에 할당되는 사례 정리 [10]파일 다운로드1
12623정성태5/2/20219531.NET Framework: 1054. C# 9 최상위 문에 STAThread 사용 [1]파일 다운로드1
12622정성태5/2/20216338오류 유형: 713. XSD 파일을 포함한 프로젝트 - The type or namespace name 'TypedTableBase<>' does not exist in the namespace 'System.Data'
12621정성태5/1/20219729.NET Framework: 1053. C# - 특정 레지스트리 변경 시 알림을 받는 방법파일 다운로드1
12620정성태4/29/202111876.NET Framework: 1052. C# - 왜 구조체는 16 바이트의 크기가 적합한가? [1]파일 다운로드1
12619정성태4/28/202112375.NET Framework: 1051. C# - 구조체의 크기가 16바이트가 넘어가면 힙에 할당된다? [2]파일 다운로드1
... 31  32  33  34  35  36  37  38  [39]  40  41  42  43  44  45  ...