Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)

C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (2) 관리 코드

지난 글에서,

C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (1) PInvoke
; https://www.sysnet.pe.kr/2/0/12612

PInvoke를 활용해 ETW의 Keyword에 속한 이벤트 목록을 구했는데요, 이게 좀 아쉬운 면이 있다면 사실 "C# - (.NET Core 2.2부터 가능한) 프로세스 내부에서 CLR ETW 이벤트 수신"은 제목에서처럼 .NET Framework 대상이 아닌 .NET Core 2.2부터 가능한 기능입니다. 그런 면에서 리눅스를 대상으로 작성될 가능성도 적지 않은데 그런 상황에서 PInvoke를 사용하는 것은 바람직한 선택은 아닙니다.

여기서 더욱 아쉬운 면이 있다면, (지난 글에도 언급했지만) 순수하게 C# 코드만으로는 공식적으로 Event ID를 구할 수 있는 방법이 없다는 것인데요, 그래서 안정적인 코드를 사용해야 한다면 미리 이벤트 ID 목록을 알아내 하드 코딩하는 것이 현재로서는 최선입니다.




그나마 다행이라면, "비공식적인 방법"으로는 Event ID 목록을 구할 수는 있습니다.

왜냐하면, EventSource 타입은 내부에 private 필드로는 CLR-ETW.man에 지정된 이벤트 목록을 System.Diagnostics.Tracing.EventSource.EventMetadata 타입의 배열로 들고 있기 때문입니다. 그래서 다음과 같은 절차의 Reflection 연산을 통해 이 목록을 구할 수 있습니다. ^^

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

    public const int GCKEYWORD = 0x00000001;
    public const int EXCEPTIONKEYWORD = 0x00008000;

    protected override void OnEventSourceCreated(EventSource eventSource)
    {
        if (eventSource.Name == "Microsoft-Windows-DotNETRuntime")
        {
            base.OnEventSourceCreated(eventSource);
            EnableEvents(eventSource, EventLevel.Informational, (EventKeywords)EXCEPTIONKEYWORD);

            ListEventID(eventSource, EXCEPTIONKEYWORD);
            return;
        }
    }

    private void ListEventID(EventSource eventSource, int filterKeyword)
    {
        Type type = eventSource.GetType();
        FieldInfo fi = type.GetField("m_eventData", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        if (fi != null)
        {
            object objMetadataArray = fi.GetValue(eventSource); // System.Diagnostics.Tracing.EventSource.EventMetadata []
            System.Collections.IEnumerable enumerator = objMetadataArray as System.Collections.IEnumerable;

            foreach (object objItem in enumerator)
            {
                object descriptor = GetDescriptor(objItem);
                if (descriptor == null)
                {
                    continue;
                }

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

                if (((int)keyword & filterKeyword) == filterKeyword)
                {
                    Console.WriteLine($"{filterKeyword} == {eventId}");
                }
            }
        }
    }

    private (long, int) GetKeywordAndEventId(object descriptor, out int eventId)
    {
        object keyword = GetPropertyReflect(descriptor, "Keywords");
        object eventValue = GetPropertyReflect(descriptor, "EventId");

        if (keyword == null || eventValue == null)
        {
            return (0, 0);
        }

        return ((long)keyword, (int)eventValue);
    }

    private object GetDescriptor(object objItem)
    {
        return GetFieldReflect(objItem, "Descriptor");
    }

    object GetPropertyReflect(object obj, string propertyName)
    {
        Type type = obj.GetType();
        PropertyInfo pi = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
        if (pi != null)
        {
            return pi.GetValue(obj);
        }

        return null;
    }

    object GetFieldReflect(object obj, string fieldName)
    {
        Type type = obj.GetType();
        FieldInfo fi = type.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance);
        if (fi != null)
        {
            return fi.GetValue(obj);
        }

        return null;
    }

    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        Console.WriteLine($"{DateTime.Now}: {eventData.EventName}");
    }
}

/* 출력 결과
32768 == 80
32768 == 250
32768 == 251
32768 == 252
32768 == 253
32768 == 254
32768 == 255
32768 == 256
*/

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





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

[연관 글]






[최초 등록일: ]
[최종 수정일: 4/23/2021]

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

비밀번호

댓글 작성자
 




