in src/PerfView/EtwEventSource.cs [71:364]
public override void ForEach(Func<EventRecord, bool> callback)
{
int cnt = 0;
double startTime = StartTimeRelativeMSec;
double endTime = EndTimeRelativeMSec;
// TODO could be more efficient about process filtering by getting all the processes that match.
Regex procRegex = null;
string procNameMustStartWith = null;
if (ProcessFilterRegex != null)
{
// As an optimization, find the part that is just alphaNumeric
procNameMustStartWith = Regex.Match(ProcessFilterRegex, @"^(\w*)").Groups[1].Value;
procRegex = new Regex(ProcessFilterRegex, RegexOptions.IgnoreCase);
}
Predicate<ETWEventRecord> textFilter = null;
if (!string.IsNullOrWhiteSpace(TextFilterRegex))
{
string pat = TextFilterRegex;
bool negate = false;
if (pat.StartsWith("!"))
{
negate = true;
pat = pat.Substring(1);
}
var textRegex = new Regex(pat, RegexOptions.IgnoreCase);
textFilter = delegate (ETWEventRecord eventRecord)
{
bool match = eventRecord.Matches(textRegex);
return negate ? !match : match;
};
}
Dictionary<string, int> columnOrder = null;
ColumnSums = null;
if (ColumnsToDisplay != null)
{
columnOrder = new Dictionary<string, int>();
for (int i = 0; i < ColumnsToDisplay.Count;)
{
// Discard duplicate columns
if (columnOrder.ContainsKey(ColumnsToDisplay[i]))
{
ColumnsToDisplay.RemoveAt(i);
continue;
}
columnOrder.Add(ColumnsToDisplay[i], i);
i++;
}
ColumnSums = new double[ColumnsToDisplay.Count];
}
if (m_selectedEvents != null)
{
ETWEventRecord emptyEventRecord = new ETWEventRecord(this);
var startStopRecords = new Dictionary<StartStopKey, double>(10);
// Figure out if you need m_activityComputer or not
// Because it is moderately expensive, and not typically used, we only include the activity stuff
// when you explicitly ask for it
m_needsComputers = false;
if (ColumnsToDisplay != null)
{
foreach (string column in ColumnsToDisplay)
{
if (column == "*" || column == "ActivityInfo" || column == "StartStopActivity")
{
m_needsComputers = true;
break;
}
}
}
/***********************************************************************/
/* The main event loop */
EventVisitedVersion.CurrentVersion++;
var source = m_tracelog.Events.FilterByTime(m_needsComputers ? 0 : startTime, endTime).GetSource(); // If you need computers, you need the events from the start.
if (m_needsComputers)
{
m_activityComputer = new ActivityComputer(source, App.GetSymbolReader());
m_startStopActivityComputer = new StartStopActivityComputer(source, m_activityComputer);
}
source.AllEvents += delegate (TraceEvent data)
{
// FilterByTime would cover this, however for m_needsComputer == true we may not be able to do it that way.
if (data.TimeStampRelativeMSec < startTime)
{
return;
}
double durationMSec = -1;
var eventFilterVersion = data.EventTypeUserData as EventVisitedVersion;
if (eventFilterVersion == null || eventFilterVersion.Version != EventVisitedVersion.CurrentVersion)
{
var eventName = data.ProviderName + "/" + data.EventName;
bool processButDontShow = false;
var shouldKeep = m_selectedAllEvents;
if (!shouldKeep)
{
if (m_selectedEvents.TryGetValue(eventName, out processButDontShow))
{
shouldKeep = true;
}
}
eventFilterVersion = new EventVisitedVersion(shouldKeep, processButDontShow);
if (!(data is UnhandledTraceEvent))
{
data.EventTypeUserData = eventFilterVersion;
}
}
if (!eventFilterVersion.ShouldProcess)
{
return;
}
// If this is a StopEvent compute the DURATION_MSEC
var opcode = data.Opcode;
var task = data.Task;
CorelationOptions corelationOptions = CorelationOptions.None;
if (data.ProviderGuid == ClrTraceEventParser.ProviderGuid)
{
// Fix Suspend and restart events to line up to make durations.
if ((int)data.ID == 9) // SuspendEEStart
{
corelationOptions = CorelationOptions.UseThreadContext;
task = (TraceEventTask)0xFFFE; // unique task
opcode = TraceEventOpcode.Start;
}
else if ((int)data.ID == 8) // SuspendEEStop
{
corelationOptions = CorelationOptions.UseThreadContext;
task = (TraceEventTask)0xFFFE; // unique task (used for both suspend and Suspend-Restart.
opcode = TraceEventOpcode.Stop;
}
else if ((int)data.ID == 3) // RestartEEStop
{
corelationOptions = CorelationOptions.UseThreadContext;
task = (TraceEventTask)0xFFFE; // unique task
opcode = TraceEventOpcode.Stop;
}
}
if (data.ProviderGuid == httpServiceProviderGuid)
{
corelationOptions = CorelationOptions.UseActivityID;
if (opcode == (TraceEventOpcode)13) // HttpServiceDeliver
{
opcode = TraceEventOpcode.Start;
}
// HttpServiceSendComplete ZeroSend FastSend
else if (opcode == (TraceEventOpcode)51 || opcode == (TraceEventOpcode)22 || opcode == (TraceEventOpcode)21)
{
opcode = TraceEventOpcode.Stop;
}
}
if (data.ProviderGuid == systemDataProviderGuid)
{
corelationOptions = CorelationOptions.UseActivityID;
if ((int)data.ID == 1) // BeginExecute
{
task = (TraceEventTask)0xFFFE; // unique task but used for both BeginExecute and EndExecute.
opcode = TraceEventOpcode.Start;
}
else if ((int)data.ID == 2) // EndExecute
{
task = (TraceEventTask)0xFFFE; // unique task but used for both BeginExecute and EndExecute.
opcode = TraceEventOpcode.Stop;
}
}
if (opcode == TraceEventOpcode.Start || opcode == TraceEventOpcode.Stop)
{
// Figure out what we use as a correlater between the start and stop.
Guid contextID = GetCoorelationIDForEvent(data, corelationOptions);
var key = new StartStopKey(data.ProviderGuid, task, contextID);
if (opcode == TraceEventOpcode.Start)
{
startStopRecords[key] = data.TimeStampRelativeMSec;
}
else
{
double startTimeStamp;
if (startStopRecords.TryGetValue(key, out startTimeStamp))
{
durationMSec = data.TimeStampRelativeMSec - startTimeStamp;
// A bit of a hack. WE use the same start event (SuspenEEStart) for two durations.
// Thus don't remove it after SuspendEEStop because we also use it for RestartEEStop.
if (!(task == (TraceEventTask)0xFFFE && (int)data.ID == 8)) // Is this the SuspendEEStop event?
{
startStopRecords.Remove(key);
}
}
}
}
if (!eventFilterVersion.ShouldShow)
{
return;
}
if (procRegex != null)
{
CSwitchTraceData cSwitch = data as CSwitchTraceData;
if (!data.ProcessName.StartsWith(procNameMustStartWith, StringComparison.OrdinalIgnoreCase))
{
if (cSwitch == null)
{
return;
}
// Special case. Context switches will work for both the old and the new process
if (!cSwitch.OldProcessName.StartsWith(procNameMustStartWith, StringComparison.OrdinalIgnoreCase))
{
return;
}
}
var fullProcessName = data.ProcessName;
if (!fullProcessName.StartsWith("("))
{
fullProcessName += " (" + data.ProcessID + ")";
}
if (!procRegex.IsMatch(fullProcessName))
{
if (cSwitch == null)
{
return;
}
// Special case. Context switches will work for both the old and the new process
var fullOldProcessName = cSwitch.OldProcessName;
if (!fullOldProcessName.StartsWith("("))
{
fullOldProcessName += " (" + cSwitch.OldProcessName + ")";
}
if (!procRegex.IsMatch(fullOldProcessName))
{
return;
}
}
}
ETWEventRecord eventRecord = null;
if (textFilter != null)
{
eventRecord = new ETWEventRecord(this, data, columnOrder, NonRestFields, durationMSec);
if (!textFilter(eventRecord))
{
return;
}
}
cnt++;
if (MaxRet < cnt)
{
// We have exceeded our MaxRet, return an empty record.
eventRecord = emptyEventRecord;
eventRecord.m_timeStampRelativeMSec = data.TimeStampRelativeMSec;
}
if (eventRecord == null)
{
eventRecord = new ETWEventRecord(this, data, columnOrder, NonRestFields, durationMSec);
}
if (ColumnSums != null)
{
var fields = eventRecord.DisplayFields;
var min = Math.Min(ColumnSums.Length, fields.Length);
for (int i = 0; i < min; i++)
{
string value = fields[i];
double asDouble;
if (value != null && double.TryParse(value, out asDouble))
{
ColumnSums[i] += asDouble;
}
}
}
if (!callback(eventRecord))
{
source.StopProcessing();
}
};
source.Process();
}
}