Microsoft MVP성태의 닷넷 이야기
.NET Framework: 925. C# - ETW를 이용한 Monitor Enter/Exit 감시 [링크 복사], [링크+제목 복사],
조회: 16573
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

C# - ETW를 이용한 Monitor Enter/Exit 감시

예전에도, Monitor Enter/Exit를 감시해 보려는 노력이 있었습니다. ^^

실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 - 세 번째 이야기(Monitor.Enter 후킹)
; https://www.sysnet.pe.kr/2/0/12165

하지만, 저런 것은 특정 프로그램을 대상으로 한다면 수용할 수도 있겠지만, 일반적인 범용 프로그램을 대상으로 하기에는 다소 위험한 코드입니다. 좀 더 시스템적으로 안정적인 방법이 필요한데 다행히 ETW로 목적을 달성할 수 있습니다. 이를 위해 필요한 코드는 지난 글에 설명한 ETW 예제 코드를 약간만 변형시키면 됩니다.

// ...[생략]...

using (var session = new TraceEventSession(sessionName, null))
{
    var restarted = session.EnableProvider(
        ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose,
        (ulong)(ClrTraceEventParser.Keywords.Contention));

    Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) { session.Dispose(); };

    using (TraceLogEventSource traceLogSource = TraceLog.CreateFromTraceEventSession(session))
    {
        traceLogSource.Clr.ContentionStart += delegate (ContentionStartTraceData data)
        {
            if (data.ProcessID != _processId)
            {
                return;
            }

            Console.WriteLine($"[{data.TimeStamp}] {data.ThreadID} Start");
            _contentionStore.Push(data);
        };

        traceLogSource.Clr.ContentionStop += delegate (ContentionStopTraceData data)
        {
            if (data.ProcessID != _processId)
            {
                return;
            }

            Console.Write($"[{data.TimeStamp}] {data.ThreadID} Stop ");

            ContentionInfo info = _contentionStore.Pop(data);
            if (info == null)
            {
                Console.WriteLine("info == null");
                return;
            }

            Console.WriteLine($"[{data.ContentionFlags}]Lock-released: {(int)((data.TimeStampRelativeMSec - info.ContentionStartRelativeMSec))}");

        };

        traceLogSource.Process();
    }

    // ...[생략]...
}

얼마나 잘 동작하는지 실습을 위해 감시할 대상 프로그램을 다음의 예제 코드로 작성해 보면,

{
    int workerCount = ...;
    ThreadPool.SetMinThreads(workerCount, workerCount);

    Thread.Sleep(2000);

    Task [] workers = new Task[workerCount];
    for (int i = 0; i < workerCount; i++)
    {
        workers[i] = Task.Run(() =>
        {
            DateTime dt1 = DateTime.Now;

            Console.WriteLine($"[{dt1}] {AppDomain.GetCurrentThreadId()} {Thread.CurrentThread.ManagedThreadId} Before-lock");
            lock (lockObj)
            {
                DateTime dt2 = DateTime.Now;
                Console.WriteLine($"[{dt2}] {AppDomain.GetCurrentThreadId()} {Thread.CurrentThread.ManagedThreadId} After-lock {(int)(dt2 - dt1).TotalMilliseconds}");

                Thread.Sleep(1000);
            }
        });
    }

    foreach (Task task in workers)
    {
        task.Wait();
    }
}


실행 시, 출력 결과가 다음과 같이 나오지만,

