是否有使用 ETW 记录异常的标准方法?
据我所知,唯一的方法是记录消息和可能的内部异常消息,因为异常类型没有强类型参数。
是否有使用 ETW 记录异常的标准方法?
据我所知,唯一的方法是记录消息和可能的内部异常消息,因为异常类型没有强类型参数。
所有 CLR 异常(第一次机会,以及可能最终导致应用程序崩溃的异常)都由 CLR 运行时提供程序在启用时记录到 ETW。
这是一个带有调用堆栈的完全“结构化”事件(如果你想要的话)。实际上,您可以使用 TraceEvent NuGet 包(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) { }
}
}
使用额外的事件并在 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);
}
}
使用级别和关键字来控制是否要一直记录它。
ETW 不是 .NET 特定的,因此不会有任何强类型的 .NET 特定 API 来记录 .NET 异常。相反,您将创建自己的强类型 API。这就是语义记录和语义记录应用程序块背后的想法。