Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

C# - ETW EventListener의 Keywords별 EventId에 따른 필터링 방법

지난 글에서 EventListener를 상속한 클래스의 생성자에 인자를 전달해 EnableEvents까지 호출하는 방법을 설명했습니다.

C# - ETW EventListener를 상속받았을 때 초기화 순서
; https://www.sysnet.pe.kr/2/0/12616

EventListener.EnableEvents는 ETW의 Keywords 범주를 지정해 관심 있는 이벤트를 선택하는 방법을 제공하는데요, 아쉽게도 Keywords에 속한 개별 EventId에 따른 필터링은 제공하지 않습니다.

여기서 재미있는 점은, EventId에 따른 Enable/Disable을 특이하게도 EventCommandEventArgs 타입에서 제공하긴 한다는 것입니다. 그리고 그 타입은 오직 EventSource.EventCommandExecuted 이벤트의 인자로만 얻을 수 있습니다. 따라서 전체적인 코딩 방식은 다음과 같은 과정을 거치게 됩니다.

using System;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Reflection;

class Program
{
    static MyEventListener _listener;

    static Program()
    {
        _listener = new MyEventListener(DotnetRuntimeKeyword.EXCEPTIONKEYWORD);
    }

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

public enum DotnetRuntimeKeyword : long
{
    GCKEYWORD = 0x01,
    EXCEPTIONKEYWORD = 0x00008000
}

internal class MyEventListener : EventListener
{
    EventSource _eventSource;
    long _filterKeyword;

    public MyEventListener(DotnetRuntimeKeyword filterKeyword)
    {
        this._filterKeyword = (long)filterKeyword;
        this.EventSourceCreated += MyEventListener_EventSourceCreated;
    }

    private void MyEventListener_EventSourceCreated(object sender, EventSourceCreatedEventArgs e)
    {
        if (e.EventSource.Name == "Microsoft-Windows-DotNETRuntime")
        {
            _eventSource = e.EventSource;
            e.EventSource.EventCommandExecuted += EventSource_EventCommandExecuted;
            EnableEvents(e.EventSource, EventLevel.Informational, (EventKeywords)this._filterKeyword);
        }
    }

    private void EventSource_EventCommandExecuted(object sender, EventCommandEventArgs e)
    {
        // ExceptionCatchStart 유형의 이벤트만 발생하도록 필터링 처리
        if (e.Command == EventCommand.Enable)
        {
            e.DisableEvent((int)ExceptionEvent.ExceptionThrown_V1);
            // e.DisableEvent((int)ExceptionEvent.ExceptionCatchStart);
            e.DisableEvent((int)ExceptionEvent.ExceptionCatchStop);
            e.DisableEvent((int)ExceptionEvent.ExceptionFinallyStart);
            e.DisableEvent((int)ExceptionEvent.ExceptionFinallyStop);
            e.DisableEvent((int)ExceptionEvent.ExceptionFilterStart);
            e.DisableEvent((int)ExceptionEvent.ExceptionFilterStop);
            e.DisableEvent((int)ExceptionEvent.ExceptionThrownStop);
        }
    }

    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        if (((long)eventData.Keywords & _filterKeyword) == _filterKeyword)
        {
            Console.WriteLine($"{DateTime.Now}: {eventData.EventName}");
        }
    }
}

public enum ExceptionEvent
{
    ExceptionThrown_V1 = 80,
    ExceptionCatchStart = 250,
    ExceptionCatchStop = 251,
    ExceptionFinallyStart = 252,
    ExceptionFinallyStop = 253,
    ExceptionFilterStart = 254,
    ExceptionFilterStop = 255,
    ExceptionThrownStop = 256,
}

이렇게 만든 MyEventListener와 함께 다음의 Main 메서드를 실행하면,

