Skip to content
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<PropertyGroup>
<LangVersion>7.3</LangVersion>
<LangVersion>8.0</LangVersion>
<Features>strict</Features>
</PropertyGroup>

Expand Down
35 changes: 27 additions & 8 deletions src/PerfView/HeapView/HeapView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ private Menu CreateMainMenu()
return menu;
}

private TraceLog GetTraceLog(StatusBar worker)
{
return m_dataFile switch
{
ETLPerfViewData etlDataFile => etlDataFile.GetTraceLog(worker.LogWriter),
LinuxPerfViewData linuxDataFile => linuxDataFile.GetTraceLog(worker.LogWriter),
EventPipePerfViewData eventPipeDataFile => eventPipeDataFile.GetTraceLog(worker.LogWriter),
_ => null,
};
}

private void LaunchViewer(List<IProcess> selectedProcesses)
{
// Single process only
Expand Down Expand Up @@ -183,28 +194,36 @@ public override void Open(Window parentWindow, StatusBar worker, Action doAfter
{
if (m_dataFile.SupportsProcesses)
{
// Only ETL/ETLX file supported
ETLPerfViewData etlDataFile = m_dataFile as ETLPerfViewData;
// Get TraceLog from the data file (supports multiple formats)
m_traceLog = GetTraceLog(worker);

if (etlDataFile == null)
if (m_traceLog != null)
{
return;
}
m_worker = worker;

m_traceLog = etlDataFile.GetTraceLog(worker.LogWriter);
m_traceLog.SelectClrProcess(LaunchViewer);
}
}
else
{
// Single process format - get the process directly
m_traceLog = GetTraceLog(worker);

if (m_traceLog != null)
{
m_worker = worker;

m_traceLog.SelectClrProcess(LaunchViewer);
var clrProcesses = m_traceLog.GetClrProcesses(out _);
if (clrProcesses.Count > 0)
{
LaunchViewer(clrProcesses);
}
}
}
}
else
{
m_mainWindow.Focus();

doAfter?.Invoke();
}
}
Expand Down
20 changes: 9 additions & 11 deletions src/PerfView/HeapView/Toolbox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,7 @@ internal static bool IsClr(this string name)
{
return name.Equals("clr", StringComparison.OrdinalIgnoreCase) ||
name.Equals("coreclr", StringComparison.OrdinalIgnoreCase) ||
name.Equals("libcoreclr", StringComparison.OrdinalIgnoreCase) ||
name.Equals("mscorwks", StringComparison.OrdinalIgnoreCase) ||
name.Equals("mrt100", StringComparison.OrdinalIgnoreCase);
}
Expand All @@ -746,6 +747,8 @@ internal static bool IsMscorlib(this string name)
{
return name.Equals("mscorlib", StringComparison.OrdinalIgnoreCase) ||
name.Equals("mscorlib.ni", StringComparison.OrdinalIgnoreCase) ||
name.Equals("system.private.corelib", StringComparison.OrdinalIgnoreCase) ||
name.Equals("system.private.corelib.il", StringComparison.OrdinalIgnoreCase) ||
name.Equals("corefx", StringComparison.OrdinalIgnoreCase);
}

Expand Down Expand Up @@ -775,8 +778,6 @@ internal static List<IProcess> GetClrProcesses(this TraceLog traceLog, out bool
{
foreach (TraceLoadedModule m in mods)
{
keep = false;

string name = m.Name;

if (name.IsClr() || name.IsMscorlib() && String.IsNullOrEmpty(clrVersion))
Expand All @@ -787,19 +788,16 @@ internal static List<IProcess> GetClrProcesses(this TraceLog traceLog, out bool
{
clrVersion = m.ModuleFile.FilePath;

int pos = clrVersion.LastIndexOf('\\');

if (pos > 0)
// Only extract version from path for clr.dll, for other modules use full path
if (name.Equals("clr", StringComparison.OrdinalIgnoreCase))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CONSIDER: This is all more easily readable with Path.GetFileName(Path.GetDirectoryName(path)) with maybe some sanity checks around the input.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored to use Path.GetFileName(Path.GetDirectoryName(path)) as suggested. Much cleaner and more readable than the manual string manipulation. (05ed532)

{
int p = clrVersion.LastIndexOf('\\', pos - 1);

if (p > 0)
var parentDir = Path.GetDirectoryName(clrVersion);
if (!String.IsNullOrEmpty(parentDir))
{
pos = p;
clrVersion = Path.GetFileName(parentDir);
}

clrVersion = clrVersion.Substring(pos + 1);
}
// For non-clr modules, clrVersion is already set to the full file path above
}

hasClr = true;
Expand Down
1 change: 1 addition & 0 deletions src/PerfView/PerfViewData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9560,6 +9560,7 @@ protected override Action<Action> OpenImpl(Window parentWindow, StatusBar worker
if (hasGC)
{
memory.AddChild(new PerfViewGCStats(this));
memory.AddChild(new MemoryAnalyzer(this));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a partial implementation, but more is required. When some one double clicks on the "GC Heap Analyzer" option, it calls MemoryAnalyzer.Open. This method is responsible for picking the process of interest and then calling LaunchViewer. When there are multiple processes, it does this by calling SelectClrProcess, passing a delegate to LaunchViewer.

MemoryAnalyzer.Open needs two changes:

  • If SupportsProcesses is false, then get the one process from the TraceLog, and directly call LaunchViewer.
  • Remove the limitation that the input file must be an ETLPerfViewData file, since there are now other multi-process formats that are supported.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've implemented the requested changes to MemoryAnalyzer.Open:

  1. Removed ETLPerfViewData limitation: Added GetTraceLog helper method that supports ETL, Linux, and EventPipe data formats
  2. Added single-process support: When SupportsProcesses is false, the method now gets CLR processes directly from TraceLog and calls LaunchViewer without user selection

The implementation handles both multi-process and single-process scenarios properly. (fc742bf)

}

if (hasGCAllocationTicks)
Expand Down