diff --git a/src/PerfView/CommandLineArgs.cs b/src/PerfView/CommandLineArgs.cs
index d41dc49da..dbc0018fa 100644
--- a/src/PerfView/CommandLineArgs.cs
+++ b/src/PerfView/CommandLineArgs.cs
@@ -210,6 +210,7 @@ public bool ShouldZip
public DateTime EndTime;
public bool ForceNgenRundown;
public bool DumpHeap;
+ public bool DisableDotNetVersionLogging;
// Collect options
public bool NoGui;
@@ -561,6 +562,8 @@ private void SetupCommandLine(CommandLineParser parser)
"Displays the hexadecimal address rather than ? when the address is unknown.");
parser.DefineOptionalQualifier("ShowOptimizationTiers", ref ShowOptimizationTiers,
"Displays the optimization tier of each code version executed for the method.");
+ parser.DefineOptionalQualifier("DisableDotNetVersionLogging", ref DisableDotNetVersionLogging,
+ "Disables capturing of .NET version information during collection.");
parser.DefineOptionalQualifier("NoGui", ref NoGui,
"Use the Command line version of the command (like on ARM). Brings up a console window. For batch scripts/automation use /LogFile instead (see users guide under 'Scripting' for more).");
parser.DefineOptionalQualifier("SafeMode", ref SafeMode, "Turn off parallelism and other risky features.");
diff --git a/src/PerfView/CommandProcessor.cs b/src/PerfView/CommandProcessor.cs
index abcebfcae..3bc34fd33 100644
--- a/src/PerfView/CommandProcessor.cs
+++ b/src/PerfView/CommandProcessor.cs
@@ -109,6 +109,8 @@ public void Run(CommandLineArgs parsedArgs)
Command cmd = null;
try
{
+ DotNetVersionLogger.Start();
+
Start(parsedArgs);
Thread.Sleep(100); // Allow time for the start rundown events OS events to happen.
DateTime startTime = DateTime.Now;
@@ -153,6 +155,7 @@ public void Run(CommandLineArgs parsedArgs)
}
finally
{
+ DotNetVersionLogger.Stop();
if (!success)
{
if (cmd != null)
@@ -205,6 +208,7 @@ public void Collect(CommandLineArgs parsedArgs)
}
else
{
+ DotNetVersionLogger.Start();
Start(parsedArgs);
WaitUntilCollectionDone(collectionCompleted, parsedArgs, DateTime.Now);
if (m_aborted)
@@ -218,6 +222,7 @@ public void Collect(CommandLineArgs parsedArgs)
}
finally
{
+ DotNetVersionLogger.Stop();
collectionCompleted.Set(); // This ensures that the GUI window closes.
if (!success)
{
@@ -2984,6 +2989,11 @@ public static string ParsedArgsAsString(string command, CommandLineArgs parsedAr
cmdLineArgs += " /ShowOptimizationTiers";
}
+ if (parsedArgs.DisableDotNetVersionLogging)
+ {
+ cmdLineArgs += " /DisableVersionLogging";
+ }
+
if (parsedArgs.ContinueOnError)
{
cmdLineArgs += " /ContinueOnError";
@@ -3445,6 +3455,12 @@ private void DoClrRundownForSession(string fileName, string sessionName, Command
try
{
Stopwatch sw = Stopwatch.StartNew();
+ if (!DotNetVersionLogger.Running)
+ {
+ DotNetVersionLogger.Start();
+ }
+
+ DotNetVersionLogger.StartRundown();
var rundownFile = Path.ChangeExtension(fileName, ".clrRundown.etl");
using (TraceEventSession clrRundownSession = new TraceEventSession(sessionName + "Rundown", rundownFile))
{
@@ -3582,6 +3598,7 @@ private void DoClrRundownForSession(string fileName, string sessionName, Command
WaitForRundownIdle(parsedArgs.MinRundownTime, parsedArgs.RundownTimeout, rundownFile);
// Complete perfview rundown.
+ DotNetVersionLogger.Stop();
PerfViewLogger.Log.CommandLineParameters(ParsedArgsAsString(null, parsedArgs), Environment.CurrentDirectory, AppInfo.VersionNumber);
PerfViewLogger.Log.StartAndStopTimes();
PerfViewLogger.Log.StopRundown();
diff --git a/src/PerfView/DotNetVersionLogger.cs b/src/PerfView/DotNetVersionLogger.cs
new file mode 100644
index 000000000..1201012aa
--- /dev/null
+++ b/src/PerfView/DotNetVersionLogger.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing.Parsers;
+using Microsoft.Diagnostics.Tracing.Parsers.Clr;
+using Microsoft.Diagnostics.Tracing.Session;
+
+namespace PerfView
+{
+ ///
+ /// Monitors for Runtime/Start events from the Microsoft-Windows-DotNETRuntime and Microsoft-Windows-DotNETRuntimeRundown providers
+ /// and logs the version information for the associated runtime DLLs.
+ ///
+ internal static class DotNetVersionLogger
+ {
+ private static VersionLogger _loggerInstance;
+
+ private sealed class VersionLogger : IDisposable
+ {
+ private const string SessionName = "PerfView-DotNetVersionLogger-Session";
+ private readonly static TextWriter Log = App.CommandProcessor.LogFile;
+ private TraceEventSession _session;
+ private AutoResetEvent _sessionStopEvent = new AutoResetEvent(false);
+ private HashSet _loggedPaths = new HashSet();
+
+ public void Dispose()
+ {
+ if (_session != null)
+ {
+ _session.Dispose();
+ _session = null;
+ }
+ }
+
+ public void Start()
+ {
+ try
+ {
+ _session = new TraceEventSession(SessionName);
+ _session.EnableProvider(
+ ClrTraceEventParser.ProviderGuid,
+ TraceEventLevel.Always,
+ (ulong)TraceEventKeyword.None,
+ new TraceEventProviderOptions() { EventIDsToEnable = new List { 187 } });
+
+ _session.Source.Clr.RuntimeStart += OnRuntimeInformationStartEvent;
+ ClrRundownTraceEventParser rundownParser = new ClrRundownTraceEventParser(_session.Source);
+ rundownParser.RuntimeStart += OnRuntimeInformationStartEvent;
+
+ Task.Factory.StartNew(() =>
+ {
+ _session.Source.Process();
+ _sessionStopEvent.Set();
+ });
+ }
+ catch (Exception ex)
+ {
+ Log.WriteLine($"Failed to start dotnet version tracking: {ex}");
+ }
+ }
+
+ public void StartRundown()
+ {
+ try
+ {
+ _session.EnableProvider(
+ ClrRundownTraceEventParser.ProviderGuid,
+ TraceEventLevel.Always,
+ (ulong)TraceEventKeyword.None,
+ new TraceEventProviderOptions() { EventIDsToEnable = new List { 187 } });
+ }
+ catch (Exception ex)
+ {
+ Log.WriteLine($"Failed to enable rundown provider: {ex}");
+ }
+ }
+
+ public void Stop()
+ {
+ if (_session != null)
+ {
+ _session.Dispose();
+ _session = null;
+
+ // Wait for the session to stop.
+ _sessionStopEvent.WaitOne();
+
+ try
+ {
+ foreach (string dllPath in _loggedPaths)
+ {
+ if (File.Exists(dllPath))
+ {
+ FileVersionInfo info = FileVersionInfo.GetVersionInfo(dllPath);
+ PerfViewLogger.Log.RuntimeVersion(dllPath, info.FileVersion);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.WriteLine($"Failed to enumerate .NET runtime version information: {ex}");
+ }
+ }
+ }
+
+ private void OnRuntimeInformationStartEvent(RuntimeInformationTraceData data)
+ {
+ _loggedPaths.Add(data.RuntimeDllPath);
+ }
+ }
+
+ public static bool Running
+ {
+ get { return _loggerInstance != null; }
+ }
+
+ public static void Start()
+ {
+ if (App.CommandLineArgs.DisableDotNetVersionLogging)
+ {
+ return;
+ }
+
+ Stop();
+
+ _loggerInstance = new VersionLogger();
+ _loggerInstance.Start();
+ }
+
+ public static void StartRundown()
+ {
+ if (_loggerInstance != null)
+ {
+ _loggerInstance.StartRundown();
+ }
+ }
+
+ public static void Stop()
+ {
+ if (App.CommandLineArgs.DisableDotNetVersionLogging)
+ {
+ return;
+ }
+
+ if (_loggerInstance != null)
+ {
+ _loggerInstance.Stop();
+ _loggerInstance.Dispose();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PerfView/PerfViewLogger.cs b/src/PerfView/PerfViewLogger.cs
index 2adb28fe4..d3ed474f0 100644
--- a/src/PerfView/PerfViewLogger.cs
+++ b/src/PerfView/PerfViewLogger.cs
@@ -99,6 +99,8 @@ public void EventStopTrigger(DateTime eventTime, int processID, int threadID, st
{ WriteEvent(25, eventTime, processID, threadID, processName, eventName, durationMSec); }
[Event(26)]
public void StopTriggerDebugMessage(DateTime eventTime, string message) { WriteEvent(26, eventTime, message); }
+ [Event(27)]
+ public void RuntimeVersion(string path, string version) { WriteEvent(27, path, version); }
public class Tasks
{
public const EventTask Tracing = (EventTask)1;
diff --git a/src/PerfViewCollect/PerfViewCollect.csproj b/src/PerfViewCollect/PerfViewCollect.csproj
index 7642acab3..d925363aa 100644
--- a/src/PerfViewCollect/PerfViewCollect.csproj
+++ b/src/PerfViewCollect/PerfViewCollect.csproj
@@ -44,6 +44,7 @@
+