static void Main(string[] args)
{
    Console.WriteLine(Environment.Version);
    Console.WriteLine("In-proc ETW Enabled on .NET Core 2.2 or later");

    while (true)
    {
        Console.ReadLine();

        try
        {
            throw new ApplicationException("Exception-thrown");
        }
        catch (Exception)
        {
            Console.WriteLine($"{DateTime.Now}: User-exception");
        }
    }
}

이제부터는 ExceptionCatchStart에 대한 OnEventWritten만 실행되는 것을 확인할 수 있습니다.




자, 그럼 여기에 EventId 목록을 알아내는 지난번 소스 코드를,

C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (2) 관리 코드
; https://www.sysnet.pe.kr/2/0/12613

곁들여서, 내가 관심 있는 이벤트만 생성자에 전달해 OnEventWritten 콜백을 받을 수 있게 만들 수 있습니다.

using System;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Reflection;

class Program
{
    static MyEventListener _listener;

    static Program()
    {
        _listener = new MyEventListener(DotnetRuntimeKeyword.EXCEPTIONKEYWORD,
            new PrimitiveEnum[] { ExceptionEvent.ExceptionCatchStart, ExceptionEvent.ExceptionCatchStop });
    }

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

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

internal class MyEventListener : EventListener
{
    // https://learn.microsoft.com/en-us/dotnet/framework/performance/clr-etw-keywords-and-levels

    EventSource _eventSource;
    long _filterKeyword;
    int[] _filterEventId;

    public MyEventListener(DotnetRuntimeKeyword filterKeyword, PrimitiveEnum[] filterEventId = null)
    {
        this._filterKeyword = (long)filterKeyword;
        this._filterEventId = filterEventId?.Select((elem) => (int)elem).ToArray();
        this.EventSourceCreated += MyEventListener_EventSourceCreated;
    }

    private void MyEventListener_EventSourceCreated(object sender, EventSourceCreatedEventArgs e)
    {
        if (e.EventSource.Name == "Microsoft-Windows-DotNETRuntime")
        {
            _eventSource = e.EventSource;
            e.EventSource.EventCommandExecuted += EventSource_EventCommandExecuted;
            EnableEvents(e.EventSource, EventLevel.Informational, (EventKeywords)this._filterKeyword);
        }
    }

    private void EventSource_EventCommandExecuted(object sender, EventCommandEventArgs e)
    {
        if (e.Command == EventCommand.Enable)
        {
            FilterEventId(_eventSource, e);
        }
    }

    private void FilterEventId(EventSource eventSource, EventCommandEventArgs e)
    {
        // ...[생략]...

        (long keyword, int eventId) = GetKeywordAndEventId(descriptor);

        if (((long)keyword & this._filterKeyword) == this._filterKeyword)
        {
            if (_filterEventId != null)
            {
                if (_filterEventId.Contains(eventId) == false)
                {
                    e.DisableEvent(eventId);
                }
            }
        }
    }

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

    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        if (((long)eventData.Keywords & _filterKeyword) == _filterKeyword)
        {
            Console.WriteLine($"{DateTime.Now}: {eventData.EventName}");
        }
    }
}

실행해 보면, { ExceptionEvent.ExceptionCatchStart, ExceptionEvent.ExceptionCatchStop } 2개의 이벤트만 받겠다고 지정했으므로 다른 Exception 관련 이벤트는 발생하지 않게 됩니다.

하지만 Keyword 수준의 필터링과는 달리 EventId의 필터링으로 인한 콜백 메서드의 최소화는 의미 있는 수준의 성능을 내지는 않습니다. 왜냐하면, EventId의 필터링이 되기까지 이미 꽤나 많은 ETW 관련 코드들이 수행되기 때문으로 단지 OnEventWritten 메서드 호출만을 회피하는 정도입니다.

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




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







