Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
(시리즈 글이 11개 있습니다.)
.NET Framework: 475. ETW(Event Tracing for Windows)를 C#에서 사용하는 방법
; https://www.sysnet.pe.kr/2/0/1804

.NET Framework: 483. 코드로 살펴 보는 ETW의 활성화 시점
; https://www.sysnet.pe.kr/2/0/1815

.NET Framework: 915. ETW(Event Tracing for Windows)를 이용한 닷넷 프로그램의 내부 이벤트 활용
; https://www.sysnet.pe.kr/2/0/12244

.NET Framework: 923. C# - ETW(Event Tracing for Windows)를 이용한 Finalizer 실행 감시
; https://www.sysnet.pe.kr/2/0/12255

.NET Framework: 932. C# - ETW 관련 Win32 API 사용 예제 코드 (1)
; https://www.sysnet.pe.kr/2/0/12292

.NET Framework: 933. C# - ETW 관련 Win32 API 사용 예제 코드 (2) NT Kernel Logger
; https://www.sysnet.pe.kr/2/0/12296

.NET Framework: 934. C# - ETW 관련 Win32 API 사용 예제 코드 (3) ETW Consumer 구현
; https://www.sysnet.pe.kr/2/0/12299

.NET Framework: 935. C# - ETW 관련 Win32 API 사용 예제 코드 (4) CLR ETW Consumer
; https://www.sysnet.pe.kr/2/0/12300

.NET Framework: 936. C# - ETW 관련 Win32 API 사용 예제 코드 (5) - Private Logger
; https://www.sysnet.pe.kr/2/0/12302

개발 환경 구성: 504. ETW - 닷넷 프레임워크 기반의 응용 프로그램을 위한 명령행 도구 etrace 소개
; https://www.sysnet.pe.kr/2/0/12303

.NET Framework: 994. C# - (.NET Core 2.2부터 가능한) 프로세스 내부에서 CLR ETW 이벤트 수신
; https://www.sysnet.pe.kr/2/0/12474




C# - ETW 관련 Win32 API 사용 예제 코드 (4) CLR ETW Consumer

지난 글에서,

C# - ETW 관련 Win32 API 사용 예제 코드 (1)
; https://www.sysnet.pe.kr/2/0/12292

C# - ETW 관련 Win32 API 사용 예제 코드 (2) NT Kernel Logger
; https://www.sysnet.pe.kr/2/0/12296

C# - ETW 관련 Win32 API 사용 예제 코드 (3) ETW Consumer 구현
; https://www.sysnet.pe.kr/2/0/12299

실습한 내용을 바탕으로 이제 CLR 관련 ETW 이벤트를,

ETW(Event Tracing for Windows)를 이용한 닷넷 프로그램의 내부 이벤트 활용
; https://www.sysnet.pe.kr/2/0/12244

Win32 API를 이용한 방법으로 재구성해 보겠습니다.




우선, CLR을 위한 ETW Provider를 찾아야겠지요. ETW Provider 목록에 보면 당연히 "ETW(Event Tracing for Windows)를 이용한 닷넷 프로그램의 내부 이벤트 활용" 글에서 다룬 "Microsoft-Windows-DotNETRuntime"과 그것의 GUID 값인 "{e13c0d23-ccbc-4e12-931b-d9cc2eee27e4}"도 나옵니다.

CLR ETW Providers
; https://learn.microsoft.com/en-us/dotnet/framework/performance/clr-etw-providers

따라서, 지난 글의 소스 코드에서 단순히 ETW Provider GUID 값만 바꿔 다음과 같이 구현할 수 있습니다.