[2020-07-08 오전 12:21:53] 26072 4 Before-lock
[2020-07-08 오전 12:21:53] 29048 5 Before-lock
[2020-07-08 오전 12:21:53] 30468 3 Before-lock
[2020-07-08 오전 12:21:53] 9148 6 Before-lock
[2020-07-08 오전 12:21:53] 31500 9 Before-lock
[2020-07-08 오전 12:21:53] 30732 8 Before-lock
[2020-07-08 오전 12:21:53] 25124 11 Before-lock
[2020-07-08 오전 12:21:53] 26072 4 After-lock 2
[2020-07-08 오전 12:21:53] 22400 10 Before-lock
[2020-07-08 오전 12:21:53] 16732 12 Before-lock
[2020-07-08 오전 12:21:53] 23136 7 Before-lock
[2020-07-08 오전 12:21:53] 336 18 Before-lock
[2020-07-08 오전 12:21:53] 12152 17 Before-lock
[2020-07-08 오전 12:21:53] 19224 20 Before-lock
[2020-07-08 오전 12:21:53] 25960 22 Before-lock
[2020-07-08 오전 12:21:53] 18180 21 Before-lock
[2020-07-08 오전 12:21:53] 20160 13 Before-lock
[2020-07-08 오전 12:21:53] 15896 19 Before-lock
[2020-07-08 오전 12:21:53] 29252 15 Before-lock
[2020-07-08 오전 12:21:53] 5088 14 Before-lock
[2020-07-08 오전 12:21:53] 7164 16 Before-lock
[2020-07-08 오전 12:21:54] 29048 5 After-lock 1008
[2020-07-08 오전 12:21:55] 30468 3 After-lock 2014
[2020-07-08 오전 12:21:56] 9148 6 After-lock 3015
[2020-07-08 오전 12:21:57] 31500 9 After-lock 4016
[2020-07-08 오전 12:21:58] 30732 8 After-lock 5022
[2020-07-08 오전 12:21:59] 25124 11 After-lock 6035
[2020-07-08 오전 12:22:00] 22400 10 After-lock 7044
[2020-07-08 오전 12:22:01] 16732 12 After-lock 8046
[2020-07-08 오전 12:22:02] 23136 7 After-lock 9053
[2020-07-08 오전 12:22:03] 336 18 After-lock 10056
[2020-07-08 오전 12:22:04] 12152 17 After-lock 11057
[2020-07-08 오전 12:22:05] 19224 20 After-lock 12062
[2020-07-08 오전 12:22:06] 25960 22 After-lock 13076
[2020-07-08 오전 12:22:07] 18180 21 After-lock 14090
[2020-07-08 오전 12:22:08] 20160 13 After-lock 15110
[2020-07-08 오전 12:22:09] 15896 19 After-lock 16123
[2020-07-08 오전 12:22:10] 29252 15 After-lock 17138
[2020-07-08 오전 12:22:11] 5088 14 After-lock 18147
[2020-07-08 오전 12:22:12] 7164 16 After-lock 19155

ETW로 모니터링한 경우는,

