-
Notifications
You must be signed in to change notification settings - Fork 383
[dotnet-trace] Update Convert and Report to handle Universal.Events #5644
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<CallTreeNodeBase> 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 (}", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you will want to separate a bit of the configuration based on whether or not the trace comes from the runtime (EventPipe) vs. one-collect. Traces from one-collect are going to have multiple processes and then either CPU time or Thread time. One of the great things about this view is that when the data is focused on the right target (usually just a single process), it can be very useful. In the current state, we're looking at all processes on the machine, and blocked time. Blocked time almost always dominates CPU time because it's not a consumable resource - it's the time that was spent by every thread when they were blocked. I'd recommend the following:
|
||
| }; | ||
| FilterStackSource filterStack = new(filterParams, stackSource, ScalingPolicyKind.ScaleToData); | ||
| CallTree callTree = new(ScalingPolicyKind.ScaleToData); | ||
| callTree.StackSource = filterStack; | ||
|
|
||
| List<CallTreeNodeBase> callTreeNodes = null; | ||
| List<CallTreeNodeBase> 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) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For symbol resolution, here is the current state in PerfView/TraceEvent:
SymbolReader.