using Microsoft.Samples.Eventing.Interop;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static bool _disposed = false;
        static int _processId = 0;

        static void Main(string[] _)
        {
            _processId = Process.GetCurrentProcess().Id;

            string sessionName = "clrETWSession";
            Guid clrProvider = new Guid("{e13c0d23-ccbc-4e12-931b-d9cc2eee27e4}");
            int result = 0;

            ulong traceHandle = 0;
            ulong sessionHandle = 0;

            try
            {
                if (EtwInterop.IsSessionActive(sessionName, out var _) == true)
                {
                }
                else
                {
                    EventTraceProperties prop = new EventTraceProperties(true, sessionName);
                    result = NativeMethods.StartTrace(out sessionHandle, sessionName, ref prop);
                    Console.WriteLine(result);

                    if (result == 0)
                    {
                        ENABLE_TRACE_PARAMETERS enableParameters = new ENABLE_TRACE_PARAMETERS();
                        enableParameters.Version = 1;
                        enableParameters.EnableProperty = (uint)EventEnableProperty.Sid;

                        result = NativeMethods.EnableTraceEx2(sessionHandle, ref clrProvider,
                            NativeMethods.EVENT_CONTROL_CODE_ENABLE_PROVIDER, (byte)TraceEventLevel.Informational,
                            0, 0, 0, ref enableParameters);

                        Console.WriteLine(result);
                    }
                }

                if (result == 0)
                {
                    EventTraceLogfile logFile = new EventTraceLogfile();
                    logFile.LoggerName = sessionName;
                    logFile.EventRecordCallback = EventRecordCallback;

                    logFile.ProcessTraceMode = NativeMethods.PROCESS_TRACE_MODE_EVENT_RECORD | NativeMethods.PROCESS_TRACE_MODE_REAL_TIME
                        | NativeMethods.PROCESS_TRACE_MODE_RAW_TIMESTAMP;
                    traceHandle = NativeMethods.OpenTrace(ref logFile);
                }

                if (traceHandle != 0)
                {
                    Thread t = new Thread((ThreadStart)(
                    () =>
                    {
                        Console.WriteLine("PrcoessTrace: " + EtwInterop.ProcessTrace(traceHandle));
                    }));

                    t.Start();

                    Console.WriteLine("Press ENTER key to exit...");
                    Console.ReadLine();

                    // Console.WriteLine("Disabled: " + EtwInterop.DisableProvider(sessionHandle, ref auditApiCalls));
                    Console.WriteLine("Closed: " + (NativeMethods.CloseTrace(traceHandle) == 0));

                    t.Join();
                }
            }
            finally
            {
                _disposed = true;
                EtwInterop.CloseActiveSession(sessionName);
            }
        }

        static private void EventRecordCallback([In] ref EventRecord eventRecord)
        {
            if (_processId != eventRecord.EventHeader.ProcessId)
            {
                return;
            }
            
            Console.WriteLine(DateTime.Now + ": " + eventRecord.EventHeader.ProcessId);
        }
    }
}




위와 같이 실행하면 현재 프로세스에서 발생하는 CLR 이벤트를 종류에 상관없이 모두 콜백을 받는데요, "ETW(Event Tracing for Windows)를 이용한 닷넷 프로그램의 내부 이벤트 활용" 글에서는 이벤트 종류를 선택할 수 있는 코드가,

userSession.EnableProvider(
    ClrTraceEventParser.ProviderGuid,
    TraceEventLevel.Verbose,
    (ulong)(
    ClrTraceEventParser.Keywords.Contention |  // thread contention timing
    ClrTraceEventParser.Keywords.Threading |   // threadpool events
    ClrTraceEventParser.Keywords.Exception |   // get the first chance exceptions
    ClrTraceEventParser.Keywords.GCHeapAndTypeNames | 
    ClrTraceEventParser.Keywords.Type | // for finalizer and exceptions type names
    ClrTraceEventParser.Keywords.GC     // garbage collector details
    )
);

있었으므로 이 부분에 대해서도 좀 더 봐야 할 필요가 있습니다. 우선, 위의 코드에서 "Keywords"에 담긴 값들을 ETW 이벤트에 대한 필터링 조건으로 주고 있는데 역어셈블을 해 보면 다음과 같이 값을 확인할 수 있습니다.