[2020-07-08 오전 12:21:53] 26072 Start
[2020-07-08 오전 12:21:53] 26072 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 30468 Start
[2020-07-08 오전 12:21:53] 30468 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 30468 Start
[2020-07-08 오전 12:21:53] 30468 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 26072 Start
[2020-07-08 오전 12:21:53] 26072 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 29048 Start
[2020-07-08 오전 12:21:53] 29048 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 29048 Start
[2020-07-08 오전 12:21:53] 29048 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 29048 Start
[2020-07-08 오전 12:21:53] 26072 Start
[2020-07-08 오전 12:21:53] 26072 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 26072 Start
[2020-07-08 오전 12:21:53] 26072 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 29048 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 9148 Start
[2020-07-08 오전 12:21:53] 9148 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 26072 Start
[2020-07-08 오전 12:21:53] 23136 Start
[2020-07-08 오전 12:21:53] 31500 Start
[2020-07-08 오전 12:21:53] 25124 Start
[2020-07-08 오전 12:21:53] 25124 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 30732 Start
[2020-07-08 오전 12:21:53] 25124 Start
[2020-07-08 오전 12:21:53] 26072 Stop [Managed]Lock-released: 0
[2020-07-08 오전 12:21:53] 22400 Start
[2020-07-08 오전 12:21:53] 22400 Stop [Managed]Lock-released: 0
[2020-07-08 오전 12:21:53] 5088 Start
[2020-07-08 오전 12:21:53] 20160 Start
[2020-07-08 오전 12:21:53] 29252 Start
[2020-07-08 오전 12:21:53] 22400 Start
[2020-07-08 오전 12:21:53] 16732 Start
[2020-07-08 오전 12:21:53] 23136 Stop [Managed]Lock-released: 2
[2020-07-08 오전 12:21:53] 336 Start
[2020-07-08 오전 12:21:53] 336 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 15896 Start
[2020-07-08 오전 12:21:53] 15896 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:21:53] 336 Start
[2020-07-08 오전 12:21:53] 15896 Start
[2020-07-08 오전 12:21:53] 12152 Start
[2020-07-08 오전 12:21:53] 7164 Start
[2020-07-08 오전 12:21:53] 23136 Start
[2020-07-08 오전 12:21:53] 336 Stop [Managed]Lock-released: 0
[2020-07-08 오전 12:21:53] 18180 Start
[2020-07-08 오전 12:21:53] 25960 Start
[2020-07-08 오전 12:21:53] 19224 Start
[2020-07-08 오전 12:21:53] 336 Start
[2020-07-08 오전 12:21:53] 12152 Stop [Managed]Lock-released: 1
[2020-07-08 오전 12:21:53] 12152 Start
[2020-07-08 오전 12:21:53] 19224 Stop [Managed]Lock-released: 0
[2020-07-08 오전 12:21:53] 19224 Start
[2020-07-08 오전 12:21:53] 25960 Stop [Managed]Lock-released: 1
[2020-07-08 오전 12:21:53] 25960 Start
[2020-07-08 오전 12:21:53] 18180 Stop [Managed]Lock-released: 2
[2020-07-08 오전 12:21:53] 18180 Start
[2020-07-08 오전 12:21:53] 20160 Stop [Managed]Lock-released: 8
[2020-07-08 오전 12:21:53] 20160 Start
[2020-07-08 오전 12:21:53] 15896 Stop [Managed]Lock-released: 9
[2020-07-08 오전 12:21:53] 29252 Stop [Managed]Lock-released: 11
[2020-07-08 오전 12:21:53] 15896 Start
[2020-07-08 오전 12:21:53] 29252 Start
[2020-07-08 오전 12:21:53] 5088 Stop [Managed]Lock-released: 11
[2020-07-08 오전 12:21:53] 5088 Start
[2020-07-08 오전 12:21:53] 7164 Stop [Managed]Lock-released: 10
[2020-07-08 오전 12:21:53] 7164 Start
[2020-07-08 오전 12:21:54] 28292 Start
[2020-07-08 오전 12:21:57] 31500 Stop [Managed]Lock-released: 4015
[2020-07-08 오전 12:21:58] 30732 Stop [Managed]Lock-released: 5022
[2020-07-08 오전 12:21:59] 25124 Stop [Managed]Lock-released: 6034
[2020-07-08 오전 12:22:00] 22400 Stop [Managed]Lock-released: 7043
[2020-07-08 오전 12:22:01] 16732 Stop [Managed]Lock-released: 8046
[2020-07-08 오전 12:22:02] 23136 Stop [Managed]Lock-released: 9049
[2020-07-08 오전 12:22:03] 336 Stop [Managed]Lock-released: 10053
[2020-07-08 오전 12:22:04] 12152 Stop [Managed]Lock-released: 11053
[2020-07-08 오전 12:22:05] 19224 Stop [Managed]Lock-released: 12060
[2020-07-08 오전 12:22:06] 25960 Stop [Managed]Lock-released: 13073
[2020-07-08 오전 12:22:07] 18180 Stop [Managed]Lock-released: 14085
[2020-07-08 오전 12:22:08] 20160 Stop [Managed]Lock-released: 15099
[2020-07-08 오전 12:22:09] 15896 Stop [Managed]Lock-released: 16113
[2020-07-08 오전 12:22:10] 29252 Stop [Managed]Lock-released: 17126
[2020-07-08 오전 12:22:11] 5088 Stop [Managed]Lock-released: 18135
[2020-07-08 오전 12:22:12] 7164 Stop [Managed]Lock-released: 19144
[2020-07-08 오전 12:22:13] 28292 Stop [Managed]Lock-released: 19163
[2020-07-08 오전 12:22:32] 23136 Start
[2020-07-08 오전 12:22:32] 23136 Stop [Native]Lock-released: 0
[2020-07-08 오전 12:22:33] 31500 Start
[2020-07-08 오전 12:22:33] 31500 Stop [Native]Lock-released: 0

원래 프로그램의 결과에서 1초, 2초, 3초에 해당하는 대기 시간이, 어떤 때는 출력이 되고 어떤 때는 출력이 안 됩니다. 그러니까, 100% 믿을 수는 없지만 그래도 성능 하락을 유발할 수 있는 정도는 알아낼 수 있으므로 그런대로 도움이 될 수 있습니다.




또 다른 경우로 다음과 같이 테스트를 해보면,

{
    Thread t1 = new Thread(threadFunc);
    Thread t2 = new Thread(threadFunc);

    t1.Start(lockObj);
    t2.Start(lockObj);

    t1.Join();
    t2.Join();
}

private static void threadFunc(object objLock)
{
    lock (objLock)
    {
        Thread.Sleep(2000);
    }
}

분명히 lock으로 인해 지연이 있는데도 불구하고 ETW로는 저 상황을 못 잡아내는 경우가 종종 있습니다. 그러니까, ETW 이벤트를 100% 믿고 진단을 해서는 안 됩니다. 그래도 메서드 가로채기 등의 방법보다는 안전하므로 현재로서는 ETW를 이용하는 것이 가장 최선으로 보입니다.

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




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







