Skip to content

Commit b33df66

Browse files
authored
Merge pull request microsoft#1232 from davidwrighton/runtimeops_v2
Runtime Loader advanced event view
2 parents 2585698 + 48b4d15 commit b33df66

File tree

13 files changed

+1093
-6
lines changed

13 files changed

+1093
-6
lines changed

src/PerfView/ClrStats.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved
2+
using Microsoft.Diagnostics.Tracing;
23
using Microsoft.Diagnostics.Tracing.Analysis;
34
using Microsoft.Diagnostics.Utilities;
45
using System.Collections.Generic;
@@ -9,9 +10,9 @@ namespace Stats
910
{
1011
internal static class ClrStats
1112
{
12-
public enum ReportType { JIT, GC };
13+
public enum ReportType { JIT, GC, RuntimeLoader };
1314

14-
public static void ToHtml(TextWriter writer, List<TraceProcess> perProc, string fileName, string title, ReportType type, bool justBody = false, bool doServerGCReport = false)
15+
public static void ToHtml(TextWriter writer, List<TraceProcess> perProc, string fileName, string title, ReportType type, bool justBody = false, bool doServerGCReport = false, RuntimeLoaderStatsData runtimeOpsStats = null)
1516
{
1617
if (!justBody)
1718
{
@@ -33,6 +34,10 @@ public static void ToHtml(TextWriter writer, List<TraceProcess> perProc, string
3334
{
3435
sortedProcs.Sort((TraceProcess p1, TraceProcess p2) => { return -p1.LoadedDotNetRuntime().GC.Stats().MaxSizePeakMB.CompareTo(p2.LoadedDotNetRuntime().GC.Stats().MaxSizePeakMB); });
3536
}
37+
else if (type == ReportType.RuntimeLoader)
38+
{
39+
sortedProcs.Sort((TraceProcess p1, TraceProcess p2) => { return -RuntimeLoaderStats.TotalCPUMSec(p1, runtimeOpsStats).CompareTo(RuntimeLoaderStats.TotalCPUMSec(p2, runtimeOpsStats)); });
40+
}
3641

3742
int count = sortedProcs.Count;
3843

@@ -53,6 +58,11 @@ public static void ToHtml(TextWriter writer, List<TraceProcess> perProc, string
5358
continue;
5459
}
5560

61+
if (type == ReportType.RuntimeLoader && !RuntimeLoaderStats.IsInteresting(data, runtimeOpsStats))
62+
{
63+
continue;
64+
}
65+
5666
var id = Shorten(data.CommandLine);
5767
if (string.IsNullOrEmpty(id))
5868
{
@@ -81,6 +91,11 @@ public static void ToHtml(TextWriter writer, List<TraceProcess> perProc, string
8191
{
8292
Stats.JitStats.ToHtml(writer, stats, mang, fileName);
8393
}
94+
95+
if (type == ReportType.RuntimeLoader && RuntimeLoaderStats.IsInteresting(stats, runtimeOpsStats))
96+
{
97+
Stats.RuntimeLoaderStats.ToHtml(writer, stats, fileName, runtimeOpsStats);
98+
}
8499
}
85100

86101
writer.WriteLine("<BR/><BR/><BR/><BR/><BR/><BR/><BR/><BR/><BR/><BR/>");

src/PerfView/CommandLineArgs.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ public static string GetHelpString(int maxLineWidth)
129129
public bool NetworkCapture; // Capture the full packets of every incoming and outgoing packet
130130
public bool NetMonCapture; // Capture a NetMon-only trace as well as a standard ETW trace (implies NetworkCapture)
131131
public bool CCWRefCount; // Capture CCW references count increasing and decreasing
132+
public bool RuntimeLoading; // Capture information about runtime loading such as R2R and type load events
132133

133134
public bool Wpr; // Collect like WPR (no zip, puts NGEN pdbs in a .ngenpdbs directory).
134135

@@ -505,6 +506,7 @@ private void SetupCommandLine(CommandLineParser parser)
505506
parser.DefineOptionalQualifier("DisableInlining", ref DisableInlining, "Turns off inlining (but only affects processes that start after trace start.");
506507
parser.DefineOptionalQualifier("JITInlining", ref JITInlining, "Turns on logging of successful and failed JIT inlining attempts.");
507508
parser.DefineOptionalQualifier("CCWRefCount", ref CCWRefCount, "Turns on logging of information about .NET Native CCW reference counting.");
509+
parser.DefineOptionalQualifier("RuntimeLoading", ref RuntimeLoading, "Turn on logging of runtime loading operations.");
508510
parser.DefineOptionalQualifier("OSHeapProcess", ref OSHeapProcess, "Turn on per-allocation profiling of allocation from the OS heap for the process with the given process ID.");
509511
parser.DefineOptionalQualifier("OSHeapExe", ref OSHeapExe, "Turn on per-allocation profiling of allocation from the OS heap for the process with the given EXE (only filename WITH extension).");
510512

src/PerfView/CommandProcessor.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,13 @@ public void Start(CommandLineArgs parsedArgs)
353353
profilerKeywords |= ETWClrProfilerTraceEventParser.Keywords.DisableInlining;
354354
}
355355

356+
if (parsedArgs.RuntimeLoading)
357+
{
358+
parsedArgs.ClrEvents |= ClrTraceEventParser.Keywords.CompilationDiagnostic;
359+
parsedArgs.ClrEvents |= ClrTraceEventParser.Keywords.MethodDiagnostic;
360+
parsedArgs.ClrEvents |= ClrTraceEventParser.Keywords.TypeDiagnostic;
361+
}
362+
356363
if (profilerKeywords != 0)
357364
{
358365
InstallETWClrProfiler(LogFile, (int)profilerKeywords);
@@ -3021,6 +3028,11 @@ private static string ParsedArgsAsString(string command, CommandLineArgs parsedA
30213028
cmdLineArgs += " /CCWRefCount";
30223029
}
30233030

3031+
if (parsedArgs.RuntimeLoading)
3032+
{
3033+
cmdLineArgs += " /RuntimeLoading";
3034+
}
3035+
30243036
// TODO FIX NOW this is sort ugly fix is so that commands are an enum
30253037
if (command == null)
30263038
{

src/PerfView/Dialogs/RunCommandDialog.xaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,11 @@
320320
</TextBlock>
321321
<CheckBox Grid.Row="0" Grid.Column="11" Name="CCWRefCountCheckBox" VerticalAlignment="Center"/>
322322

323-
<CheckBox Grid.Row="0" Grid.Column="13" Name="Unused2" Visibility="Hidden" VerticalAlignment="Center"/>
323+
<TextBlock Grid.Row="0" Grid.Column="12" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="8,2,5,2"
324+
ToolTip="Log extended information about .NET Runtime loading including R2R and type load events." >
325+
<Hyperlink Command="Help" CommandParameter="RuntimeLoadingCheckBox">.NET Loader:</Hyperlink>
326+
</TextBlock>
327+
<CheckBox Grid.Row="0" Grid.Column="13" Name="RuntimeLoadingCheckBox" VerticalAlignment="Center" />
324328
</Grid>
325329

326330
<!-- Check boxes for more clr events (GC Heap related) -->

src/PerfView/Dialogs/RunCommandDialog.xaml.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ public RunCommandDialog(CommandLineArgs args, MainWindow mainWindow, bool isColl
202202
CCWRefCountCheckBox.IsChecked = true;
203203
}
204204

205+
if (args.RuntimeLoading)
206+
{
207+
RuntimeLoadingCheckBox.IsChecked = true;
208+
}
209+
205210
if (args.DumpHeap)
206211
{
207212
HeapSnapshotCheckBox.IsChecked = true;
@@ -774,6 +779,8 @@ private void OKButtonClick(object sender, RoutedEventArgs e)
774779
}
775780

776781
m_args.CCWRefCount = CCWRefCountCheckBox.IsChecked ?? false;
782+
m_args.RuntimeLoading = RuntimeLoadingCheckBox.IsChecked ?? false;
783+
777784
m_args.DumpHeap = HeapSnapshotCheckBox.IsChecked ?? false;
778785

779786
if (providers.Length > 0)

src/PerfView/PerfViewData.cs

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,11 @@ protected virtual string DoCommand(string command, StatusBar worker, out Action
10611061
return DoCommand(command, worker);
10621062
}
10631063

1064+
protected virtual string DoCommand(Uri commandUri, StatusBar worker, out Action continuation)
1065+
{
1066+
return DoCommand(commandUri.LocalPath, worker, out continuation);
1067+
}
1068+
10641069
public override void Open(Window parentWindow, StatusBar worker, Action doAfter)
10651070
{
10661071
if (Viewer == null)
@@ -1087,7 +1092,7 @@ public override void Open(Window parentWindow, StatusBar worker, Action doAfter)
10871092
Viewer.StatusBar.StartWork("Following Hyperlink", delegate ()
10881093
{
10891094
Action continuation;
1090-
var message = DoCommand(e.Uri.LocalPath, Viewer.StatusBar, out continuation);
1095+
var message = DoCommand(e.Uri, Viewer.StatusBar, out continuation);
10911096
Viewer.StatusBar.EndWork(delegate ()
10921097
{
10931098
if (message != null)
@@ -3531,6 +3536,82 @@ protected override void WriteHtmlBody(TraceLog dataFile, TextWriter writer, stri
35313536
private Dictionary<int/*pid*/, Microsoft.Diagnostics.Tracing.Analysis.TraceProcess> m_gcStats;
35323537
}
35333538

3539+
public class PerfViewRuntimeLoaderStats : PerfViewHtmlReport
3540+
{
3541+
public PerfViewRuntimeLoaderStats(PerfViewFile dataFile) : base(dataFile, "Runtime Loader") { }
3542+
protected override string DoCommand(Uri commandUri, StatusBar worker, out Action continuation)
3543+
{
3544+
continuation = null;
3545+
3546+
string command = commandUri.LocalPath;
3547+
string textStr = "txt/";
3548+
string csvStr = "csv/";
3549+
bool text = command.StartsWith(textStr);
3550+
bool csv = command.StartsWith(csvStr);
3551+
3552+
if (text || csv)
3553+
{
3554+
var rest = command.Substring(textStr.Length);
3555+
3556+
3557+
bool tree = true;
3558+
List<string> filters = null;
3559+
if (!String.IsNullOrEmpty(commandUri.Query))
3560+
{
3561+
filters = new List<string>();
3562+
tree = commandUri.Query.Contains("TreeView");
3563+
if (commandUri.Query.Contains("JIT"))
3564+
filters.Add("JIT");
3565+
if (commandUri.Query.Contains("R2R_Found"))
3566+
filters.Add("R2R_Found");
3567+
if (commandUri.Query.Contains("R2R_Failed"))
3568+
filters.Add("R2R_Failed");
3569+
if (commandUri.Query.Contains("TypeLoad"))
3570+
filters.Add("TypeLoad");
3571+
if (commandUri.Query.Contains("AssemblyLoad"))
3572+
filters.Add("AssemblyLoad");
3573+
}
3574+
string identifier = $"{(tree?"Tree":"Flat")}_";
3575+
if (filters != null)
3576+
{
3577+
foreach (var filter in filters)
3578+
{
3579+
identifier = identifier + "_" + filter;
3580+
}
3581+
}
3582+
3583+
var startMSec = double.Parse(rest.Substring(rest.IndexOf(',') + 1));
3584+
var processId = int.Parse(rest.Substring(0, rest.IndexOf(',')));
3585+
var processData = m_runtimeData.GetProcessDataFromProcessIDAndTimestamp(processId, startMSec);
3586+
3587+
var txtFile = CacheFiles.FindFile(FilePath, ".runtimeLoaderstats." + processId.ToString() + "_" + ((long)startMSec).ToString() + "_" + identifier + (csv ? ".csv" : ".txt"));
3588+
if (!File.Exists(txtFile) || File.GetLastWriteTimeUtc(txtFile) < File.GetLastWriteTimeUtc(FilePath) ||
3589+
File.GetLastWriteTimeUtc(txtFile) < File.GetLastWriteTimeUtc(SupportFiles.MainAssemblyPath))
3590+
{
3591+
Stats.RuntimeLoaderStats.ToTxt(txtFile, processData, filters.ToArray(), tree);
3592+
}
3593+
Command.Run(Command.Quote(txtFile), new CommandOptions().AddStart().AddTimeout(CommandOptions.Infinite));
3594+
System.Threading.Thread.Sleep(500); // Give it time to start a bit.
3595+
return "Opening Txt " + txtFile;
3596+
}
3597+
return "Unknown command " + command;
3598+
}
3599+
3600+
protected override void WriteHtmlBody(TraceLog dataFile, TextWriter writer, string fileName, TextWriter log)
3601+
{
3602+
using (var source = dataFile.Events.GetSource())
3603+
{
3604+
Microsoft.Diagnostics.Tracing.Analysis.TraceLoadedDotNetRuntimeExtensions.NeedLoadedDotNetRuntimes(source);
3605+
CLRRuntimeActivityComputer runtimeLoaderComputer = new CLRRuntimeActivityComputer(source);
3606+
source.Process();
3607+
m_runtimeData = runtimeLoaderComputer.RuntimeLoaderData;
3608+
Stats.ClrStats.ToHtml(writer, Microsoft.Diagnostics.Tracing.Analysis.TraceProcessesExtensions.Processes(source).ToList(), fileName, "Runtime Loader", Stats.ClrStats.ReportType.RuntimeLoader, true, runtimeOpsStats : m_runtimeData);
3609+
}
3610+
}
3611+
3612+
private RuntimeLoaderStatsData m_runtimeData;
3613+
}
3614+
35343615
public class PerfViewJitStats : PerfViewHtmlReport
35353616
{
35363617
public PerfViewJitStats(PerfViewFile dataFile) : base(dataFile, "JITStats") { }
@@ -6921,6 +7002,9 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
69217002
bool hasGCEvents = false;
69227003
bool hasProjectNExecutionTracingEvents = false;
69237004
bool hasDefenderEvents = false;
7005+
bool hasTypeLoad = false;
7006+
bool hasAssemblyLoad = false;
7007+
bool hasJIT = false;
69247008

69257009
var stackEvents = new List<TraceEventCounts>();
69267010
foreach (var counts in tracelog.Stats)
@@ -6981,6 +7065,19 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
69817065
hasDefenderEvents = true;
69827066
}
69837067

7068+
if (name.StartsWith("Method/JittingStarted"))
7069+
{
7070+
hasJIT = true;
7071+
}
7072+
if (name.StartsWith("TypeLoad/Start"))
7073+
{
7074+
hasTypeLoad = true;
7075+
}
7076+
if (name.StartsWith("Loader/AssemblyLoad"))
7077+
{
7078+
hasAssemblyLoad = true;
7079+
}
7080+
69847081
if (counts.StackCount > 0)
69857082
{
69867083
hasAnyStacks = true;
@@ -7288,6 +7385,11 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
72887385

72897386
advanced.Children.Add(new PerfViewJitStats(this));
72907387

7388+
if (hasJIT || hasAssemblyLoad || hasTypeLoad)
7389+
{
7390+
advanced.Children.Add(new PerfViewRuntimeLoaderStats(this));
7391+
}
7392+
72917393
advanced.Children.Add(new PerfViewEventStats(this));
72927394

72937395
m_Children.Add(new PerfViewEventSource(this));
@@ -8420,6 +8522,8 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
84208522

84218523
bool hasGC = false;
84228524
bool hasJIT = false;
8525+
bool hasTypeLoad = false;
8526+
bool hasAssemblyLoad = false;
84238527
if (m_traceLog != null)
84248528
{
84258529
foreach (TraceEventCounts eventStats in m_traceLog.Stats)
@@ -8432,6 +8536,14 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
84328536
{
84338537
hasJIT = true;
84348538
}
8539+
else if (eventStats.EventName.StartsWith("TypeLoad/Start"))
8540+
{
8541+
hasTypeLoad = true;
8542+
}
8543+
else if (eventStats.EventName.StartsWith("Loader/AssemblyLoad"))
8544+
{
8545+
hasAssemblyLoad = true;
8546+
}
84358547
}
84368548
}
84378549

@@ -8466,6 +8578,11 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
84668578
{
84678579
advanced.AddChild(new PerfViewJitStats(this));
84688580
}
8581+
8582+
if (hasJIT || hasTypeLoad || hasAssemblyLoad)
8583+
{
8584+
advanced.AddChild(new PerfViewRuntimeLoaderStats(this));
8585+
}
84698586
}
84708587

84718588
if (memory.Children.Count > 0)
@@ -8633,6 +8750,8 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
86338750
bool hasGCAllocationTicks = false;
86348751
bool hasObjectUpdate = false;
86358752
bool hasMemAllocStacks = false;
8753+
bool hasTypeLoad = false;
8754+
bool hasAssemblyLoad = false;
86368755
if (m_traceLog != null)
86378756
{
86388757
foreach (TraceEventCounts eventStats in m_traceLog.Stats)
@@ -8666,6 +8785,14 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
86668785
{
86678786
hasMemAllocStacks = true;
86688787
}
8788+
else if (eventStats.EventName.StartsWith("TypeLoad/Start"))
8789+
{
8790+
hasTypeLoad = true;
8791+
}
8792+
else if (eventStats.EventName.StartsWith("Loader/AssemblyLoad"))
8793+
{
8794+
hasAssemblyLoad = true;
8795+
}
86698796
}
86708797
}
86718798

@@ -8712,6 +8839,11 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
87128839
{
87138840
advanced.AddChild(new PerfViewJitStats(this));
87148841
}
8842+
8843+
if (hasJIT || hasTypeLoad || hasAssemblyLoad)
8844+
{
8845+
advanced.AddChild(new PerfViewRuntimeLoaderStats(this));
8846+
}
87158847
}
87168848

87178849
if (memory.Children.Count > 0)

0 commit comments

Comments
 (0)