Как лучше всего регистрировать исключения с помощью ETW?

Существует ли стандартный способ регистрации исключений с помощью ETW?

Насколько я видел, единственный способ сделать это - зарегистрировать сообщение и, возможно, внутреннее сообщение об исключении, поскольку для типа исключения нет строго типизированного параметра.


person jaffa    schedule 27.09.2013    source источник


Ответы (3)


Используйте дополнительное событие и запустите это событие в блоке catch и передайте сообщение об исключении в качестве параметра в событие.

[Event(1, Message = "Application Falure: {0}", Level = EventLevel.Error, Keywords = Keywords.Diagnostic)]
public void Failure(string message) 
{ 
    if (this.IsEnabled())
    {
        this.WriteEvent(1, message); 
    }
}

Поиграйте с уровнем и ключевым словом, чтобы контролировать, хотите ли вы регистрировать это все время или нет.

person magicandre1981    schedule 27.09.2013
comment
Поскольку вы не можете использовать исключения строгого типа для EventSource, было бы лучше просто назвать его так: EventSource.Log.Failure(MyException.ToString())? - person jaffa; 30.09.2013

Все исключения CLR (первоначальные и те, которые могут привести к сбою вашего приложения) регистрируются в ETW поставщиком среды выполнения CLR, если он включен.

Это полностью "структурированное" событие СО стеками вызовов (если они вам нужны). На самом деле вы можете написать приложение для мониторинга, используя NuGet-пакет TraceEvent (Install-Package Microsoft.Diagnostics.Tracing.TraceEvent).

Я вставляю код мониторинга, который часто использую. Поместите это в консольное приложение, вызовите метод Run и создайте несколько управляемых исключений из любого процесса, он распечатает информацию и их стеки вызовов.

ПРИМЕЧАНИЕ. Вам понадобится указанный пакет NuGet, а затем укажите ссылки на его сборки, после чего этот код будет скомпилирован.

class TraceLogMonitor
{
    static TextWriter Out = AllSamples.Out;

    public static void Run()
    {
        var monitoringTimeSec = 10;
       TraceEventSession session = null;

        Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs cancelArgs) =>
        {
            if (session != null)
                session.Dispose();
            cancelArgs.Cancel = true;
        };

        var exceptionGeneationTask = Task.Factory.StartNew(delegate
        {
            Thread.Sleep(3000);
            ThrowException();
        });

        Timer timer = null;

        using (session = new TraceEventSession("TraceLogSession"))
        {
            Out.WriteLine("Enabling Image load, Process and Thread events.  These are needed to look up native method names.");
            session.EnableKernelProvider(

                KernelTraceEventParser.Keywords.ImageLoad |
                KernelTraceEventParser.Keywords.Process,  
                KernelTraceEventParser.Keywords.None
                );

            Out.WriteLine("Enabling CLR Exception and Load events (and stack for those events)");

            session.EnableProvider(
                ClrTraceEventParser.ProviderGuid,
                TraceEventLevel.Informational,
                (ulong)(ClrTraceEventParser.Keywords.Jit |              
                ClrTraceEventParser.Keywords.JittedMethodILToNativeMap |
                ClrTraceEventParser.Keywords.Loader |                   
                ClrTraceEventParser.Keywords.Exception |               
                ClrTraceEventParser.Keywords.Stack));                   

            Out.WriteLine("Enabling CLR Events to 'catch up' on JIT compiled code in running processes.");
            session.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Informational,
                (ulong)(ClrTraceEventParser.Keywords.Jit |          
                ClrTraceEventParser.Keywords.JittedMethodILToNativeMap | 
                ClrTraceEventParser.Keywords.Loader |               
                ClrTraceEventParser.Keywords.StartEnumeration));    

            TextWriter SymbolLookupMessages = new StringWriter();

            var symbolPath = new SymbolPath(SymbolPath.SymbolPathFromEnvironment).Add(SymbolPath.MicrosoftSymbolServerPath);
            SymbolReader symbolReader = new SymbolReader(SymbolLookupMessages, symbolPath.ToString());