[최초 등록일: ]
[최종 수정일: 7/8/2020]

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

비밀번호

댓글 작성자
 




... 151  152  [153]  154  155  156  157  158  159  160  161  162  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
1227정성태2/3/201229263.NET Framework: 299. 해당 어셈블리가 Debug 빌드인지, Release 빌드인지 알아내는 방법파일 다운로드1
1226정성태1/28/201270176.NET Framework: 298. 홀 펀칭(Hole Punching)을 이용한 Private IP 간 통신 - C# [15]파일 다운로드3
1225정성태1/24/201225784.NET Framework: 297. 특정 EXE 파일의 실행을 Internet Explorer처럼 "Protected Mode"로 실행하는 방법 [1]파일 다운로드1
1224정성태1/21/201237285개발 환경 구성: 139. 아마존 EC2에 새로 추가된 "1년 무료 Windows 서버 인스턴스"가 있다는데, 직접 만들어 볼까요? ^^ [11]
1223정성태1/20/201227301.NET Framework: 296. 괜찮은 문자열 해시함수? - 두 번째 이야기 [1]파일 다운로드1
1222정성태1/18/201235025.NET Framework: 295. 괜찮은 문자열 해시 함수? [4]파일 다운로드1
1221정성태1/17/201224012오류 유형: 147. System.Runtime.InteropServices.COMException (0x80005000)
1220정성태1/15/201224167.NET Framework: 294. Master web.config 파일을 수정하려면?파일 다운로드1
1219정성태1/15/201226569.NET Framework: 293. Microsoft PowerPoint 슬라이드를 HTML 파일로 ".files" 폴더 없이 저장하는 방법 (C# 코드)파일 다운로드1
1218정성태1/15/201239095.NET Framework: 292. RSACryptoServiceProvider의 공개키와 개인키 구분 [1]파일 다운로드2
1217정성태1/14/201241183.NET Framework: 291. .NET에서 WAV, MP3 파일 재생하는 방법 [1]파일 다운로드1
1216정성태1/14/201229893오류 유형: 146. Microsoft Visual C++ 재배포 패키지 - 설치 로그 남기는 방법 [1]
1215정성태1/9/201227455제니퍼 .NET: 20. 제니퍼 닷넷 적용 사례 (3) - '닷넷'이 문제일까? '닷넷 개발자'가 문제일까? [6]
1214정성태1/3/201224298제니퍼 .NET: 19. 제니퍼 닷넷 설치/제거 방법 - IIS
1213정성태12/31/201124222.NET Framework: 290. WCF - 접속된 클라이언트의 IP 주소 알아내는 방법 - 두 번째 이야기
1212정성태12/31/201124339오류 유형: 145. The trust relationship between this workstation and the primary domain failed.
1211정성태12/31/201129136.NET Framework: 289. WindowsFormsHost를 사용하는 XBAP 응용 프로그램파일 다운로드1
1210정성태12/30/201148106.NET Framework: 288. FFmpeg.exe를 이용한 C# 동영상 인코더 예제 [9]파일 다운로드1
1209정성태12/29/201122753개발 환경 구성: 138. BizTalk 2006 설치 방법
1208정성태12/28/201145723.NET Framework: 287. Excel Sheet를 WinForm에서 사용하는 방법 [8]파일 다운로드2
1207정성태12/26/201124998.NET Framework: 286. x86/x64로 구분된 코드를 포함하는 경우, 다중으로 어셈블리를 만들어야 할까요?파일 다운로드1
1206정성태12/25/201126013.NET Framework: 285. Shader 강좌와 함께 배워보는 XNA Framework (3) - 텍스처 매핑 예제파일 다운로드1
1205정성태12/25/201131703.NET Framework: 284. Thread 개체의 Interrupt와 Abort의 차이점파일 다운로드1
1204정성태12/22/201125202.NET Framework: 283. MEF를 ASP.NET에 성능 손실 없이 적용하려면? [7]
1203정성태12/21/201125570제니퍼 .NET: 18. MEF가 적용된 ASP.NET 웹 사이트를 제니퍼 닷넷으로 모니터링 해본 결과! [6]
1202정성태12/21/201125984오류 유형: 144. The database '...' cannot be opened because it is version 661.
... 151  152  [153]  154  155  156  157  158  159  160  161  162  163  164  165  ...