public static StackSource CPUStacks( this Microsoft.Diagnostics.Tracing.Etlx.TraceLog eventLog, Microsoft.Diagnostics.Tracing.Etlx.TraceProcess process = null, Predicate <TraceEvent> predicate = null) { Microsoft.Diagnostics.Tracing.Etlx.TraceEvents events; if (process == null) { events = eventLog.Events.Filter((x) => ((predicate == null) || predicate(x)) && x is SampledProfileTraceData && x.ProcessID != 0); } else { events = process.EventsInProcess.Filter((x) => ((predicate == null) || predicate(x)) && x is SampledProfileTraceData); } var traceStackSource = new TraceEventStackSource(events); traceStackSource.ShowUnknownAddresses = true; // Clone the samples so that the caller doesn't have to go back to the ETL file from here on. return(CopyStackSource.Clone(traceStackSource)); }
public static AspNetCoreParserResults ParseDotNetCoreRequests(Microsoft.Diagnostics.Tracing.Etlx.TraceLog dataFile, int minRequestDurationMilliseconds) { AspNetCoreParserResults results = new AspNetCoreParserResults(); var processes = new List <AspNetCoreProcess>(); var aspnetCoreRequests = new Dictionary <string, AspNetCoreRequest>(); var requestsFullTrace = new Dictionary <AspNetCoreRequestId, List <AspNetCoreTraceEvent> >(); var failedRequests = new Dictionary <AspNetCoreRequest, List <AspNetCoreTraceEvent> >(); try { var source = dataFile.Events.GetSource(); var parser = new DynamicTraceEventParser(source); var clrParser = new ClrTraceEventParser(source); parser.AddCallbackForProviderEvent(MicrosoftExtensionsLoggingProvider, StartEvent, delegate(TraceEvent data) { ParseExtensionsLoggingEvent(data, minRequestDurationMilliseconds, "Arguments", aspnetCoreRequests, failedRequests, requestsFullTrace, AspNetCoreRequestEventType.Start); }); parser.AddCallbackForProviderEvent(MicrosoftExtensionsLoggingProvider, StopEvent, delegate(TraceEvent data) { ParseExtensionsLoggingEvent(data, minRequestDurationMilliseconds, string.Empty, aspnetCoreRequests, failedRequests, requestsFullTrace, AspNetCoreRequestEventType.Stop); }); parser.AddCallbackForProviderEvent(MicrosoftExtensionsLoggingProvider, FormatMessageEvent, delegate(TraceEvent data) { ParseExtensionsLoggingEvent(data, minRequestDurationMilliseconds, "FormattedMessage", aspnetCoreRequests, failedRequests, requestsFullTrace, AspNetCoreRequestEventType.Message); }); clrParser.ThreadPoolWorkerThreadWait += delegate(ThreadPoolWorkerThreadTraceData data) { if (!processes.Any(p => p.Id == data.ProcessID && p.Name == data.ProcessName)) { var coreProcess = new AspNetCoreProcess { Id = data.ProcessID, Name = data.ProcessName }; processes.Add(coreProcess); } }; source.Process(); } catch (Exception ex) { Logger.LogDiagnoserErrorEvent("Failed while parsing .net core events", ex); } foreach (var request in aspnetCoreRequests.Values) { if (request.EndTimeRelativeMSec == 0) { request.EndTimeRelativeMSec = dataFile.SessionEndTimeRelativeMSec; request.IncompleteRequest = true; } } results.AspNetCoreRequestsFullTrace = requestsFullTrace; results.Requests = aspnetCoreRequests; results.Processes = processes; results.FailedRequests = failedRequests; return(results); }
/// <summary> /// Reads trace and prints managed stacks /// </summary> /// <param name="console"></param> /// <param name="traceFile"></param> /// <returns>Returns 0 for success and 1 for failure</returns> /// <remarks>This code is adopted from josalem code https://github.com/josalem/DotStack </remarks> public static int Analyze(IConsole console, FileInfo traceFile) { if (string.IsNullOrEmpty(traceFile.FullName)) { Console.ForegroundColor = ConsoleColor.Red; console.Error.WriteLine("-f [process-id] Process ID is required."); Console.ResetColor(); return(1); } Console.WriteLine("[Trace analysis started...]\n"); // Both the namespaces have TraceLog, here we use Microsoft.Diagnostics.Tracing.Etlx.TraceLog; // The following line creates a etlx file and then does analysis using that. string tempEtlxFilename = TraceLog.CreateFromEventPipeDataFile(traceFile.FullName); using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }) using (var eventLog = new TraceLog(tempEtlxFilename)) { var stackSource = new MutableTraceEventStackSource(eventLog) { OnlyManagedCodeStacks = true }; var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader); computer.GenerateThreadTimeStacks(stackSource); var samplesForThread = new Dictionary <string, List <StackSourceSample> >(); stackSource.ForEach((sample) => { var stackIndex = sample.StackIndex; while (!stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false).StartsWith("Thread (")) { stackIndex = stackSource.GetCallerIndex(stackIndex); } var threadName = stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false); if (samplesForThread.TryGetValue(threadName, out var samples)) { samples.Add(sample); } else { samplesForThread[threadName] = new List <StackSourceSample>() { sample }; } }); foreach (var(threadName, samples) in samplesForThread) { PrintStack(threadName, samples[0], stackSource); } } Console.WriteLine("\n[Trace analysis completed...]"); return(0); }
public static void ToHtml(TextWriter writer, List <TraceProcess> perProc, string fileName, string title, ReportType type, bool justBody = false, bool doServerGCReport = false, RuntimeLoaderStatsData runtimeOpsStats = null, Microsoft.Diagnostics.Tracing.Etlx.TraceLog traceLog = null) { if (!justBody) { writer.WriteLine("<html>"); writer.WriteLine("<head>"); writer.WriteLine("<title>{0}</title>", Path.GetFileNameWithoutExtension(fileName)); writer.WriteLine("<meta charset=\"UTF-8\"/>"); writer.WriteLine("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>"); writer.WriteLine("</head>"); writer.WriteLine("<body>"); } writer.WriteLine("<H2>{0}</H2>", title); List <TraceProcess> sortedProcs = perProc; if (type == ReportType.JIT) { sortedProcs.Sort((TraceProcess p1, TraceProcess p2) => { return(-p1.LoadedDotNetRuntime().JIT.Stats().TotalCpuTimeMSec.CompareTo(p2.LoadedDotNetRuntime().JIT.Stats().TotalCpuTimeMSec)); }); } else if (type == ReportType.GC) { sortedProcs.Sort((TraceProcess p1, TraceProcess p2) => { return(-p1.LoadedDotNetRuntime().GC.Stats().MaxSizePeakMB.CompareTo(p2.LoadedDotNetRuntime().GC.Stats().MaxSizePeakMB)); }); } else if (type == ReportType.RuntimeLoader) { sortedProcs.Sort((TraceProcess p1, TraceProcess p2) => { return(-RuntimeLoaderStats.TotalCPUMSec(p1, runtimeOpsStats).CompareTo(RuntimeLoaderStats.TotalCPUMSec(p2, runtimeOpsStats))); }); } else if (type == ReportType.FileVersion) { sortedProcs.Sort((TraceProcess p1, TraceProcess p2) => { return(string.Compare(p1.Name, p2.Name)); }); } int count = sortedProcs.Count; if (count > 1) { writer.WriteLine("<UL>"); foreach (var data in sortedProcs) { var mang = data.LoadedDotNetRuntime(); if (mang == null) { continue; } if (type == ReportType.JIT && !mang.JIT.Stats().Interesting) { continue; } if (type == ReportType.RuntimeLoader && !RuntimeLoaderStats.IsInteresting(data, runtimeOpsStats)) { continue; } var id = Shorten(data.CommandLine); if (string.IsNullOrEmpty(id)) { id = data.Name; } writer.WriteLine("<LI><A HREF=\"#Stats_{0}\">Process {0,5}: {1}</A></LI>", data.ProcessID, XmlUtilities.XmlEscape(id)); } writer.WriteLine("</UL>"); writer.WriteLine("<HR/><HR/><BR/><BR/>"); } foreach (TraceProcess stats in sortedProcs) { var mang = stats.LoadedDotNetRuntime(); if (mang == null) { continue; } if (type == ReportType.GC) { Stats.GcStats.ToHtml(writer, stats, mang, fileName, doServerGCReport); } if (type == ReportType.JIT && mang.JIT.Stats().Interesting) { Stats.JitStats.ToHtml(writer, stats, mang, fileName); } if (type == ReportType.RuntimeLoader && RuntimeLoaderStats.IsInteresting(stats, runtimeOpsStats)) { Stats.RuntimeLoaderStats.ToHtml(writer, stats, fileName, runtimeOpsStats); } if (type == ReportType.FileVersion) { Stats.FileVersionInformation.ToHtml(writer, stats, traceLog); } } writer.WriteLine("<BR/><BR/><BR/><BR/><BR/><BR/><BR/><BR/><BR/><BR/>"); if (!justBody) { writer.WriteLine("</body>"); writer.WriteLine("</html>"); } }