[최초 등록일: ]
[최종 수정일: 11/7/2023]

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)
1210정성태12/30/201149440.NET Framework: 288. FFmpeg.exe를 이용한 C# 동영상 인코더 예제 [9]파일 다운로드1
1209정성태12/29/201124123개발 환경 구성: 138. BizTalk 2006 설치 방법
1208정성태12/28/201147655.NET Framework: 287. Excel Sheet를 WinForm에서 사용하는 방법 [8]파일 다운로드2
1207정성태12/26/201126445.NET Framework: 286. x86/x64로 구분된 코드를 포함하는 경우, 다중으로 어셈블리를 만들어야 할까요?파일 다운로드1
1206정성태12/25/201127242.NET Framework: 285. Shader 강좌와 함께 배워보는 XNA Framework (3) - 텍스처 매핑 예제파일 다운로드1
1205정성태12/25/201133078.NET Framework: 284. Thread 개체의 Interrupt와 Abort의 차이점파일 다운로드1
1204정성태12/22/201126662.NET Framework: 283. MEF를 ASP.NET에 성능 손실 없이 적용하려면? [7]
1203정성태12/21/201127051제니퍼 .NET: 18. MEF가 적용된 ASP.NET 웹 사이트를 제니퍼 닷넷으로 모니터링 해본 결과! [6]
1202정성태12/21/201127433오류 유형: 144. The database '...' cannot be opened because it is version 661.
1201정성태12/14/201142470디버깅 기술: 47. .NET Reflector를 이용한 "소스 코드가 없는" 어셈블리 디버깅 [4]
1200정성태12/11/201128266디버깅 기술: 46. Windbg 확장 DLL 만들기 (2) - Debugger Extension API 사용파일 다운로드1
1199정성태12/11/201129544VC++: 55. JNI DLL 컴파일 시 x86과 x64의 Export된 함수의 이름이 왜 다를까요? [2]파일 다운로드1
1198정성태12/9/201133391디버깅 기술: 45. Windbg 확장 DLL 만들기 (1) - 스레드를 강제 종료시키는 명령어 [2]파일 다운로드1
1197정성태12/9/201131511.NET Framework: 282. Shader 강좌와 함께 배워보는 XNA Framework (2) - RenderMonkey의 Shader/Model 파일 연동파일 다운로드2
1196정성태12/9/201134927.NET Framework: 281. Shader 강좌와 함께 배워보는 XNA Framework (1) - 기초 프로그램 구조 [3]파일 다운로드2
1195정성태12/8/201149184오류 유형: 143. DXSDK_Jun10.exe 설치 시 "Error Code: S1023" 오류 해결하는 방법 [4]
1194정성태12/8/201137024개발 환경 구성: 137. Visual C++ 런타임 구성요소에 대한 디버그 버전 설치하는 방법
1193정성태12/8/201124025오류 유형: 142. Windows Phone SDK 7.1 설치 시 Expression Blend 제거를 요구하는 경우
1192정성태12/8/201127041개발 환경 구성: 136. Windows 7 SP1의 IIS에서 사용자 프로파일을 로드하는 방법
1191정성태12/6/201128110.NET Framework: 280. MVC3에서 JavaScriptSerializer 재정의하는 방법파일 다운로드1
1190정성태12/6/201131266오류 유형: 141. Visual C++ 컴파일 오류 - error C2275: 'xxxxx' : illegal use of this type as an expression [1]
1189정성태12/6/201138445VS.NET IDE: 70. Visual Studio에서 프로젝트 로드가 안된다면?
1188정성태12/3/201127471개발 환경 구성: 135. 마이크로소프트 TFS 호스팅 서비스 - Preview [3]
1187정성태12/2/201132232개발 환경 구성: 134. Robocopy 오류 및 종료 코드
1186정성태12/1/201134133.NET Framework: 279. WPF - 그리기 성능 및 Blurring 문제파일 다운로드1
1185정성태11/29/201124367.NET Framework: 278. WPF - Content의 Changed 이벤트에 해당하는게 뭔가요?파일 다운로드1
... 151  152  153  154  [155]  156  157  158  159  160  161  162  163  164  165  ...