Skip to content
Open
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
169 changes: 144 additions & 25 deletions src/PerfView/EventViewer/EventWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using EventSources;
using Diagnostics.Tracing.StackSources;
using EventSources;
using Microsoft.Diagnostics.Symbols;
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.Etlx;
using Microsoft.Diagnostics.Tracing.Stacks;
using Microsoft.Diagnostics.Tracing.TraceUtilities.FilterQueryExpression;
using Microsoft.Diagnostics.Utilities;
using Microsoft.IdentityModel.Tokens;
Expand Down Expand Up @@ -459,42 +462,39 @@ private void OpenStacks(string stackSourceName)
var startTimeRelativeMSec = m_source.StartTimeRelativeMSec;
var endTimeRelativeMSec = m_source.EndTimeRelativeMSec;
var selectedCells = Grid.SelectedCells;
if (selectedCells.Count == 1 || selectedCells.Count == 2)
if (selectedCells.Count == 2)
{
string start = GetCellStringValue(selectedCells[0]);
double parsedStart;
if (!double.TryParse(start, out parsedStart))
{
if (selectedCells.Count != 1)
{
StatusBar.LogError("Could not parse " + start + " as a number.");
OpenSelectedStacks(stackSourceName, selectedCells);
return;
}
StatusBar.Log("Assuming total current time range");
}
else

startTimeRelativeMSec = parsedStart;
string end = this.GetCellStringValue(selectedCells[1]);
if (!double.TryParse(end, out endTimeRelativeMSec))
{
startTimeRelativeMSec = parsedStart;
endTimeRelativeMSec = parsedStart;
if (selectedCells.Count == 2)
{
string end = GetCellStringValue(selectedCells[1]);
if (!double.TryParse(end, out endTimeRelativeMSec))
{
StatusBar.LogError("Could not parse " + end + " as a number.");
return;
}
}
this.StatusBar.LogError("Could not parse " + end + " as a number.");
this.OpenSelectedStacks(stackSourceName, selectedCells);
return;
}

// Make sure that start < end
if (endTimeRelativeMSec < startTimeRelativeMSec)
{
var tmp = startTimeRelativeMSec;
startTimeRelativeMSec = endTimeRelativeMSec;
endTimeRelativeMSec = tmp;
}
// Make sure that start < end
if (endTimeRelativeMSec < startTimeRelativeMSec)
{
var tmp = startTimeRelativeMSec;
startTimeRelativeMSec = endTimeRelativeMSec;
endTimeRelativeMSec = tmp;
}
}
else if (selectedCells.Count != 2)
{
OpenSelectedStacks(stackSourceName, selectedCells);
return;
}

// TODO FIX NOW: this should call a routine that does the opening of the stack view
// (m_lookedUpCachedSymbolsForETLData should not be needed ...)
Expand Down Expand Up @@ -557,6 +557,125 @@ private void OpenStacks(string stackSourceName)
});
}
}

