성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - ETW EventListener의 Keywords별 EventId에 따른 필터링 방법</h1> <p> 지난 글에서 EventListener를 상속한 클래스의 생성자에 인자를 전달해 EnableEvents까지 호출하는 방법을 설명했습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - ETW EventListener를 상속받았을 때 초기화 순서 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12616'>https://www.sysnet.pe.kr/2/0/12616</a> </pre> <br /> <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.tracing.eventlistener.enableevents'>EventListener.EnableEvents</a>는 ETW의 Keywords 범주를 지정해 관심 있는 이벤트를 선택하는 방법을 제공하는데요, 아쉽게도 Keywords에 속한 개별 EventId에 따른 필터링은 제공하지 않습니다.<br /> <br /> 여기서 재미있는 점은, EventId에 따른 <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.tracing.eventcommandeventargs.enableevent'>Enable</a>/<a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.tracing.eventcommandeventargs.disableevent'>Disable</a>을 특이하게도 <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.tracing.eventcommandeventargs'>EventCommandEventArgs</a> 타입에서 제공하긴 한다는 것입니다. 그리고 그 타입은 오직 <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.tracing.eventsource.eventcommandexecuted'>EventSource.EventCommandExecuted</a> 이벤트의 인자로만 얻을 수 있습니다. 따라서 전체적인 코딩 방식은 다음과 같은 과정을 거치게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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; <span style='color: blue; font-weight: bold'>this.EventSourceCreated += MyEventListener_EventSourceCreated;</span> } private void <span style='color: blue; font-weight: bold'>MyEventListener_EventSourceCreated</span>(object sender, EventSourceCreatedEventArgs e) { if (e.EventSource.Name == "Microsoft-Windows-DotNETRuntime") { _eventSource = e.EventSource; <span style='color: blue; font-weight: bold'>e.EventSource.EventCommandExecuted += EventSource_EventCommandExecuted;</span> EnableEvents(e.EventSource, EventLevel.Informational, (EventKeywords)this._filterKeyword); } } private void <span style='color: blue; font-weight: bold'>EventSource_EventCommandExecuted</span>(object sender, EventCommandEventArgs e) { // ExceptionCatchStart 유형의 이벤트만 발생하도록 필터링 처리 if (e.Command == EventCommand.Enable) { <span style='color: blue; font-weight: bold'>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);</span> } } 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, } </pre> <br /> 이렇게 만든 MyEventListener와 함께 다음의 Main 메서드를 실행하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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 { <span style='color: blue; font-weight: bold'>throw new ApplicationException("Exception-thrown");</span> } catch (Exception) { Console.WriteLine($"{DateTime.Now}: User-exception"); } } } </pre> <br /> 이제부터는 ExceptionCatchStart에 대한 OnEventWritten만 실행되는 것을 확인할 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 자, 그럼 여기에 EventId 목록을 알아내는 지난번 소스 코드를,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (2) 관리 코드 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12613'>https://www.sysnet.pe.kr/2/0/12613</a> </pre> <br /> 곁들여서, 내가 관심 있는 이벤트만 생성자에 전달해 OnEventWritten 콜백을 받을 수 있게 만들 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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 <a target='tab' href='https://www.sysnet.pe.kr/2/0/12606'>PrimitiveEnum</a>[] { <span style='color: blue; font-weight: bold'>ExceptionEvent.ExceptionCatchStart, ExceptionEvent.ExceptionCatchStop</span> }); } // ...[생략]... } // ...[생략]... internal class MyEventListener : EventListener { // <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/framework/performance/clr-etw-keywords-and-levels'>https://learn.microsoft.com/en-us/dotnet/framework/performance/clr-etw-keywords-and-levels</a> EventSource _eventSource; long _filterKeyword; int[] _filterEventId; public MyEventListener(DotnetRuntimeKeyword filterKeyword, PrimitiveEnum[] filterEventId = null) { this._filterKeyword = (long)filterKeyword; <span style='color: blue; font-weight: bold'>this._filterEventId = filterEventId?.Select((elem) => (int)elem).ToArray();</span> this.EventSourceCreated += MyEventListener_EventSourceCreated; } private void MyEventListener_EventSourceCreated(object sender, EventSourceCreatedEventArgs e) { if (e.EventSource.Name == "Microsoft-Windows-DotNETRuntime") { _eventSource = e.EventSource; <span style='color: blue; font-weight: bold'>e.EventSource.EventCommandExecuted += EventSource_EventCommandExecuted;</span> EnableEvents(e.EventSource, EventLevel.Informational, (EventKeywords)this._filterKeyword); } } private void EventSource_EventCommandExecuted(object sender, EventCommandEventArgs e) { if (e.Command == EventCommand.Enable) { <span style='color: blue; font-weight: bold'>FilterEventId(_eventSource, e);</span> } } private void FilterEventId(EventSource eventSource, EventCommandEventArgs e) { // ...[생략]... (long keyword, int eventId) = GetKeywordAndEventId(descriptor); if (((long)keyword & this._filterKeyword) == this._filterKeyword) { if (_filterEventId != null) { <span style='color: blue; font-weight: bold'>if (_filterEventId.Contains(eventId) == false) { e.DisableEvent(eventId); }</span> } } } // ...[생략]... protected override void OnEventWritten(EventWrittenEventArgs eventData) { if (((long)eventData.Keywords & _filterKeyword) == _filterKeyword) { Console.WriteLine($"{DateTime.Now}: {eventData.EventName}"); } } } </pre> <br /> 실행해 보면, { ExceptionEvent.ExceptionCatchStart, ExceptionEvent.ExceptionCatchStop } 2개의 이벤트만 받겠다고 지정했으므로 다른 Exception 관련 이벤트는 발생하지 않게 됩니다.<br /> <br /> 하지만 Keyword 수준의 필터링과는 달리 EventId의 필터링으로 인한 콜백 메서드의 최소화는 의미 있는 수준의 성능을 내지는 않습니다. 왜냐하면, EventId의 필터링이 되기까지 이미 꽤나 많은 ETW 관련 코드들이 수행되기 때문으로 단지 OnEventWritten 메서드 호출만을 회피하는 정도입니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1756&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2122
(왼쪽의 숫자를 입력해야 합니다.)