2501 lines
96 KiB
C#
2501 lines
96 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Windows.Forms;
|
|
|
|
using TD.SandDock;
|
|
using Time = System.Double;
|
|
|
|
namespace ProfileEventViewer
|
|
{
|
|
public partial class ProfileEventTrace : UserTabbedDocument
|
|
{
|
|
#region Internal classes, structures and enums
|
|
|
|
struct View
|
|
{
|
|
public Time TimeStart { get; private set;}
|
|
public Time TimeEnd { get; private set; }
|
|
public Time ZoomFactor { get { return 1.0 / (TimeEnd - TimeStart); } }
|
|
|
|
public delegate void ViewChangedHandler(View view);
|
|
public event ViewChangedHandler ViewChanged;
|
|
|
|
const Time MIN_DIFF = 5;
|
|
const Time MAX_DIFF = 100000000000;
|
|
|
|
private void SetZoom(Time startTime, Time endTime)
|
|
{
|
|
SetZoom(startTime, endTime, TimeStart, 0);
|
|
}
|
|
|
|
// In charge of changing the zoom and not putting bad values in it!
|
|
private void SetZoom(Time startTime, Time endTime, Time zoomLocation, Time startRatio)
|
|
{
|
|
if (endTime - startTime < MIN_DIFF) // Maximal zoom
|
|
{
|
|
TimeStart = zoomLocation - startRatio * MIN_DIFF;
|
|
TimeEnd = zoomLocation + (1 - startRatio) * MIN_DIFF;
|
|
}
|
|
else if (endTime - startTime > MAX_DIFF) // Minimal zoom
|
|
{
|
|
TimeStart = zoomLocation - startRatio * MAX_DIFF;
|
|
TimeEnd = zoomLocation + (1 - startRatio) * MAX_DIFF;
|
|
}
|
|
else
|
|
{
|
|
TimeStart = startTime;
|
|
TimeEnd = endTime;
|
|
}
|
|
|
|
if (ViewChanged != null)
|
|
ViewChanged(this);
|
|
}
|
|
|
|
private void ChangeZoom(Time startDelta, Time endDelta)
|
|
{
|
|
SetZoom(TimeStart + startDelta, TimeEnd + endDelta);
|
|
}
|
|
|
|
private void ChangeZoom(Time startDelta, Time endDelta, Time zoomLocation, Time startRatio)
|
|
{
|
|
SetZoom(TimeStart + startDelta, TimeEnd + endDelta, zoomLocation, startRatio);
|
|
}
|
|
|
|
public void ZoomOnTime(Time timeLocation, Time timeDx)
|
|
{
|
|
// where removing dx * timeStepPerPixel * 2 units from the selection
|
|
// split it around the focus so that the focus location on screen remains
|
|
// constant
|
|
Time timeToRemove = timeDx * 2;
|
|
|
|
Time ratio = (TimeEnd - timeLocation) / (TimeEnd - TimeStart);
|
|
|
|
ChangeZoom(timeToRemove * (1 - ratio), -timeToRemove * ratio, timeLocation, (1 - ratio));
|
|
}
|
|
|
|
public void Zoom(Time timeDx)
|
|
{
|
|
Time centerLocation = TimeStart + (TimeEnd - TimeStart) / 2;
|
|
ZoomOnTime(centerLocation, timeDx);
|
|
}
|
|
|
|
public void ZoomToAbsolute(Time start, Time end)
|
|
{
|
|
SetZoom(start, end, start, 0);
|
|
}
|
|
|
|
public void CenterOnLocation(Time location)
|
|
{
|
|
Time dt = location - (TimeStart + TimeEnd) / 2;
|
|
Pan(dt);
|
|
}
|
|
|
|
public void Pan(Time delta)
|
|
{
|
|
ChangeZoom(delta, delta);
|
|
}
|
|
|
|
public bool IsRangeVisible(Time rangeStart, Time rangeEnd)
|
|
{
|
|
System.Diagnostics.Debug.Assert(rangeStart <= rangeEnd);
|
|
return !(rangeStart > TimeEnd || rangeEnd < TimeStart || rangeStart == rangeEnd);
|
|
}
|
|
}
|
|
|
|
public class SelectionInfo
|
|
{
|
|
public Time m_MinTimeExtent = Time.MaxValue;
|
|
public Time m_MaxTimeExtent = Time.MinValue;
|
|
public ClockTime m_ClockTime = new ClockTime();
|
|
|
|
public Time m_TotalTime;
|
|
public Time m_MinTime = Time.MaxValue;
|
|
public Time m_MaxTime = Time.MinValue;
|
|
public UInt32 m_Calls;
|
|
};
|
|
|
|
struct DrawEventContext
|
|
{
|
|
public int m_Y;
|
|
public Graphics Graphics;
|
|
};
|
|
|
|
struct FindEventContext
|
|
{
|
|
public Time Time;
|
|
public int TargetDepth;
|
|
}
|
|
|
|
enum MouseMode
|
|
{
|
|
None,
|
|
Pan,
|
|
ZoomOnLocation,
|
|
MarkRange,
|
|
SelectContexts
|
|
};
|
|
|
|
#endregion
|
|
|
|
#region Static stuff
|
|
|
|
private const int EventHeight = 20;
|
|
private const int TitleHeight = 30;
|
|
private const int MaxDataEventWidth = 300;
|
|
|
|
private static readonly Color DataEventColor = Color.Yellow;
|
|
private static readonly Color FilteredDataEventColor = Color.FromArgb(100, Color.Yellow);
|
|
|
|
private static readonly Color FilteredOutColor = Color.FromArgb(100, 100, 100, 100);
|
|
|
|
private static readonly Pen engineFrameDelimiterPen = new Pen(Color.FromArgb(25, 200, 0, 0), 1);
|
|
private static readonly Pen graphicFrameDelimiterPen = new Pen(Color.FromArgb(25, 0, 0, 200), 1);
|
|
|
|
private static readonly Pen rectPen = new Pen(Color.Black, 1);
|
|
private static readonly Pen rectFilteredOutPen = new Pen(FilteredOutColor);
|
|
private static readonly Pen adjustedRectPen = new Pen(Color.Black, 1);
|
|
private static readonly Pen adjustedRectFilteredOutPen = new Pen(FilteredOutColor);
|
|
private static readonly Pen selectedRectPen = new Pen(Color.Black, 2);
|
|
private static readonly Pen selectedRectFilteredOutPen = new Pen(FilteredOutColor, 2);
|
|
private static readonly Pen adjustedSelectedRectPen = new Pen(Color.Black, 2);
|
|
private static readonly Pen adjustedSelectedRectFilteredOutPen = new Pen(FilteredOutColor, 2);
|
|
private static readonly Pen[,,] eventPens;
|
|
|
|
private static readonly SolidBrush rectBrush = new SolidBrush(Color.DarkGray);
|
|
private static readonly Brush contextBackgroundBrushLight = new SolidBrush(Color.LightGray);
|
|
private static readonly Brush contextBackgroundBrushDark = new SolidBrush(Color.Gray);
|
|
private static readonly Brush contextButtonBrush = new SolidBrush(Color.WhiteSmoke);
|
|
private static readonly Font contextTitleFont = new Font("Arial Black", 16);
|
|
private static readonly Font barFont = new Font("Arial Narrow", 10);
|
|
private static readonly Font dataEventFont = new Font("Arial Narrow", 9);
|
|
private static readonly Brush fontBrush = new SolidBrush(Color.Black);
|
|
private static readonly Brush fontFilteredOutBrush = new SolidBrush(Color.FromArgb(80, 80, 80, 80));
|
|
private static readonly Brush rangeBrush = new SolidBrush(Color.FromArgb(64, 64, 64, 64));
|
|
|
|
private static readonly Pen tspen = new Pen(Color.Black);
|
|
private static readonly Pen tspen2 = new Pen(Color.Black);
|
|
|
|
private static readonly SolidBrush sleepBrush = new SolidBrush(Color.FromArgb(40, Color.DarkRed));
|
|
private static readonly Pen sleepPen = new Pen(Color.FromArgb(96, Color.DarkRed));
|
|
private static readonly SolidBrush sleepFontBrush = new SolidBrush(Color.FromArgb(190, Color.DarkRed));
|
|
private static readonly SolidBrush filteredOutSleepBrush = new SolidBrush(Color.FromArgb(20, sleepBrush.Color));
|
|
private static readonly Pen filteredOutSleepPen = new Pen(Color.FromArgb(48, sleepPen.Color));
|
|
private static readonly SolidBrush filteredOutSleepFontBrush = new SolidBrush(Color.FromArgb(95, sleepFontBrush.Color));
|
|
|
|
private static readonly SolidBrush awakeBrush = new SolidBrush(Color.Ivory);
|
|
private static readonly Pen awakePen = new Pen(Color.Black);
|
|
private static readonly SolidBrush filteredOutAwakeBrush = new SolidBrush(Color.FromArgb(100, awakeBrush.Color));
|
|
private static readonly Pen filteredOutAwakePen = new Pen(Color.FromArgb(100, awakePen.Color));
|
|
|
|
private static readonly Pen timeLinePrimaryPen = new Pen(Color.FromArgb(32, Color.Black));
|
|
private static readonly Pen timeLineSecondaryPen = new Pen(Color.FromArgb(16, Color.Black));
|
|
private static readonly Brush timeLineBrush = new SolidBrush(Color.FromArgb(128, Color.Black));
|
|
private static readonly Font timeLineFont = new Font("Arial Black", 8);
|
|
|
|
private static readonly Pen highlightPen = new Pen(Color.Black, 2);
|
|
|
|
private static readonly Brush engineFrameBrush = new SolidBrush(Color.FromArgb(100, 200, 0, 0));
|
|
private static readonly Brush graphicFrameBrush = new SolidBrush(Color.FromArgb(100, 0, 0, 200));
|
|
private static readonly Brush whiteFontBrush = new SolidBrush(Color.White);
|
|
|
|
static ProfileEventTrace()
|
|
{
|
|
adjustedRectPen.DashStyle = DashStyle.Dash;
|
|
adjustedRectFilteredOutPen.DashStyle = DashStyle.Dash;
|
|
adjustedSelectedRectPen.DashStyle = DashStyle.Dash;
|
|
adjustedSelectedRectFilteredOutPen.DashStyle = DashStyle.Dash;
|
|
|
|
eventPens = new Pen[2 /*IsAdjusted*/, 2/*IsSelected*/, 2/*IsHidden*/];
|
|
eventPens[0, 0, 0] = rectPen;
|
|
eventPens[0, 0, 1] = rectFilteredOutPen;
|
|
eventPens[0, 1, 0] = selectedRectPen;
|
|
eventPens[0, 1, 1] = selectedRectFilteredOutPen;
|
|
eventPens[1, 0, 0] = adjustedRectPen;
|
|
eventPens[1, 0, 1] = adjustedRectFilteredOutPen;
|
|
eventPens[1, 1, 0] = adjustedSelectedRectPen;
|
|
eventPens[1, 1, 1] = adjustedSelectedRectFilteredOutPen;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
|
|
public delegate void FilterChangedHandler(ProfileEventTrace sender);
|
|
public event FilterChangedHandler EventsFilterChanged;
|
|
public event FilterChangedHandler ContextsFilterChanged;
|
|
|
|
public delegate void RangeSelectionChangedHandler(ProfileEventTrace sender, bool markingEnded, bool contentChanged);
|
|
public event RangeSelectionChangedHandler RangeSelectionChanged;
|
|
|
|
public delegate void ContextSelectionChangedHandler(ProfileEventTrace sender, HashSet<string> filteredContexts);
|
|
public event ContextSelectionChangedHandler ContextSelectionChanged;
|
|
|
|
public delegate void SelectionTimesChangedHandler(ProfileEventTrace sender);
|
|
public event SelectionTimesChangedHandler SelectionTimesChanged;
|
|
|
|
#endregion
|
|
|
|
#region Private Members
|
|
|
|
private ProfileEventDoc m_Doc;
|
|
private ProfileEventData m_Data;
|
|
private Summary m_Summary;
|
|
private TaskGraph m_TaskGraph;
|
|
private Filters m_Filters = new Filters();
|
|
private Regex m_ContextsVisibilityFilter = new Regex(".*", RegexOptions.Compiled);
|
|
|
|
private View m_View;
|
|
private int m_YOffset;
|
|
private Time m_RangeStart;
|
|
private Time m_RangeEnd;
|
|
private Time m_FocusTime;
|
|
|
|
private int m_SelectContextStartYOffset;
|
|
private int m_SelectContextEndYOffset;
|
|
|
|
private Time m_TimeStepPerPixel;
|
|
|
|
private Hashtable m_SelectionTimes = new Hashtable();
|
|
|
|
private bool m_ShowThreadInfo = false;
|
|
private bool m_ShowThreadInfoContexts = false;
|
|
private bool m_ShowTimeLine = false;
|
|
private bool m_ShowSecondaryTimeLineMarkers = false;
|
|
|
|
private bool m_ShowParentShadow = false;
|
|
private bool m_ShowTinyEvents = true;
|
|
|
|
private int m_LastX;
|
|
private int m_LastY;
|
|
|
|
private MouseMode m_MouseMode = MouseMode.None;
|
|
|
|
private ProfileEventData.ProfileContext.Event m_SelectedProfileEvent = null;
|
|
private ProfileEventData.ProfileContext.Data m_SelectedDataEvent = null;
|
|
|
|
private ProfileEventData.ProfileContext.Event m_ToolTipEvent = null;
|
|
private ProfileEventData.ProfileContext.Data m_ToolTipDataEvent = null;
|
|
private ProfileEventData.ProfileContext.SleepEvent m_SleepToolTip = null;
|
|
|
|
private HashSet<string> m_FilteredContexts = new HashSet<string>();
|
|
|
|
// Create a compatible Graphics
|
|
private Bitmap m_OffScreenBitmap = null;
|
|
private Graphics m_OffScreenGraphics = null;
|
|
private bool m_needRedraw = true;
|
|
|
|
#endregion
|
|
|
|
public ProfileEventTrace(ProfileEventDoc doc)
|
|
{
|
|
InitializeComponent();
|
|
|
|
m_Doc = doc;
|
|
m_Data = doc.Data;
|
|
|
|
Text = doc.Name + " - Trace";
|
|
|
|
doc.SelectedProfileEventChanged += new EventDelegate(OnSelectedProfileEventChanged);
|
|
doc.SelectedDataEventChanged += new EventDelegate(OnSelectedDataEventChanged);
|
|
|
|
ShowTimeLine = false;
|
|
ShowSecondaryTimeLineMarkers = true;
|
|
|
|
// set the time start to the extent
|
|
TimeExtent extent = m_Data.TimeExtent;
|
|
m_View.ViewChanged += new View.ViewChangedHandler(m_View_ViewChanged);
|
|
m_View.ZoomToAbsolute(extent.Min, extent.Max);
|
|
|
|
Redraw();
|
|
|
|
this.MouseWheel += new MouseEventHandler(ProfileEventsViewer_MouseWheel);
|
|
|
|
m_Summary = new Summary(this);
|
|
m_TaskGraph = null;
|
|
|
|
engineFrameDelimiterPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
|
|
graphicFrameDelimiterPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
|
|
|
|
eventToolTip.UseFading = false;
|
|
}
|
|
|
|
public ProfileEventDoc Doc
|
|
{
|
|
get { return m_Doc; }
|
|
}
|
|
|
|
public ProfileEventData Data
|
|
{
|
|
get { return m_Data; }
|
|
}
|
|
|
|
public Summary Summary
|
|
{
|
|
get { return m_Summary; }
|
|
}
|
|
|
|
public TaskGraph TaskGraph
|
|
{
|
|
get { return m_TaskGraph; }
|
|
set { m_TaskGraph = value; }
|
|
}
|
|
|
|
public bool ShowThreadInfo
|
|
{
|
|
get { return m_ShowThreadInfo; }
|
|
set
|
|
{
|
|
m_ShowThreadInfo = value;
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
public bool ShowThreadInfoContexts
|
|
{
|
|
get { return m_ShowThreadInfoContexts; }
|
|
set
|
|
{
|
|
m_ShowThreadInfoContexts = value;
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
public bool ShowTimeLine
|
|
{
|
|
get { return m_ShowTimeLine; }
|
|
set
|
|
{
|
|
m_ShowTimeLine = value;
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
public bool ShowSecondaryTimeLineMarkers
|
|
{
|
|
get { return m_ShowSecondaryTimeLineMarkers; }
|
|
set
|
|
{
|
|
m_ShowSecondaryTimeLineMarkers = value;
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
public bool HasSelection { get { return m_RangeEnd - m_RangeStart > 0; } }
|
|
public bool HasProfileEventSelection { get { return m_SelectedProfileEvent != null; } }
|
|
public bool HasDataEventSelection { get { return m_SelectedDataEvent != null; } }
|
|
|
|
public Hashtable SelectionTimes
|
|
{
|
|
get { return m_SelectionTimes; }
|
|
}
|
|
|
|
private Time ConvertXToTime(int x)
|
|
{
|
|
Time timeStepPerPixel = TimeStepPerPixel();
|
|
|
|
return (Time)((x * timeStepPerPixel) + m_View.TimeStart);
|
|
}
|
|
|
|
private Int64 ConvertTimeToXForDrawing(Time time)
|
|
{
|
|
Time timeStepPerPixel = TimeStepPerPixel();
|
|
|
|
// Allow 10 pixels outside the bounds of the screen
|
|
Time clampedTime = Math.Min(Math.Max(time, m_View.TimeStart - 10 * timeStepPerPixel), m_View.TimeEnd + 10 * timeStepPerPixel);
|
|
|
|
return (Int64)((clampedTime - m_View.TimeStart) / timeStepPerPixel);
|
|
}
|
|
|
|
private Int64 ConvertTimeToX(Time time)
|
|
{
|
|
Time timeStepPerPixel = TimeStepPerPixel();
|
|
|
|
return (Int64)((time - m_View.TimeStart) / timeStepPerPixel);
|
|
}
|
|
|
|
private bool IsContextShown(ProfileEventData.ProfileContext context)
|
|
{
|
|
return !context.IsHidden &&
|
|
(context.ContainsProfileEvents() || (context.ContainsAnyEvents() && ShowThreadInfoContexts));
|
|
}
|
|
|
|
public ProfileEventData.ProfileContext.Event FindEvent(ProfileEventData.ProfileContext.Event profileEvent, UInt32 profileID, ref int count, ref int depth)
|
|
{
|
|
// recursive function
|
|
if (profileEvent == null)
|
|
return null;
|
|
|
|
if (profileEvent.Inside(m_RangeStart, m_RangeEnd))
|
|
{
|
|
if (profileEvent.ProfileID == profileID)
|
|
--count;
|
|
|
|
if (count == 0)
|
|
{
|
|
depth = 1;
|
|
return profileEvent;
|
|
}
|
|
|
|
if (profileEvent.Child != null)
|
|
{
|
|
ProfileEventData.ProfileContext.Event child = profileEvent.Child;
|
|
while (child != null)
|
|
{
|
|
ProfileEventData.ProfileContext.Event candidate = FindEvent(child, profileID, ref count, ref depth);
|
|
if (candidate != null)
|
|
{
|
|
++depth;
|
|
return candidate;
|
|
}
|
|
|
|
child = child.Sibling;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private bool TryGetProfileIDFromEventName(string itemName, out UInt32 profileID)
|
|
{
|
|
profileID = 0;
|
|
|
|
foreach (UInt32 id in m_Data.Profiles.Keys)
|
|
{
|
|
ProfileEventData.ProfileDesc desc = (ProfileEventData.ProfileDesc)m_Data.Profiles[id];
|
|
if (desc.Name == itemName)
|
|
{
|
|
profileID = id;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool TryGetEventFromIdAndCount(UInt32 itemId, int clickCount, out ProfileEventData.ProfileContext.Event targetEvent, out int yOffset, out int depth)
|
|
{
|
|
targetEvent = null;
|
|
yOffset = m_YOffset;
|
|
depth = 0;
|
|
|
|
//UInt32 profileID = 0;
|
|
//if (!TryGetProfileIDFromEventName(itemName, out profileID))
|
|
// return false;
|
|
UInt32 profileID = itemId;
|
|
|
|
// from the profile ID, find occurrences of the event
|
|
int count = clickCount;
|
|
do
|
|
{
|
|
int contextmaxy = 0;
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
{
|
|
int contextStart;
|
|
bool isContextFiltered = m_FilteredContexts.Contains(context.Name);
|
|
|
|
if (!IsContextShown(context) || isContextFiltered)
|
|
continue;
|
|
|
|
contextmaxy += TitleHeight;
|
|
contextStart = contextmaxy;
|
|
contextmaxy += (context.VisibleLevelCount * EventHeight);
|
|
|
|
foreach (ProfileEventData.ProfileContext.Event profileEvent in context.Heads)
|
|
{
|
|
targetEvent = FindEvent(profileEvent, profileID, ref count, ref depth);
|
|
if (targetEvent != null)
|
|
{
|
|
yOffset = contextStart + TitleHeight + (depth - 1) * EventHeight;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
} while (count < clickCount);
|
|
|
|
return false;
|
|
}
|
|
|
|
private void ShowEventToolTip(ProfileEventData.ProfileContext.Event target, int yOffset)
|
|
{
|
|
string itemName = ((ProfileEventData.ProfileDesc)m_Data.Profiles[target.ProfileID]).Name;
|
|
|
|
string toolTipString;
|
|
if (target.Task != null)
|
|
{
|
|
eventToolTip.ToolTipTitle = String.Format("{0} - Task: {1}", itemName, target.Task.Desc.Name);
|
|
toolTipString = String.Format("Event time {0}\nTotal task time: {1}", TimeToString(target.Length), TimeToString(target.Task.TotalTime));
|
|
|
|
if (target.Task.PreTasks.Count > 0)
|
|
{
|
|
toolTipString += "\n\nPre Tasks:\n";
|
|
foreach (ProfileEventData.TaskInstance instance in target.Task.PreTasks)
|
|
{
|
|
toolTipString += String.Format("{0}\n", instance.Desc.Name);
|
|
}
|
|
}
|
|
|
|
if (target.Task.PostTasks.Count > 0)
|
|
{
|
|
toolTipString += "\nPost Tasks:\n";
|
|
foreach (ProfileEventData.TaskInstance instance in target.Task.PostTasks)
|
|
{
|
|
toolTipString += String.Format("{0}\n", instance.Desc.Name);
|
|
}
|
|
toolTipString += "\n";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
eventToolTip.ToolTipTitle = itemName;
|
|
toolTipString = TimeToString(target.Length);
|
|
}
|
|
|
|
m_ToolTipEvent = target;
|
|
m_ToolTipDataEvent = null;
|
|
m_SleepToolTip = null;
|
|
eventToolTip.InitialDelay = 0;
|
|
eventToolTip.AutoPopDelay = 100000;
|
|
eventToolTip.ShowAlways = true;
|
|
eventToolTip.Active = true;
|
|
eventToolTip.BackColor = m_Data.GetProfileColor(target.ProfileID);
|
|
eventToolTip.SetToolTip(this, toolTipString);
|
|
eventToolTip.Show(toolTipString, this, (int)ConvertTimeToX((m_View.TimeStart + m_View.TimeEnd) / 2), m_YOffset + yOffset - EventHeight / 2);
|
|
}
|
|
|
|
private void CenterViewVerticallyOnOffset(int yOffset)
|
|
{
|
|
m_YOffset = ClientSize.Height / 2 - yOffset;
|
|
}
|
|
|
|
public void CenterViewOnItem(UInt32 itemId, int clickCount)
|
|
{
|
|
ProfileEventData.ProfileContext.Event target;
|
|
int yOffset, depth;
|
|
|
|
if (!TryGetEventFromIdAndCount(itemId, clickCount, out target, out yOffset, out depth))
|
|
return;
|
|
|
|
Time time = target.Time + target.Length / 2;
|
|
m_View.CenterOnLocation(time);
|
|
|
|
if (target.Context.VisibleLevelCount < depth)
|
|
target.Context.VisibleLevelCount = depth;
|
|
|
|
CenterViewVerticallyOnOffset(yOffset);
|
|
|
|
ShowEventToolTip(target, yOffset);
|
|
|
|
Redraw();
|
|
}
|
|
|
|
private void ZoomOnEvent(ProfileEventData.ProfileContext.Event target)
|
|
{
|
|
if (target == null)
|
|
return;
|
|
|
|
m_View.ZoomToAbsolute(target.Time - target.Length / 20, target.EndTime + target.Length / 20);
|
|
m_View.CenterOnLocation(target.Time + (Time)(target.EndTime - target.Time) / 2); // Center on location if zoom is too small
|
|
}
|
|
|
|
public void ZoomViewOnItem(UInt32 itemId, int clickCount)
|
|
{
|
|
ProfileEventData.ProfileContext.Event target;
|
|
int yOffset, depth;
|
|
|
|
if (!TryGetEventFromIdAndCount(itemId, clickCount, out target, out yOffset, out depth))
|
|
return;
|
|
|
|
ZoomOnEvent(target);
|
|
|
|
if (target.Context.VisibleLevelCount < depth )
|
|
target.Context.VisibleLevelCount = depth ;
|
|
|
|
CenterViewVerticallyOnOffset(yOffset);
|
|
|
|
ShowEventToolTip(target, yOffset);
|
|
|
|
Redraw();
|
|
}
|
|
|
|
private void ProfileEventTrace_MouseClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button != MouseButtons.Left)
|
|
return;
|
|
|
|
ProfileEventData.ProfileContext.Data clickedDataEvent = null;
|
|
ProfileEventData.ProfileContext.Event clickedProfileEvent = GetEventFromMousePosition(m_LastX, m_LastY);
|
|
|
|
if (clickedProfileEvent == null)
|
|
{
|
|
clickedDataEvent = GetDataEventFromMousePosition(m_LastX, m_LastY);
|
|
|
|
if (clickedDataEvent == null)
|
|
{
|
|
bool ctrlIsDown = (Control.ModifierKeys & Keys.Control) == Keys.Control;
|
|
|
|
// Handle context plus/minus buttons
|
|
int yOffset = m_YOffset;
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
{
|
|
if (!IsContextShown(context))
|
|
continue;
|
|
|
|
Rectangle rectButtonMinus = new Rectangle(2, yOffset + 2, 11, 11);
|
|
Rectangle rectButtonPlus = new Rectangle(2, yOffset + 17, 11, 11);
|
|
|
|
if (rectButtonMinus.Contains(m_LastX, m_LastY))
|
|
{
|
|
if (!ctrlIsDown)
|
|
--context.VisibleLevelCount;
|
|
else
|
|
context.VisibleLevelCount = 0;
|
|
Redraw();
|
|
return;
|
|
}
|
|
|
|
if (rectButtonPlus.Contains(m_LastX, m_LastY))
|
|
{
|
|
if (!ctrlIsDown)
|
|
++context.VisibleLevelCount;
|
|
else
|
|
context.VisibleLevelCount = context.MaxLevel + 1;
|
|
|
|
Redraw();
|
|
return;
|
|
}
|
|
|
|
yOffset += TitleHeight + (context.VisibleLevelCount * EventHeight);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_Doc.SelectedProfileEvent = clickedProfileEvent;
|
|
m_Doc.SelectedDataEvent = clickedDataEvent;
|
|
}
|
|
|
|
private void ProfileEventTrace_MouseDoubleClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button != MouseButtons.Left)
|
|
return;
|
|
|
|
ProfileEventData.ProfileContext.Event targetEvent = GetEventFromMousePosition(m_LastX, m_LastY);
|
|
if (targetEvent != null)
|
|
{
|
|
bool ctrlIsDown = (Control.ModifierKeys & Keys.Control) == Keys.Control;
|
|
|
|
if (!ctrlIsDown)
|
|
{
|
|
// Double click: change profile event filter
|
|
string profileName = m_Data.GetProfileName(targetEvent.ProfileID);
|
|
string newFilterName = string.Format(@"""{0}""", profileName);
|
|
if (m_Filters.OriginalFilter != newFilterName)
|
|
SetEventsFilterString(newFilterName);
|
|
else
|
|
SetEventsFilterString(string.Empty);
|
|
}
|
|
else
|
|
{
|
|
// Ctrl + Double click: change selection range to targeted event's range
|
|
// Ctrl + Shift + Double click: extend selection range to include targeted event's range
|
|
bool shiftIsDown = (Control.ModifierKeys & Keys.Shift) == Keys.Shift;
|
|
bool extendSelection = shiftIsDown;
|
|
|
|
m_Doc.SelectedProfileEvent = targetEvent;
|
|
AdjustRangeToSelectedEvent(extendSelection);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProfileEventsViewer_MouseWheel(object sender, MouseEventArgs e)
|
|
{
|
|
Time timeStepPerPixel = TimeStepPerPixel();
|
|
Time timeDx = (timeStepPerPixel);
|
|
|
|
// Cap the delta, so that we don't zoom in too much
|
|
const int MAX_DELTA = 240;
|
|
const int MIN_DELTA = -360; // It's longer to unzoom than to zoom...
|
|
int delta = Math.Max(Math.Min(e.Delta, MAX_DELTA), MIN_DELTA);
|
|
|
|
Time location = ConvertXToTime(e.X);
|
|
|
|
if (Control.ModifierKeys == Keys.Shift)
|
|
{
|
|
m_View.ZoomOnTime(location, timeDx * (delta / 10));
|
|
}
|
|
else
|
|
{
|
|
m_View.ZoomOnTime(location, timeDx * delta);
|
|
}
|
|
|
|
Redraw();
|
|
}
|
|
|
|
private void ProfileEventsViewer_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
|
|
{
|
|
m_LastX = e.X;
|
|
m_LastY = e.Y;
|
|
|
|
bool shiftIsDown = (Control.ModifierKeys & Keys.Shift) == Keys.Shift;
|
|
bool ctrlIsDown = (Control.ModifierKeys & Keys.Control) == Keys.Control;
|
|
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
if (shiftIsDown)
|
|
{
|
|
m_MouseMode = MouseMode.Pan;
|
|
Cursor = Cursors.NoMove2D;
|
|
Capture = true;
|
|
}
|
|
else if (ctrlIsDown)
|
|
{
|
|
m_FocusTime = ConvertXToTime(e.X);
|
|
m_MouseMode = MouseMode.ZoomOnLocation;
|
|
Cursor = Cursors.NoMoveHoriz;
|
|
Capture = true;
|
|
}
|
|
}
|
|
|
|
if (e.Button == MouseButtons.Middle)
|
|
{
|
|
m_MouseMode = MouseMode.Pan;
|
|
Cursor = Cursors.NoMove2D;
|
|
Capture = true;
|
|
}
|
|
|
|
if (e.Button == MouseButtons.Right)
|
|
{
|
|
Capture = true;
|
|
|
|
if (!ctrlIsDown)
|
|
{
|
|
m_MouseMode = MouseMode.MarkRange;
|
|
Cursor = Cursors.NoMoveHoriz;
|
|
|
|
if (!shiftIsDown || m_RangeStart == m_RangeEnd) // Create new selection
|
|
{
|
|
m_RangeStart = ConvertXToTime(e.X);
|
|
m_RangeEnd = m_RangeStart;
|
|
m_FocusTime = m_RangeStart;
|
|
}
|
|
else // Extend selection
|
|
{
|
|
Time clickTime = ConvertXToTime(e.X);
|
|
if (clickTime < m_RangeStart)
|
|
{
|
|
m_FocusTime = m_RangeEnd;
|
|
m_RangeStart = clickTime;
|
|
}
|
|
else
|
|
{
|
|
m_FocusTime = m_RangeStart;
|
|
m_RangeEnd = clickTime;
|
|
}
|
|
}
|
|
|
|
FireSelectionChanged(false, true);
|
|
}
|
|
else
|
|
{
|
|
// Select contexts.
|
|
m_MouseMode = MouseMode.SelectContexts;
|
|
Cursor = Cursors.NoMoveVert;
|
|
m_SelectContextStartYOffset = e.Y - m_YOffset;
|
|
m_SelectContextEndYOffset = m_SelectContextStartYOffset;
|
|
}
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
private void OnSelectedProfileEventChanged(ProfileEventDoc doc)
|
|
{
|
|
if (m_SelectedProfileEvent != doc.SelectedProfileEvent)
|
|
{
|
|
m_SelectedProfileEvent = m_Doc.SelectedProfileEvent;
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
private void OnSelectedDataEventChanged(ProfileEventDoc doc)
|
|
{
|
|
if (m_SelectedDataEvent != doc.SelectedDataEvent)
|
|
{
|
|
m_SelectedDataEvent = m_Doc.SelectedDataEvent;
|
|
Redraw();
|
|
}
|
|
|
|
}
|
|
|
|
private void ProfileEventsViewer_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
|
|
{
|
|
MouseMode oldMouseMode = m_MouseMode;
|
|
|
|
m_MouseMode = MouseMode.None;
|
|
Cursor = Cursors.Default;
|
|
Capture = false;
|
|
|
|
if (oldMouseMode == MouseMode.MarkRange)
|
|
{
|
|
FireSelectionChanged(true, true);
|
|
}
|
|
else if (oldMouseMode == MouseMode.SelectContexts)
|
|
{
|
|
SelectContextsInYRange(m_SelectContextStartYOffset, m_SelectContextEndYOffset, true);
|
|
m_SelectContextStartYOffset = m_SelectContextEndYOffset;
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
private void ProfileEventsViewer_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
|
|
{
|
|
int dx = e.X - m_LastX;
|
|
int dy = e.Y - m_LastY;
|
|
|
|
m_LastX = e.X;
|
|
m_LastY = e.Y;
|
|
|
|
// return if nothing to do
|
|
if (dx == 0 && dy == 0)
|
|
return;
|
|
|
|
// Handle tooltips
|
|
if (m_MouseMode == MouseMode.None)
|
|
{
|
|
ProfileEventData.ProfileContext.Event profileEvent = GetEventFromMousePosition(m_LastX, m_LastY);
|
|
|
|
// draw a tooltip
|
|
if (profileEvent != null)
|
|
{
|
|
if (m_ToolTipEvent != profileEvent)
|
|
{
|
|
string toolTipString;
|
|
if (profileEvent.Task != null)
|
|
{
|
|
eventToolTip.ToolTipTitle = String.Format("{0} - Task: {1}", m_Data.GetProfileName(profileEvent.ProfileID), profileEvent.Task.Desc.Name);
|
|
toolTipString = String.Format("Event time {0}\nTotal task time: {1}", TimeToString(profileEvent.Length), TimeToString(profileEvent.Task.TotalTime));
|
|
|
|
if (profileEvent.Task.PreTasks.Count > 0)
|
|
{
|
|
toolTipString += "\n\nPre Tasks:\n";
|
|
foreach (ProfileEventData.TaskInstance instance in profileEvent.Task.PreTasks)
|
|
{
|
|
toolTipString += String.Format("{0}\n", instance.Desc.Name);
|
|
}
|
|
}
|
|
|
|
if (profileEvent.Task.PostTasks.Count > 0)
|
|
{
|
|
toolTipString += "\nPost Tasks:\n";
|
|
foreach (ProfileEventData.TaskInstance instance in profileEvent.Task.PostTasks)
|
|
{
|
|
toolTipString += String.Format("{0}\n", instance.Desc.Name);
|
|
}
|
|
toolTipString += "\n";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
eventToolTip.ToolTipTitle = m_Data.GetProfileName(profileEvent.ProfileID);
|
|
toolTipString = TimeToString(profileEvent.Length);
|
|
}
|
|
|
|
m_ToolTipEvent = profileEvent;
|
|
m_ToolTipDataEvent = null;
|
|
m_SleepToolTip = null;
|
|
eventToolTip.InitialDelay = 0;
|
|
eventToolTip.AutoPopDelay = 100000;
|
|
eventToolTip.ShowAlways = true;
|
|
eventToolTip.Active = true;
|
|
eventToolTip.BackColor = m_Data.GetProfileColor(profileEvent.ProfileID);
|
|
eventToolTip.SetToolTip(this, toolTipString);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ProfileEventData.ProfileContext.Data dataEvent = GetDataEventFromMousePosition(m_LastX, m_LastY);
|
|
if (dataEvent != null)
|
|
{
|
|
if (m_ToolTipDataEvent != dataEvent)
|
|
{
|
|
eventToolTip.ToolTipTitle = "";
|
|
m_ToolTipEvent = null;
|
|
m_ToolTipDataEvent = dataEvent;
|
|
m_SleepToolTip = null;
|
|
eventToolTip.InitialDelay = 0;
|
|
eventToolTip.AutoPopDelay = 100000;
|
|
eventToolTip.ShowAlways = true;
|
|
eventToolTip.Active = true;
|
|
eventToolTip.BackColor = Color.Yellow;
|
|
eventToolTip.SetToolTip(this, dataEvent.m_String);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ProfileEventData.ProfileContext.SleepEvent sleepEvent = GetSleepEventFromMousePosition(m_LastX, m_LastY);
|
|
if (sleepEvent != null && ShowThreadInfo)
|
|
{
|
|
if(sleepEvent != m_SleepToolTip)
|
|
{
|
|
// find the ThreadSwitchEvents related to this sleep event..
|
|
String ContextTo = "Unknown (Lock?)";
|
|
String ContextFrom = "Unknown (Unlock?)";
|
|
|
|
for (int i = 0; i < m_Data.ThreadSwitchs.Count; i++)
|
|
{
|
|
if (sleepEvent.m_Start == m_Data.ThreadSwitchs[i].m_Time)
|
|
{
|
|
// find the Context to which this thread switched to...
|
|
foreach (ProfileEventData.ProfileContext loopcontext in m_Data.Contexts.Values)
|
|
if (loopcontext.OsThreadID == m_Data.ThreadSwitchs[i].m_newThreadID && loopcontext.OsThreadID != 0)
|
|
ContextTo = loopcontext.Name;
|
|
}
|
|
if (sleepEvent.m_End == m_Data.ThreadSwitchs[i].m_Time)
|
|
{
|
|
// find the Context to which this thread switched from...
|
|
foreach (ProfileEventData.ProfileContext loopcontext2 in m_Data.Contexts.Values)
|
|
if (loopcontext2.OsThreadID == m_Data.ThreadSwitchs[i].m_oldThreadID && loopcontext2.OsThreadID != 0)
|
|
ContextFrom = loopcontext2.Name;
|
|
}
|
|
}
|
|
|
|
eventToolTip.ToolTipTitle = String.Format("This thread is IDLE: ");
|
|
m_ToolTipEvent = null;
|
|
m_ToolTipDataEvent = null;
|
|
m_SleepToolTip = sleepEvent;
|
|
eventToolTip.InitialDelay = 0;
|
|
eventToolTip.AutoPopDelay = 100000;
|
|
eventToolTip.ShowAlways = true;
|
|
eventToolTip.Active = true;
|
|
eventToolTip.BackColor = Color.Yellow;
|
|
String ToolTipText = String.Format("The thread switched to : {0}\nAnd then switched back from : {1}.", ContextTo, ContextFrom);
|
|
eventToolTip.SetToolTip(this, ToolTipText);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_ToolTipEvent = null;
|
|
m_ToolTipDataEvent = null;
|
|
m_SleepToolTip = null;
|
|
eventToolTip.Active = false;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
m_ToolTipEvent = null;
|
|
m_ToolTipDataEvent = null;
|
|
m_SleepToolTip = null;
|
|
eventToolTip.Active = false;
|
|
}
|
|
|
|
|
|
// get window width
|
|
Time timeStepPerPixel = TimeStepPerPixel();
|
|
Time timeDx = (timeStepPerPixel * dx);
|
|
switch (m_MouseMode)
|
|
{
|
|
case MouseMode.Pan:
|
|
m_View.Pan(-timeDx);
|
|
m_YOffset += dy;
|
|
WrapAround(e.X, e.Y, dx, dy);
|
|
break;
|
|
case MouseMode.ZoomOnLocation:
|
|
m_View.ZoomOnTime(m_FocusTime, timeDx);
|
|
WrapAround(e.X, e.Y, dx, dy);
|
|
break;
|
|
case MouseMode.MarkRange:
|
|
Time currentSelectedTime = ConvertXToTime(e.X);
|
|
if (currentSelectedTime < m_FocusTime)
|
|
{
|
|
m_RangeEnd = m_FocusTime;
|
|
m_RangeStart = currentSelectedTime;
|
|
}
|
|
else
|
|
{
|
|
m_RangeStart = m_FocusTime;
|
|
m_RangeEnd = currentSelectedTime;
|
|
}
|
|
|
|
FireSelectionChanged(false, true);
|
|
|
|
break;
|
|
case MouseMode.SelectContexts:
|
|
m_SelectContextEndYOffset = e.Y - m_YOffset;
|
|
SelectContextsInYRange(m_SelectContextStartYOffset, m_SelectContextEndYOffset, false);
|
|
break;
|
|
}
|
|
|
|
Redraw();
|
|
}
|
|
|
|
private void SelectContextsInYRange(int startYRange, int endYRange, bool applyFilterChanges)
|
|
{
|
|
// If the range is empty, clear the selection
|
|
if (startYRange == endYRange)
|
|
{
|
|
m_FilteredContexts.Clear();
|
|
}
|
|
else
|
|
{
|
|
int start = Math.Min(startYRange, endYRange);
|
|
int end = Math.Max(startYRange, endYRange);
|
|
|
|
int currentYOffset = 0;
|
|
|
|
// Find overlapping contexts.
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
{
|
|
if (!IsContextShown(context))
|
|
continue;
|
|
|
|
int contextStart = currentYOffset;
|
|
int contextEnd = contextStart + TitleHeight + (context.VisibleLevelCount * EventHeight);
|
|
|
|
bool overlaps = ((contextStart <= start && start <= contextEnd) ||
|
|
(contextStart <= end && end <= contextEnd) ||
|
|
(contextStart > start && contextEnd < end));
|
|
|
|
if (overlaps)
|
|
m_FilteredContexts.Remove(context.Name);
|
|
else
|
|
m_FilteredContexts.Add(context.Name);
|
|
|
|
currentYOffset = contextEnd;
|
|
}
|
|
}
|
|
|
|
if (applyFilterChanges)
|
|
{
|
|
if (ContextSelectionChanged != null)
|
|
ContextSelectionChanged(this, m_FilteredContexts);
|
|
|
|
ComputeSelectionTimes();
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
private void WrapAround(int x, int y, int dx, int dy)
|
|
{
|
|
int newX = x, newY = y;
|
|
|
|
if (x > ClientSize.Width)
|
|
newX = x % ClientSize.Width;
|
|
else if (x < 0)
|
|
newX = ClientSize.Width + x % ClientSize.Width;
|
|
|
|
if (y > ClientSize.Height)
|
|
newY = y % ClientSize.Height;
|
|
else if (y < 0)
|
|
newY = ClientSize.Height + y % ClientSize.Height;
|
|
|
|
m_LastX = newX;
|
|
m_LastY = newY;
|
|
|
|
if (newX != x || newY != y)
|
|
{
|
|
Point p = new Point(newX, newY);
|
|
Point newMousePos = PointToScreen(p);
|
|
Cursor.Position = newMousePos;
|
|
}
|
|
}
|
|
|
|
public string TimeToString(Time length)
|
|
{
|
|
UInt32 frequency = m_Data.GetFrequency();
|
|
Time frequencyInUS = ((Time)frequency) / 1000000;
|
|
|
|
Time lengthInUS = length / frequencyInUS;
|
|
|
|
if (lengthInUS <= 0)
|
|
{
|
|
return String.Format("{0} cycles", length);
|
|
}
|
|
else if (lengthInUS < 500)
|
|
{
|
|
return String.Format("{0:F3} us", lengthInUS);
|
|
}
|
|
else if (lengthInUS < 1000000)
|
|
{
|
|
return String.Format("{0:F2} ms", (lengthInUS / 1000));
|
|
}
|
|
else
|
|
{
|
|
return String.Format("{0:F2} s", (lengthInUS / 1000000));
|
|
}
|
|
}
|
|
|
|
struct EventScreenInfo
|
|
{
|
|
public Int64 m_ScreenX;
|
|
public Int64 m_ScreenLength;
|
|
public Int64 m_ScreenXE;
|
|
public Int64 m_ScreenY;
|
|
};
|
|
|
|
private bool ComputeEventScreenInfo(ProfileEventData.ProfileContext.Event profileEvent, ref DrawEventContext drawContext, ref EventScreenInfo info)
|
|
{
|
|
ProfileEventData.ProfileContext targetContext = profileEvent.Context;
|
|
if (targetContext.IsHidden)
|
|
return false;
|
|
|
|
Int64 screenXE = (Int64)ConvertTimeToX(profileEvent.Time + profileEvent.Length);
|
|
Int64 screenX = (Int64)ConvertTimeToX(profileEvent.Time);
|
|
screenX = Math.Max(-20, screenX);
|
|
screenXE = Math.Min(ClientSize.Width + 20, screenXE);
|
|
if (screenXE == screenX)
|
|
screenXE++;
|
|
Int64 screenLength = screenXE - screenX;
|
|
|
|
info.m_ScreenLength = screenLength;
|
|
info.m_ScreenX = screenX;
|
|
info.m_ScreenXE = screenXE;
|
|
|
|
|
|
// find the Y
|
|
int contextmaxy = (Int32)m_YOffset;
|
|
|
|
// find which context it's in
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
{
|
|
int contextStart;
|
|
|
|
if (!IsContextShown(context))
|
|
continue;
|
|
|
|
contextmaxy += TitleHeight;
|
|
contextStart = contextmaxy;
|
|
contextmaxy += (context.VisibleLevelCount * EventHeight);
|
|
|
|
if (context == targetContext)
|
|
{
|
|
info.m_ScreenY = contextStart;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void DrawEvent(ProfileEventData.ProfileContext.Event profileEvent, bool isContextFiltered, ref DrawEventContext drawContext, int maxStack)
|
|
{
|
|
if (maxStack == 0)
|
|
return;
|
|
|
|
if (profileEvent.Inside(m_View.TimeStart, m_View.TimeEnd))
|
|
{
|
|
// compute screen X size
|
|
Int64 screenX = ConvertTimeToX(profileEvent.Time);
|
|
Int64 screenXE = ConvertTimeToX(profileEvent.EndTime);
|
|
|
|
string profileEventName = m_Data.GetProfileName(profileEvent.ProfileID);
|
|
bool filteredOut = isContextFiltered;
|
|
if (!filteredOut && m_Filters.IsValid)
|
|
filteredOut = !m_Filters.IsAllowed(profileEventName);
|
|
|
|
// compute correct color
|
|
Color profileColor = m_Data.GetProfileColor(profileEvent.ProfileID);
|
|
if (filteredOut)
|
|
profileColor = Color.FromArgb(25, profileColor);
|
|
|
|
// draw a rectangle
|
|
rectBrush.Color = profileColor;
|
|
screenX = Math.Max(-20, screenX);
|
|
screenXE = Math.Min(ClientSize.Width + 20, screenXE);
|
|
if (screenXE == screenX)
|
|
screenXE++;
|
|
|
|
Int64 screenLength = screenXE - screenX;
|
|
|
|
if (screenLength > 1)
|
|
drawContext.Graphics.FillRectangle(rectBrush, screenX, drawContext.m_Y, screenLength, EventHeight);
|
|
else if (!m_ShowTinyEvents)
|
|
return;
|
|
|
|
int adjustedIndex = (profileEvent.IsAdjusted ? 1 : 0);
|
|
int selectedIndex = (m_SelectedProfileEvent == profileEvent ? 1 : 0);
|
|
int filteredIndex = (filteredOut ? 1 : 0);
|
|
Pen drawPen = eventPens[adjustedIndex, selectedIndex, filteredIndex];
|
|
|
|
drawContext.Graphics.DrawRectangle(drawPen, screenX, drawContext.m_Y, screenLength, EventHeight);
|
|
|
|
// set the clip region and display the profile name (if there's any chance it could fit)
|
|
if (screenLength > 20)
|
|
{
|
|
Region oldClipRegion = drawContext.Graphics.Clip;
|
|
drawContext.Graphics.SetClip(new Rectangle((int)screenX + 1, drawContext.m_Y, (int)screenLength - 1, EventHeight));
|
|
|
|
string profileName = String.Format("{0}{1}, {2}", (screenX < 0) ? "<< " : "", profileEventName, TimeToString(profileEvent.Length));
|
|
|
|
drawContext.Graphics.DrawString(profileName, barFont, filteredOut ? fontFilteredOutBrush : fontBrush, Math.Max(screenX, 0), drawContext.m_Y);
|
|
drawContext.Graphics.Clip = oldClipRegion;
|
|
}
|
|
|
|
// must draw the childs (if we were not too small)
|
|
if (profileEvent.Child != null && screenLength > 1 && maxStack > 1)
|
|
{
|
|
if (m_ShowParentShadow)
|
|
drawContext.Graphics.FillRectangle(rangeBrush, screenX, drawContext.m_Y + EventHeight, screenLength, EventHeight);
|
|
|
|
drawContext.m_Y += EventHeight;
|
|
|
|
ProfileEventData.ProfileContext.Event child = profileEvent.Child;
|
|
|
|
while (child != null)
|
|
{
|
|
DrawEvent(child, isContextFiltered, ref drawContext, maxStack - 1);
|
|
child = child.Sibling;
|
|
}
|
|
|
|
drawContext.m_Y -= EventHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
private int GetDataEventScreenWidth(ProfileEventData.ProfileContext.Data dataEvent)
|
|
{
|
|
Time maxTimeLength = dataEvent.GetMaxTimeLength();
|
|
int maxWidth = MaxDataEventWidth;
|
|
int screenLength = 1;
|
|
if (maxTimeLength > 0)
|
|
{
|
|
Int64 maxScreenWidth = (Int64)(maxTimeLength / TimeStepPerPixel());
|
|
if (maxScreenWidth < maxWidth)
|
|
maxWidth = (int)maxScreenWidth;
|
|
}
|
|
if (maxWidth <= 0)
|
|
screenLength = 1;
|
|
else if (maxWidth > 20)
|
|
screenLength = (int)m_OffScreenGraphics.MeasureString(dataEvent.m_String, dataEventFont, maxWidth).Width;
|
|
else
|
|
screenLength = maxWidth;
|
|
|
|
return screenLength;
|
|
}
|
|
|
|
private void DrawDataEvent(ProfileEventData.ProfileContext.Data dataEvent, bool isContextFiltered, ref DrawEventContext drawContext)
|
|
{
|
|
if (dataEvent.Inside(m_View.TimeStart, m_View.TimeEnd))
|
|
{
|
|
int eventY = drawContext.m_Y - EventHeight;
|
|
int gapHeight = 5;
|
|
int eventHeight = EventHeight - gapHeight;
|
|
|
|
// compute screen X size
|
|
Int64 screenX = ConvertTimeToX(dataEvent.m_Time);
|
|
Int64 screenLength = GetDataEventScreenWidth(dataEvent);
|
|
Int64 screenXE = screenX + screenLength;
|
|
|
|
// draw a data event poly
|
|
// 1___________2
|
|
// | __________|
|
|
// 5|/4 3
|
|
//
|
|
bool filteredOut = isContextFiltered;
|
|
if (!filteredOut && m_Filters.IsValid)
|
|
filteredOut = !m_Filters.IsAllowed(dataEvent.m_String);
|
|
|
|
rectBrush.Color = (filteredOut ? FilteredDataEventColor : DataEventColor);
|
|
screenX = Math.Max(-20, screenX);
|
|
screenXE = Math.Min(ClientSize.Width + 20, screenXE);
|
|
screenLength = screenXE - screenX;
|
|
Point[] eventPoly = {
|
|
new Point((int)screenX, eventY),
|
|
new Point((int)screenX+(int)screenLength, eventY),
|
|
new Point((int)screenX+(int)screenLength, eventY+eventHeight),
|
|
new Point((int)screenX+gapHeight, eventY+eventHeight),
|
|
new Point((int)screenX, eventY+eventHeight+gapHeight),
|
|
};
|
|
|
|
if (screenLength > 1)
|
|
drawContext.Graphics.FillPolygon(rectBrush, eventPoly);
|
|
|
|
int adjustedIndex = 0; // Data events are never adjusted
|
|
int selectedIndex = (m_SelectedDataEvent == dataEvent ? 1 : 0);
|
|
int filteredIndex = (filteredOut ? 1 : 0);
|
|
Pen drawPen = eventPens[adjustedIndex, selectedIndex, filteredIndex];
|
|
drawContext.Graphics.DrawPolygon(drawPen, eventPoly);
|
|
|
|
// set the clip region and display the profile name (if there's any chance it could fit)
|
|
if (screenLength > 20)
|
|
{
|
|
Region oldClipRegion = drawContext.Graphics.Clip;
|
|
drawContext.Graphics.SetClip(new Rectangle((int)screenX + 1, eventY, (int)screenLength - 1, EventHeight));
|
|
|
|
drawContext.Graphics.DrawString(dataEvent.m_String, dataEventFont, filteredOut ? fontFilteredOutBrush : fontBrush, Math.Max(screenX, 0), eventY);
|
|
drawContext.Graphics.Clip = oldClipRegion;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HightLightEvent(ProfileEventData.ProfileContext.Event forEvent, Color color, ref DrawEventContext drawContext)
|
|
{
|
|
if (forEvent == null)
|
|
return;
|
|
|
|
// Locate the event screen coordinates
|
|
EventScreenInfo info = new EventScreenInfo();
|
|
if (ComputeEventScreenInfo(forEvent, ref drawContext, ref info))
|
|
{
|
|
// Draw rectangle
|
|
highlightPen.Color = color;
|
|
drawContext.Graphics.DrawRectangle(highlightPen, info.m_ScreenX, info.m_ScreenY, info.m_ScreenLength, EventHeight);
|
|
}
|
|
}
|
|
|
|
void HightLightTaskInstance(ProfileEventData.TaskInstance taskInstance, Color color, ref DrawEventContext drawContext)
|
|
{
|
|
foreach (ProfileEventData.ProfileContext.Event profileEvent in taskInstance.Events)
|
|
{
|
|
HightLightEvent(profileEvent, color, ref drawContext);
|
|
}
|
|
}
|
|
|
|
void DrawLinkedTasks(ProfileEventData.TaskInstance taskInstance, ref DrawEventContext drawContext, bool preTasks)
|
|
{
|
|
List<ProfileEventData.TaskInstance> instanceList = taskInstance.PostTasks;
|
|
Color color = Color.Green;
|
|
|
|
if (preTasks)
|
|
{
|
|
instanceList = taskInstance.PreTasks;
|
|
color = Color.Red;
|
|
}
|
|
|
|
foreach (ProfileEventData.TaskInstance task in instanceList)
|
|
{
|
|
if (task.Events.Count > 0)
|
|
HightLightTaskInstance(task, color, ref drawContext);
|
|
else
|
|
// probably a node task follow the node to the next task
|
|
DrawLinkedTasks(task, ref drawContext, preTasks);
|
|
}
|
|
}
|
|
|
|
void DrawTaskLinks(ProfileEventData.ProfileContext.Event forEvent, ref DrawEventContext drawContext)
|
|
{
|
|
ProfileEventData.TaskInstance instance = forEvent.Task;
|
|
|
|
if (instance != null)
|
|
{
|
|
// highlight the selected event
|
|
HightLightTaskInstance(instance, Color.Blue, ref drawContext);
|
|
|
|
DrawLinkedTasks(instance, ref drawContext, true);
|
|
DrawLinkedTasks(instance, ref drawContext, false);
|
|
}
|
|
|
|
}
|
|
|
|
private void DrawThreadSwitch(ref DrawEventContext drawContext)
|
|
{
|
|
const Int32 trisizeX = 5;
|
|
const Int32 trisizeY = 10;
|
|
const float MaxThreadSwitchInView = 500.0F;
|
|
|
|
if (ShowThreadInfo)
|
|
{
|
|
// Draw Thread Switch
|
|
Int32 X = 0;
|
|
Int32 oldX = -1;
|
|
|
|
List<Int32>ThreadSwitchX = new List<Int32>();
|
|
foreach (ThreadSwitch ts in m_Data.ThreadSwitchs)
|
|
{
|
|
X = (Int32)ConvertTimeToX((Time)ts.m_Time);
|
|
if (X < ClientSize.Width && X > 0)
|
|
ThreadSwitchX.Add(X);
|
|
}
|
|
|
|
if(ThreadSwitchX.Count < MaxThreadSwitchInView && ThreadSwitchX.Count > 0)
|
|
{
|
|
double alpha = ((1.0 - (ThreadSwitchX.Count / MaxThreadSwitchInView)) * 128.0);
|
|
alpha /= (1 + ((128.0 - alpha) / 5));
|
|
|
|
tspen.Color = Color.FromArgb((int)alpha, Color.Black);
|
|
// tspen.DashPattern = new float[] { 1.0F, 2.0F };
|
|
tspen2.Color = Color.FromArgb((int)alpha, Color.Black);
|
|
|
|
Point p1 = new Point(0, drawContext.m_Y - 15);
|
|
Point p2 = new Point(0, ClientSize.Height - 1);
|
|
|
|
foreach (int x in ThreadSwitchX)
|
|
{
|
|
p1.X = x;
|
|
// tspen.DashOffset = p1.X % 3;
|
|
if (oldX != p1.X)
|
|
{
|
|
p2.X = p1.X;
|
|
oldX = p1.X;
|
|
drawContext.Graphics.DrawLine(tspen, p1, p2);
|
|
drawContext.Graphics.DrawLine(tspen2, p1.X - trisizeX, p1.Y - trisizeY, p1.X, p1.Y);
|
|
drawContext.Graphics.DrawLine(tspen2, p1.X + trisizeX, p1.Y - trisizeY, p1.X, p1.Y);
|
|
drawContext.Graphics.DrawLine(tspen2, p1.X - trisizeX, p1.Y - trisizeY, p1.X + trisizeX, p1.Y - trisizeY);
|
|
}
|
|
}
|
|
}
|
|
|
|
// End thread switch draw...
|
|
}
|
|
}
|
|
|
|
void DrawSleepEvent(ProfileEventData.ProfileContext.SleepEvent sleep, bool isContextFiltered, ref DrawEventContext drawContext, int numContextLevel)
|
|
{
|
|
if (ShowThreadInfo)
|
|
{
|
|
Int32 Height = numContextLevel * EventHeight;
|
|
Int32 screenY = drawContext.m_Y + (EventHeight * 1 / 2);
|
|
Int32 textScreenY;
|
|
if (Height > EventHeight)
|
|
{
|
|
Height -= EventHeight;
|
|
textScreenY = screenY + (Height - EventHeight) / 2;
|
|
}
|
|
else
|
|
{
|
|
screenY = drawContext.m_Y;
|
|
textScreenY = screenY;
|
|
}
|
|
|
|
|
|
Int32 screenX = (Int32)ConvertTimeToX(sleep.m_Start);
|
|
Int32 screenXE = (Int32)ConvertTimeToX(sleep.m_End);
|
|
screenX = Math.Max(-20, screenX);
|
|
screenXE = Math.Min(ClientSize.Width + 20, screenXE);
|
|
Int32 screenLength = screenXE - screenX;
|
|
|
|
if (screenX > ClientSize.Width || screenXE < 0)
|
|
return;
|
|
|
|
Rectangle rect = new Rectangle(screenX, screenY, screenXE - screenX, Height);
|
|
// draw a rectangle
|
|
|
|
if (screenLength > 1)
|
|
drawContext.Graphics.FillRectangle(isContextFiltered ? filteredOutSleepBrush : sleepBrush, rect);
|
|
drawContext.Graphics.DrawRectangle(isContextFiltered ? filteredOutSleepPen : sleepPen, rect);
|
|
|
|
// set the clip region and display the profile name (if there's any chance it could fit)
|
|
if (screenLength > 20)
|
|
{
|
|
Region oldClipRegion = drawContext.Graphics.Clip;
|
|
drawContext.Graphics.SetClip(new Rectangle((int)screenX + 1, textScreenY, (int)screenXE - screenX - 1, Height));
|
|
|
|
string sleepInfo = String.Format("{0}{1}, {2}", (screenX < 0) ? "<< " : "", "Idle", TimeToString(sleep.m_End - sleep.m_Start));
|
|
|
|
drawContext.Graphics.DrawString(sleepInfo, barFont, isContextFiltered ? filteredOutSleepFontBrush : sleepFontBrush, Math.Max(screenX, 0), textScreenY);
|
|
drawContext.Graphics.Clip = oldClipRegion;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawAwakeEvent(ProfileEventData.ProfileContext.SleepEvent awake, bool isContextFiltered, ref DrawEventContext drawContext, int numContextLevel)
|
|
{
|
|
if (numContextLevel == 0)
|
|
return;
|
|
|
|
//if (ShowThreadInfo)
|
|
{
|
|
|
|
Int32 Height = EventHeight;
|
|
Int32 screenY = drawContext.m_Y;
|
|
|
|
Int32 screenX = (Int32)ConvertTimeToX(awake.m_Start);
|
|
Int32 screenXE = (Int32)ConvertTimeToX(awake.m_End);
|
|
screenX = Math.Max(-20, screenX);
|
|
screenXE = Math.Min(ClientSize.Width + 20, screenXE);
|
|
if (screenX > ClientSize.Width || screenXE < 0)
|
|
return;
|
|
|
|
Int32 screenLength = screenXE - screenX;
|
|
Rectangle rect = new Rectangle(screenX, screenY, screenLength, Height);
|
|
|
|
// draw a rectangle
|
|
if (screenLength > 1)
|
|
drawContext.Graphics.FillRectangle(isContextFiltered ? filteredOutAwakeBrush : awakeBrush, rect);
|
|
drawContext.Graphics.DrawRectangle(isContextFiltered ? filteredOutAwakePen : awakePen, rect);
|
|
|
|
// set the clip region and display the profile name (if there's any chance it could fit)
|
|
if (screenLength > 20)
|
|
{
|
|
Region oldClipRegion = drawContext.Graphics.Clip;
|
|
drawContext.Graphics.SetClip(new Rectangle((int)screenX + 1, screenY, (int)screenXE - screenX - 1, Height));
|
|
|
|
string awakeInfo = String.Format("{0}{1}, {2}", (screenX < 0) ? "<< " : "", "Working", TimeToString(awake.m_End - awake.m_Start));
|
|
|
|
drawContext.Graphics.DrawString(awakeInfo, barFont, isContextFiltered ? fontFilteredOutBrush : fontBrush, Math.Max(screenX, 0), screenY);
|
|
drawContext.Graphics.Clip = oldClipRegion;
|
|
}
|
|
}
|
|
}
|
|
|
|
Time FindNextTimeMarker(Time startTime, Time timeStep)
|
|
{
|
|
return FindNextTimeMarker(startTime, timeStep, true);
|
|
}
|
|
|
|
Time FindNextTimeMarker(Time startTime, Time timeStep, bool ahead)
|
|
{
|
|
return Math.Floor((startTime / timeStep) + (ahead ? 1 : 0)) * timeStep;
|
|
}
|
|
|
|
IEnumerable<Time> GetTimeMarkers(double deltaExp, Time multiplier)
|
|
{
|
|
Time timeStepPerPixel = TimeStepPerPixel();
|
|
Time markerTimeStep = (Time)Math.Pow(10.0, Math.Floor(Math.Log10((double)timeStepPerPixel)) + deltaExp) * multiplier;
|
|
|
|
// Return at least one marker before the screen
|
|
yield return FindNextTimeMarker(ConvertXToTime(0), markerTimeStep, false);
|
|
|
|
Time nextTime = FindNextTimeMarker(ConvertXToTime(0), markerTimeStep);
|
|
while (ConvertTimeToX(nextTime) < ClientSize.Width)
|
|
{
|
|
yield return nextTime;
|
|
nextTime = FindNextTimeMarker(nextTime, markerTimeStep);
|
|
};
|
|
|
|
// Return at least one marker after the screen
|
|
yield return nextTime;
|
|
}
|
|
|
|
IEnumerable<Time> GetPrimaryTimeMarkers()
|
|
{
|
|
return GetTimeMarkers(2, 5);
|
|
}
|
|
|
|
IEnumerable<Time> GetSecondaryTimeMarkers()
|
|
{
|
|
return GetTimeMarkers(2, 1);
|
|
}
|
|
|
|
void DrawTimeLine(ref DrawEventContext drawContext)
|
|
{
|
|
List<Time> primaryMarkers = GetPrimaryTimeMarkers().ToList();
|
|
foreach (Time primaryMarker in primaryMarkers)
|
|
{
|
|
int screenX = (int)ConvertTimeToX(primaryMarker);
|
|
drawContext.Graphics.DrawLine(timeLinePrimaryPen, screenX, 0, screenX, ClientSize.Height);
|
|
}
|
|
|
|
if (ShowSecondaryTimeLineMarkers)
|
|
{
|
|
foreach (Time primaryMarker in GetSecondaryTimeMarkers())
|
|
{
|
|
int screenX = (int)ConvertTimeToX(primaryMarker);
|
|
drawContext.Graphics.DrawLine(timeLineSecondaryPen, screenX, 0, screenX, ClientSize.Height);
|
|
}
|
|
}
|
|
|
|
string scaleLabelStr = "Distance between primary markers";
|
|
string scaleStr = TimeToString(primaryMarkers[1] - primaryMarkers[0]);
|
|
float scaleLabelWidth = drawContext.Graphics.MeasureString(scaleLabelStr, timeLineFont).Width;
|
|
float scaleWidth = drawContext.Graphics.MeasureString(scaleStr, timeLineFont).Width;
|
|
drawContext.Graphics.DrawString(scaleLabelStr, timeLineFont, timeLineBrush, ClientSize.Width - scaleLabelWidth, ClientSize.Height - 2 * EventHeight);
|
|
drawContext.Graphics.DrawString(scaleStr, timeLineFont, timeLineBrush, ClientSize.Width - scaleWidth, ClientSize.Height - EventHeight);
|
|
}
|
|
|
|
void UpdateOffscreenSurface()
|
|
{
|
|
if (m_OffScreenBitmap != null)
|
|
m_OffScreenBitmap.Dispose();
|
|
|
|
if (m_OffScreenGraphics != null)
|
|
m_OffScreenGraphics.Dispose();
|
|
|
|
if (ClientSize.Width != 0 && ClientSize.Height != 0)
|
|
{
|
|
m_OffScreenBitmap = new Bitmap(ClientSize.Width, ClientSize.Height);
|
|
m_OffScreenGraphics = Graphics.FromImage(m_OffScreenBitmap);
|
|
m_needRedraw = true;
|
|
}
|
|
}
|
|
|
|
protected override void OnLostFocus(EventArgs e)
|
|
{
|
|
m_ToolTipEvent = null;
|
|
m_ToolTipDataEvent = null;
|
|
m_SleepToolTip = null;
|
|
eventToolTip.Active = false;
|
|
}
|
|
|
|
|
|
protected override void OnPaintBackground(PaintEventArgs pevent)
|
|
{
|
|
// do not paint background once we've loaded data
|
|
if (m_Data == null)
|
|
{
|
|
base.OnPaintBackground(pevent);
|
|
}
|
|
}
|
|
|
|
private ProfileEventData.ProfileContext.Event GetEventFromMousePositionInContext(FindEventContext findContext, ProfileEventData.ProfileContext.Event profileEvent)
|
|
{
|
|
if (findContext.Time >= profileEvent.Time &&
|
|
findContext.Time <= profileEvent.EndTime)
|
|
{
|
|
// could be in this one, if the depth is good
|
|
if (findContext.TargetDepth == profileEvent.Level)
|
|
{
|
|
return profileEvent;
|
|
}
|
|
|
|
// depth isn't correct, search the childs
|
|
if (profileEvent.Child != null)
|
|
{
|
|
ProfileEventData.ProfileContext.Event child = profileEvent.Child;
|
|
|
|
while (child != null)
|
|
{
|
|
profileEvent = GetEventFromMousePositionInContext(findContext, child);
|
|
|
|
if (profileEvent != null)
|
|
return profileEvent;
|
|
|
|
child = child.Sibling;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private ProfileEventData.ProfileContext.Event GetEventFromMousePosition(int x, int y)
|
|
{
|
|
if (x < 0 || x > ClientSize.Width || m_Data == null)
|
|
return null;
|
|
|
|
int contextmaxy = (Int32)m_YOffset;
|
|
|
|
FindEventContext findContext = new FindEventContext();
|
|
|
|
// We add half a pixel to get the time in the middle of a pixel, so a 2 pixels tasks can be pointed onto more easily
|
|
findContext.Time = (((Time)x + 0.5) * TimeStepPerPixel()) + m_View.TimeStart;
|
|
|
|
// find which context it's in
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
{
|
|
int contextStart;
|
|
|
|
if (!IsContextShown(context))
|
|
continue;
|
|
|
|
contextmaxy += TitleHeight;
|
|
contextStart = contextmaxy;
|
|
contextmaxy += (context.VisibleLevelCount * EventHeight);
|
|
|
|
if (y >= contextStart && y < contextmaxy)
|
|
{
|
|
findContext.TargetDepth = (y - contextStart) / (EventHeight);
|
|
|
|
foreach (ProfileEventData.ProfileContext.Event profileEvent in context.Heads)
|
|
{
|
|
ProfileEventData.ProfileContext.Event foundEvent = GetEventFromMousePositionInContext(findContext, profileEvent);
|
|
|
|
if (foundEvent != null)
|
|
return foundEvent;
|
|
}
|
|
}
|
|
|
|
contextStart = contextmaxy;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private ProfileEventData.ProfileContext.Data GetDataEventFromMousePosition(int x, int y)
|
|
{
|
|
if (x < 0 || x > ClientSize.Width || m_Data == null)
|
|
return null;
|
|
|
|
Time mouseTime = (((Time)x) * TimeStepPerPixel()) + m_View.TimeStart;
|
|
|
|
int contextmaxy = (Int32)m_YOffset;
|
|
|
|
// find which context it's in
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
{
|
|
int contextStart;
|
|
|
|
if (!IsContextShown(context))
|
|
continue;
|
|
|
|
contextmaxy += TitleHeight;
|
|
contextStart = contextmaxy;
|
|
contextmaxy += (context.VisibleLevelCount * EventHeight);
|
|
|
|
if (y >= (contextStart - EventHeight) && y < contextStart)
|
|
{
|
|
foreach (ProfileEventData.ProfileContext.Data dataEvent in context.DataEvents)
|
|
{
|
|
if (mouseTime >= dataEvent.m_Time && mouseTime <= dataEvent.HypotheticalEndTime)
|
|
{
|
|
// Time fits... let's check against what was rendered
|
|
Int64 dataEventX = (Int64)((dataEvent.m_Time - m_View.TimeStart) / TimeStepPerPixel());
|
|
int screenLength = GetDataEventScreenWidth(dataEvent);
|
|
if (x <= (dataEventX + screenLength))
|
|
return dataEvent;
|
|
}
|
|
}
|
|
}
|
|
|
|
contextStart = contextmaxy;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
private ProfileEventData.ProfileContext.SleepEvent GetSleepEventFromMousePosition(int x, int y)
|
|
{
|
|
if (x < 0 || x > ClientSize.Width || m_Data == null)
|
|
return null;
|
|
|
|
int contextmaxy = (Int32)m_YOffset;
|
|
|
|
// find which context it's in
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
{
|
|
int contextStart;
|
|
|
|
if (!IsContextShown(context))
|
|
continue;
|
|
|
|
contextmaxy += TitleHeight;
|
|
contextStart = contextmaxy;
|
|
contextmaxy += (context.VisibleLevelCount * EventHeight);
|
|
if (y >= contextStart && y < contextmaxy)
|
|
{
|
|
foreach (ProfileEventData.ProfileContext.SleepEvent sleepEvent in context.SleepEvents)
|
|
{
|
|
if (x > ConvertTimeToX(sleepEvent.m_Start) && x <= ConvertTimeToX(sleepEvent.m_End))
|
|
return sleepEvent;
|
|
}
|
|
}
|
|
contextStart = contextmaxy;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void ComputeContextSelectionTimes(ProfileEventData.ProfileContext.Event profileEvent)
|
|
{
|
|
if (profileEvent.Inside(m_RangeStart, m_RangeEnd))
|
|
{
|
|
// Only consider the included time
|
|
Time start = profileEvent.Time;
|
|
Time end = profileEvent.Time + profileEvent.Length;
|
|
|
|
if (start < m_RangeStart)
|
|
start = m_RangeStart;
|
|
if (end > m_RangeEnd)
|
|
end = m_RangeEnd;
|
|
|
|
// Add
|
|
// profileEvent.m_ProfileID += end-start;
|
|
SelectionInfo oldVal = (SelectionInfo)m_SelectionTimes[profileEvent.ProfileID];
|
|
oldVal.m_MinTimeExtent = Math.Min(oldVal.m_MinTimeExtent, start);
|
|
oldVal.m_MaxTimeExtent = Math.Max(oldVal.m_MaxTimeExtent, end);
|
|
oldVal.m_MinTime = Math.Min(oldVal.m_MinTime, (end - start));
|
|
oldVal.m_MaxTime = Math.Max(oldVal.m_MaxTime, (end - start));
|
|
oldVal.m_TotalTime += (end - start);
|
|
oldVal.m_Calls++;
|
|
if (IsAnyContextFilteredOrHidden())
|
|
oldVal.m_ClockTime.Merge(start, end);
|
|
|
|
//m_SelectionTimes[profileEvent.ProfileID] = oldVal;
|
|
// Childs
|
|
if (profileEvent.Child != null)
|
|
{
|
|
ProfileEventData.ProfileContext.Event child = profileEvent.Child;
|
|
|
|
while (child != null)
|
|
{
|
|
ComputeContextSelectionTimes(child);
|
|
child = child.Sibling;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ComputeSelectionTimes()
|
|
{
|
|
// Clear the times
|
|
m_SelectionTimes.Clear();
|
|
|
|
// Do nothing if no data or no selection
|
|
if (m_Data != null && m_RangeStart != m_RangeEnd)
|
|
{
|
|
// Pre-enter all the profile ids
|
|
foreach (UInt32 id in m_Data.Profiles.Keys)
|
|
{
|
|
ProfileEventData.ProfileDesc profile = (ProfileEventData.ProfileDesc) m_Data.Profiles[id];
|
|
ClockTime clockTime = (!IsAnyContextFilteredOrHidden()) ? profile.ClockTime.GetClockTimeWithinRange(m_RangeStart, m_RangeEnd) : new ClockTime();
|
|
m_SelectionTimes.Add(id, new SelectionInfo() { m_ClockTime = clockTime });
|
|
}
|
|
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
{
|
|
if (context.IsHidden || m_FilteredContexts.Contains(context.Name))
|
|
continue;
|
|
|
|
foreach (ProfileEventData.ProfileContext.Event profileEvent in context.Heads)
|
|
{
|
|
ComputeContextSelectionTimes(profileEvent);
|
|
}
|
|
}
|
|
|
|
// Extra step when some contexts are filtered or hidden
|
|
if (IsAnyContextFilteredOrHidden())
|
|
foreach (SelectionInfo info in m_SelectionTimes.Values)
|
|
info.m_ClockTime.CoalesceResults();
|
|
}
|
|
|
|
if (SelectionTimesChanged != null)
|
|
SelectionTimesChanged(this);
|
|
}
|
|
|
|
void FireSelectionChanged(bool markingEnded, bool contentChanged)
|
|
{
|
|
if (markingEnded && contentChanged)
|
|
ComputeSelectionTimes();
|
|
|
|
if (RangeSelectionChanged != null)
|
|
RangeSelectionChanged(this, markingEnded, contentChanged);
|
|
}
|
|
|
|
public void Redraw()
|
|
{
|
|
m_needRedraw = true;
|
|
Invalidate();
|
|
}
|
|
|
|
private void ProfileEventsViewer_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
|
|
{
|
|
if (m_Data == null || ClientSize.Width == 0 || ClientSize.Height == 0)
|
|
return;
|
|
|
|
if (m_OffScreenBitmap == null)
|
|
UpdateOffscreenSurface();
|
|
|
|
if (m_needRedraw)
|
|
{
|
|
Int64 width = ClientSize.Width;
|
|
Time timeStepPerPixel = TimeStepPerPixel();
|
|
|
|
DrawEventContext drawContext = new DrawEventContext();
|
|
drawContext.Graphics = m_OffScreenGraphics;
|
|
drawContext.Graphics.Clear(SystemColors.Control);
|
|
drawContext.m_Y = (Int32)m_YOffset;
|
|
|
|
// draw the thing
|
|
int contextIndex = 0;
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
{
|
|
if (!IsContextShown(context))
|
|
continue;
|
|
|
|
bool isContextFiltered = m_FilteredContexts.Contains(context.Name);
|
|
|
|
Brush titleBrush = ((contextIndex & 1) == 0) ? contextBackgroundBrushLight : contextBackgroundBrushDark;
|
|
Brush textBrush = (isContextFiltered ? fontFilteredOutBrush : fontBrush);
|
|
// Print context name
|
|
drawContext.Graphics.FillRectangle(titleBrush, 0, drawContext.m_Y, width, (context.VisibleLevelCount * EventHeight) + TitleHeight);
|
|
drawContext.Graphics.DrawString(context.Name, contextTitleFont, textBrush, 15, drawContext.m_Y);
|
|
|
|
Rectangle rectButtonMinus = new Rectangle(2, drawContext.m_Y + 2, 11, 11);
|
|
Rectangle rectButtonPlus = new Rectangle(2, drawContext.m_Y + 17, 11, 11);
|
|
|
|
drawContext.Graphics.FillRectangle(contextButtonBrush, rectButtonMinus);
|
|
drawContext.Graphics.FillRectangle(contextButtonBrush, rectButtonPlus);
|
|
|
|
bool isMinusButtonFiltered = isContextFiltered || (context.VisibleLevelCount == 0);
|
|
bool isPlusButtonFiltered = isContextFiltered || (context.VisibleLevelCount == context.MaxLevel + 1);
|
|
drawContext.Graphics.DrawRectangle(isMinusButtonFiltered ? rectFilteredOutPen : rectPen, rectButtonMinus);
|
|
drawContext.Graphics.DrawRectangle(isPlusButtonFiltered ? rectFilteredOutPen : rectPen, rectButtonPlus);
|
|
|
|
StringFormat buttonFormat = new StringFormat();
|
|
buttonFormat.Alignment = StringAlignment.Center;
|
|
buttonFormat.LineAlignment = StringAlignment.Center;
|
|
|
|
drawContext.Graphics.DrawString("-", barFont, isMinusButtonFiltered ? fontFilteredOutBrush : fontBrush, 8, drawContext.m_Y + 7, buttonFormat);
|
|
drawContext.Graphics.DrawString("+", barFont, isPlusButtonFiltered ? fontFilteredOutBrush : fontBrush, 8, drawContext.m_Y + 22, buttonFormat);
|
|
|
|
drawContext.m_Y += TitleHeight;
|
|
|
|
drawContext.m_Y += (context.VisibleLevelCount * EventHeight);
|
|
contextIndex++;
|
|
}
|
|
|
|
drawContext.m_Y = (Int32)m_YOffset;
|
|
contextIndex = 0;
|
|
|
|
DrawThreadSwitch(ref drawContext);
|
|
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
{
|
|
if (!IsContextShown(context))
|
|
continue;
|
|
|
|
bool isContextFiltered = m_FilteredContexts.Contains(context.Name);
|
|
|
|
drawContext.m_Y += TitleHeight;
|
|
int contextHeight = (context.VisibleLevelCount * EventHeight);
|
|
|
|
if (drawContext.m_Y > ClientSize.Height)
|
|
break;
|
|
else if (drawContext.m_Y + contextHeight <= 0)
|
|
{
|
|
drawContext.m_Y += contextHeight;
|
|
continue;
|
|
}
|
|
|
|
foreach (ProfileEventData.ProfileContext.Event profileEvent in context.Heads)
|
|
{
|
|
DrawEvent(profileEvent, isContextFiltered, ref drawContext, context.VisibleLevelCount);
|
|
}
|
|
|
|
foreach (ProfileEventData.ProfileContext.Data dataEvent in context.DataEvents)
|
|
{
|
|
DrawDataEvent(dataEvent, isContextFiltered, ref drawContext);
|
|
}
|
|
|
|
foreach (ProfileEventData.ProfileContext.SleepEvent sleepEvent in context.SleepEvents)
|
|
{
|
|
DrawSleepEvent(sleepEvent, isContextFiltered, ref drawContext, context.VisibleLevelCount);
|
|
}
|
|
|
|
if (context.Heads.Count == 0)
|
|
{
|
|
foreach (ProfileEventData.ProfileContext.SleepEvent awakeEvent in context.AwakeEvents)
|
|
{
|
|
DrawAwakeEvent(awakeEvent, isContextFiltered, ref drawContext, context.VisibleLevelCount);
|
|
}
|
|
}
|
|
|
|
drawContext.m_Y += contextHeight;
|
|
|
|
contextIndex++;
|
|
}
|
|
|
|
DrawFrameDelimiters(ref drawContext);
|
|
|
|
Int64 rangeX, rangeXE, rangeWidth;
|
|
|
|
foreach (ProfileEventData.Frame frame in m_Data.Frames)
|
|
{
|
|
Time min = frame.Extent.Min;
|
|
Time max = frame.Extent.Max;
|
|
|
|
if (!m_View.IsRangeVisible(min, max))
|
|
continue;
|
|
|
|
rangeX = ConvertTimeToXForDrawing(min);
|
|
rangeXE = ConvertTimeToXForDrawing(max);
|
|
rangeWidth = rangeXE - rangeX;
|
|
|
|
Brush brush;
|
|
const int height = 17;
|
|
RectangleF rect = new RectangleF(rangeX, ClientSize.Height - height, rangeWidth, height);
|
|
if (frame.Type == ProfileEventData.Frame.FrameType.Engine)
|
|
{
|
|
brush = engineFrameBrush;
|
|
}
|
|
else
|
|
{
|
|
brush = graphicFrameBrush;
|
|
rect.Offset(0, - height);
|
|
}
|
|
|
|
// display the selection range extent
|
|
drawContext.Graphics.FillRectangle(brush, rect);
|
|
|
|
// display range length
|
|
StringFormat format = new StringFormat();
|
|
format.Trimming = StringTrimming.EllipsisCharacter;
|
|
drawContext.Graphics.DrawString(TimeToString(max - min), dataEventFont, whiteFontBrush, rect, format);
|
|
}
|
|
|
|
if (m_SelectedProfileEvent != null)
|
|
{
|
|
DrawTaskLinks(m_SelectedProfileEvent, ref drawContext);
|
|
}
|
|
|
|
if (m_View.IsRangeVisible(m_RangeStart, m_RangeEnd))
|
|
{
|
|
// draw the selection range
|
|
rangeX = ConvertTimeToXForDrawing(m_RangeStart);
|
|
rangeXE = ConvertTimeToXForDrawing(m_RangeEnd);
|
|
if (rangeXE == rangeX)
|
|
rangeXE++;
|
|
rangeWidth = rangeXE - rangeX;
|
|
|
|
// display the selection range extent
|
|
drawContext.Graphics.FillRectangle(rangeBrush, rangeX, 0, rangeWidth, ClientSize.Height);
|
|
|
|
drawContext.Graphics.DrawString(TimeToString(m_RangeEnd - m_RangeStart), barFont, fontBrush, Math.Max(rangeX, 0), ClientSize.Height - EventHeight);
|
|
}
|
|
else if (m_RangeStart != m_RangeEnd)
|
|
{
|
|
// display range length and direction of selection
|
|
if (m_RangeEnd < m_View.TimeStart)
|
|
{
|
|
drawContext.Graphics.DrawString(string.Format("\u25c4 {0}", TimeToString(m_RangeEnd - m_RangeStart)), barFont, fontBrush, 0, ClientSize.Height - EventHeight);
|
|
}
|
|
else // if (m_RangeStart > m_View.TimeEnd)
|
|
{
|
|
string selectionLengthStr = string.Format("{0} \u25ba", TimeToString(m_RangeEnd - m_RangeStart));
|
|
int strLength = (int)m_OffScreenGraphics.MeasureString(selectionLengthStr, barFont).Width;
|
|
drawContext.Graphics.DrawString(selectionLengthStr, barFont, fontBrush, ClientSize.Width - strLength, ClientSize.Height - EventHeight);
|
|
}
|
|
}
|
|
|
|
// Draw context selection
|
|
if (m_SelectContextStartYOffset != m_SelectContextEndYOffset)
|
|
{
|
|
int start = Math.Min(m_SelectContextStartYOffset, m_SelectContextEndYOffset) + m_YOffset;
|
|
int end = Math.Max(m_SelectContextStartYOffset, m_SelectContextEndYOffset) + m_YOffset;
|
|
drawContext.Graphics.FillRectangle(rangeBrush, 0, start, ClientSize.Width, end - start);
|
|
}
|
|
|
|
if (ShowTimeLine)
|
|
DrawTimeLine(ref drawContext);
|
|
|
|
m_needRedraw = false;
|
|
}
|
|
e.Graphics.DrawImage(m_OffScreenBitmap, 0, 0);
|
|
}
|
|
|
|
private void DrawFrameDelimiters(ref DrawEventContext drawContext)
|
|
{
|
|
foreach (FrameDelimiter delimiter in m_Data.FrameDelimiters)
|
|
{
|
|
if (delimiter.Time < m_View.TimeStart)
|
|
continue;
|
|
if (delimiter.Time > m_View.TimeEnd)
|
|
break;
|
|
|
|
// compute screen X position
|
|
Int64 screenX = ConvertTimeToX(delimiter.Time);
|
|
|
|
if (delimiter.Type == FrameDelimiter.DelimiterType.Engine)
|
|
drawContext.Graphics.DrawLine(engineFrameDelimiterPen, screenX, 0, screenX, drawContext.Graphics.VisibleClipBounds.Height);
|
|
else if (delimiter.Type == FrameDelimiter.DelimiterType.Graphic)
|
|
drawContext.Graphics.DrawLine(graphicFrameDelimiterPen, screenX, 0, screenX, drawContext.Graphics.VisibleClipBounds.Height);
|
|
}
|
|
}
|
|
|
|
private void ProfileEventsViewer_MouseEnter(object sender, System.EventArgs e)
|
|
{
|
|
if (!Capture)
|
|
{
|
|
m_MouseMode = MouseMode.None;
|
|
|
|
m_LastX = 0;
|
|
m_LastY = 0;
|
|
}
|
|
}
|
|
|
|
private void ProfileEventsViewer_Resize(object sender, System.EventArgs e)
|
|
{
|
|
if (m_Data == null || ClientSize.Width == 0 || ClientSize.Height == 0)
|
|
return;
|
|
|
|
// Cache the time step
|
|
m_TimeStepPerPixel = (m_View.TimeEnd - m_View.TimeStart) / ClientSize.Width;
|
|
|
|
UpdateOffscreenSurface();
|
|
Update();
|
|
Redraw();
|
|
}
|
|
|
|
public void ZoomExtentAll()
|
|
{
|
|
m_View.ZoomToAbsolute(m_Data.TimeExtent.Min, m_Data.TimeExtent.Max);
|
|
|
|
Redraw();
|
|
}
|
|
|
|
public void ZoomExtentSelected()
|
|
{
|
|
m_View.ZoomToAbsolute(m_RangeStart, m_RangeEnd);
|
|
|
|
Redraw();
|
|
}
|
|
|
|
public void CopySelectedEventNameToClipboard()
|
|
{
|
|
if (m_SelectedProfileEvent != null)
|
|
{
|
|
Clipboard.SetText(m_Data.GetProfileName(m_SelectedProfileEvent.ProfileID));
|
|
}
|
|
else if (m_SelectedDataEvent != null)
|
|
{
|
|
Clipboard.SetText(m_SelectedDataEvent.m_String);
|
|
}
|
|
}
|
|
|
|
public void SelectAll()
|
|
{
|
|
m_RangeStart = m_View.TimeStart;
|
|
m_RangeEnd = m_View.TimeEnd;
|
|
|
|
FireSelectionChanged(true, true);
|
|
|
|
Redraw();
|
|
}
|
|
|
|
public void DeselectAll()
|
|
{
|
|
m_RangeStart = m_RangeEnd = 0;
|
|
|
|
FireSelectionChanged(true, true);
|
|
|
|
Redraw();
|
|
}
|
|
|
|
public void TrimSelection()
|
|
{
|
|
m_RangeStart = m_SelectionTimes.Values.Cast<SelectionInfo>().Select(info => info.m_MinTimeExtent).Min();
|
|
m_RangeEnd = m_SelectionTimes.Values.Cast<SelectionInfo>().Select(info => info.m_MaxTimeExtent).Max();
|
|
|
|
FireSelectionChanged(true, false);
|
|
|
|
// No need to recompute selection times, as they shouldn't have changed! :D
|
|
Redraw();
|
|
}
|
|
|
|
public void AdjustRangeToSelectedEvent(bool extendSelection)
|
|
{
|
|
if (m_SelectedProfileEvent == null)
|
|
return;
|
|
|
|
AdjustRangeToEvent(m_SelectedProfileEvent, extendSelection);
|
|
}
|
|
|
|
public void AdjustRangeToEvent(ProfileEventData.ProfileContext.Event targetEvent, bool extendSelection)
|
|
{
|
|
bool timeChanged = !extendSelection;
|
|
|
|
if (!extendSelection || m_RangeStart == m_RangeEnd)
|
|
{
|
|
m_RangeStart = targetEvent.Time;
|
|
m_RangeEnd = targetEvent.EndTime;
|
|
}
|
|
else
|
|
{
|
|
if (m_RangeStart > targetEvent.Time)
|
|
{
|
|
m_RangeStart = targetEvent.Time;
|
|
timeChanged = true;
|
|
}
|
|
if (m_RangeEnd < targetEvent.EndTime)
|
|
{
|
|
m_RangeEnd = targetEvent.EndTime;
|
|
timeChanged = true;
|
|
}
|
|
}
|
|
|
|
FireSelectionChanged(true, timeChanged);
|
|
Redraw();
|
|
}
|
|
|
|
public void SelectEngineFrames()
|
|
{
|
|
List<FrameDelimiter> engineDelimiters = m_Data.FrameDelimiters.Where(d => d.Type == FrameDelimiter.DelimiterType.Engine).ToList();
|
|
|
|
if (engineDelimiters.Count == 0)
|
|
{
|
|
MessageBox.Show("There are no engine frame delimiters.", "Select Engine Frames", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
return;
|
|
}
|
|
|
|
if (m_RangeStart == m_RangeEnd)
|
|
{
|
|
MessageBox.Show("There is no selection. Start by approximately selecting the frames you want.", "Select Engine Frames", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
if (m_RangeStart < engineDelimiters[0].Time)
|
|
m_RangeStart = m_Data.TimeExtent.Min;
|
|
|
|
if (m_RangeEnd < engineDelimiters[0].Time)
|
|
m_RangeEnd = engineDelimiters[0].Time;
|
|
|
|
int i = 0;
|
|
for (; i < engineDelimiters.Count - 1; ++i)
|
|
{
|
|
if (m_RangeStart > engineDelimiters[i].Time && m_RangeStart < engineDelimiters[i + 1].Time)
|
|
{
|
|
m_RangeStart = engineDelimiters[i].Time;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (; i < engineDelimiters.Count - 1; ++i)
|
|
{
|
|
if (m_RangeEnd > engineDelimiters[i].Time && m_RangeEnd < engineDelimiters[i + 1].Time)
|
|
{
|
|
m_RangeEnd = engineDelimiters[i + 1].Time;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_RangeStart > engineDelimiters.Last().Time)
|
|
m_RangeStart = engineDelimiters.Last().Time;
|
|
|
|
if (m_RangeEnd > engineDelimiters.Last().Time)
|
|
m_RangeEnd = m_Data.TimeExtent.Max;
|
|
|
|
FireSelectionChanged(true, true);
|
|
Redraw();
|
|
}
|
|
|
|
public void SelectGraphicFrames()
|
|
{
|
|
List<FrameDelimiter> graphicDelimiters = m_Data.FrameDelimiters.Where(d => d.Type == FrameDelimiter.DelimiterType.Graphic).ToList();
|
|
|
|
if (graphicDelimiters.Count == 0)
|
|
{
|
|
MessageBox.Show("There are no graphic frame delimiters.", "Select Graphic Frames", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
return;
|
|
}
|
|
|
|
if (m_RangeStart == m_RangeEnd)
|
|
{
|
|
MessageBox.Show("There is no selection. Start by approximately selecting the frames you want.", "Select Graphic Frames", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
if (m_RangeStart < graphicDelimiters[0].Time)
|
|
m_RangeStart = m_Data.TimeExtent.Min;
|
|
|
|
if (m_RangeEnd < graphicDelimiters[0].Time)
|
|
m_RangeEnd = graphicDelimiters[0].Time;
|
|
|
|
int i = 0;
|
|
for (; i < graphicDelimiters.Count - 1; ++i)
|
|
{
|
|
if (m_RangeStart > graphicDelimiters[i].Time && m_RangeStart < graphicDelimiters[i + 1].Time)
|
|
{
|
|
m_RangeStart = graphicDelimiters[i].Time;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (; i < graphicDelimiters.Count - 1; ++i)
|
|
{
|
|
if (m_RangeEnd > graphicDelimiters[i].Time && m_RangeEnd < graphicDelimiters[i + 1].Time)
|
|
{
|
|
m_RangeEnd = graphicDelimiters[i + 1].Time;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_RangeStart > graphicDelimiters.Last().Time)
|
|
m_RangeStart = graphicDelimiters.Last().Time;
|
|
|
|
if (m_RangeEnd > graphicDelimiters.Last().Time)
|
|
m_RangeEnd = m_Data.TimeExtent.Max;
|
|
|
|
FireSelectionChanged(true, true);
|
|
Redraw();
|
|
}
|
|
|
|
public void ZoomIn()
|
|
{
|
|
Time timeStepPerPixel = TimeStepPerPixel();
|
|
Time timeDx = (timeStepPerPixel);
|
|
const int DELTA = 240;
|
|
|
|
m_View.Zoom(timeDx * DELTA);
|
|
|
|
Redraw();
|
|
}
|
|
|
|
public void ZoomOut()
|
|
{
|
|
Time timeStepPerPixel = TimeStepPerPixel();
|
|
Time timeDx = (timeStepPerPixel);
|
|
const int DELTA = -240;
|
|
|
|
m_View.Zoom(timeDx * DELTA);
|
|
|
|
Redraw();
|
|
}
|
|
|
|
public void ZoomOnSelectedEvent()
|
|
{
|
|
ZoomOnEvent(m_SelectedProfileEvent);
|
|
|
|
Redraw();
|
|
}
|
|
|
|
public void SetEventsFilterString(string filter)
|
|
{
|
|
m_Filters.SetFilter(filter);
|
|
Redraw();
|
|
|
|
if (EventsFilterChanged != null)
|
|
EventsFilterChanged(this);
|
|
}
|
|
|
|
public string GetOriginalEventsFilterString()
|
|
{
|
|
return m_Filters.OriginalFilter;
|
|
}
|
|
|
|
public bool SetContextsVisibilityFilterString(string filter)
|
|
{
|
|
if (string.IsNullOrEmpty(filter))
|
|
filter = ".*";
|
|
|
|
try
|
|
{
|
|
Regex regex = new Regex(filter, RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
m_ContextsVisibilityFilter = regex;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool selectionTimesHaveChanged = false;
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
{
|
|
bool wasHidden = context.IsHidden;
|
|
context.IsHidden = !m_ContextsVisibilityFilter.IsMatch(context.Name);
|
|
if (!wasHidden ^ context.IsHidden) // If the visibility has changed
|
|
selectionTimesHaveChanged |= !m_FilteredContexts.Contains(context.Name);
|
|
}
|
|
|
|
if (selectionTimesHaveChanged)
|
|
ComputeSelectionTimes();
|
|
|
|
Redraw();
|
|
|
|
if (ContextsFilterChanged != null)
|
|
ContextsFilterChanged(this);
|
|
|
|
return true;
|
|
}
|
|
|
|
public void SetParentShadowActive()
|
|
{
|
|
m_ShowParentShadow = !m_ShowParentShadow;
|
|
Redraw();
|
|
}
|
|
|
|
public void SetShowTinyEvents()
|
|
{
|
|
m_ShowTinyEvents = !m_ShowTinyEvents;
|
|
Redraw();
|
|
}
|
|
|
|
public string GetOriginalContextsVisibilityFilterString()
|
|
{
|
|
return m_ContextsVisibilityFilter.ToString();
|
|
}
|
|
|
|
public void FilterContext(string contextName)
|
|
{
|
|
if (m_FilteredContexts.Add(contextName))
|
|
{
|
|
if (ContextSelectionChanged != null)
|
|
ContextSelectionChanged(this, m_FilteredContexts);
|
|
|
|
ComputeSelectionTimes();
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
public void FilterContexts(IEnumerable<string> contextNames)
|
|
{
|
|
bool elementsWereAdded = false;
|
|
foreach (string contextName in contextNames)
|
|
elementsWereAdded |= m_FilteredContexts.Add(contextName);
|
|
|
|
if (elementsWereAdded)
|
|
{
|
|
if (ContextSelectionChanged != null)
|
|
ContextSelectionChanged(this, m_FilteredContexts);
|
|
|
|
ComputeSelectionTimes();
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
public void UnfilterContext(string contextName)
|
|
{
|
|
if (m_FilteredContexts.Remove(contextName))
|
|
{
|
|
if (ContextSelectionChanged != null)
|
|
ContextSelectionChanged(this, m_FilteredContexts);
|
|
|
|
ComputeSelectionTimes();
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
public void UnfilterContexts(IEnumerable<string> contextNames)
|
|
{
|
|
bool elementsWereRemoved = false;
|
|
foreach (string contextName in contextNames)
|
|
elementsWereRemoved |= m_FilteredContexts.Remove(contextName);
|
|
|
|
if (elementsWereRemoved)
|
|
{
|
|
if (ContextSelectionChanged != null)
|
|
ContextSelectionChanged(this, m_FilteredContexts);
|
|
|
|
ComputeSelectionTimes();
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
public bool IsAnyContextFilteredOrHidden()
|
|
{
|
|
if (m_FilteredContexts.Count > 0)
|
|
return true;
|
|
|
|
foreach (ProfileEventData.ProfileContext context in m_Data.Contexts.Values)
|
|
if (context.IsHidden)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void m_View_ViewChanged(ProfileEventTrace.View view)
|
|
{
|
|
// Cache the new time step
|
|
m_TimeStepPerPixel = (view.TimeEnd - view.TimeStart) / ClientSize.Width;
|
|
}
|
|
|
|
private Time TimeStepPerPixel()
|
|
{
|
|
return m_TimeStepPerPixel;
|
|
}
|
|
|
|
private void ProfileEventTrace_Closed(object sender, EventArgs e)
|
|
{
|
|
// Assumes this trace will never be reopened.
|
|
m_Summary.Close();
|
|
m_Summary = null;
|
|
|
|
if (m_TaskGraph != null)
|
|
{
|
|
m_TaskGraph.Close();
|
|
m_TaskGraph = null;
|
|
}
|
|
|
|
m_Doc = null;
|
|
m_Data = null;
|
|
}
|
|
}
|
|
}
|