[Flags]
public enum Keywords : long
{
    None = 0L,
    All = -65L,
    GC = 1L,
    GCHandle = 2L,
    Binder = 4L,
    Loader = 8L,
    Jit = 0x10L,
    NGen = 0x20L,
    StartEnumeration = 0x40L,
    StopEnumeration = 0x80L,
    Security = 0x400L,
    AppDomainResourceManagement = 0x800L,
    JitTracing = 0x1000L,
    Interop = 0x2000L,
    Contention = 0x4000L,
    Exception = 0x8000L,
    Threading = 0x10000L,
    JittedMethodILToNativeMap = 0x20000L,
    OverrideAndSuppressNGenEvents = 0x40000L,
    Type = 0x80000L,
    GCHeapDump = 0x100000L,
    GCSampledObjectAllocationHigh = 0x200000L,
    GCHeapSurvivalAndMovement = 0x400000L,
    GCHeapCollect = 0x800000L,
    GCHeapAndTypeNames = 0x1000000L,
    GCSampledObjectAllocationLow = 0x2000000L,
    GCAllObjectAllocation = 0x2200000L,
    SupressNGen = 0x40000L,
    PerfTrack = 0x20000000L,
    Stack = 0x40000000L,
    ThreadTransfer = 0x80000000L,
    Debugger = 0x100000000L,
    Monitoring = 0x200000000L,
    Codesymbols = 0x400000000L,
    Compilation = 0x1000000000L,
    CompilationDiagnostic = 0x2000000000L,
    Default = 0x14c14fccbdL,
    JITSymbols = 0x60098L,
    GCHeapSnapshot = 0x1980001L
}

그런데, 이 값들은 Microsoft.Diagnostics.Tracing.TraceEvent 라이브러리 제작자가 임의로 결정한 것이 아니고 ETW manifest 파일, 즉 공급자가 정해 놓은 것입니다.

C:\Windows\Microsoft.NET\Framework\v4.0.30319\CLR-ETW.man
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\CLR-ETW.man

실제로 CLR-ETW.man 파일을 보면 다음과 같은 내용들을 볼 수 있습니다.

...[생략]...

