using System; using System.IO; using System.Text; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Linq; // Keep in sync with ProfileEventViewerApp.cs using Time = System.Double; namespace ProfileEventViewer { public enum InvalidValues { ContextID = 65535 } public class TimeExtent { public Time Min; public Time Max; public Time Length { get { return Max - Min; } } public TimeExtent() { Min = Time.MaxValue; Max = Time.MinValue; } public void Merge(TimeExtent other) { Min = Math.Min(other.Min, Min); Max = Math.Max(other.Max, Max); } public void Merge(Time start, Time end) { Min = Math.Min(start, Min); Max = Math.Max(end, Max); } public bool Contains(Time time) { return time >= Min && time <= Max; } public bool Contains(TimeExtent extent) { return ContainsRange(extent.Min, extent.Max); } public bool ContainsRange(Time start, Time end) { return Contains(start) && Contains(end); } public bool Overlaps(TimeExtent extent) { return Overlaps(extent.Min, extent.Max); } public bool Overlaps(Time start, Time end) { return Contains(start) || Contains(end) || (start < Min && end > Max); } } class EventFile { public EventFileHeader m_Header; public List m_Contexts = new List(); public List m_Profiles = new List(); public List m_ProfileEvents = new List(); public List m_TaskDescriptions = new List(); public List m_TaskEvents = new List(); public List m_DataEvents = new List(); public List m_FrameEvents = new List(); public List m_ThreadSwitches = new List(); public void Serialize(BinaryReader reader) { m_Header.Serialize(reader); m_Contexts.Capacity = (int)m_Header.m_NbContextDescs; for (int i = 0; i < m_Header.m_NbContextDescs; i++) { EventFileThreadDesc threadDesc = new EventFileThreadDesc(); if (m_Header.m_Version <= 2) threadDesc.Serialize(reader); else threadDesc.SerializeRev3(reader); m_Contexts.Add(threadDesc); } m_Profiles.Capacity = (int)m_Header.m_NbProfileDescs; for (int i = 0; i < m_Header.m_NbProfileDescs; i++) { EventFileProfileDesc profileDesc = new EventFileProfileDesc(); profileDesc.Serialize(reader); m_Profiles.Add(profileDesc); } m_ProfileEvents.Capacity = (int)m_Header.m_NbProfileEvents; for (int i = 0; i < m_Header.m_NbProfileEvents; i++) { ProfileEvent profileEvent = new ProfileEvent(); profileEvent.Serialize(reader); m_ProfileEvents.Add(profileEvent); } m_TaskDescriptions.Capacity = (int)m_Header.m_NbTaskDescs; for (int i = 0; i < m_Header.m_NbTaskDescs; i++) { EventFileTaskDesc taskDesc = new EventFileTaskDesc(); taskDesc.Serialize(reader); m_TaskDescriptions.Add(taskDesc); } m_TaskEvents.Capacity = (int)m_Header.m_NbTaskEvents; for (int i = 0; i < m_Header.m_NbTaskEvents; i++) { TaskEvent taskEvent = new TaskEvent(); taskEvent.Serialize(reader); m_TaskEvents.Add(taskEvent); } m_DataEvents.Capacity = (int)m_Header.m_NbDataEvents; for (int i = 0; i < m_Header.m_NbDataEvents; i++) { DataEvent dataEvent = new DataEvent(); dataEvent.Serialize(reader); m_DataEvents.Add(dataEvent); } m_FrameEvents.Capacity = (int)m_Header.m_NbFrameEvents; for (int i = 0; i < m_Header.m_NbFrameEvents; i++) { FrameEvent frameEvent = new FrameEvent(); frameEvent.Serialize(reader); m_FrameEvents.Add(frameEvent); } m_ThreadSwitches.Capacity = (int)m_Header.m_NbThreadSwitchEvents; for (int i = 0; i < m_Header.m_NbThreadSwitchEvents; i++) { ThreadSwitch threadSwitch = new ThreadSwitch(); threadSwitch.Serialize(reader); m_ThreadSwitches.Add(threadSwitch); } } public void Serialize(BinaryWriter writer) { m_Header.Serialize(writer); System.Diagnostics.Debug.Assert(m_Contexts.Count == m_Header.m_NbContextDescs); foreach (EventFileThreadDesc context in m_Contexts) { context.Serialize(writer); } System.Diagnostics.Debug.Assert(m_Profiles.Count == m_Header.m_NbProfileDescs); foreach (EventFileProfileDesc profile in m_Profiles) { profile.Serialize(writer); } System.Diagnostics.Debug.Assert(m_ProfileEvents.Count == m_Header.m_NbProfileEvents); foreach (ProfileEvent profileEvent in m_ProfileEvents) { profileEvent.Serialize(writer); } System.Diagnostics.Debug.Assert(m_TaskDescriptions.Count == m_Header.m_NbTaskDescs); foreach (EventFileTaskDesc taskDesc in m_TaskDescriptions) { taskDesc.Serialize(writer); } System.Diagnostics.Debug.Assert(m_TaskEvents.Count == m_Header.m_NbTaskEvents); foreach (TaskEvent taskEvent in m_TaskEvents) { taskEvent.Serialize(writer); } System.Diagnostics.Debug.Assert(m_DataEvents.Count == m_Header.m_NbDataEvents); foreach (DataEvent dataEvent in m_DataEvents) { dataEvent.Serialize(writer); } System.Diagnostics.Debug.Assert(m_ThreadSwitches.Count == m_Header.m_NbThreadSwitchEvents); foreach (ThreadSwitch threadSwitch in m_ThreadSwitches) { threadSwitch.Serialize(writer); } } public void FillData(ProfileEventData data) { foreach (EventFileThreadDesc context in m_Contexts) { data.AddContext(context.m_ThreadID, context.m_OSThreadID, context.m_Name); } foreach (EventFileProfileDesc profile in m_Profiles) { data.AddProfile(profile.m_ProfileID, profile.m_Name); } UInt32 engineFrameDelimiterProfileId = data.GetProfileIDFromName("BeginFrameTask"); UInt32 graphicFrameDelimiterProfileId = data.GetProfileIDFromName("GPUFrame"); UInt32 graphicFrameDelimiterProfileIdbis = data.GetProfileIDFromName("Frame"); Time frameCPUBeginTime = 0; Time frameGPUBeginTime = 0; bool addFrames = m_FrameEvents.Count == 0; // Read all the events // Assumes events are ordered in time. foreach (ProfileEvent profileEvent in m_ProfileEvents) { // TODO: Multithread this in some way? Could get us a little gain... ProfileEventData.ProfileContext context = data.GetEventContext(profileEvent.m_ContextID); context.AddEvent(profileEvent.m_Time, profileEvent.m_ProfileID, profileEvent.m_Type); if (profileEvent.m_ProfileID == engineFrameDelimiterProfileId && profileEvent.m_Type == 0) { data.FrameDelimiters.Add(new FrameDelimiter((Time)profileEvent.m_Time, FrameDelimiter.DelimiterType.Engine)); // If no frames are provided add one if (addFrames) { if (frameCPUBeginTime != 0 && frameCPUBeginTime < profileEvent.m_Time) { // Add to the dumped array data.AddFrame(frameCPUBeginTime, profileEvent.m_Time, ProfileEventData.Frame.FrameType.Engine); } frameCPUBeginTime = profileEvent.m_Time; } } else if (profileEvent.m_ProfileID == graphicFrameDelimiterProfileId || (profileEvent.m_ProfileID == graphicFrameDelimiterProfileIdbis && context.Name == "GPU")) { data.FrameDelimiters.Add(new FrameDelimiter((Time)profileEvent.m_Time, FrameDelimiter.DelimiterType.Graphic)); // If no frames are provided add one if (addFrames) { // End if (profileEvent.m_Type == 1) { if (frameGPUBeginTime != 0 && frameGPUBeginTime < profileEvent.m_Time) { // Add to the dumped array data.AddFrame(frameGPUBeginTime, profileEvent.m_Time, ProfileEventData.Frame.FrameType.Graphic); } } // Begin else if (profileEvent.m_Type == 0) frameGPUBeginTime = profileEvent.m_Time; } } } // Calculate all overlapping times // TODO: Multithread this whole step if possible? May gain a little. Stack events = new Stack(); foreach (ProfileEventData.ProfileContext context in data.Contexts.Values) foreach (ProfileEventData.ProfileContext.Event evt in context.Heads) events.Push(evt); while (events.Count > 0) { ProfileEventData.ProfileContext.Event evt = events.Pop(); if (evt.Length == 0) continue; if (data.Profiles.ContainsKey(evt.ProfileID)) { ProfileEventData.ProfileDesc profile = (ProfileEventData.ProfileDesc)data.Profiles[evt.ProfileID]; profile.ClockTime.Merge(evt.Time, evt.EndTime); } ProfileEventData.ProfileContext.Event child = evt.Child; while (child != null) { events.Push(child); child = child.Sibling; } } foreach (ProfileEventData.ProfileDesc profile in data.Profiles.Values) profile.ClockTime.CoalesceResults(); foreach (EventFileTaskDesc taskDesc in m_TaskDescriptions) { data.AddTaskDesc(taskDesc.m_TaskID, taskDesc.m_Name); } // Read all the tasks events // Assumes events are ordered in time by context, which is not useful at all... ; ) List taskEvents = new List(m_TaskEvents); // Sort on time taskEvents.Sort(delegate(TaskEvent t1, TaskEvent t2) { if (t1.m_Time < t2.m_Time) return -1; else if (t1.m_Time == t2.m_Time) return 0; else return 1; }); foreach (TaskEvent taskEvent in taskEvents) { data.AddTaskEvent(taskEvent.m_Time, taskEvent.m_TaskID1, taskEvent.m_TaskID2, taskEvent.m_Type, taskEvent.m_ContextID); } data.LastTaskEventAdded(); foreach (DataEvent dataEvent in m_DataEvents) { ProfileEventData.ProfileContext context = data.GetEventContext(dataEvent.m_ContextID); if (context != null) context.AddDataEvent(dataEvent.m_Time, dataEvent.m_Data); else Console.WriteLine("Data event \"{0}\" (time {1}) has unknown context ID {2}.", dataEvent.m_Data, dataEvent.m_Time, dataEvent.m_ContextID); } data.LastDataEventAdded(); foreach (FrameEvent frameEvent in m_FrameEvents) { data.AddFrame(frameEvent.m_BeginTime, frameEvent.m_EndTime, ProfileEventData.Frame.FrameType.Engine); } foreach (ThreadSwitch threadSwitch in m_ThreadSwitches) { data.AddThreadSwitch(threadSwitch); } data.SortThreadSwitch(); data.ComputePerContextThreadSleep(); } public void Merge(EventFile eventFile) { // TODO: Validation. m_Header.Merge(eventFile.m_Header); uint contextIdBias = (uint)m_Contexts.Count; uint osThreadBias = m_Contexts.Select(c => c.m_OSThreadID).Max() + 1; uint threadBias = m_Contexts.Select(c => c.m_ThreadID).Max() + 1; m_Contexts.Capacity = (int)m_Header.m_NbContextDescs; foreach (EventFileThreadDesc context in eventFile.m_Contexts) { EventFileThreadDesc newContext = context; newContext.m_OSThreadID += osThreadBias; newContext.m_ThreadID += threadBias; m_Contexts.Add(newContext); } uint profileIdBias = (uint)m_Profiles.Count; m_Profiles.Capacity = (int)m_Header.m_NbProfileDescs; foreach (EventFileProfileDesc profile in eventFile.m_Profiles) { EventFileProfileDesc newProfile = profile; newProfile.m_ProfileID += profileIdBias; m_Profiles.Add(newProfile); } m_ProfileEvents.Capacity = (int)m_Header.m_NbProfileEvents; foreach (ProfileEvent profileEvent in eventFile.m_ProfileEvents) { ProfileEvent newProfileEvent = profileEvent; newProfileEvent.m_ContextID += contextIdBias; newProfileEvent.m_ProfileID += profileIdBias; m_ProfileEvents.Add(newProfileEvent); } uint taskIdBias = (uint)m_TaskDescriptions.Count; m_TaskDescriptions.Capacity = (int)m_Header.m_NbTaskDescs; foreach (EventFileTaskDesc taskDesc in eventFile.m_TaskDescriptions) { EventFileTaskDesc newTaskDesc = taskDesc; newTaskDesc.m_TaskID += taskIdBias; m_TaskDescriptions.Add(newTaskDesc); } m_TaskEvents.Capacity = (int)m_Header.m_NbTaskEvents; foreach (TaskEvent taskEvent in eventFile.m_TaskEvents) { TaskEvent newTaskEvent = taskEvent; newTaskEvent.m_ContextID += contextIdBias; newTaskEvent.m_TaskID1 += taskIdBias; newTaskEvent.m_TaskID2 += taskIdBias; m_TaskEvents.Add(newTaskEvent); } m_DataEvents.Capacity = (int)m_Header.m_NbDataEvents; foreach (DataEvent dataEvent in eventFile.m_DataEvents) { DataEvent newDataEvent = dataEvent; if (newDataEvent.m_ContextID != (uint)InvalidValues.ContextID) newDataEvent.m_ContextID += contextIdBias; m_DataEvents.Add(newDataEvent); } m_FrameEvents.Capacity = (int)m_Header.m_NbFrameEvents; foreach (FrameEvent frameEvent in eventFile.m_FrameEvents) { FrameEvent newFrameEvent = frameEvent; newFrameEvent.m_ContextID += contextIdBias; m_FrameEvents.Add(newFrameEvent); } m_ThreadSwitches.Capacity = (int)m_Header.m_NbThreadSwitchEvents; foreach (ThreadSwitch threadSwitch in eventFile.m_ThreadSwitches) { ThreadSwitch newThreadSwitch = threadSwitch; newThreadSwitch.m_oldThreadID += osThreadBias; newThreadSwitch.m_newThreadID += osThreadBias; m_ThreadSwitches.Add(newThreadSwitch); } } } struct EventFileHeader { public uint m_Version; public uint m_ExtraInfoCount; public UInt32 m_Frequency; public string m_Descriptor; public uint m_NbContextDescs; public uint m_NbProfileDescs; public uint m_NbTaskDescs; public uint m_NbProfileEvents; public uint m_NbDataEvents; public uint m_NbFrameEvents; public uint m_NbTaskEvents; public uint m_NbThreadSwitchEvents; static ASCIIEncoding asciiEncoder = new ASCIIEncoding(); public void Serialize(BinaryReader reader) { m_Version = reader.ReadUInt32(); m_ExtraInfoCount = (m_Version >= 5) ? reader.ReadUInt32() : 0; m_Frequency = reader.ReadUInt32(); byte[] descriptor = reader.ReadBytes(256); m_Descriptor = asciiEncoder.GetString(descriptor); int index = m_Descriptor.IndexOf("\0"); if (index >= 0) m_Descriptor = m_Descriptor.Substring(0, index); m_NbContextDescs = reader.ReadUInt32(); m_NbProfileDescs = reader.ReadUInt32(); m_NbTaskDescs = (m_Version > 1) ? reader.ReadUInt32() : 0; m_NbProfileEvents = reader.ReadUInt32(); m_NbDataEvents = reader.ReadUInt32(); m_NbFrameEvents = reader.ReadUInt32(); m_NbTaskEvents = (m_Version > 1) ? reader.ReadUInt32() : 0; m_NbThreadSwitchEvents = (m_Version > 2) ? reader.ReadUInt32() : 0; } public void Serialize(BinaryWriter writer) { UInt32 version = 5; writer.Write(version); writer.Write(m_ExtraInfoCount); writer.Write(m_Frequency); byte[] descriptor = new byte[256]; descriptor.Initialize(); byte[] encodedDescriptor = asciiEncoder.GetBytes(m_Descriptor); Array.Copy(encodedDescriptor, descriptor, Math.Min(encodedDescriptor.Length, descriptor.Length)); writer.Write(descriptor, 0, 256); writer.Write(m_NbContextDescs); writer.Write(m_NbProfileDescs); writer.Write(m_NbTaskDescs); writer.Write(m_NbProfileEvents); writer.Write(m_NbDataEvents); writer.Write(m_NbFrameEvents); writer.Write(m_NbTaskEvents); writer.Write(m_NbThreadSwitchEvents); } public void Merge(EventFileHeader header) { // TODO: Validation if (m_NbContextDescs + header.m_NbContextDescs >= (uint)InvalidValues.ContextID) throw new InvalidDataException(string.Format("The number of combined contexts ({0}) cannot exceed {1}. Cannot merge files.", m_NbContextDescs + header.m_NbContextDescs, InvalidValues.ContextID.ToString())); if (m_Frequency != header.m_Frequency) throw new InvalidOperationException(string.Format("The frequencies must be the same in both files to allow for merging. Frequencies: {0} vs {1}.", m_Frequency, header.m_Frequency)); m_Version = Math.Max(m_Version, header.m_Version); m_Descriptor = string.Format("Merged: {0}, {1}", m_Descriptor, header.m_Descriptor); m_NbContextDescs += header.m_NbContextDescs; m_NbProfileDescs += header.m_NbProfileDescs; m_NbTaskDescs += header.m_NbTaskDescs; m_NbProfileEvents += header.m_NbProfileEvents; m_NbDataEvents += header.m_NbDataEvents; m_NbFrameEvents += header.m_NbFrameEvents; m_NbTaskEvents += header.m_NbTaskEvents; m_NbThreadSwitchEvents += header.m_NbThreadSwitchEvents; } } struct EventFileProfileDesc { public string m_Name; public uint m_ProfileID; static ASCIIEncoding asciiEncoder = new ASCIIEncoding(); public void Serialize(BinaryReader reader) { byte[] name = reader.ReadBytes(60); // decode the name m_Name = asciiEncoder.GetString(name); int index = m_Name.IndexOf("\0"); if (index >= 0) m_Name = m_Name.Substring(0, index); m_ProfileID = reader.ReadUInt32(); } public void Serialize(BinaryWriter writer) { byte[] name = new byte[60]; name.Initialize(); byte[] encodedName = asciiEncoder.GetBytes(m_Name); Array.Copy(encodedName, name, Math.Min(encodedName.Length, name.Length)); writer.Write(name, 0, 60); writer.Write(m_ProfileID); } } struct EventFileTaskDesc { public string m_Name; public uint m_TaskID; static ASCIIEncoding asciiEncoder = new ASCIIEncoding(); public void Serialize(BinaryReader reader) { byte[] name = reader.ReadBytes(60); // decode the name m_Name = asciiEncoder.GetString(name); int index = m_Name.IndexOf("\0"); if (index >= 0) m_Name = m_Name.Substring(0, index); m_TaskID = reader.ReadUInt32(); } public void Serialize(BinaryWriter writer) { byte[] name = new byte[60]; name.Initialize(); byte[] encodedName = asciiEncoder.GetBytes(m_Name); Array.Copy(encodedName, name, Math.Min(encodedName.Length, name.Length)); writer.Write(name, 0, 60); writer.Write(m_TaskID); } } struct EventFileThreadDesc { static ASCIIEncoding asciiEncoder = new ASCIIEncoding(); public string m_Name; public uint m_ThreadID; public uint m_OSThreadID; public void Serialize(BinaryReader reader) { byte[] name = reader.ReadBytes(60); // decode the name m_Name = asciiEncoder.GetString(name); int index = m_Name.IndexOf("\0"); if (index >= 0) m_Name = m_Name.Substring(0, index); m_ThreadID = reader.ReadUInt32(); m_OSThreadID = 0; } public void SerializeRev3(BinaryReader reader) { Serialize(reader); m_OSThreadID = reader.ReadUInt32(); } public void Serialize(BinaryWriter writer) { byte[] name = new byte[60]; name.Initialize(); byte[] encodedName = asciiEncoder.GetBytes(m_Name); Array.Copy(encodedName, name, Math.Min(encodedName.Length, name.Length)); writer.Write(name, 0, 60); writer.Write(m_ThreadID); writer.Write(m_OSThreadID); } } struct ProfileEvent { public UInt64 m_Time; public uint m_ProfileID; public uint m_Type; public uint m_ContextID; public void Serialize(BinaryReader reader) { m_Time = reader.ReadUInt64(); m_ProfileID = reader.ReadUInt32(); m_Type = reader.ReadUInt16(); m_ContextID = reader.ReadUInt16(); } public void Serialize(BinaryWriter writer) { writer.Write(m_Time); writer.Write(m_ProfileID); writer.Write((UInt16)m_Type); writer.Write((UInt16)m_ContextID); } } struct TaskEvent { public UInt64 m_Time; public uint m_TaskID1; public uint m_TaskID2; public uint m_ContextID; public uint m_Type; public void Serialize(BinaryReader reader) { m_Time = reader.ReadUInt64(); m_TaskID1 = reader.ReadUInt32(); m_TaskID2 = reader.ReadUInt32(); m_ContextID = reader.ReadUInt16(); m_Type = reader.ReadUInt16(); reader.ReadInt32(); // burn 4 bytes because of the alignment of the structure in the engine } public void Serialize(BinaryWriter writer) { writer.Write(m_Time); writer.Write(m_TaskID1); writer.Write(m_TaskID2); writer.Write((UInt16)m_ContextID); writer.Write((UInt16)m_Type); writer.Write((UInt32)0); // burn 4 bytes because of the alignment of the structure in the engine } } struct FrameEvent { public UInt64 m_BeginTime; public UInt64 m_EndTime; public uint m_FrameNumber; public uint m_ContextID; public void Serialize(BinaryReader reader) { m_BeginTime = reader.ReadUInt64(); m_EndTime = reader.ReadUInt64(); m_FrameNumber = reader.ReadUInt32(); m_ContextID = reader.ReadUInt32(); } } struct DataEvent { public UInt64 m_Time; public uint m_ContextID; public string m_Data; static ASCIIEncoding asciiEncoder = new ASCIIEncoding(); public void Serialize(BinaryReader reader) { m_Time = reader.ReadUInt64(); m_ContextID = reader.ReadUInt16(); byte[] data = reader.ReadBytes(118); m_Data = asciiEncoder.GetString(data); int index = m_Data.IndexOf("\0"); if (index >= 0) m_Data = m_Data.Substring(0, index); } public void Serialize(BinaryWriter writer) { writer.Write(m_Time); writer.Write((UInt16)m_ContextID); byte[] data = new byte[118]; data.Initialize(); byte[] encodedData = asciiEncoder.GetBytes(m_Data); Array.Copy(encodedData, data, Math.Min(encodedData.Length, data.Length)); writer.Write(data, 0, 118); } } public struct ThreadSwitch { public UInt64 m_Time; public uint m_oldThreadID; public uint m_newThreadID; public void Serialize(BinaryReader reader) { m_Time = reader.ReadUInt64(); m_oldThreadID = reader.ReadUInt32(); m_newThreadID = reader.ReadUInt32(); } public void Serialize(BinaryWriter writer) { writer.Write(m_Time); writer.Write(m_oldThreadID); writer.Write(m_newThreadID); } } public struct FrameDelimiter { public enum DelimiterType { Engine, Graphic } private Time m_Time; private DelimiterType m_Type; public Time Time { get { return m_Time; } } public DelimiterType Type { get { return m_Type; } } public FrameDelimiter(Time time, DelimiterType type) { m_Time = time; m_Type = type; } } public class ProfileEventData { public class Frame { public enum FrameType { Engine, Graphic } public Frame(Time begin, Time end, FrameType type) { m_Extent.Min = begin; m_Extent.Max = end; m_Type = type; } TimeExtent m_Extent = new TimeExtent(); FrameType m_Type = FrameType.Engine; public TimeExtent Extent { get { return m_Extent; } } public FrameType Type { get { return m_Type; } } } public class TaskInstance { public TaskDesc Desc; public List PreTasks = new List(); public List PostTasks = new List(); public List Events = new List(); public TimeExtent TimeExtent = new TimeExtent(); public Time TotalTime = 0; public string GetFullDescription() { string desc = Desc.Name; if (PreTasks.Count > 0) { desc += "\n\nPre Tasks:\n"; foreach (TaskInstance instance in PreTasks) { desc += String.Format("{0}\n", instance.Desc.Name); } } if (PostTasks.Count > 0) { desc += "\nPost Tasks:\n"; foreach (TaskInstance instance in PostTasks) { desc += String.Format("{0}\n", instance.Desc.Name); } desc += "\n"; } return desc; } }; public class ProfileContext { public class Event { public const int EventStart = 0; public const int EventStop = 1; Time m_Time; Time m_Length; Event m_Child; Event m_LastChild; // Quick access. Event m_Sibling; Event m_Father; UInt32 m_ProfileID; UInt32 m_EventType; UInt32 m_Level; ProfileContext m_Context; // Task data TaskInstance m_Task; bool m_IsConstructing; bool m_IsAdjusted; public Event(Time time, UInt32 profileID, UInt32 type) { // set event data m_Time = time; m_IsConstructing = true; m_IsAdjusted = false; m_ProfileID = profileID; m_EventType = type; } public Event Sibling { get { return m_Sibling; } set { m_Sibling = value; } } public ProfileContext Context { get { return m_Context; } set { m_Context = value; } } public Event Father { get { return m_Father; } set { m_Father = value; } } public Event Child { get { return m_Child; } set { m_Child = value; } } public Event LastChild { get { return m_LastChild; } set { m_LastChild = value; } } public TaskInstance Task { get { return m_Task; } set { m_Task = value; } } public Time EndTime { set { m_IsConstructing = false; m_Length = (value - m_Time); } get { return m_Time + m_Length; } } public Time Time { get { return m_Time; } } public Time Length { get { return m_Length; } } public UInt32 Level { get { return m_Level; } set { m_Level = value; } } public UInt32 ProfileID { get { return m_ProfileID; } } public bool IsAdjusted { get { return m_IsAdjusted; } } public bool Inside(Time min, Time max) { if (m_Length == 0) return false; if ((m_Time < max) && (m_Time + m_Length) > min) return true; return false; } public void AdjustTime(ref int adjustedCount) { if (!m_IsConstructing) return; ++adjustedCount; Console.WriteLine("Adjusted time for event in profile {0}, context {1}, time {2}, level {3}", m_ProfileID, m_Context.ContextID, m_Time, m_Level); Event child = Child; while (child != null) { child.AdjustTime(ref adjustedCount); child = child.Sibling; } EndTime = (LastChild != null ? LastChild.EndTime : Time); m_IsAdjusted = true; } }; public class Data { public Time m_Time; public string m_String; public Data m_NextData; public Data(Time time, string data) { // set event data m_Time = time; m_String = data; m_NextData = null; } public bool Inside(Time min, Time max) { if ((m_Time < max) && (m_Time > min)) return true; return false; } public Time HypotheticalEndTime { get { if (m_NextData != null) return m_NextData.m_Time; return Time.MaxValue; } } public Time GetMaxTimeLength() { if(m_NextData != null) { return m_NextData.m_Time - m_Time; } else { return 0; } } } public class SleepEvent { public Time m_Start; public Time m_End; public SleepEvent(Time start, Time end) { m_Start = start; m_End = end; } } List m_Heads = new List(); List m_DataEvents = new List(); List m_SleepEvents = new List(); List m_AwakeEvents = new List(); Event m_LastEvent; int m_VisibleLevelCount = 1; int m_MaxLevel = 0; UInt32 m_ContextID; UInt32 m_OsThreadID; string m_Name; bool m_IsFiltered = false; public ProfileContext(string name, UInt32 contextID, UInt32 OsThreadID) { m_Name = name; m_ContextID = contextID; m_OsThreadID = OsThreadID; } public ProfileContext(string name, UInt32 contextID) { m_Name = name; m_ContextID = contextID; m_OsThreadID = 0; } public UInt32 ContextID { get { return m_ContextID; } } public UInt32 OsThreadID { get { return m_OsThreadID; } } public string Name { get { return m_Name; } } public int VisibleLevelCount { get { return m_VisibleLevelCount; } set { m_VisibleLevelCount = Math.Max(0, Math.Min(m_MaxLevel + 1, value)); } } public int MaxLevel { get { return m_MaxLevel; } } public List Heads { get { return m_Heads; } } public List DataEvents { get { return m_DataEvents; } } public List SleepEvents { get { return m_SleepEvents; } } public List AwakeEvents { get { return m_AwakeEvents; } } public bool ContainsAnyEvents() { return Heads.Count != 0 || SleepEvents.Count != 0 || AwakeEvents.Count != 0; } public bool ContainsProfileEvents() { return Heads.Count != 0; } public bool IsHidden { get { return m_IsFiltered; } set { m_IsFiltered = value; } } public void AddEvent(UInt64 time, UInt32 profileID, UInt32 type) { // create the event Event profileEvent = null; Time convertedTime = (Time)time; if (type == Event.EventStart) { if (m_LastEvent != null && convertedTime < m_LastEvent.Time) { Console.WriteLine("A start event's time for profile ID {0} in context {1} has been adjusted because its time is before the preceding event's time.", profileID, Name); profileEvent = new Event(m_LastEvent.Time, profileID, type); } else { profileEvent = new Event(convertedTime, profileID, type); } profileEvent.Context = this; } else { if (m_LastEvent == null) { Console.WriteLine("Unmatched stop event: ProfileID: {0}, time {1}", profileID, time); return; } } if (m_LastEvent == null) { // new head event m_Heads.Add(profileEvent); m_LastEvent = profileEvent; } else { // check if we're under the current event or if (type == Event.EventStart) { profileEvent.Father = m_LastEvent; profileEvent.Level = profileEvent.Father.Level + 1; m_MaxLevel = (int)Math.Max(profileEvent.Level, m_MaxLevel); m_VisibleLevelCount = m_MaxLevel + 1; // link to last event if (m_LastEvent.Child == null) { m_LastEvent.Child = profileEvent; m_LastEvent.LastChild = profileEvent; m_LastEvent = profileEvent; } else { // This boosts loading performance _a lot_ in some cases. Event lastChild = m_LastEvent.LastChild; lastChild.Sibling = profileEvent; m_LastEvent.LastChild = profileEvent; m_LastEvent = profileEvent; } } else if (type == Event.EventStop) { // close the last event if (convertedTime < m_LastEvent.Time) { Console.WriteLine("An end event's time for profile ID {0} in context {1} has been adjusted because it would create a negative length event.", profileID, Name); if (m_LastEvent.LastChild != null) m_LastEvent.EndTime = m_LastEvent.LastChild.EndTime; else m_LastEvent.EndTime = m_LastEvent.Time; } else { if (m_LastEvent.ProfileID != profileID) Console.WriteLine("Trying to close a {0} event with a {1} event on {2} context.", m_LastEvent.ProfileID, profileID, Name); else m_LastEvent.EndTime = time; } m_LastEvent = m_LastEvent.Father; } } } public Event FindEvent(UInt64 time) { foreach (Event profileEvent in m_Heads) { if (profileEvent.Inside(time, time)) { return profileEvent; } } return null; } public void AddDataEvent(Time time, string data) { Data newDataEvent = new Data(time, data); m_DataEvents.Add(newDataEvent); } public void LastDataEventAdded() { // Events are not necessarily sorted (since we use a ring buffer to store them in engine); sort them. m_DataEvents.Sort((d1, d2) => Math.Sign(d1.m_Time - d2.m_Time)); // Then, link them together. for (int i = 0; i < m_DataEvents.Count - 1; ++i) m_DataEvents[i].m_NextData = m_DataEvents[i + 1]; } public TimeExtent TimeExtent { get { TimeExtent extent = new TimeExtent(); foreach (Event profileEvent in m_Heads) { if (profileEvent.Time != 0) { extent.Merge(profileEvent.Time, profileEvent.EndTime); } } return extent; } } public bool IsTerminated { get { return m_LastEvent == null; } } public void AddSleepEvent(UInt64 Start, UInt64 End) { SleepEvent se = new SleepEvent(Start, End); m_SleepEvents.Add(se); } public void AddAwakeEvent(UInt64 Start, UInt64 End) { SleepEvent se = new SleepEvent(Start, End); m_AwakeEvents.Add(se); } } public ProfileEventData(Stream fromStream) { BinaryReader reader = new BinaryReader(fromStream); // construct data from the file EventFile eventFile = new EventFile(); eventFile.Serialize(reader); eventFile.FillData(this); m_Frequency = eventFile.m_Header.m_Frequency; m_Descriptor = eventFile.m_Header.m_Descriptor; } // map from profileID to name Hashtable m_Profiles = new Hashtable(); // map from contextID to it's ProfileContextEvents Hashtable m_Contexts = new Hashtable(); Hashtable m_Tasks = new Hashtable(); List m_Frames = new List(); List m_ThreadSwitch = new List(); public List ThreadSwitchs { get { return m_ThreadSwitch; } } List m_TaskInstances = new List(); public List Tasks { get { return m_TaskInstances; } } private List m_FrameDelimiters = new List(); public List FrameDelimiters { get { return m_FrameDelimiters; } } // clock frequency UInt32 m_Frequency = 1; string m_Descriptor; void SetFrequency(UInt32 frequency) { m_Frequency = frequency; } void SetDescriptor(string descriptor) { m_Descriptor = descriptor; } public UInt32 GetFrequency() { return m_Frequency; } public class ProfileDesc { public string Name; public Color Color; public ClockTime ClockTime = new ClockTime(); }; public struct TaskDesc { public string Name; public Color Color; public UInt32 TaskID; }; System.Random rnd = new System.Random(42); public void AddProfile(UInt32 profileID, string name) { if (!m_Profiles.Contains(profileID)) { ProfileDesc desc = new ProfileDesc(); desc.Name = name; desc.Color = Color.FromArgb(155 + rnd.Next(100), 155 + rnd.Next(100), 155 + rnd.Next(100)); m_Profiles[profileID] = desc; } } public void AddFrame(Time begin, Time end, Frame.FrameType type) { Frame frameEvent = new Frame(begin, end, type); m_Frames.Add(frameEvent); } public void AddTaskDesc(UInt32 taskID, string name) { TaskDesc desc = new TaskDesc(); desc.Color = Color.FromArgb(155 + rnd.Next(100), 155 + rnd.Next(100), 155 + rnd.Next(100)); desc.Name = name; desc.TaskID = taskID; m_Tasks[taskID] = desc; } public string GetProfileName(UInt32 profileID) { if (m_Profiles.Contains(profileID)) { return ((ProfileDesc)m_Profiles[profileID]).Name; } return "Unknown profile"; } public UInt32 GetProfileIDFromName(string name) { foreach (UInt32 key in m_Profiles.Keys) { ProfileDesc profile = (ProfileDesc)m_Profiles[key]; if (profile.Name == name) return key; } return UInt32.MaxValue; } public Color GetProfileColor(UInt32 profileID) { if (m_Profiles.Contains(profileID)) { return ((ProfileDesc)m_Profiles[profileID]).Color; } return Color.DarkGray; } public void AddContext(UInt32 contextID, UInt32 osThreadID, string name) { if (!m_Contexts.Contains(contextID)) { ProfileContext context = new ProfileContext(name, contextID, osThreadID); m_Contexts[contextID] = context; } } public void AddContext(UInt32 contextID, string name) { if (!m_Contexts.Contains(contextID)) { ProfileContext context = new ProfileContext(name, contextID); m_Contexts[contextID] = context; } } public void AddThreadSwitch(ThreadSwitch ts) { if(ts.m_Time < TimeExtent.Max && ts.m_Time > TimeExtent.Min) m_ThreadSwitch.Add(ts); } public ProfileContext GetEventContext(UInt32 contextID) { return (ProfileContext)m_Contexts[contextID]; } TimeExtent m_TimeExtent; public TimeExtent TimeExtent { get { if(m_TimeExtent == null) { m_TimeExtent = new TimeExtent(); foreach (ProfileContext context in m_Contexts.Values) { m_TimeExtent.Merge(context.TimeExtent); } } return m_TimeExtent; } } public Hashtable Contexts { get { return m_Contexts; } } public Hashtable Profiles { get { return m_Profiles; } } public List Frames { get { return m_Frames; } } enum TaskEventType { Start, End, Gate }; List openedTaskInstances = new List(); public TaskInstance FindOpenedTask(UInt32 taskID, bool create) { TaskInstance instance = openedTaskInstances.Find(delegate(TaskInstance val) { return val.Desc.TaskID == taskID; }); if (instance == null && create) { instance = new TaskInstance(); if (m_Tasks.Contains(taskID)) instance.Desc = (TaskDesc)m_Tasks[taskID]; else { instance.Desc = new TaskDesc(); instance.Desc.Name = "Unknown Task"; instance.Desc.Color = Color.Black; instance.Desc.TaskID = taskID; m_Tasks.Add(taskID, instance.Desc); } openedTaskInstances.Add(instance); m_TaskInstances.Add(instance); } return instance; } public void AddTaskEvent(UInt64 time, UInt32 taskID1, UInt32 taskID2, UInt32 type, UInt32 contextID) { TaskEventType eventType = (TaskEventType)type; if (taskID1 == 247706716 || taskID2 == 247706716) { Console.Write(""); } switch (eventType) { case TaskEventType.Start: { // Locate the task instance (create if it doesn't exist) TaskInstance instance = FindOpenedTask(taskID1, true); // Locate the corresponding event ProfileContext context = GetEventContext(contextID); ProfileContext.Event profileEvent = context.FindEvent(time); if (profileEvent != null) { // Link event and task instance profileEvent.Task = instance; if (!instance.Events.Exists(delegate(ProfileContext.Event theEvent) { return profileEvent == theEvent; })) { instance.Events.Add(profileEvent); } } instance.TimeExtent.Merge(time, time); } break; case TaskEventType.End: { // Locate the task instance (create if it doesn't exist) TaskInstance instance = FindOpenedTask(taskID1, false); if (instance != null) { instance.TimeExtent.Merge(time, time); } } break; case TaskEventType.Gate: { // Locate the task instance TaskInstance instance = FindOpenedTask(taskID1, false); if (instance != null) { TaskInstance nextTaskInstance = FindOpenedTask(taskID2, true); if (!instance.PostTasks.Exists(delegate(TaskInstance val) { return val.Desc.TaskID == taskID2; })) { instance.PostTasks.Add(nextTaskInstance); } if (!nextTaskInstance.PreTasks.Exists(delegate(TaskInstance val) { return val.Desc.TaskID == taskID1; })) { nextTaskInstance.PreTasks.Add(instance); } } else if (taskID1 == 0) { TaskInstance roottask = FindOpenedTask(taskID2, false); openedTaskInstances.Remove(roottask); } } break; } } public void LastTaskEventAdded() { openedTaskInstances.Clear(); // update the total time foreach (TaskInstance instance in m_TaskInstances) { foreach (ProfileContext.Event profileEvent in instance.Events) { instance.TotalTime += profileEvent.Length; } } } public void LastDataEventAdded() { foreach (ProfileContext context in m_Contexts.Values) context.LastDataEventAdded(); } public void SortThreadSwitch() { // Sort the thread switch by time, since it is the dump of a ring buffer. m_ThreadSwitch.Sort(delegate(ThreadSwitch t1, ThreadSwitch t2) { if (t1.m_Time < t2.m_Time) return -1; else if (t1.m_Time == t2.m_Time) return 0; else return 1; }); } public void ComputePerContextThreadSleep() { foreach (ProfileContext context in m_Contexts.Values) { if(context.OsThreadID != 0) { // This is ugly and have to be refactored... it works though... for (int i = 0; i < m_ThreadSwitch.Count; i++) { if (m_ThreadSwitch[i].m_oldThreadID == context.OsThreadID) { for (int j = i + 1; j < m_ThreadSwitch.Count; j++) { if (m_ThreadSwitch[j].m_newThreadID == context.OsThreadID) { context.AddSleepEvent(m_ThreadSwitch[i].m_Time, m_ThreadSwitch[j].m_Time); i = j - 1; break; } } } if (m_ThreadSwitch[i].m_newThreadID == context.OsThreadID) { for (int j = i + 1; j < m_ThreadSwitch.Count; j++) { if (m_ThreadSwitch[j].m_oldThreadID == context.OsThreadID) { context.AddAwakeEvent(m_ThreadSwitch[i].m_Time, m_ThreadSwitch[j].m_Time); i = j - 1; break; } } } } } } } } }