            Out.WriteLine("Open a real time TraceLog session (which understands how to decode stacks).");
            using (TraceLogEventSource traceLogSource = TraceLog.CreateFromTraceEventSession(session)) 
            {
                Action<TraceEvent> PrintEvent = ((TraceEvent data) => Print(data, symbolReader));

                traceLogSource.Clr.ExceptionStart += PrintEvent;
                traceLogSource.Clr.LoaderModuleLoad += PrintEvent;

                traceLogSource.Kernel.PerfInfoSample += ((SampledProfileTraceData data) => Print(data, symbolReader));

                Out.WriteLine("Waiting {0} sec for Events.  Run managed code to see data. ", monitoringTimeSec);
                Out.WriteLine("Keep in mind there is a several second buffering delay");

                timer = new Timer(delegate(object state)
                {
                    Out.WriteLine("Stopped Monitoring after {0} sec", monitoringTimeSec);
                    if (session != null)
                        session.Dispose();
                    session = null;
                }, null, monitoringTimeSec * 1000, Timeout.Infinite);

                traceLogSource.Process();
            }
        }
        Out.WriteLine("Finished");
        if (timer != null)
            timer.Dispose();
    }

    static void Print(TraceEvent data, SymbolReader symbolReader)
    {
        if (data.Opcode == TraceEventOpcode.DataCollectionStart)
            return;

        if (data is ExceptionTraceData && ((ExceptionTraceData) data).ExceptionType.Length == 0)
            return;

        Out.WriteLine("EVENT: {0}", data.ToString());
        var callStack = data.CallStack();
        if (callStack != null)
        {
            ResolveNativeCode(callStack, symbolReader);
            Out.WriteLine("CALLSTACK: {0}", callStack.ToString());
        }
    }

    static private void ResolveNativeCode(TraceCallStack callStack, SymbolReader symbolReader)
    {
        while (callStack != null)
        {
            var codeAddress = callStack.CodeAddress;
            if (codeAddress.Method == null)
            {
                var moduleFile = codeAddress.ModuleFile;
                if (moduleFile == null)
                    Trace.WriteLine(string.Format("Could not find module for Address 0x{0:x}", codeAddress.Address));
                else
                    codeAddress.CodeAddresses.LookupSymbolsForModule(symbolReader, moduleFile);
            }
            callStack = callStack.Caller;
        }
    }

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
    private static void ThrowException()
    {
        ThrowException1();
    }

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
    private static void ThrowException1()
    {
        Out.WriteLine("Causing an exception to happen so a CLR Exception Start event will be generated.");
        try
        {
            throw new Exception("This is a test exception thrown to generate a CLR event");
        }
        catch (Exception) { }
    }
}
person mjsabby    schedule 08.10.2014
comment
Я вижу следующее исключение при вызове CallStack(): Attempted to use TraceLog support on a non-TraceLog TraceEventSource Вы когда-нибудь видели это раньше? - person Aaron Hudon; 01.05.2020

ETW не является специфичным для .NET, поэтому не будет строго типизированного API-интерфейса .NET для регистрации исключений .net. Вместо этого вы должны создать свой собственный строго типизированный API. В этом заключается идея Semantic Logging и блока приложения Semantic Logging.

person Erik Funkenbusch    schedule 04.10.2014
comment
так что, в конце концов, для использования ETW можно использовать только примитивные типы данных, строки и числа (в виде строк)? Достаточно ли просто сериализовать Exception в строку? Или есть какой-то шаблон для приложений, отличных от .net, для отслеживания исключений? - person BozoJoe; 10.10.2014
comment
ETW — это двоичная полезная нагрузка, которая поддерживает структуры вашего определения. msdn.microsoft.com/en -us/library/windows/desktop/ Просто, когда вы идете по этому пути, вам нужно написать собственный декодер. В случае с .NET есть четко определенный вариант, уже предоставленный командой .NET. Однако, как и многие другие вещи в нативном коде, это можно сделать несколькими способами. Исключения С++? Исключения SEH? и т. д. Наиболее интересной частью исключения является стек вызовов (и его EIP для получения номеров строк), который стандартизирован ETW, и его обходчик стека. Любая информация сверх этого - ваш звонок. - person mjsabby; 11.10.2014