Skip to content

Commit 2e59555

Browse files
authored
Merge pull request microsoft#1186 from brianrob/recipes
Automated Performance Analysis Framework
2 parents ea844af + e52b484 commit 2e59555

15 files changed

+679
-3
lines changed

PerfView.sln

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Microsoft Visual Studio Solution File, Format Version 12.00
2-
# Visual Studio 15
3-
VisualStudioVersion = 15.0.26918.3
2+
# Visual Studio Version 16
3+
VisualStudioVersion = 16.0.30104.5
44
MinimumVisualStudioVersion = 15.0
55
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ETWClrProfilerX86", "src\EtwClrProfiler\ETWClrProfilerX86.vcxproj", "{E9980619-4016-4A4A-B7CC-F8B0E483BDB8}"
66
EndProject

src/PerfView/PerfViewData.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Graphs;
44
using Microsoft.Diagnostics.Symbols;
55
using Microsoft.Diagnostics.Tracing;
6+
using Microsoft.Diagnostics.Tracing.AutomatedAnalysis;
67
using Microsoft.Diagnostics.Tracing.Etlx;
78
using Microsoft.Diagnostics.Tracing.EventPipe;
89
using Microsoft.Diagnostics.Tracing.Parsers;
@@ -1516,6 +1517,17 @@ private void MakeModuleCsv(List<TraceProcess> processes, string filepath)
15161517
#endregion
15171518
}
15181519

