From d58df343e717005e85430ba1eaa89f8cf12cb3c5 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Fri, 21 Nov 2025 14:35:04 -0500 Subject: [PATCH 1/3] Bump TraceEvent Version --- eng/Versions.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 82fe464071..f5f5c89b2a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -29,7 +29,7 @@ 9.0.8 17.10.0-beta1.24272.1 - 3.1.23 + 3.1.28 11.0.0-beta.25528.108 8.0.1 8.0.3 @@ -46,8 +46,8 @@ 4.5.5 4.3.0 4.5.4 - 8.0.0 - 8.0.5 + 9.0.8 + 9.0.8 2.0.3 1.2.0-beta.556 10.0.26100.1 From 7dd9d0b3fb39611e4af40d872638046abd130eb0 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Fri, 21 Nov 2025 15:37:55 -0500 Subject: [PATCH 2/3] [dotnet-trace] Consolidate trace to stacks conversion --- .../CommandLine/Commands/ReportCommand.cs | 66 ++++++--------- .../dotnet-trace/ThreadTimeProviderType.cs | 84 +++++++++++++++++++ .../dotnet-trace/TraceFileFormatConverter.cs | 42 +++------- 3 files changed, 123 insertions(+), 69 deletions(-) create mode 100644 src/Tools/dotnet-trace/ThreadTimeProviderType.cs diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/ReportCommand.cs b/src/Tools/dotnet-trace/CommandLine/Commands/ReportCommand.cs index 7330cc1604..989778f17d 100644 --- a/src/Tools/dotnet-trace/CommandLine/Commands/ReportCommand.cs +++ b/src/Tools/dotnet-trace/CommandLine/Commands/ReportCommand.cs @@ -41,55 +41,43 @@ private static int TopNReport(string traceFile, int number, bool inclusive, bool { try { - string tempEtlxFilename = TraceLog.CreateFromEventPipeDataFile(traceFile); int count = 0; int index = 0; List nodesToReport = new(); - using (SymbolReader symbolReader = new(System.IO.TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }) - using (TraceLog eventLog = new(tempEtlxFilename)) - { - MutableTraceEventStackSource stackSource = new(eventLog) - { - OnlyManagedCodeStacks = true - }; - - SampleProfilerThreadTimeComputer computer = new(eventLog, symbolReader); + MutableTraceEventStackSource stackSource = ThreadTimeStackSourceHelper.GenerateStackSourceFromTrace(traceFile); - computer.GenerateThreadTimeStacks(stackSource); - - FilterParams filterParams = new() - { - FoldRegExs = "CPU_TIME;UNMANAGED_CODE_TIME;{Thread (}", - }; - FilterStackSource filterStack = new(filterParams, stackSource, ScalingPolicyKind.ScaleToData); - CallTree callTree = new(ScalingPolicyKind.ScaleToData); - callTree.StackSource = filterStack; + FilterParams filterParams = new() + { + FoldRegExs = "CPU_TIME;UNMANAGED_CODE_TIME;{Thread (}", + }; + FilterStackSource filterStack = new(filterParams, stackSource, ScalingPolicyKind.ScaleToData); + CallTree callTree = new(ScalingPolicyKind.ScaleToData); + callTree.StackSource = filterStack; - List callTreeNodes = null; + List callTreeNodes = null; - if (!inclusive) - { - callTreeNodes = callTree.ByIDSortedExclusiveMetric(); - } - else - { - callTreeNodes = callTree.ByIDSortedInclusiveMetric(); - } + if (!inclusive) + { + callTreeNodes = callTree.ByIDSortedExclusiveMetric(); + } + else + { + callTreeNodes = callTree.ByIDSortedInclusiveMetric(); + } - int totalElements = callTreeNodes.Count; - while (count < number && index < totalElements) + int totalElements = callTreeNodes.Count; + while (count < number && index < totalElements) + { + CallTreeNodeBase node = callTreeNodes[index]; + index++; + if (!unwantedMethodNames.Any(node.Name.Contains)) { - CallTreeNodeBase node = callTreeNodes[index]; - index++; - if (!unwantedMethodNames.Any(node.Name.Contains)) - { - nodesToReport.Add(node); - count++; - } + nodesToReport.Add(node); + count++; } - - PrintReportHelper.TopNWriteToStdOut(nodesToReport, inclusive, verbose); } + + PrintReportHelper.TopNWriteToStdOut(nodesToReport, inclusive, verbose); return 0; } catch (Exception ex) diff --git a/src/Tools/dotnet-trace/ThreadTimeProviderType.cs b/src/Tools/dotnet-trace/ThreadTimeProviderType.cs new file mode 100644 index 0000000000..b4ea524012 --- /dev/null +++ b/src/Tools/dotnet-trace/ThreadTimeProviderType.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using Microsoft.Diagnostics.Symbols; +using Microsoft.Diagnostics.Tracing; +using Microsoft.Diagnostics.Tracing.Etlx; +using Microsoft.Diagnostics.Tracing.Stacks; + +namespace Microsoft.Diagnostics.Tools.Trace +{ + internal enum ThreadTimeProviderType + { + SampleProfiler, + UniversalEvents, + Unknown + } + + internal static class ThreadTimeStackSourceHelper + { + public static MutableTraceEventStackSource GenerateStackSourceFromTrace(string traceFile, bool includeEventSourceEvents = false, bool continueOnError = false) + { + string etlxFilePath = TraceLog.CreateFromEventPipeDataFile(traceFile, null, new TraceLogOptions() { ContinueOnError = continueOnError }); + using SymbolReader symbolReader = new(TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }; + using TraceLog eventLog = new(etlxFilePath); + MutableTraceEventStackSource stackSource = new(eventLog); + + ThreadTimeProviderType providerType = DetectProviderType(eventLog); + switch (providerType) + { + case ThreadTimeProviderType.SampleProfiler: + { + stackSource.OnlyManagedCodeStacks = true; + SampleProfilerThreadTimeComputer computer = new(eventLog, symbolReader) + { + IncludeEventSourceEvents = includeEventSourceEvents, + }; + computer.GenerateThreadTimeStacks(stackSource); + break; + } + case ThreadTimeProviderType.UniversalEvents: + { + stackSource.OnlyManagedCodeStacks = false; +#pragma warning disable 618 + ThreadTimeStackComputer computer = new(eventLog, symbolReader) + { + IncludeEventSourceEvents = false, + }; + computer.GenerateThreadTimeStacks(stackSource); +#pragma warning restore 618 + break; + } + case ThreadTimeProviderType.Unknown: + default: + throw new DiagnosticToolException("The trace does not contain SampleProfiler or Universal.Events data required for thread-time analysis."); + } + + if (File.Exists(etlxFilePath)) + { + File.Delete(etlxFilePath); + } + + return stackSource; + } + + private static ThreadTimeProviderType DetectProviderType(TraceLog eventLog) + { + foreach (TraceEvent evt in eventLog.Events) + { + if (string.Equals(evt.ProviderName, "Microsoft-DotNETCore-SampleProfiler", StringComparison.Ordinal)) + { + return ThreadTimeProviderType.SampleProfiler; + } + if (string.Equals(evt.ProviderName, "Universal.Events", StringComparison.Ordinal)) + { + return ThreadTimeProviderType.UniversalEvents; + } + } + + return ThreadTimeProviderType.Unknown; + } + } +} diff --git a/src/Tools/dotnet-trace/TraceFileFormatConverter.cs b/src/Tools/dotnet-trace/TraceFileFormatConverter.cs index a6e08ac74c..5b95145fa2 100644 --- a/src/Tools/dotnet-trace/TraceFileFormatConverter.cs +++ b/src/Tools/dotnet-trace/TraceFileFormatConverter.cs @@ -69,38 +69,20 @@ internal static void ConvertToFormat(TextWriter stdOut, TextWriter stdError, Tra private static void Convert(TraceFileFormat format, string fileToConvert, string outputFilename, bool continueOnError = false) { - string etlxFilePath = TraceLog.CreateFromEventPipeDataFile(fileToConvert, null, new TraceLogOptions() { ContinueOnError = continueOnError }); - using (SymbolReader symbolReader = new(TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }) - using (TraceLog eventLog = new(etlxFilePath)) - { - MutableTraceEventStackSource stackSource = new(eventLog) - { - OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks. - }; - - SampleProfilerThreadTimeComputer computer = new(eventLog, symbolReader) - { - IncludeEventSourceEvents = false // SpeedScope handles only CPU samples, events are not supported - }; - computer.GenerateThreadTimeStacks(stackSource); - - switch (format) - { - case TraceFileFormat.Speedscope: - SpeedScopeStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename); - break; - case TraceFileFormat.Chromium: - ChromiumStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename, compress: false); - break; - default: - // we should never get here - throw new DiagnosticToolException($"Invalid TraceFileFormat \"{format}\""); - } - } + // Speedscope/Chromium outputs only understand CPU samples; exclude EventSource events. + MutableTraceEventStackSource stackSource = ThreadTimeStackSourceHelper.GenerateStackSourceFromTrace(fileToConvert, includeEventSourceEvents: false, continueOnError); - if (File.Exists(etlxFilePath)) + switch (format) { - File.Delete(etlxFilePath); + case TraceFileFormat.Speedscope: + SpeedScopeStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename); + break; + case TraceFileFormat.Chromium: + ChromiumStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename, compress: false); + break; + default: + // we should never get here + throw new DiagnosticToolException($"Invalid TraceFileFormat \"{format}\""); } } } From 9bb16d9a185b4b8f01ca8443a76efb4538372419 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Fri, 21 Nov 2025 18:16:36 -0500 Subject: [PATCH 3/3] Remove path property generation --- src/Tools/dotnet-trace/dotnet-trace.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tools/dotnet-trace/dotnet-trace.csproj b/src/Tools/dotnet-trace/dotnet-trace.csproj index 6b14eef616..9be52fb7f5 100644 --- a/src/Tools/dotnet-trace/dotnet-trace.csproj +++ b/src/Tools/dotnet-trace/dotnet-trace.csproj @@ -13,7 +13,7 @@ - +