private void OpenSelectedStacks(string stackSourceName, IList<DataGridCellInfo> selectedCells)
{
if (selectedCells == null || selectedCells.Count == 0)
{
StatusBar.LogError("No events selected.");
return;
}

StatusBar.StartWork("Reading " + DataSource.Name, delegate ()
{
PerfViewStackSource dataSource = null;
var dataFile = DataSource.DataFile;
if (dataFile != null)
{
if (stackSourceName == null)
{
stackSourceName = dataFile.DefaultStackSourceName;
}

dataSource = dataFile.GetStackSource(stackSourceName);
}

if (dataSource == null)
{
throw new ApplicationException("Could not find stack source " + stackSourceName);
}

// Collect unique event records (a row may have multiple selected cells).
var uniqueRecords = selectedCells
.Select(c => c.Item as EventRecord)
.Where(r => r != null)
.Distinct()
.ToList();

if (uniqueRecords.Count == 0)
{
StatusBar.EndWork(delegate ()
{
StatusBar.LogError("No event records found in selection.");
});
return;
}

StackSource aggregateSource;

// For ETW sources, filter by exact EventIndex so concurrent events on other
// threads at the same timestamp are excluded.
var etwRecords = uniqueRecords.OfType<ETWEventSource.ETWEventRecord>().ToList();
var startTimeRelativeMSec = etwRecords.Min(r => r.TimeStampRelatveMSec);
var endTimeRelativeMSec = etwRecords.Max(r => r.TimeStampRelatveMSec);
if (etwRecords.Count == uniqueRecords.Count)
{
var selectedIndices = new HashSet<EventIndex>(etwRecords.Select(r => r.Index));
aggregateSource = dataSource.GetStackSource(
StatusBar.LogWriter,
startTimeRelativeMSec - .001,
endTimeRelativeMSec + .001,
data => selectedIndices.Contains(data.EventIndex));
}
else
{
// Fall back to per-event time windows for non-ETW sources.
var sources = new List<StackSource>();
foreach (var record in uniqueRecords)
{
sources.Add(dataSource.GetStackSource(
StatusBar.LogWriter,
record.TimeStampRelatveMSec - .001,
record.TimeStampRelatveMSec + .001));
}
aggregateSource = InternStackSource.Merge(sources);
}

if (aggregateSource == null)
{
StatusBar.EndWork(delegate ()
{
StatusBar.LogError("Could not open stacks for selected events.");
});
return;
}

if (!m_lookedUpCachedSymbolsForETLData)
{
m_lookedUpCachedSymbolsForETLData = true;
StatusBar.Log("Quick Looking up symbols from PDB cache.");
var etlDataFile = dataFile as ETLPerfViewData;
if (etlDataFile != null)
{
var traceLog = etlDataFile.GetTraceLog(StatusBar.LogWriter);
using (var reader = etlDataFile.GetSymbolReader(StatusBar.LogWriter,
SymbolReaderOptions.CacheOnly | SymbolReaderOptions.NoNGenSymbolCreation))
{
var moduleFiles = ETLPerfViewData.GetInterestingModuleFiles(etlDataFile, 5.0, StatusBar.LogWriter, null);
foreach (var moduleFile in moduleFiles)
{
traceLog.CodeAddresses.LookupSymbolsForModule(reader, moduleFile);
}
}
}
StatusBar.Log("Quick Done looking up symbols from PDB cache.");
}

StatusBar.EndWork(delegate ()
{
App.CommandProcessor.NoExitOnElevate = true;

var stackWindow = new PerfView.StackWindow(this, dataSource);
stackWindow.StatusBar.Log("Read " + DataSource.Name);
dataSource.ConfigureStackWindow(stackWindow);
stackWindow.StartTextBox.Text = startTimeRelativeMSec.ToString();
stackWindow.EndTextBox.Text = endTimeRelativeMSec.ToString();
stackWindow.Show();
stackWindow.SetStackSource(aggregateSource);
});
});
}

private void DoProcessFilter(object sender, ExecutedRoutedEventArgs e)
{
var selectedCells = Grid.SelectedCells;
Expand Down
19 changes: 18 additions & 1 deletion src/PerfView/PerfViewData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4571,7 +4571,24 @@ public virtual StackSource GetStackSource(TextWriter log, double startRelativeMS
return ret;
}

// TODO not clear I want this method
public virtual StackSource GetStackSource(TextWriter log, double startRelativeMSec, double endRelativeMSec, Predicate<TraceEvent> predicate)
{
StackSource ret = DataFile.OpenStackSourceImpl(SourceName, log, startRelativeMSec, endRelativeMSec, predicate);
if (ret == null)
{
// Fall back to implementation without predicate
ret = DataFile.OpenStackSourceImpl(SourceName, log, startRelativeMSec, endRelativeMSec);
}

if (ret == null)
{
throw new ApplicationException("Not a file type that supports the StackView.");
}

return ret;
}

// TODO not clear I want this method
protected virtual StackSource OpenStackSource(
string streamName, TextWriter log, double startRelativeMSec = 0, double endRelativeMSec = double.PositiveInfinity, Predicate<TraceEvent> predicate = null)
{
Expand Down
24 changes: 24 additions & 0 deletions src/TraceEvent/Stacks/Stacks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,30 @@ public InternStackSource(StackSource source, StackSourceStacks sourceStacks)
Interner.DoneInterning();
}

/// <summary>
/// Merge multiple stack sources into a single unified source by re-interning all frames by name.
/// Frames with the same name across sources (e.g. the same process frame) are automatically merged.
/// </summary>
public static InternStackSource Merge(IEnumerable<StackSource> sources)
{
if (sources == null)
{
throw new ArgumentNullException(nameof(sources));
}

var ret = new InternStackSource();
foreach (var source in sources)
{
if (source == null)
{
continue;
}
ret.ReadAllSamples(source, source, 1.0F);
}
ret.Interner.DoneInterning();
return ret;
}

/// <summary>
/// Create a new InternStackSource
/// </summary>
Expand Down