1520+
public class AutomatedAnalysisReport : PerfViewHtmlReport
1521+
{
1522+
public AutomatedAnalysisReport(PerfViewFile dataFile) : base(dataFile, "Automatic CPU Analysis") { }
1523+
1524+
protected override void WriteHtmlBody(TraceLog dataFile, TextWriter writer, string fileName, TextWriter log)
1525+
{
1526+
AutomatedAnalysisManager manager = new AutomatedAnalysisManager(dataFile, log, App.GetSymbolReader(dataFile.FilePath));
1527+
manager.GenerateReport(writer);
1528+
}
1529+
}
1530+
15191531
public class PerfViewIisStats : PerfViewHtmlReport
15201532
{
15211533
private Dictionary<Guid, IisRequest> m_Requests = new Dictionary<Guid, IisRequest>();
@@ -6840,6 +6852,7 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
68406852
var advanced = new PerfViewTreeGroup("Advanced Group");
68416853
var memory = new PerfViewTreeGroup("Memory Group");
68426854
var obsolete = new PerfViewTreeGroup("Old Group");
6855+
var experimental = new PerfViewTreeGroup("Experimental Group");
68436856
m_Children = new List<PerfViewTreeItem>();
68446857

68456858
bool hasCPUStacks = false;
@@ -7028,7 +7041,7 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
70287041
if (hasCPUStacks)
70297042
{
70307043
m_Children.Add(new PerfViewStackSource(this, "CPU"));
7031-
7044+
experimental.Children.Add(new AutomatedAnalysisReport(this));
70327045
if (!App.CommandLineArgs.ShowOptimizationTiers &&
70337046
tracelog.Events.Any(
70347047
e => e is MethodLoadUnloadTraceDataBase td && td.OptimizationTier != OptimizationTier.Unknown))
@@ -7245,6 +7258,11 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
72457258
m_Children.Add(obsolete);
72467259
}
72477260

7261+
if(AppLog.InternalUser && 0 < experimental.Children.Count)
7262+
{
7263+
m_Children.Add(experimental);
7264+
}
7265+
72487266
return null;
72497267
}
72507268
// public override string DefaultStackSourceName { get { return "CPU"; } }
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.Diagnostics.Tracing.Etlx;
4+
using Microsoft.Diagnostics.Tracing.Stacks;
5+
using System.IO;
6+
using System.Text.RegularExpressions;
7+
using Microsoft.Diagnostics.Symbols;
8+
9+
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
10+
{
11+
public sealed class AutomatedAnalysisExecutionContext
12+
{
13+
internal AutomatedAnalysisExecutionContext(TraceLog traceLog, TextWriter textLog, SymbolReader symbolReader, AutomatedAnalysisIssueCollection issues)
14+
{
15+
TraceLog = traceLog;
16+
TextLog = textLog;
17+
SymbolReader = symbolReader;
18+
Issues = issues;
19+
}
20+
21+
public SymbolReader SymbolReader { get; }
22+
23+
public TraceLog TraceLog { get; }
24+
25+
public TextWriter TextLog { get; }
26+
27+
public AutomatedAnalysisIssueCollection Issues { get; }
28+
}
29+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
3+
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
4+
{
5+
public sealed class AutomatedAnalysisIssue
6+
{
7+
public AutomatedAnalysisIssue(
8+
string title,
9+
string description,
10+
string url)
11+
{
12+
Title = title;
13+
Description = description;
14+
URL = url;
15+
}
16+
17+
public string Title { get; private set; }
18+
public string Description { get; private set; }
19+
public string URL { get; private set; }
20+
}
21+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using Microsoft.Diagnostics.Tracing.Etlx;
2+
using System;
3+
using System.Collections;
4+
using System.Collections.Generic;
5+
using System.Collections.ObjectModel;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
11+
{
12+
public sealed class AutomatedAnalysisIssueCollection : IEnumerable<KeyValuePair<TraceProcess, List<AutomatedAnalysisIssue>>>
13+
{
14+
private Dictionary<TraceProcess, List<AutomatedAnalysisIssue>> _issues = new Dictionary<TraceProcess, List<AutomatedAnalysisIssue>>();
15+
16+
public List<AutomatedAnalysisIssue> this[TraceProcess process]
17+
{
18+
get
19+
{
20+
List<AutomatedAnalysisIssue> issues;
21+
if(!_issues.TryGetValue(process, out issues))
22+
{
23+
issues = new List<AutomatedAnalysisIssue>();
24+
_issues.Add(process, issues);
25+
}
26+
27+
return issues;
28+
}
29+
}
30+
31+
public IEnumerator GetEnumerator()
32+
{
33+
return ((IEnumerable)_issues).GetEnumerator();
34+
}
35+
36+
IEnumerator<KeyValuePair<TraceProcess, List<AutomatedAnalysisIssue>>> IEnumerable<KeyValuePair<TraceProcess, List<AutomatedAnalysisIssue>>>.GetEnumerator()
37+
{
38+
return ((IEnumerable<KeyValuePair<TraceProcess, List<AutomatedAnalysisIssue>>>)_issues).GetEnumerator();
39+
}
40+
}
41+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System.IO;
2+
using System.Collections.Generic;
3+
using Microsoft.Diagnostics.Tracing.Etlx;
4+
using Microsoft.Diagnostics.Symbols;
5+
6+
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
7+
{
8+
public sealed class AutomatedAnalysisManager
9+
{
10+
private TraceLog _traceLog;
11+
private TextWriter _textLog;
12+
private SymbolReader _symbolReader;
13+
private AutomatedAnalysisIssueCollection _issueCollection = new AutomatedAnalysisIssueCollection();
14+
15+
public AutomatedAnalysisManager(TraceLog traceLog, TextWriter textLog, SymbolReader symbolReader)
16+
{
17+
_traceLog = traceLog;
18+
_textLog = textLog;
19+
_symbolReader = symbolReader;
20+
}
21+
public void GenerateReport(TextWriter writer)
22+
{
23+
using (AutomatedAnalysisReportGenerator reportGenerator = new AutomatedAnalysisReportGenerator(writer))
24+
{
25+
List<AutomatedAnalysisRule> allRules = new List<AutomatedAnalysisRule>();
26+
List<AutomatedAnalysisPerProcessRule> perProcessRules = new List<AutomatedAnalysisPerProcessRule>();
27+
28+
// Run global rules, deferring per-process rules.
29+
AutomatedAnalysisExecutionContext executionContext = new AutomatedAnalysisExecutionContext(_traceLog, _textLog, _symbolReader, _issueCollection);
30+
foreach (AutomatedAnalysisRule rule in AutomatedAnalysisRuleResolver.GetRules())
31+
{
32+
// Create a list of all executed rules so that they can be written into the report.
33+
allRules.Add(rule);
34+
35+
if (rule is AutomatedAnalysisPerProcessRule)
36+
{
37+
// Defer per-process rules.
38+
perProcessRules.Add((AutomatedAnalysisPerProcessRule)rule);
39+
}
40+
else
41+
{
42+
// Execute the rule.
43+
rule.RunRule(executionContext, null);
44+
}
45+
}
46+
47+
// Run per-process rules.
48+
foreach (TraceProcess process in executionContext.TraceLog.Processes)
49+
{
50+
if (process.ManagedProcess())
51+
{
52+
// Create the process context.
53+
ProcessContext processContext = new ProcessContext(executionContext, process);
54+
55+
foreach (AutomatedAnalysisPerProcessRule rule in perProcessRules)
56+
{
57+
rule.RunRule(executionContext, processContext);
58+
}
59+
}
60+
}
61+
62+
// Write out issues.
63+
foreach (KeyValuePair<TraceProcess, List<AutomatedAnalysisIssue>> pair in _issueCollection)
64+
{
65+
if (pair.Value.Count > 0)
66+
{
67+
reportGenerator.WriteIssuesForProcess(pair.Key, pair.Value);
68+
}
69+
}
70+
71+
// Write the list of executed rules.
72+
reportGenerator.WriteExecutedRulesList(allRules);
73+
}
74+
}
75+
}
76+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.Diagnostics.Tracing.Etlx;
4+
5+
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
6+
{
7+
public abstract class AutomatedAnalysisPerProcessRule : AutomatedAnalysisRule
8+
{
9+
protected abstract void Execute(AutomatedAnalysisExecutionContext executionContext, ProcessContext processContext);
10+
11+
internal override void RunRule(AutomatedAnalysisExecutionContext executionContext, ProcessContext processContext)
12+
{
13+
Execute(executionContext, processContext);
14+
}
15+
16+
protected override void Execute(AutomatedAnalysisExecutionContext executionContext)
17+
{
18+
throw new InvalidOperationException();
19+
}
20+
}
21+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using Microsoft.Diagnostics.Tracing.Etlx;
8+
9+
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
10+
{
11+
public sealed class AutomatedAnalysisReportGenerator : IDisposable
12+
{
13+
private TextWriter _writer;
14+
15+
public AutomatedAnalysisReportGenerator(TextWriter writer)
16+
{
17+
_writer = writer;
18+
StartReport();
19+
}
20+
21+
void IDisposable.Dispose()
22+
{
23+
EndReport();
24+
}
25+
26+
private void StartReport()
27+
{
28+
_writer.WriteLine("<html>");
29+
_writer.WriteLine("<head>");
30+
_writer.WriteLine("<title>Automated CPU Analysis</title>");
31+
_writer.WriteLine("<meta charset=\"UTF-8\"/>");
32+
_writer.WriteLine("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>");
33+
_writer.WriteLine("</head>");
34+
_writer.WriteLine("<body>");
35+
_writer.WriteLine("<H2>Automated CPU Analysis</H2>");
36+
}
37+
38+
private void EndReport()
39+
{
40+
_writer.WriteLine("</body>");
41+
_writer.WriteLine("</html>");
42+
}
43+
44+
public void WriteIssuesForProcess(TraceProcess process, List<AutomatedAnalysisIssue> issues)
45+
{
46+
_writer.WriteLine($"<H3>Process {process.ProcessID}: {process.CommandLine}</H3>");
47+
_writer.WriteLine("<Table Border=\"1\">");
48+
_writer.WriteLine("<TR><TH>Issue Title</TH><TH>Notes</TH></TR>");
49+
foreach(AutomatedAnalysisIssue issue in issues)
50+
{
51+
_writer.WriteLine($"<TR><TD>{issue.Title}</TD><TD>{issue.Description}<BR/><BR/>More details: <A HREF=\"{issue.URL}\">{issue.URL}</A></TD></TR>");
52+
}
53+
_writer.WriteLine("</Table>");
54+
}
55+
56+
public void WriteExecutedRulesList(List<AutomatedAnalysisRule> rules)
57+
{
58+
if(rules.Count > 0)
59+
{
60+
_writer.WriteLine("<H3>Rules Executed:</H3>");
61+
_writer.WriteLine("<ul style=\"list-style-type:circle\">");
62+
foreach (AutomatedAnalysisRule rule in rules)
63+
{
64+
_writer.WriteLine($"<li>{rule.GetType().AssemblyQualifiedName}</li>");
65+
}
66+
_writer.WriteLine("</ul>");
67+
}
68+
else
69+
{
70+
_writer.WriteLine($"<H3>No rules were executed. Check '{AutomatedAnalysisRuleResolver.RulesDirectory}' for DLLs containing rules.</H3>");
71+
}
72+
}
73+
}
74+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Collections.Generic;
2+
3+
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
4+
{
5+
public abstract class AutomatedAnalysisRule
6+
{
7+
protected abstract void Execute(AutomatedAnalysisExecutionContext executionContext);
8+
9+
internal virtual void RunRule(AutomatedAnalysisExecutionContext executionContext, ProcessContext processContext)
10+
{
11+
Execute(executionContext);
12+
}
13+
}
14+
}

0 commit comments

Comments
 (0)