in src/PerfView/JitStats.cs [17:375]
public static void ToHtml(TextWriter writer, TraceProcess stats, TraceLoadedDotNetRuntime runtime, string fileName)
{
JITStatsEx statsEx = JITStatsEx.Create(runtime);
var usersGuideFile = ClrStatsUsersGuide.WriteUsersGuide(fileName);
bool hasInliningEvents = runtime.JIT.Stats().InliningSuccesses.Count > 0 || runtime.JIT.Stats().InliningFailures.Count > 0;
writer.WriteLine("<H3><A Name=\"Stats_{0}\"><font color=\"blue\">JIT Stats for for Process {1,5}: {2}</font><A></H3>", stats.ProcessID, stats.ProcessID, stats.Name);
writer.WriteLine("<UL>");
{
if (!string.IsNullOrEmpty(stats.CommandLine))
{
writer.WriteLine("<LI>CommandLine: {0}</LI>", stats.CommandLine);
}
writer.WriteLine("<LI>Process CPU Time: {0:n0} msec</LI>", stats.CPUMSec);
writer.WriteLine("<LI>Guidance on JIT data:");
writer.WriteLine("<UL>");
{
writer.WriteLine("<LI> <A HREF=\"{0}#UnderstandingJITPerf\">JIT Perf Users Guide</A></LI>", usersGuideFile);
if (runtime.JIT.Stats().BackgroundJitThread != 0 || runtime.JIT.Stats().BackgroundJitAbortedAtMSec != 0)
{
writer.WriteLine("<LI>Background JIT compilation (System.Runtime.ProfileOptimize) in use - <A HREF=\"{0}#UnderstandingBackgroundJIT\">Guide</A></LI>", usersGuideFile);
writer.WriteLine(" <UL>");
if (runtime.JIT.Stats().RecordedModules.Count == 0)
{
writer.WriteLine(" <LI><font color=\"red\">This trace is missing some background JIT events, which could result in incorrect information in the background JIT blocking reason column.</font></LI>");
writer.WriteLine(" <LI><font color=\"red\">Re-collect the trace enabling \"Background JIT\" events on the collection menu to fix this.</font></LI>");
}
if (runtime.JIT.Stats().BackgroundJitAbortedAtMSec != 0)
{
writer.WriteLine(" <LI><font color=\"red\">WARNING: Background JIT aborted at {0:n3} Msec</font></LI>", runtime.JIT.Stats().BackgroundJitAbortedAtMSec);
writer.WriteLine(" <LI>The last assembly before the abort was '{0}' loaded {1} at {2:n3}</LI>",
runtime.JIT.Stats().LastAssemblyLoadNameBeforeAbort, runtime.JIT.Stats().LastAssemblyLoadBeforeAbortSuccessful ? "successfully" : "unsuccessfully",
runtime.JIT.Stats().LastAssemblyLoadBeforeAbortMSec);
}
writer.WriteLine(" </UL>");
}
else if (runtime.JIT.Stats().BackgroundJitThread == 0)
{
if (runtime.JIT.Stats().BackgroundJITEventsOn)
{
writer.WriteLine("<LI>Background JIT compilation (System.Runtime.ProfileOptimize) not in use - <A HREF=\"{0}#UnderstandingBackgroundJIT\">Guide</A></LI>", usersGuideFile);
writer.WriteLine("<UL><LI>If there is a lot of JIT time enabling this may improve startup performance</LI></UL>");
}
else
{
writer.WriteLine("<LI>Background JIT compilation (System.Runtime.ProfileOptimize) events are not being collected - <A HREF=\"{0}#UnderstandingBackgroundJIT\">Guide</A></LI>", usersGuideFile);
writer.WriteLine("<UL><LI>If you are interested in seeing them enable the 'Background JIT' checkbox in the 'Advanced' section of the collection dialog when collecting the data.</LI></UL>");
}
}
if (!runtime.IsTieredCompilationEnabled)
{
writer.WriteLine("<LI>Tiered compilation not in use - <A HREF=\"{0}#UnderstandingTieredCompilation\">Guide</A></LI>", usersGuideFile);
writer.WriteLine("<UL><LI>On .Net Core, enabling this may improve application performance</LI></UL>");
}
else
{
writer.WriteLine("<LI>Tiered compilation in use - <A HREF=\"{0}#UnderstandingTieredCompilation\">Guide</A></LI>", usersGuideFile);
}
}
writer.WriteLine("</UL>");
writer.WriteLine("</LI>");
writer.WriteLine("<LI>Raw data:");
writer.WriteLine("<UL>");
{
writer.WriteLine("<LI>Individual JIT Events <A HREF=\"#Events_{0}\">Html</A> | <A HREF=\"command:excel/{0}\">Excel</A></LI>", stats.ProcessID);
if (hasInliningEvents)
{
writer.WriteLine("<LI>Inlining Decisions <A HREF=\"#Inlining_{0}\">Html</A> | <A HREF=\"command:excelInlining/{0}\">Excel</A></LI>", stats.ProcessID);
}
else
{
writer.WriteLine("<LI><I>No JIT Inlining data available. Consider enabling the JITInlining option.</I></LI>");
}
if (runtime.JIT.Stats().BackgroundJitThread != 0 || runtime.JIT.Stats().BackgroundJitAbortedAtMSec != 0)
{
writer.WriteLine("<LI>Background Jit Diagnostics <A HREF=\"command:excelBackgroundDiag/{0}\">Excel</A></LI>", stats.ProcessID);
}
}
writer.WriteLine("</UL>");
writer.WriteLine("</LI>");
//
// Summary table by trigger
//
writer.WriteLine("<LI> Summary of jitting time by trigger:");
writer.WriteLine("<Center>");
writer.WriteLine("<Table Border=\"1\">");
writer.WriteLine("<TR>" +
"<TH Title=\"The reason why the JIT was invoked.\">Jitting Trigger</TH>" +
"<TH Title=\"The number of times the JIT was invoked\">Num Compilations</TH>" +
"<TH Title=\"The % of all compilations that triggered by this trigger\">% of total jitted compilations</TH>" +
"<TH Title=\"The total time used by all compilations with the given trigger\">Jit Time msec</TH>" +
"<TH Title=\"The total time used by all compilations with the given trigger as a % of total CPU time used for the process\">Jit Time (% of total process CPU)</TH>" +
"</TR>");
writer.WriteLine(FormatThreadingModelTableRow("TOTAL", runtime.JIT.Stats().Count, runtime.JIT.Stats().TotalCpuTimeMSec, stats, runtime));
writer.WriteLine(FormatThreadingModelTableRow(CompilationThreadKind.Foreground, runtime.JIT.Stats().CountForeground, runtime.JIT.Stats().TotalForegroundCpuTimeMSec, stats, runtime));
writer.WriteLine(FormatThreadingModelTableRow(CompilationThreadKind.MulticoreJitBackground, runtime.JIT.Stats().CountBackgroundMultiCoreJit, runtime.JIT.Stats().TotalBackgroundMultiCoreJitCpuTimeMSec, stats, runtime));
writer.WriteLine(FormatThreadingModelTableRow(CompilationThreadKind.TieredCompilationBackground, runtime.JIT.Stats().CountBackgroundTieredCompilation, runtime.JIT.Stats().TotalBackgroundTieredCompilationCpuTimeMSec, stats, runtime));
writer.WriteLine("</Table>");
writer.WriteLine("</Center>");
writer.WriteLine("</LI>");
//
// Module table
//
// Sort the module list by Jit Time;
List<string> moduleNames = new List<string>(statsEx.TotalModuleStats.Keys);
moduleNames.Sort(delegate (string x, string y)
{
double diff = statsEx.TotalModuleStats[y].TotalCpuTimeMSec - statsEx.TotalModuleStats[x].TotalCpuTimeMSec;
if (diff > 0)
{
return 1;
}
else if (diff < 0)
{
return -1;
}
return 0;
});
writer.WriteLine("<LI> Summary of jitting time by module:</P>");
writer.WriteLine("<Center>");
writer.WriteLine("<Table Border=\"1\">");
writer.Write("<TR>" +
"<TH Title=\"The name of the module\">Name</TH>" +
"<TH Title=\"The total CPU time spent jitting for all methods in this module\">JitTime<BR/>msec</TH>" +
"<TH Title=\"The number of times the JIT was invoked for methods in this module\">Num Compilations</TH>" +
"<TH Title=\"The total amount of IL processed by the JIT for all methods in this module\">IL Size</TH>" +
"<TH Title=\"The total amount of native code produced by the JIT for all methods in this module\">Native Size</TH>" +
"<TH Title=\"Time spent jitting synchronously to produce code for methods that were just invoked. These compilations often consume time at startup.\">" + GetLongNameForThreadClassification(CompilationThreadKind.Foreground) + "<BR/>msec</TH>" +
"<TH Title=\"Time spent jitting asynchronously to produce code for methods the runtime speculates will be invoked in the future.\">" + GetLongNameForThreadClassification(CompilationThreadKind.MulticoreJitBackground) + "<BR/>msec</TH>" +
"<TH Title=\"Time spent jitting asynchronously to produce code for methods that is more optimized than their initial code.\">" + GetLongNameForThreadClassification(CompilationThreadKind.TieredCompilationBackground) + "<BR/>msec</TH>");
if (runtime.JIT.Stats().IsJitAllocSizePresent)
{
writer.Write("<TH Title=\"The total amount of heap memory requested for the code produced by the JIT for all methods in this module. \">JIT Hotcode request size</TH>");
writer.Write("<TH Title=\"The total amount of heap memory requested for the read-only data of code produced by the JIT for all methods in this module. \">JIT RO-data request size</TH>");
writer.Write("<TH Title=\"The total amount of heap memory allocated for the code produced by the JIT for all methods in this module. \">Allocated size for JIT code</TH>");
}
writer.WriteLine("</TR>");
string moduleTableRow = "<TR>" +
"<TD Align=\"Left\">{0}</TD>" +
"<TD Align=\"Center\">{1:n1}</TD>" +
"<TD Align=\"Center\">{2:n0}</TD>" +
"<TD Align=\"Center\">{3:n0}</TD>" +
"<TD Align=\"Center\">{4:n0}</TD>" +
"<TD Align=\"Center\">{5:n1}</TD>" +
"<TD Align=\"Center\">{6:n1}</TD>" +
"<TD Align=\"Center\">{7:n1}</TD>";
writer.Write(moduleTableRow,
"TOTAL",
runtime.JIT.Stats().TotalCpuTimeMSec,
runtime.JIT.Stats().Count,
runtime.JIT.Stats().TotalILSize,
runtime.JIT.Stats().TotalNativeSize,
runtime.JIT.Stats().TotalForegroundCpuTimeMSec,
runtime.JIT.Stats().TotalBackgroundMultiCoreJitCpuTimeMSec,
runtime.JIT.Stats().TotalBackgroundTieredCompilationCpuTimeMSec);
string allocSizeColumns = "";
if (runtime.JIT.Stats().IsJitAllocSizePresent)
{
allocSizeColumns +=
"<TD Align=\"Center\">{0:n0}</TD>" +
"<TD Align=\"Center\">{1:n0}</TD>" +
"<TD Align=\"Center\">{2:n0}</TD>";
writer.Write(allocSizeColumns,
runtime.JIT.Stats().TotalHotCodeAllocSize,
runtime.JIT.Stats().TotalRODataAllocSize,
runtime.JIT.Stats().TotalAllocSizeForJitCode);
}
writer.WriteLine();
foreach (string moduleName in moduleNames)
{
JITStats info = statsEx.TotalModuleStats[moduleName];
writer.Write(moduleTableRow,
moduleName.Length == 0 ? "<UNKNOWN>" : moduleName,
info.TotalCpuTimeMSec,
info.Count,
info.TotalILSize,
info.TotalNativeSize,
info.TotalForegroundCpuTimeMSec,
info.TotalBackgroundMultiCoreJitCpuTimeMSec,
info.TotalBackgroundTieredCompilationCpuTimeMSec);
if (runtime.JIT.Stats().IsJitAllocSizePresent)
{
writer.Write(allocSizeColumns,
info.TotalHotCodeAllocSize,
info.TotalRODataAllocSize,
info.TotalAllocSizeForJitCode);
}
writer.WriteLine("</TR>");
}
writer.WriteLine("</Table>");
writer.WriteLine("</Center>");
writer.WriteLine("</LI>");
}
writer.WriteLine("</UL>");
bool backgroundJitEnabled = runtime.JIT.Stats().BackgroundJitThread != 0;
writer.WriteLine("<HR/>");
writer.WriteLine("<H4><A Name=\"Events_{0}\">Individual JIT Events for Process {1,5}: {2}<A></H4>", stats.ProcessID, stats.ProcessID, stats.Name);
// We limit the number of JIT events we ut on the page because it makes the user exerience really bad (browsers crash)
const int maxEvents = 1000;
if (runtime.JIT.Methods.Count >= maxEvents)
{
writer.WriteLine("<p><Font color=\"red\">Warning: Truncating JIT events to " + maxEvents + ". <A HREF=\"command:excel/{0}\">View in excel</A> to look all of them.</font></p>", stats.ProcessID);
}
bool showOptimizationTiers = ShouldShowOptimizationTiers(runtime);
writer.WriteLine("<Center>");
writer.WriteLine("<Table Border=\"1\">");
writer.Write(
"<TR>" +
"<TH>Start<BR/>(msec)</TH>" +
"<TH>Jit Time<BR/>(msec)</TH>" +
"<TH>IL<BR/>Size</TH>" +
"<TH>Native<BR/>Size</TH>");
if (runtime.JIT.Stats().IsJitAllocSizePresent)
{
writer.Write(
"<TH>JIT Hotcode<BR/>request size</TH>" +
"<TH>JIT RO-data<BR/>request size</TH>" +
"<TH>Allocated size<BR/>for JIT code</TH>" +
"<TH>JIT Allocation<BR/>Flags</TH>"
);
}
if (showOptimizationTiers)
{
writer.Write("<TH Title=\"The optimization tier at which the method was jitted.\">Optimization<BR/>Tier</TH>");
}
writer.Write(
"<TH>Method Name</TH>" +
"<TH Title=\"Is Jit compilation occurring in the background for Multicore JIT (MC), in the background for tiered compilation (TC), or in the foreground on first execution of a method (FG).\">Trigger</TH>" +
"<TH>Module</TH>");
if (backgroundJitEnabled)
{
writer.Write(
"<TH Title=\"How far ahead of the method usage was relative to the background JIT operation.\">Distance Ahead</TH>" +
"<TH Title=\"Why the method was not JITTed in the background.\">Background JIT Blocking Reason</TH>");
}
writer.WriteLine("</TR>");
int eventCount = 0;
foreach (TraceJittedMethod _event in runtime.JIT.Methods)
{
writer.Write(
"<TR>" +
"<TD Align=\"Center\">{0:n3}</TD>" +
"<TD Align=\"Center\">{1:n1}</TD>" +
"<TD Align=\"Center\">{2:n0}</TD>" +
"<TD Align=\"Center\">{3:n0}</TD>",
_event.StartTimeMSec,
_event.CompileCpuTimeMSec,
_event.ILSize,
_event.NativeSize);
if (_event.IsJitAllocSizePresent)
{
writer.Write(
"<TD Align=\"Center\">{0}</TD>" +
"<TD Align=\"Center\">{1}</TD>" +
"<TD Align=\"Center\">{2}</TD>" +
"<TD Align=\"Center\">{3}</TD>",
_event.JitHotCodeRequestSize,
_event.JitRODataRequestSize,
_event.AllocatedSizeForJitCode,
_event.JitAllocFlag
);
}
if (showOptimizationTiers)
{
writer.Write(
"<TD Align=\"Center\">{0}</TD>",
_event.OptimizationTier == OptimizationTier.Unknown ? string.Empty : _event.OptimizationTier.ToString());
}
writer.Write(
"<TD Align=Left>{0}</TD>" +
"<TD Align=\"Center\">{1}</TD>" +
"<TD Align=\"Center\">{2}</TD>",
_event.MethodName ?? " ",
GetShortNameForThreadClassification(_event.CompilationThreadKind),
_event.ModuleILPath.Length != 0 ? Path.GetFileName(_event.ModuleILPath) : "<UNKNOWN>");
if (backgroundJitEnabled)
{
writer.Write(
"<TD Align=\"Center\">{0:n3}</TD>" +
"<TD Align=\"Left\">{1}</TD>",
_event.DistanceAhead,
_event.CompilationThreadKind == CompilationThreadKind.MulticoreJitBackground ? "Not blocked" : _event.BlockedReason);
}
writer.WriteLine("</TR>");
eventCount++;
if (eventCount >= maxEvents)
{
break;
}
}
writer.WriteLine("</Table>");
writer.WriteLine("</Center>");
if (hasInliningEvents)
{
writer.WriteLine("<HR/>");
writer.WriteLine("<A Name=\"Inlining_{0}\">", stats.ProcessID);
writer.WriteLine("<H4>Successful Inlinings for Process {0,5}: {1}<A></H4>", stats.ProcessID, stats.Name);
writer.WriteLine("<Center>");
writer.WriteLine("<Table Border=\"1\">");
writer.Write("<TR><TH>Method Begin Compiled</TH><TH>Inliner</TH><TH>Inlinee</TH></TR>");
foreach (InliningSuccessResult success in runtime.JIT.Stats().InliningSuccesses)
{
writer.Write("<TR><TD>{0}</TD><TD>{1}</TD><TD>{2}</TD></TR>", success.MethodBeingCompiled, success.Inliner, success.Inlinee);
}
writer.WriteLine("</Table>");
writer.WriteLine("</Center>");
writer.WriteLine("<H4>Failed Inlinings for Process {0,5}: {1}<A></H4>", stats.ProcessID, stats.Name);
writer.WriteLine("<Center>");
writer.WriteLine("<Table Border=\"1\">");
writer.Write("<TR><TH>Method Begin Compiled</TH><TH>Inliner</TH><TH>Inlinee</TH><TH>Failure Reason</TH></TR>");
foreach (InliningFailureResult failure in runtime.JIT.Stats().InliningFailures)
{
writer.Write("<TR><TD>{0}</TD><TD>{1}</TD><TD>{2}</TD><TD>{3}</TD></TR>", failure.MethodBeingCompiled, failure.Inliner, failure.Inlinee, failure.Reason);
}
writer.WriteLine("</Table>");
writer.WriteLine("</Center>");
}
writer.WriteLine("<HR/><HR/><BR/><BR/>");
}