Skip to content
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

Show aggregate analyzer report #452

Merged
merged 3 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 87 additions & 1 deletion src/StructuredLogViewer.Core/Analyzers/CscTaskAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Microsoft.Build.Logging.StructuredLogger
{
public class CscTaskAnalyzer
{
public static void Analyze(Task task)
public static Folder Analyze(Task task)
{
Folder analyzerReport = null;
Folder parent = null;
Expand All @@ -25,6 +28,10 @@ public static void Analyze(Task task)
lastAssembly.Name = text;
analyzerReport.AddChild(lastAssembly);
parent = lastAssembly;

// Remove the message since we are already using the same text for the containing folder
message.Parent.Children.Remove(message);
continue;
}

if (parent != null)
Expand All @@ -33,6 +40,85 @@ public static void Analyze(Task task)
parent.AddChild(message);
}
}

return analyzerReport;
}

public static void CreateMergedReport(Folder destination, Folder[] analyzerReports)
{
var assemblyData = new Dictionary<string, AnalyzerAssemblyData>();
foreach (var report in analyzerReports)
{
foreach (var folder in report.Children.OfType<Folder>())
{
var data = AnalyzerAssemblyData.FromFolder(folder);
if (assemblyData.TryGetValue(data.Name, out var existingData))
{
existingData.CombineWith(data);
}
else
{
assemblyData.Add(data.Name, data);
}
}
}

foreach (var data in assemblyData.OrderByDescending(data => data.Value.TotalTime))
{
var folder = new Folder { Name = $"{TextUtilities.DisplayDuration(data.Value.TotalTime, showZero: true)} {data.Value.Name}" };
foreach (var analyzer in data.Value.AnalyzerTimes.OrderByDescending(analyzer => analyzer.Value).ThenBy(analyzer => analyzer.Key, StringComparer.OrdinalIgnoreCase))
{
folder.AddChild(new Item { Name = analyzer.Key, Text = TextUtilities.DisplayDuration(analyzer.Value, showZero: true) });
}

destination.AddChild(folder);
}
}

private sealed class AnalyzerAssemblyData
{
public readonly string Name;
public TimeSpan TotalTime;
public readonly Dictionary<string, TimeSpan> AnalyzerTimes = new Dictionary<string, TimeSpan>();

public AnalyzerAssemblyData(string name, TimeSpan totalTime)
{
Name = name;
TotalTime = totalTime;
}

public void CombineWith(AnalyzerAssemblyData other)
{
Debug.Assert(Name == other.Name);
TotalTime += other.TotalTime;
foreach (var pair in other.AnalyzerTimes)
{
_ = AnalyzerTimes.TryGetValue(pair.Key, out var existingTime);
AnalyzerTimes[pair.Key] = existingTime + pair.Value;
}
}

public static AnalyzerAssemblyData FromFolder(Folder folder)
{
var (assemblyName, assemblyTime) = ParseLine(folder.Name);
var data = new AnalyzerAssemblyData(assemblyName, assemblyTime);
foreach (var message in folder.Children.OfType<Message>())
{
var (analyzerName, analyzerTime) = ParseLine(message.Text);
data.AnalyzerTimes[analyzerName] = analyzerTime;
}

return data;

static (string name, TimeSpan time) ParseLine(string line)
{
var columns = line.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
if (!double.TryParse(columns[0].Trim(), out var totalTimeSeconds))
totalTimeSeconds = 0;

return (columns[2].Trim(), TimeSpan.FromSeconds(totalTimeSeconds));
}
}
}
}
}
13 changes: 12 additions & 1 deletion src/StructuredLogViewer.Core/BuildAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class BuildAnalyzer
private ResolveAssemblyReferenceAnalyzer resolveAssemblyReferenceAnalyzer;
private int index;
private Dictionary<string, TimeSpan> taskDurations = new Dictionary<string, TimeSpan>();
private readonly List<Folder> analyzerReports = new List<Folder>();

public BuildAnalyzer(Build build)
{
Expand Down Expand Up @@ -204,6 +205,12 @@ private void PostAnalyzeBuild(Build build)
top10Tasks.AddChild(new Item { Name = kvp.Key, Text = TextUtilities.DisplayDuration(kvp.Value) });
}
}

if (analyzerReports.Count > 0)
{
var analyzerReportSummary = build.GetOrCreateNodeWithName<Folder>($"Analyzer Summary");
CscTaskAnalyzer.CreateMergedReport(analyzerReportSummary, analyzerReports.ToArray());
}
}

private void PostAnalyzeProject(Project project)
Expand Down Expand Up @@ -263,7 +270,11 @@ private void AnalyzeTask(Task task)
}
else if (task.Name == "Csc")
{
CscTaskAnalyzer.Analyze(task);
var analyzerReport = CscTaskAnalyzer.Analyze(task);
if (analyzerReport is not null)
{
analyzerReports.Add(analyzerReport);
}
}

doubleWritesAnalyzer.AnalyzeTask(task);
Expand Down
4 changes: 2 additions & 2 deletions src/StructuredLogger/TextUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,11 @@ public static string ParseQuotedSubstring(string text)
return text.Substring(firstQuote + 1, secondQuote - firstQuote - 1);
}

public static string DisplayDuration(TimeSpan span)
public static string DisplayDuration(TimeSpan span, bool showZero = false)
{
if (span.TotalMilliseconds < 1)
{
return "";
return showZero ? "0 ms" : "";
}

if (span.TotalSeconds > 3600)
Expand Down