1630 lines
55 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|