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(); } }
private unsafe Guid GetCoorelationIDForEvent(TraceEvent data, CorelationOptions options) { int?intContextID = null; // When the obvious ID is an integer if ((options & CorelationOptions.UseThreadContext) == 0) { if ((options & CorelationOptions.UseActivityID) == 0) { // If the payloads have parameters that indicate it is a correlation event, use that. var names = data.PayloadNames; if (names != null && names.Length > 0) { int fieldNum = -1; // First try to use a field as the correlater if (0 < names.Length) { if (names[0].EndsWith("id", StringComparison.OrdinalIgnoreCase) || string.Compare("Name", names[0], StringComparison.OrdinalIgnoreCase) == 0 || // Used for simple generic taskss names[0] == "Count") // Hack for GC/Start { fieldNum = 0; } else if (1 < names.Length && names[1] == "ContextId") // This is for ASP.NET events { fieldNum = 1; } } if (0 <= fieldNum) { var value = data.PayloadValue(fieldNum); if (value is Guid) { return((Guid)value); } else { if (value != null) { intContextID = value.GetHashCode(); // Use the hash if it is not a GUID } } } } } // If we have not found a context field, and there is an activity ID use that. if (data.ActivityID != Guid.Empty) { if (!intContextID.HasValue) { return(data.ActivityID); } //TODO Currently, people may have recursive tasks that are not marked (because they can't if they want it to work before V4.6) // For now we don't try to correlate with activity IDS. if (false && StartStopActivityComputer.IsActivityPath(data.ActivityID, data.ProcessID)) { int guidHash = data.ActivityID.GetHashCode(); // Make up a correlater that is the combination of both the value and the Activity ID, the tail is arbitrary // TODO this is causing unnecessary collisions. return(new Guid(intContextID.Value, (short)guidHash, (short)(guidHash >> 16), 45, 34, 34, 67, 4, 4, 5, 5)); } } } // If we have not found a context, use the thread as a context. if (!intContextID.HasValue) { intContextID = data.ThreadID; // By default use the thread as the correlation ID } return(new Guid(intContextID.Value, 1, 5, 45, 23, 23, 3, 5, 5, 4, 5)); }