Skip to content

Commit e079ca6

Browse files
authored
Extensibility for Automated Analysis Data Sources (microsoft#1246)
1 parent be08a56 commit e079ca6

9 files changed

+214
-47
lines changed

src/TraceEvent/AutomatedAnalysis/AutomatedAnalysisExecutionContext.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using Microsoft.Diagnostics.Tracing.Etlx;
4-
using Microsoft.Diagnostics.Tracing.Stacks;
1+
using Microsoft.Diagnostics.Tracing.Etlx;
52
using System.IO;
6-
using System.Text.RegularExpressions;
73
using Microsoft.Diagnostics.Symbols;
84

95
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
106
{
117
public sealed class AutomatedAnalysisExecutionContext
128
{
9+
internal AutomatedAnalysisExecutionContext(IAutomatedAnalysisTrace trace, TextWriter textLog, AutomatedAnalysisIssueCollection issues)
10+
{
11+
Trace = trace;
12+
TextLog = textLog;
13+
Issues = issues;
14+
15+
AutomatedAnalysisTraceLog traceLog = trace as AutomatedAnalysisTraceLog;
16+
if(traceLog != null)
17+
{
18+
TraceLog = traceLog.TraceLog;
19+
SymbolReader = traceLog.SymbolReader;
20+
}
21+
}
1322
internal AutomatedAnalysisExecutionContext(TraceLog traceLog, TextWriter textLog, SymbolReader symbolReader, AutomatedAnalysisIssueCollection issues)
1423
{
1524
TraceLog = traceLog;
@@ -22,6 +31,8 @@ internal AutomatedAnalysisExecutionContext(TraceLog traceLog, TextWriter textLog
2231

2332
public TraceLog TraceLog { get; }
2433

34+
public IAutomatedAnalysisTrace Trace { get; }
35+
2536
public TextWriter TextLog { get; }
2637

2738
public AutomatedAnalysisIssueCollection Issues { get; }

src/TraceEvent/AutomatedAnalysis/AutomatedAnalysisIssueCollection.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99

1010
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
1111
{
12-
public sealed class AutomatedAnalysisIssueCollection : IEnumerable<KeyValuePair<TraceProcess, List<AutomatedAnalysisIssue>>>
12+
public sealed class AutomatedAnalysisIssueCollection : IEnumerable<KeyValuePair<AutomatedAnalysisTraceProcess, List<AutomatedAnalysisIssue>>>
1313
{
14-
private Dictionary<TraceProcess, List<AutomatedAnalysisIssue>> _issues = new Dictionary<TraceProcess, List<AutomatedAnalysisIssue>>();
14+
private Dictionary<AutomatedAnalysisTraceProcess, List<AutomatedAnalysisIssue>> _issues = new Dictionary<AutomatedAnalysisTraceProcess, List<AutomatedAnalysisIssue>>();
1515

16-
public List<AutomatedAnalysisIssue> this[TraceProcess process]
16+
public List<AutomatedAnalysisIssue> this[AutomatedAnalysisTraceProcess process]
1717
{
1818
get
1919
{
@@ -28,14 +28,31 @@ public List<AutomatedAnalysisIssue> this[TraceProcess process]
2828
}
2929
}
3030

31+
// Keep this for now because GLAD depends on it.
32+
public List<AutomatedAnalysisIssue> this[TraceProcess process]
33+
{
34+
get
35+
{
36+
AutomatedAnalysisTraceProcess traceProcess = new AutomatedAnalysisTraceProcess((int)process.ProcessIndex, process.ProcessID, process.CommandLine, process.ManagedProcess());
37+
List<AutomatedAnalysisIssue> issues;
38+
if (!_issues.TryGetValue(traceProcess, out issues))
39+
{
40+
issues = new List<AutomatedAnalysisIssue>();
41+
_issues.Add(traceProcess, issues);
42+
}
43+
44+
return issues;
45+
}
46+
}
47+
3148
public IEnumerator GetEnumerator()
3249
{
3350
return ((IEnumerable)_issues).GetEnumerator();
3451
}
3552

36-
IEnumerator<KeyValuePair<TraceProcess, List<AutomatedAnalysisIssue>>> IEnumerable<KeyValuePair<TraceProcess, List<AutomatedAnalysisIssue>>>.GetEnumerator()
53+
IEnumerator<KeyValuePair<AutomatedAnalysisTraceProcess, List<AutomatedAnalysisIssue>>> IEnumerable<KeyValuePair<AutomatedAnalysisTraceProcess, List<AutomatedAnalysisIssue>>>.GetEnumerator()
3754
{
38-
return ((IEnumerable<KeyValuePair<TraceProcess, List<AutomatedAnalysisIssue>>>)_issues).GetEnumerator();
55+
return ((IEnumerable<KeyValuePair<AutomatedAnalysisTraceProcess, List<AutomatedAnalysisIssue>>>)_issues).GetEnumerator();
3956
}
4057
}
4158
}

src/TraceEvent/AutomatedAnalysis/AutomatedAnalysisManager.cs

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.IO;
23
using System.Collections.Generic;
34
using Microsoft.Diagnostics.Tracing.Etlx;
@@ -7,60 +8,90 @@ namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
78
{
89
public sealed class AutomatedAnalysisManager
910
{
10-
private TraceLog _traceLog;
11+
private IAutomatedAnalysisTrace _trace;
1112
private TextWriter _textLog;
12-
private SymbolReader _symbolReader;
13-
private AutomatedAnalysisIssueCollection _issueCollection = new AutomatedAnalysisIssueCollection();
1413

1514
public AutomatedAnalysisManager(TraceLog traceLog, TextWriter textLog, SymbolReader symbolReader)
1615
{
17-
_traceLog = traceLog;
16+
_trace = new AutomatedAnalysisTraceLog(traceLog, symbolReader);
1817
_textLog = textLog;
19-
_symbolReader = symbolReader;
2018
}
21-
public void GenerateReport(TextWriter writer)
19+
20+
public AutomatedAnalysisManager(IAutomatedAnalysisTrace trace, TextWriter textLog)
2221
{
23-
using (AutomatedAnalysisReportGenerator reportGenerator = new AutomatedAnalysisReportGenerator(writer))
22+
_trace = trace;
23+
_textLog = textLog;
24+
}
25+
26+
public AutomatedAnalysisIssueCollection Issues { get; private set; }
27+
28+
public List<AutomatedAnalysisRule> ExecuteRules()
29+
{
30+
Issues = new AutomatedAnalysisIssueCollection();
31+
32+
List<AutomatedAnalysisRule> allRules = new List<AutomatedAnalysisRule>();
33+
List<AutomatedAnalysisPerProcessRule> perProcessRules = new List<AutomatedAnalysisPerProcessRule>();
34+
35+
// Run global rules, deferring per-process rules.
36+
AutomatedAnalysisExecutionContext executionContext = new AutomatedAnalysisExecutionContext(_trace, _textLog, Issues);
37+
foreach (AutomatedAnalysisRule rule in AutomatedAnalysisRuleResolver.GetRules())
2438
{
25-
List<AutomatedAnalysisRule> allRules = new List<AutomatedAnalysisRule>();
26-
List<AutomatedAnalysisPerProcessRule> perProcessRules = new List<AutomatedAnalysisPerProcessRule>();
39+
// Create a list of all executed rules so that they can be written into the report.
40+
allRules.Add(rule);
2741

28-
// Run global rules, deferring per-process rules.
29-
AutomatedAnalysisExecutionContext executionContext = new AutomatedAnalysisExecutionContext(_traceLog, _textLog, _symbolReader, _issueCollection);
30-
foreach (AutomatedAnalysisRule rule in AutomatedAnalysisRuleResolver.GetRules())
42+
if (rule is AutomatedAnalysisPerProcessRule)
3143
{
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)
44+
// Defer per-process rules.
45+
perProcessRules.Add((AutomatedAnalysisPerProcessRule)rule);
46+
}
47+
else
48+
{
49+
// Execute the rule.
50+
try
3651
{
37-
// Defer per-process rules.
38-
perProcessRules.Add((AutomatedAnalysisPerProcessRule)rule);
52+
rule.RunRule(executionContext, null);
3953
}
40-
else
54+
catch (Exception ex)
4155
{
42-
// Execute the rule.
43-
rule.RunRule(executionContext, null);
56+
_textLog.WriteLine($"Error while executing rule '{rule.GetType().FullName}': {ex}");
4457
}
4558
}
59+
}
4660

47-
// Run per-process rules.
48-
foreach (TraceProcess process in executionContext.TraceLog.Processes)
61+
// Run per-process rules.
62+
foreach (AutomatedAnalysisTraceProcess process in executionContext.Trace.Processes)
63+
{
64+
if (process.ContainsManagedCode)
4965
{
50-
if (process.ManagedProcess())
51-
{
52-
// Create the process context.
53-
ProcessContext processContext = new ProcessContext(executionContext, process);
66+
// Create the process context.
67+
ProcessContext processContext = new ProcessContext(executionContext, process);
5468

55-
foreach (AutomatedAnalysisPerProcessRule rule in perProcessRules)
69+
foreach (AutomatedAnalysisPerProcessRule rule in perProcessRules)
70+
{
71+
try
5672
{
5773
rule.RunRule(executionContext, processContext);
5874
}
75+
catch (Exception ex)
76+
{
77+
_textLog.WriteLine($"Error while executing rule '{rule.GetType().FullName}': {ex}");
78+
}
5979
}
6080
}
81+
}
82+
83+
return allRules;
84+
}
85+
86+
public void GenerateReport(TextWriter writer)
87+
{
88+
using (AutomatedAnalysisReportGenerator reportGenerator = new AutomatedAnalysisReportGenerator(writer))
89+
{
90+
// Execute rules.
91+
List<AutomatedAnalysisRule> allRules = ExecuteRules();
6192

6293
// Write out issues.
63-
foreach (KeyValuePair<TraceProcess, List<AutomatedAnalysisIssue>> pair in _issueCollection)
94+
foreach (KeyValuePair<AutomatedAnalysisTraceProcess, List<AutomatedAnalysisIssue>> pair in Issues)
6495
{
6596
if (pair.Value.Count > 0)
6697
{

src/TraceEvent/AutomatedAnalysis/AutomatedAnalysisReportGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ private void EndReport()
4141
_writer.WriteLine("</html>");
4242
}
4343

44-
public void WriteIssuesForProcess(TraceProcess process, List<AutomatedAnalysisIssue> issues)
44+
public void WriteIssuesForProcess(AutomatedAnalysisTraceProcess process, List<AutomatedAnalysisIssue> issues)
4545
{
46-
_writer.WriteLine($"<H3>Process {process.ProcessID}: {process.CommandLine}</H3>");
46+
_writer.WriteLine($"<H3>Process {process.DisplayID}: {process.Description}</H3>");
4747
_writer.WriteLine("<Table Border=\"1\">");
4848
_writer.WriteLine("<TR><TH>Issue Title</TH><TH>Notes</TH></TR>");
4949
foreach(AutomatedAnalysisIssue issue in issues)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.Collections.Generic;
2+
using Microsoft.Diagnostics.Symbols;
3+
using Microsoft.Diagnostics.Tracing.Etlx;
4+
using Microsoft.Diagnostics.Tracing.Stacks;
5+
6+
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
7+
{
8+
internal sealed class AutomatedAnalysisTraceLog : IAutomatedAnalysisTrace
9+
{
10+
internal AutomatedAnalysisTraceLog(TraceLog traceLog, SymbolReader symbolReader)
11+
{
12+
TraceLog = traceLog;
13+
SymbolReader = symbolReader;
14+
}
15+
16+
internal TraceLog TraceLog { get; }
17+
internal SymbolReader SymbolReader { get; }
18+
19+
IEnumerable<AutomatedAnalysisTraceProcess> IAutomatedAnalysisTrace.Processes
20+
{
21+
get
22+
{
23+
foreach(TraceProcess traceProcess in TraceLog.Processes)
24+
{
25+
yield return new AutomatedAnalysisTraceProcess((int)traceProcess.ProcessIndex, traceProcess.ProcessID, traceProcess.CommandLine, traceProcess.ManagedProcess());
26+
}
27+
}
28+
}
29+
30+
StackView IAutomatedAnalysisTrace.GetCPUStacks(AutomatedAnalysisTraceProcess process)
31+
{
32+
StackView stackView = null;
33+
TraceProcess traceProcess = TraceLog.Processes[(ProcessIndex)process.UniqueID];
34+
if(traceProcess != null)
35+
{
36+
StackSource stackSource = TraceLog.CPUStacks(traceProcess);
37+
stackView = new StackView(traceProcess.Log, stackSource, SymbolReader);
38+
}
39+
return stackView;
40+
}
41+
}
42+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Microsoft.Diagnostics.Tracing.Analysis;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
9+
{
10+
public sealed class AutomatedAnalysisTraceProcess
11+
{
12+
public AutomatedAnalysisTraceProcess(int uniqueID, int displayID, string description, bool containsManagedCode)
13+
{
14+
UniqueID = uniqueID;
15+
DisplayID = displayID;
16+
Description = description;
17+
ContainsManagedCode = containsManagedCode;
18+
}
19+
20+
public int UniqueID { get; }
21+
22+
public int DisplayID { get; }
23+
24+
public string Description { get; }
25+
26+
public bool ContainsManagedCode { get; }
27+
28+
public override int GetHashCode()
29+
{
30+
return UniqueID;
31+
}
32+
33+
public override bool Equals(object obj)
34+
{
35+
if(obj is AutomatedAnalysisTraceProcess)
36+
{
37+
return UniqueID == ((AutomatedAnalysisTraceProcess)obj).UniqueID;
38+
}
39+
40+
return false;
41+
}
42+
}
43+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.Collections.Generic;
2+
3+
namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis
4+
{
5+
public interface IAutomatedAnalysisTrace
6+
{
7+
IEnumerable<AutomatedAnalysisTraceProcess> Processes { get; }
8+
9+
StackView GetCPUStacks(AutomatedAnalysisTraceProcess process);
10+
}
11+
}

src/TraceEvent/AutomatedAnalysis/ProcessContext.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,24 @@ public sealed class ProcessContext
99
{
1010
private StackView _cpuStacks;
1111
private SymbolReader _symbolReader;
12+
private AutomatedAnalysisExecutionContext _executionContext;
1213

13-
public ProcessContext(AutomatedAnalysisExecutionContext executionContext, TraceProcess process)
14+
public ProcessContext(AutomatedAnalysisExecutionContext executionContext, AutomatedAnalysisTraceProcess process)
1415
{
15-
Process = process;
16+
_executionContext = executionContext;
17+
AutomatedAnalysisProcess = process;
18+
AutomatedAnalysisTraceLog traceLog = executionContext.Trace as AutomatedAnalysisTraceLog;
19+
if(traceLog != null)
20+
{
21+
Process = traceLog.TraceLog.Processes[(ProcessIndex)process.UniqueID];
22+
}
23+
1624
Issues = executionContext.Issues[process];
1725
_symbolReader = executionContext.SymbolReader;
1826
}
1927

28+
public AutomatedAnalysisTraceProcess AutomatedAnalysisProcess { get; }
29+
2030
public TraceProcess Process { get; }
2131

2232
public List<AutomatedAnalysisIssue> Issues { get; }
@@ -27,8 +37,7 @@ public StackView CPUStacks
2737
{
2838
if (_cpuStacks == null)
2939
{
30-
StackSource stackSource = Process.Log.CPUStacks(Process);
31-
_cpuStacks = new StackView(Process.Log, stackSource, _symbolReader);
40+
_cpuStacks = _executionContext.Trace.GetCPUStacks(AutomatedAnalysisProcess);
3241
}
3342
return _cpuStacks;
3443
}

src/TraceEvent/AutomatedAnalysis/StackView.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ public StackView(TraceLog traceLog, StackSource stackSource, SymbolReader symbol
2525
_traceLog = traceLog;
2626
_rawStackSource = stackSource;
2727
_symbolReader = symbolReader;
28-
LookupWarmNGENSymbols();
28+
if (traceLog != null && symbolReader != null)
29+
{
30+
LookupWarmNGENSymbols();
31+
}
2932
}
3033

3134
public CallTree CallTree
@@ -98,7 +101,7 @@ public CallTreeNodeBase GetCallTreeNode(string symbolName)
98101
}
99102

100103
// Check to see if we should attempt to load symbols.
101-
if (!_resolvedSymbolModules.Contains(symbolParts[0]))
104+
if (_traceLog != null && _symbolReader != null && !_resolvedSymbolModules.Contains(symbolParts[0]))
102105
{
103106
// Look for an unresolved symbols node for the module.
104107
string unresolvedSymbolsNodeName = symbolParts[0] + "!?";

0 commit comments

Comments
 (0)