JD2022-TU1/main/extern/ProfileEventViewer4/ProfileEvents.cs

1630 lines
55 KiB
C#

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<EventFileThreadDesc> m_Contexts = new List<EventFileThreadDesc>();
public List<EventFileProfileDesc> m_Profiles = new List<EventFileProfileDesc>();
public List<ProfileEvent> m_ProfileEvents = new List<ProfileEvent>();
public List<EventFileTaskDesc> m_TaskDescriptions = new List<EventFileTaskDesc>();
public List<TaskEvent> m_TaskEvents = new List<TaskEvent>();
public List<DataEvent> m_DataEvents = new List<DataEvent>();
public List<FrameEvent> m_FrameEvents = new List<FrameEvent>();
public List<ThreadSwitch> m_ThreadSwitches = new List<ThreadSwitch>();
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<ProfileEventData.ProfileContext.Event> events = new Stack<ProfileEventData.ProfileContext.Event>();
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<TaskEvent> taskEvents = new List<TaskEvent>(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<TaskInstance> PreTasks = new List<TaskInstance>();
public List<TaskInstance> PostTasks = new List<TaskInstance>();
public List<ProfileContext.Event> Events = new List<ProfileContext.Event>();
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<ProfileEventData.ProfileContext.Event> m_Heads = new List<ProfileEventData.ProfileContext.Event>();
List<ProfileEventData.ProfileContext.Data> m_DataEvents = new List<ProfileEventData.ProfileContext.Data>();
List<SleepEvent> m_SleepEvents = new List<SleepEvent>();
List<SleepEvent> m_AwakeEvents = new List<SleepEvent>();
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<ProfileEventData.ProfileContext.Event> Heads
{
get { return m_Heads; }
}
public List<ProfileEventData.ProfileContext.Data> DataEvents
{
get { return m_DataEvents; }
}
public List<SleepEvent> SleepEvents
{
get { return m_SleepEvents; }
}
public List<SleepEvent> 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<ProfileEventData.Frame> m_Frames = new List<ProfileEventData.Frame>();
List<ThreadSwitch> m_ThreadSwitch = new List<ThreadSwitch>();
public List<ThreadSwitch> ThreadSwitchs
{
get { return m_ThreadSwitch; }
}
List<TaskInstance> m_TaskInstances = new List<TaskInstance>();
public List<TaskInstance> Tasks
{
get { return m_TaskInstances; }
}
private List<FrameDelimiter> m_FrameDelimiters = new List<FrameDelimiter>();
public List<FrameDelimiter> 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<ProfileEventData.Frame> Frames
{
get { return m_Frames; }
}
enum TaskEventType
{
Start,
End,
Gate
};
List<TaskInstance> openedTaskInstances = new List<TaskInstance>();
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;
}
}
}
}
}
}
}
}
}