<keywords>
    <keyword name="GCKeyword" mask="0x1" message="$(string.RuntimePublisher.GCKeywordMessage)" symbol="CLR_GC_KEYWORD"/>
    <keyword name="GCHandleKeyword" mask="0x2" message="$(string.RuntimePublisher.GCHandleKeywordMessage)" symbol="CLR_GCHANDLE_KEYWORD"/>
    <keyword name="FusionKeyword" mask="0x4" message="$(string.RuntimePublisher.FusionKeywordMessage)" symbol="CLR_FUSION_KEYWORD"/>
    <keyword name="LoaderKeyword" mask="0x8" message="$(string.RuntimePublisher.LoaderKeywordMessage)" symbol="CLR_LOADER_KEYWORD"/>
    <keyword name="JitKeyword" mask="0x10" message="$(string.RuntimePublisher.JitKeywordMessage)" symbol="CLR_JIT_KEYWORD"/>
    <keyword name="NGenKeyword" mask="0x20" message="$(string.RuntimePublisher.NGenKeywordMessage)" symbol="CLR_NGEN_KEYWORD"/>
    <keyword name="StartEnumerationKeyword" mask="0x40" message="$(string.RuntimePublisher.StartEnumerationKeywordMessage)" symbol="CLR_STARTENUMERATION_KEYWORD"/>
    <keyword name="EndEnumerationKeyword" mask="0x80" message="$(string.RuntimePublisher.EndEnumerationKeywordMessage)" symbol="CLR_ENDENUMERATION_KEYWORD"/>
    <!-- Keyword mask 0x100 is now defunct -->
    <!-- Keyword mask 0x200 is now defunct -->
    <keyword name="SecurityKeyword" mask="0x400" message="$(string.RuntimePublisher.SecurityKeywordMessage)" symbol="CLR_SECURITY_KEYWORD"/>
    <keyword name="AppDomainResourceManagementKeyword" mask="0x800" message="$(string.RuntimePublisher.AppDomainResourceManagementKeywordMessage)" symbol="CLR_APPDOMAINRESOURCEMANAGEMENT_KEYWORD"/>
    <keyword name="JitTracingKeyword" mask="0x1000" message="$(string.RuntimePublisher.JitTracingKeywordMessage)" symbol="CLR_JITTRACING_KEYWORD"/>
    <keyword name="InteropKeyword" mask="0x2000" message="$(string.RuntimePublisher.InteropKeywordMessage)" symbol="CLR_INTEROP_KEYWORD"/>
    <keyword name="ContentionKeyword" mask="0x4000" message="$(string.RuntimePublisher.ContentionKeywordMessage)" symbol="CLR_CONTENTION_KEYWORD"/>
    <keyword name="ExceptionKeyword" mask="0x8000" message="$(string.RuntimePublisher.ExceptionKeywordMessage)" symbol="CLR_EXCEPTION_KEYWORD"/>
    <keyword name="ThreadingKeyword" mask="0x10000" message="$(string.RuntimePublisher.ThreadingKeywordMessage)" symbol="CLR_THREADING_KEYWORD"/>
    <keyword name="JittedMethodILToNativeMapKeyword" mask="0x20000" message="$(string.RuntimePublisher.JittedMethodILToNativeMapKeywordMessage)" symbol="CLR_JITTEDMETHODILTONATIVEMAP_KEYWORD"/>
    <keyword name="OverrideAndSuppressNGenEventsKeyword" mask="0x40000" message="$(string.RuntimePublisher.OverrideAndSuppressNGenEventsKeywordMessage)" symbol="CLR_OVERRIDEANDSUPPRESSNGENEVENTS_KEYWORD"/>
    <keyword name="TypeKeyword" mask="0x80000" message="$(string.RuntimePublisher.TypeKeywordMessage)" symbol="CLR_TYPE_KEYWORD"/>
    <keyword name="GCHeapDumpKeyword" mask="0x100000" message="$(string.RuntimePublisher.GCHeapDumpKeywordMessage)" symbol="CLR_GCHEAPDUMP_KEYWORD"/>
    <keyword name="GCSampledObjectAllocationHighKeyword" mask="0x200000" message="$(string.RuntimePublisher.GCSampledObjectAllocationHighKeywordMessage)" symbol="CLR_GCHEAPALLOCHIGH_KEYWORD"/>
    <keyword name="GCHeapSurvivalAndMovementKeyword" mask="0x400000" message="$(string.RuntimePublisher.GCHeapSurvivalAndMovementKeywordMessage)" symbol="CLR_GCHEAPSURVIVALANDMOVEMENT_KEYWORD"/>
    <keyword name="GCHeapCollectKeyword" mask="0x800000" message="$(string.RuntimePublisher.GCHeapCollectKeyword)" symbol="CLR_GCHEAPCOLLECT_KEYWORD"/>
    <keyword name="GCHeapAndTypeNamesKeyword" mask="0x1000000" message="$(string.RuntimePublisher.GCHeapAndTypeNamesKeyword)" symbol="CLR_GCHEAPANDTYPENAMES_KEYWORD"/>
    <keyword name="GCSampledObjectAllocationLowKeyword" mask="0x2000000" message="$(string.RuntimePublisher.GCSampledObjectAllocationLowKeywordMessage)" symbol="CLR_GCHEAPALLOCLOW_KEYWORD"/>
    <keyword name="PerfTrackKeyword" mask="0x20000000" message="$(string.RuntimePublisher.PerfTrackKeywordMessage)" symbol="CLR_PERFTRACK_KEYWORD"/>
    <keyword name="StackKeyword" mask="0x40000000" message="$(string.RuntimePublisher.StackKeywordMessage)" symbol="CLR_STACK_KEYWORD"/>
    <keyword name="ThreadTransferKeyword" mask="0x80000000" message="$(string.RuntimePublisher.ThreadTransferKeywordMessage)" symbol="CLR_THREADTRANSFER_KEYWORD"/>
    <keyword name="DebuggerKeyword" mask="0x100000000" message="$(string.RuntimePublisher.DebuggerKeywordMessage)" symbol="CLR_DEBUGGER_KEYWORD"/>
    <keyword name="MonitoringKeyword" mask="0x200000000" message="$(string.RuntimePublisher.MonitoringKeywordMessage)" symbol="CLR_MONITORING_KEYWORD"/>
</keywords>

...[생략]...

이런 식으로 ETW Provider 측에서 제공하는 "Keywords"에 기반을 둔 필터링은 EnableProvider2 메서드의 5번째 인자에 전달하면 되고,

ulong matchAnyKeywords = (ulong)ClrProviderKeywords.Exception;

result = NativeMethods.EnableTraceEx2(sessionHandle, ref clrProvider,
    NativeMethods.EVENT_CONTROL_CODE_ENABLE_PROVIDER, (byte)TraceEventLevel.Informational,
    matchAnyKeywords, 0, 0, ref enableParameters);