... 61  62  63  64  65  66  67  68  69  70  71  [72]  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
11847정성태3/17/201915204Linux: 7. 리눅스 C/C++ - 공유 라이브러리 동적 로딩 후 export 함수 사용 방법파일 다운로드1
11846정성태3/15/201913576Linux: 6. getenv, setenv가 언어/운영체제마다 호환이 안 되는 문제
11845정성태3/15/201914296Linux: 5. Linux 응용 프로그램의 (C++) so 의존성 줄이기(ReleaseMinDependency) [3]
11844정성태3/14/201915124개발 환경 구성: 434. Visual Studio 2019 - 리눅스 프로젝트를 이용한 공유/실행(so/out) 프로그램 개발 환경 설정 [1]파일 다운로드1
11843정성태3/14/201910860기타: 75. MSDN 웹 사이트를 기본으로 영문 페이지로 열고 싶다면?
11842정성태3/13/201910269개발 환경 구성: 433. 마이크로소프트의 CoreCLR 프로파일러 예제를 Visual Studio CMake로 빌드하는 방법 [1]파일 다운로드1
11841정성태3/13/201910236VS.NET IDE: 132. Visual Studio 2019 - CMake의 컴파일러를 기본 g++에서 clang++로 변경
11840정성태3/13/201911339오류 유형: 526. 윈도우 10 Ubuntu App 환경에서는 USB 외장 하드 접근 불가
11839정성태3/12/201914109디버깅 기술: 124. .NET Core 웹 앱을 호스팅하는 Azure App Services의 프로세스 메모리 덤프 및 windbg 분석 개요 [3]
11838정성태3/7/201916858.NET Framework: 811. (번역글) .NET Internals Cookbook Part 1 - Exceptions, filters and corrupted processes [1]파일 다운로드1
11837정성태3/6/201926611기타: 74. 도서: 시작하세요! C# 7.3 프로그래밍 [10]
11836정성태3/5/201914414오류 유형: 525. Visual Studio 2019 Preview 4/RC - C# 8.0 Missing compiler required member 'System.Range..ctor' [1]
11835정성태3/5/201914189.NET Framework: 810. C# 8.0의 Index/Range 연산자를 .NET Framework에서 사용하는 방법 및 비동기 스트림의 컴파일 방법 [3]파일 다운로드1
11834정성태3/4/201913072개발 환경 구성: 432. Visual Studio 없이 최신 C# (8.0) 컴파일러를 사용하는 방법
11833정성태3/4/201913837개발 환경 구성: 431. Visual Studio 2019 - CMake를 이용한 공유/실행(so/out) 리눅스 프로젝트 설정파일 다운로드1
11832정성태3/4/201910871오류 유형: 524. Visual Studio CMake - rsync: connection unexpectedly closed
11831정성태3/4/201910458오류 유형: 523. Visual Studio 2019 - 새 창으로 뜬 윈도우를 닫을 때 비정상 종료
11830정성태2/26/201910244오류 유형: 522. 이벤트 로그 - Error opening event log file State. Log will not be processed. Return code from OpenEventLog is 87.
11829정성태2/26/201912145개발 환경 구성: 430. 마이크로소프트의 CoreCLR 프로파일러 예제 빌드 방법 - 리눅스 환경 [1]
11828정성태2/26/201918554개발 환경 구성: 429. Component Services 관리자의 RuntimeBroker 설정이 2개 있는 경우 [8]
11827정성태2/26/201912462오류 유형: 521. Visual Studio - Could not start the 'rsync' command on the remote host, please install it using your system package manager.
11826정성태2/26/201912344오류 유형: 520. 우분투에 .NET Core SDK 설치 시 패키지 의존성 오류
11825정성태2/25/201917209개발 환경 구성: 428. Visual Studio 2019 - CMake를 이용한 리눅스 빌드 환경 설정 [1]
11824정성태2/25/201912017오류 유형: 519. The SNMP Service encountered an error while accessing the registry key SYSTEM\CurrentControlSet\Services\SNMP\Parameters\TrapConfiguration. [1]
11823정성태2/21/201913506오류 유형: 518. IIS 관리 콘솔이 뜨지 않는 문제
11822정성태2/20/201911635오류 유형: 517. docker에 설치한 MongoDB 서버로 연결이 안 되는 경우
... 61  62  63  64  65  66  67  68  69  70  71  [72]  73  74  75  ...