따라서 위의 코드는 Exception에 대한 조건만을 전달했으므로 이후 콜백 함수는 Exception이 발생했을 때에만 호출되는 것을 확인할 수 있습니다.




EnableTraceEx2의 6번째 인자를 보면 또 다른 필터링 조건이 있는데, 다음의 글에서 자세한 설명을 볼 수 있습니다.

Tampering with Windows Event Tracing: Background, Offense, and Defense
; https://medium.com/palantir/tampering-with-windows-event-tracing-background-offense-and-defense-4be7ac62ac63

Microsoft.Diagnostics.Tracing.TraceEvent 라이브러리에는 다음과 같이 정의되어 있고,

// Used int the EVENT_FILTER_DESCRIPTOR.Type field 
internal const int EVENT_FILTER_TYPE_NONE = (0x00000000);
internal const int EVENT_FILTER_TYPE_SCHEMATIZED = unchecked((int)(0x80000000));
internal const int EVENT_FILTER_TYPE_SYSTEM_FLAGS = unchecked((int)(0x80000001));
internal const int EVENT_FILTER_TYPE_TRACEHANDLE = unchecked((int)(0x80000002));      // Used with CAPTURE_STATE to get a rundown delivered only to your session
internal const int EVENT_FILTER_TYPE_PID = unchecked((int)(0x80000004));              // Ptr points at array of ints.   (Size determined by byteSize/sizeof(int)
internal const int EVENT_FILTER_TYPE_EXECUTABLE_NAME = unchecked((int)(0x80000008));  // Ptr points at string, can have ';' to separate names. 
internal const int EVENT_FILTER_TYPE_PACKAGE_ID = unchecked((int)(0x80000010));       // Ptr points at string, can have ';' to separate names.
internal const int EVENT_FILTER_TYPE_PACKAGE_APP_ID = unchecked((int)(0x80000020));   // Package Relative App Id = (PRAID);
internal const int EVENT_FILTER_TYPE_PAYLOAD = unchecked((int)(0x80000100));          // Can filter on 
internal const int EVENT_FILTER_TYPE_EVENT_ID = unchecked((int)(0x80000200));         // Ptr points at EVENT_FILTER_EVENT_ID
internal const int EVENT_FILTER_TYPE_STACKWALK = unchecked((int)(0x80001000));        // Ptr points at EVENT_FILTER_EVENT_ID

가장 관심 있는 항목이 PID와 같은 조건이 될 수 있습니다. 사실 시스템 전체에 걸쳐서 이벤트가 오기 때문에 잦은 콜백 함수는 시스템 부하를 늘리므로, 가령 Process ID를 필터링 조건으로 지정해 특정 프로세스에 해당하는 이벤트만 받게 해도 부담이 줄기 때문입니다.

단지, 6번째 인자에 해당하는 필터링 조건은 운영체제 제한이 있는 듯한데, Microsoft.Diagnostics.Tracing.TraceEvent의 소스 코드에 윈도우 8.1부터 가능하다고 나옵니다.

/// <summary>
/// This return true on OS version beyond 8.1 (windows Version 6.3).   It means most of the
/// per-event filtering is supported.  
/// </summary>
public static bool FilteringSupported
{
    get
    {
        if (!s_IsEtwFilteringSupported.HasValue)
        {
            var ret = false;

            // For Windows Versions above windows 8, OSVersion lies and returns 6.2 (window 8) even though
            // the windows version is higher.  We have to try harder to figure out whether we are windows 8 or something
            // later.   Currently we look at the file version number of an OS DLL.  
            // There is probably a better way.   
            var winDir = Environment.GetEnvironmentVariable("WinDir");
            var kernel32 = Path.Combine(winDir, @"system32\Kernel32.dll");
            if (File.Exists(kernel32))
            {
                using (var kernel32PE = new PEFile.PEFile(kernel32))
                {
                    var versionInfo = kernel32PE.GetFileVersionInfo();
                    if (versionInfo != null)
                    {
                        // versionInfo.FileVersion is now the real version number we want but it is a string, not a 
                        // number.   Our tests is if version number bigger than 6.3 (as a string) or a two or more digit 
                        // major version.   
                        if (string.Compare("6.3", versionInfo.FileVersion) <= 0 || 2 <= versionInfo.FileVersion.IndexOf('.'))
                        {
                            ret = true;
                        }
                    }
                }
            }
            s_IsEtwFilteringSupported = ret;
        }
        return s_IsEtwFilteringSupported.Value;
    }
}

이에 대한 구현 코드는, 사실상 Microsoft.Diagnostics.Tracing.TraceEvent 소스 코드를 그대로 베끼는 것이 더 나을 정도이므로 여기서는 생략하겠습니다. ^^

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




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/28/2023]

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

비밀번호

댓글 작성자
 




[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13917정성태4/30/2025117VS.NET IDE: 199. Directory.Build.props에 정의한 속성에 대해 Condition 제약으로 값을 변경하는 방법
13916정성태4/23/2025503디버깅 기술: 221. WinDbg 분석 사례 - ASP.NET HttpCookieCollection을 다중 스레드에서 사용할 경우 무한 루프 현상 - 두 번째 이야기
13915정성태4/13/20251705닷넷: 2331. C# - 실행 시에 메서드 가로채기 (.NET 9)파일 다운로드1
13914정성태4/11/20252008디버깅 기술: 220. windbg 분석 사례 - x86 ASP.NET 웹 응용 프로그램의 CPU 100% 현상 (4)
13913정성태4/10/20251230오류 유형: 950. Process Explorer - 64비트 윈도우에서 32비트 프로세스의 덤프를 뜰 때 "Error writing dump file: Access is denied." 오류
13912정성태4/9/2025891닷넷: 2330. C# - 실행 시에 메서드 가로채기 (.NET 5 ~ .NET 8)파일 다운로드1
13911정성태4/8/20251135오류 유형: 949. WinDbg - .NET Core/5+ 응용 프로그램 디버깅 시 sos 확장을 자동으로 로드하지 못하는 문제
13910정성태4/8/20251298디버깅 기술: 219. WinDbg - 명령어 내에서 환경 변수 사용법
13909정성태4/7/20251790닷넷: 2329. C# - 실행 시에 메서드 가로채기 (.NET Framework 4.8)파일 다운로드1
13908정성태4/2/20252214닷넷: 2328. C# - MailKit: SMTP, POP3, IMAP 지원 라이브러리
13907정성태3/29/20252078VS.NET IDE: 198. (OneDrive, Dropbox 등의 공유 디렉터리에 있는) C# 프로젝트의 출력 경로 변경하기
13906정성태3/27/20252350닷넷: 2327. C# - 초기화되지 않은 메모리에 접근하는 버그?파일 다운로드1
13905정성태3/26/20252396Windows: 281. C++ - Windows / Critical Section의 안정화를 위해 도입된 "Keyed Event"파일 다운로드1
13904정성태3/25/20251945디버깅 기술: 218. Windbg로 살펴보는 Win32 Critical Section파일 다운로드1
13903정성태3/24/20251556VS.NET IDE: 197. (OneDrive, Dropbox 등의 공유 디렉터리에 있는) C++ 프로젝트의 출력 경로 변경하기
13902정성태3/24/20251771개발 환경 구성: 742. Oracle - 테스트용 hr 계정 및 데이터 생성파일 다운로드1
13901정성태3/9/20252154Windows: 280. Hyper-V의 3가지 Thread Scheduler (Classic, Core, Root)
13900정성태3/8/20252377스크립트: 72. 파이썬 - SQLAlchemy + oracledb 연동
13899정성태3/7/20251843스크립트: 71. 파이썬 - asyncio의 ContextVar 전달
13898정성태3/5/20252168오류 유형: 948. Visual Studio - Proxy Authentication Required: dotnetfeed.blob.core.windows.net
13897정성태3/5/20252406닷넷: 2326. C# - PowerShell과 연동하는 방법 (두 번째 이야기)파일 다운로드1
13896정성태3/5/20252219Windows: 279. Hyper-V Manager - VM 목록의 CPU Usage 항목이 항상 0%로 나오는 문제
13895정성태3/4/20252262Linux: 117. eBPF / bpf2go - Map에 추가된 요소의 개수를 확인하는 방법
13894정성태2/28/20252302Linux: 116. eBPF / bpf2go - BTF Style Maps 정의 구문과 데이터 정렬 문제
13893정성태2/27/20252244Linux: 115. eBPF (bpf2go) - ARRAY / HASH map 기본 사용법
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...