525 lines
No EOL
20 KiB
C#
525 lines
No EOL
20 KiB
C#
using System;
|
|
using System.Drawing;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.Data;
|
|
using System.IO;
|
|
|
|
// Keep in sync with ProfileEvent.cs
|
|
using Time = System.Double;
|
|
using TD.SandDock;
|
|
|
|
namespace ProfileEventViewer
|
|
{
|
|
public partial class Summary : UserDockableWindow
|
|
{
|
|
private ProfileEventTrace m_Parent = null;
|
|
private int m_ItemClickCount = 1;
|
|
private UInt32 m_LastItemID = UInt32.MaxValue;
|
|
private Timer m_SelectionTimer = new Timer();
|
|
private bool m_SelectionChanged = true;
|
|
|
|
private List<ListViewInfo> m_ListViewInfo = new List<ListViewInfo>();
|
|
private List<ListViewInfo> m_FilteredListViewInfo = new List<ListViewInfo>();
|
|
private SortInfo m_SortInfo = new SortInfo() { Ascending = true, Column = 0 };
|
|
|
|
private ListViewCache m_ListViewCache = new ListViewCache();
|
|
|
|
private Filters Filters { get; set; }
|
|
|
|
public List<ListViewInfo> ListViewInfos
|
|
{
|
|
get { return m_ListViewInfo; }
|
|
}
|
|
|
|
public Summary(ProfileEventTrace parent)
|
|
{
|
|
InitializeComponent();
|
|
m_Parent = parent;
|
|
Text = m_Parent.Text.Replace(" Trace", " Summary");
|
|
Filters = new Filters();
|
|
|
|
m_SelectionTimer.Interval = 50;
|
|
m_SelectionTimer.Tick += new EventHandler(m_SelectionTimer_Tick);
|
|
|
|
toolStripStatusTotalTime.Text = string.Empty;
|
|
toolStripStatusSelectedTime.Text = string.Empty;
|
|
|
|
foreach (ProfileEventData.ProfileContext context in parent.Data.Contexts.Values)
|
|
{
|
|
if (!context.ContainsProfileEvents())
|
|
continue;
|
|
|
|
ToolStripMenuItem item = new ToolStripMenuItem(context.Name);
|
|
item.Checked = true;
|
|
item.CheckOnClick = true;
|
|
item.Visible = !context.IsHidden;
|
|
item.Tag = context;
|
|
item.Click += menuItemContext_Click;
|
|
contextMenuStripList.Items.Add(item);
|
|
}
|
|
|
|
parent.ContextSelectionChanged += ContextSelectionChanged;
|
|
parent.SelectionTimesChanged += SelectionTimesChanged;
|
|
parent.ContextsFilterChanged += ContextsFilterChanged;
|
|
}
|
|
|
|
void ContextSelectionChanged(ProfileEventTrace sender, HashSet<string> filteredContexts)
|
|
{
|
|
// Update the menu's checkboxes.
|
|
int baseIndex = contextMenuStripList.Items.IndexOf(toolStripSeparatorContextNames) + 1;
|
|
|
|
for (int i = baseIndex; i < contextMenuStripList.Items.Count; ++i)
|
|
{
|
|
ToolStripMenuItem item = (ToolStripMenuItem)contextMenuStripList.Items[i];
|
|
item.Checked = !filteredContexts.Contains(item.Text);
|
|
}
|
|
}
|
|
|
|
void SelectionTimesChanged(ProfileEventTrace sender)
|
|
{
|
|
m_SelectionChanged = true;
|
|
|
|
if (DockSituation != DockSituation.None)
|
|
{
|
|
UpdateWithTrace(m_SelectionChanged);
|
|
m_SelectionChanged = false;
|
|
}
|
|
}
|
|
|
|
void ContextsFilterChanged(ProfileEventTrace sender)
|
|
{
|
|
// Update the menu's visibility.
|
|
int baseIndex = contextMenuStripList.Items.IndexOf(toolStripSeparatorContextNames) + 1;
|
|
|
|
for (int i = baseIndex; i < contextMenuStripList.Items.Count; ++i)
|
|
{
|
|
ToolStripMenuItem item = (ToolStripMenuItem)contextMenuStripList.Items[i];
|
|
ProfileEventData.ProfileContext context = item.Tag as ProfileEventData.ProfileContext;
|
|
if (context != null)
|
|
item.Visible = !context.IsHidden;
|
|
}
|
|
}
|
|
|
|
void m_SummaryList_ColumnClick(object sender, ColumnClickEventArgs e)
|
|
{
|
|
if (e.Column == m_SortInfo.Column)
|
|
{
|
|
m_SortInfo.Ascending = !m_SortInfo.Ascending;
|
|
}
|
|
else if (e.Column == 0) // ProfileName
|
|
{
|
|
m_SortInfo.Ascending = true;
|
|
}
|
|
else // All other columns are numbers; I prefer to see the biggest ones first.
|
|
{
|
|
m_SortInfo.Ascending = false;
|
|
}
|
|
|
|
m_SortInfo.Column = e.Column;
|
|
|
|
SortUsingCurrentSettings();
|
|
|
|
m_SummaryList.Invalidate();
|
|
}
|
|
|
|
private void SortUsingCurrentSettings()
|
|
{
|
|
m_ListViewCache.Clear();
|
|
m_SummaryList.VirtualListSize = 0;
|
|
m_SummaryList.SelectedIndices.Clear(); // Clear selection, as it is not well supported to sort it...
|
|
|
|
int signChanger = m_SortInfo.Ascending ? 1 : -1;
|
|
|
|
if (m_SortInfo.Column == 0) // ProfileName
|
|
m_FilteredListViewInfo.Sort((x, y) => signChanger * CaseInsensitiveComparer.Default.Compare(x.ProfileName, y.ProfileName));
|
|
else if (m_SortInfo.Column == 1) // TotalTime
|
|
m_FilteredListViewInfo.Sort((x, y) => signChanger * Math.Sign(x.TotalTime - y.TotalTime));
|
|
else if (m_SortInfo.Column == 2) // ClockTime
|
|
m_FilteredListViewInfo.Sort((x, y) => signChanger * Math.Sign(x.ClockTime - y.ClockTime));
|
|
else if (m_SortInfo.Column == 3) // CallCount
|
|
m_FilteredListViewInfo.Sort((x, y) => signChanger * x.CallCount.CompareTo(y.CallCount));
|
|
else if (m_SortInfo.Column == 4) // MeanTime
|
|
m_FilteredListViewInfo.Sort((x, y) => signChanger * Math.Sign(x.MeanTime - y.MeanTime));
|
|
else if (m_SortInfo.Column == 5) // MinTime
|
|
m_FilteredListViewInfo.Sort((x, y) => signChanger * Math.Sign(x.MinTime - y.MinTime));
|
|
else if (m_SortInfo.Column == 6) // MaxTime
|
|
m_FilteredListViewInfo.Sort((x, y) => signChanger * Math.Sign(x.MaxTime - y.MaxTime));
|
|
|
|
m_SummaryList.VirtualListSize = m_FilteredListViewInfo.Count;
|
|
|
|
UpdateSelectionSummary();
|
|
}
|
|
|
|
private void textBoxFilter_TextChanged(object sender, EventArgs e)
|
|
{
|
|
Filters.SetFilter(textBoxFilter.Text.Trim());
|
|
UpdateListViewItem();
|
|
}
|
|
|
|
private void UpdateListViewItem()
|
|
{
|
|
m_FilteredListViewInfo.Clear();
|
|
m_ListViewCache.Clear();
|
|
|
|
if (!Filters.IsValid)
|
|
{
|
|
m_FilteredListViewInfo.AddRange(m_ListViewInfo);
|
|
}
|
|
else
|
|
{
|
|
m_FilteredListViewInfo.AddRange(m_ListViewInfo.Where(info => Filters.IsAllowed(info.ProfileName)));
|
|
}
|
|
|
|
SortUsingCurrentSettings();
|
|
|
|
if (m_FilteredListViewInfo.Count == 0)
|
|
toolStripStatusTotalTime.Text = string.Empty;
|
|
else
|
|
{
|
|
Time totalTime = m_FilteredListViewInfo.Sum(info => info.TotalTime);
|
|
Time minExtent = m_FilteredListViewInfo.Min(info => info.MinTimeExtent);
|
|
Time maxExtent = m_FilteredListViewInfo.Max(info => info.MaxTimeExtent);
|
|
|
|
ClockTime mergedClockTime = new ClockTime();
|
|
m_FilteredListViewInfo.ForEach(info => mergedClockTime.Merge(info.GetClockTimeObject()));
|
|
mergedClockTime.CoalesceResults();
|
|
|
|
toolStripStatusTotalTime.Text = string.Format("{0} / {1} / {2} / {3}", m_Parent.TimeToString(totalTime), m_Parent.TimeToString(mergedClockTime.GetTime()), m_Parent.TimeToString(maxExtent - minExtent), m_FilteredListViewInfo.Count);
|
|
}
|
|
|
|
m_SummaryList.Invalidate();
|
|
}
|
|
|
|
public void UpdateWithTrace(bool selectionChanged)
|
|
{
|
|
if (!selectionChanged)
|
|
return;
|
|
|
|
// Supports detail mode only.
|
|
m_SummaryList.View = View.Details;
|
|
|
|
m_ListViewCache.Clear();
|
|
m_ListViewInfo.Clear();
|
|
|
|
foreach (UInt32 id in m_Parent.SelectionTimes.Keys)
|
|
{
|
|
ProfileEventTrace.SelectionInfo info = (ProfileEventTrace.SelectionInfo)m_Parent.SelectionTimes[id];
|
|
Time time = info.m_TotalTime;
|
|
|
|
if (time > (Time)0)
|
|
{
|
|
ListViewInfo listViewInfo = new ListViewInfo()
|
|
{
|
|
ProfileName = m_Parent.Data.GetProfileName(id),
|
|
ProfileId = id,
|
|
CallCount = info.m_Calls,
|
|
TotalTime = info.m_TotalTime,
|
|
MinTime = info.m_MinTime,
|
|
MaxTime = info.m_MaxTime,
|
|
ClockTime = info.m_ClockTime.GetTime(),
|
|
MinTimeExtent = info.m_MinTimeExtent,
|
|
MaxTimeExtent = info.m_MaxTimeExtent
|
|
};
|
|
|
|
listViewInfo.SetClockTimeObject(info.m_ClockTime);
|
|
|
|
m_ListViewInfo.Add(listViewInfo);
|
|
}
|
|
}
|
|
|
|
UpdateListViewItem();
|
|
}
|
|
|
|
private void Summary_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
if (e.CloseReason == CloseReason.UserClosing)
|
|
{
|
|
e.Cancel = true;
|
|
Hide();
|
|
}
|
|
}
|
|
|
|
private void m_SummaryList_DoubleClick(object sender, EventArgs e)
|
|
{
|
|
if (menuItemDblClkCenterView.Checked)
|
|
CenterViewOnSelection();
|
|
else
|
|
ZoomViewOnSelection();
|
|
}
|
|
|
|
private bool HandleEventCycle()
|
|
{
|
|
if (m_SummaryList.SelectedIndices.Count == 0)
|
|
return false;
|
|
|
|
UInt32 itemId = m_FilteredListViewInfo[m_SummaryList.SelectedIndices[0]].ProfileId;
|
|
if (itemId == m_LastItemID)
|
|
++m_ItemClickCount;
|
|
else
|
|
m_ItemClickCount = 1;
|
|
|
|
m_LastItemID = itemId;
|
|
|
|
return true;
|
|
}
|
|
|
|
private void ZoomViewOnSelection()
|
|
{
|
|
HandleEventCycle();
|
|
|
|
m_Parent.Open(); // Ensure visible.
|
|
m_Parent.ZoomViewOnItem(m_LastItemID, m_ItemClickCount);
|
|
}
|
|
|
|
private void CenterViewOnSelection()
|
|
{
|
|
HandleEventCycle();
|
|
|
|
m_Parent.Open(); // Ensure visible.
|
|
m_Parent.CenterViewOnItem(m_LastItemID, m_ItemClickCount);
|
|
}
|
|
|
|
private void menuItemCenterOnItem_Click(object sender, EventArgs e)
|
|
{
|
|
CenterViewOnSelection();
|
|
}
|
|
|
|
private void menuItemZoomOnItem_Click(object sender, EventArgs e)
|
|
{
|
|
ZoomViewOnSelection();
|
|
}
|
|
|
|
private void buttonExportCSV_Click(object sender, EventArgs e)
|
|
{
|
|
SaveFileDialog fileDlg = new SaveFileDialog();
|
|
fileDlg.OverwritePrompt = true;
|
|
fileDlg.Filter = "Comma Separated file (*.csv) | *.csv";
|
|
|
|
if (fileDlg.ShowDialog() == DialogResult.OK)
|
|
{
|
|
SaveCSV(fileDlg.FileName);
|
|
}
|
|
}
|
|
|
|
private void SaveCSV(string fileName)
|
|
{
|
|
StreamWriter sw = System.IO.File.CreateText(fileName);
|
|
|
|
foreach (System.Reflection.PropertyInfo property in typeof(ListViewInfo).GetProperties())
|
|
sw.Write(string.Format("{0};", property.Name));
|
|
sw.WriteLine();
|
|
|
|
foreach (ListViewInfo info in m_FilteredListViewInfo)
|
|
{
|
|
foreach (System.Reflection.PropertyInfo property in typeof(ListViewInfo).GetProperties())
|
|
sw.Write(string.Format("{0};", property.GetValue(info, null).ToString()));
|
|
sw.WriteLine();
|
|
}
|
|
|
|
sw.Close();
|
|
MessageBox.Show(string.Format("File {0} created.", fileName));
|
|
}
|
|
|
|
private void SelectAll()
|
|
{
|
|
// Weird way of selecting, but a helluva lot faster than doing it by hand!
|
|
// ... As a note, SendKeys may trigger a first chance exception of type NullReferenceException
|
|
// So when using this feature while debugging, you should not trap NullReferenceExceptions, otherwise the feature won't work
|
|
m_SummaryList.Focus();
|
|
m_SummaryList.BeginUpdate();
|
|
ListViewItem topItem = m_SummaryList.TopItem;
|
|
SendKeys.Send("{END}"); // Send END key
|
|
SendKeys.Send(" "); // Send "space" key (selects the last element)
|
|
SendKeys.SendWait("+{HOME}"); // Send Shift + HOME keys (to select all)
|
|
m_SummaryList.TopItem = topItem; // Try to keep the same scroll position (doesn't always exactly work, but it's better than having it scrolled up all the way to the top)
|
|
m_SummaryList.EndUpdate();
|
|
}
|
|
|
|
private void Summary_DockSituationChanged(object sender, EventArgs e)
|
|
{
|
|
statusStrip.SizingGrip = (DockSituation == DockSituation.Floating);
|
|
|
|
if (m_Parent != null)
|
|
{
|
|
if (DockSituation != DockSituation.None)
|
|
{
|
|
UpdateWithTrace(m_SelectionChanged);
|
|
m_SelectionChanged = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void m_SummaryList_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
|
|
{
|
|
m_SelectionTimer.Start();
|
|
}
|
|
|
|
private void m_SummaryList_VirtualItemsSelectionRangeChanged(object sender, ListViewVirtualItemsSelectionRangeChangedEventArgs e)
|
|
{
|
|
m_SelectionTimer.Start();
|
|
}
|
|
|
|
private void m_SelectionTimer_Tick(object sender, EventArgs e)
|
|
{
|
|
UpdateSelectionSummary();
|
|
}
|
|
|
|
private void UpdateSelectionSummary()
|
|
{
|
|
m_SelectionTimer.Stop();
|
|
|
|
if (m_Parent == null)
|
|
return;
|
|
|
|
if (m_SummaryList.SelectedIndices.Count == 0)
|
|
{
|
|
toolStripStatusSelectedTime.Text = string.Empty;
|
|
return;
|
|
}
|
|
|
|
// Calculate the total time for selected events
|
|
Time totalTime = 0;
|
|
Time minExtent = Time.MaxValue;
|
|
Time maxExtent = Time.MinValue;
|
|
ClockTime mergedClockTime = new ClockTime();
|
|
foreach (int index in m_SummaryList.SelectedIndices)
|
|
{
|
|
ListViewInfo info = m_FilteredListViewInfo[index];
|
|
totalTime += info.TotalTime;
|
|
minExtent = Math.Min(minExtent, info.MinTimeExtent);
|
|
maxExtent = Math.Max(maxExtent, info.MaxTimeExtent);
|
|
mergedClockTime.Merge(info.GetClockTimeObject());
|
|
}
|
|
mergedClockTime.CoalesceResults();
|
|
|
|
toolStripStatusSelectedTime.Text = string.Format("{0} / {1} / {2} / {3}", m_Parent.TimeToString(totalTime), m_Parent.TimeToString(mergedClockTime.GetTime()), m_Parent.TimeToString(maxExtent - minExtent), m_SummaryList.SelectedIndices.Count);
|
|
}
|
|
|
|
private void menuItemSelectAll_Click(object sender, EventArgs e)
|
|
{
|
|
SelectAll();
|
|
}
|
|
|
|
private void m_SummaryList_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
|
|
{
|
|
if (m_ListViewCache.IsIndexInCache(e.ItemIndex))
|
|
{
|
|
e.Item = m_ListViewCache.GetListViewItem(e.ItemIndex);
|
|
return;
|
|
}
|
|
|
|
ListViewInfo info = m_FilteredListViewInfo[e.ItemIndex];
|
|
|
|
ListViewItem item = new ListViewItem(info.ProfileName);
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, m_Parent.TimeToString(info.TotalTime)));
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, m_Parent.TimeToString(info.ClockTime)));
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, info.CallCount.ToString()));
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, m_Parent.TimeToString(info.MeanTime)));
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, m_Parent.TimeToString(info.MinTime)));
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, m_Parent.TimeToString(info.MaxTime)));
|
|
|
|
e.Item = item;
|
|
}
|
|
|
|
private void m_SummaryList_CacheVirtualItems(object sender, CacheVirtualItemsEventArgs e)
|
|
{
|
|
if (m_ListViewCache.IsRangeInCache(e.StartIndex, e.EndIndex))
|
|
return; // Requested cache is a subset of the current one
|
|
|
|
List<ListViewItem> listViewCache = new List<ListViewItem>(e.EndIndex - e.StartIndex + 1);
|
|
for (int i = e.StartIndex; i <= e.EndIndex; ++i)
|
|
{
|
|
ListViewInfo info = m_FilteredListViewInfo[i];
|
|
|
|
ListViewItem item = new ListViewItem(info.ProfileName);
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, m_Parent.TimeToString(info.TotalTime)));
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, m_Parent.TimeToString(info.ClockTime)));
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, info.CallCount.ToString()));
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, m_Parent.TimeToString(info.MeanTime)));
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, m_Parent.TimeToString(info.MinTime)));
|
|
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, m_Parent.TimeToString(info.MaxTime)));
|
|
|
|
listViewCache.Add(item);
|
|
}
|
|
|
|
m_ListViewCache.Populate(listViewCache, e.StartIndex);
|
|
}
|
|
|
|
private void menuItemAll_Click(object sender, EventArgs e)
|
|
{
|
|
List<string> contextNames = new List<string>();
|
|
|
|
int baseIndex = contextMenuStripList.Items.IndexOf(toolStripSeparatorContextNames) + 1;
|
|
|
|
for (int i = baseIndex; i < contextMenuStripList.Items.Count; ++i)
|
|
{
|
|
ToolStripMenuItem item = (ToolStripMenuItem)contextMenuStripList.Items[i];
|
|
contextNames.Add(item.Text);
|
|
item.Checked = true;
|
|
}
|
|
|
|
m_Parent.UnfilterContexts(contextNames);
|
|
}
|
|
|
|
|
|
private void menuItemNone_Click(object sender, EventArgs e)
|
|
{
|
|
List<string> contextNames = new List<string>();
|
|
|
|
int baseIndex = contextMenuStripList.Items.IndexOf(toolStripSeparatorContextNames) + 1;
|
|
|
|
for (int i = baseIndex; i < contextMenuStripList.Items.Count; ++i)
|
|
{
|
|
ToolStripMenuItem item = (ToolStripMenuItem)contextMenuStripList.Items[i];
|
|
contextNames.Add(item.Text);
|
|
item.Checked = false;
|
|
}
|
|
|
|
m_Parent.FilterContexts(contextNames);
|
|
}
|
|
|
|
private void menuItemContext_Click(object sender, EventArgs e)
|
|
{
|
|
ToolStripMenuItem item = (ToolStripMenuItem)sender;
|
|
if (item.Checked)
|
|
m_Parent.UnfilterContext(item.Text);
|
|
else
|
|
m_Parent.FilterContext(item.Text);
|
|
}
|
|
|
|
private void contextMenuStripList_Opening(object sender, CancelEventArgs e)
|
|
{
|
|
if (m_SummaryList.SelectedIndices.Count == 0)
|
|
{
|
|
menuItemCenterOnItem.Visible = false;
|
|
menuItemZoomOnItem.Visible = false;
|
|
}
|
|
else
|
|
{
|
|
string profileName = m_FilteredListViewInfo[m_SummaryList.SelectedIndices[0]].ProfileName;
|
|
menuItemCenterOnItem.Visible = true;
|
|
menuItemCenterOnItem.Text = "&Center on next " + profileName;
|
|
|
|
menuItemZoomOnItem.Visible = true;
|
|
menuItemZoomOnItem.Text = "&Zoom on next " + profileName;
|
|
}
|
|
}
|
|
|
|
private void menuItemDblClkCenterView_Click(object sender, EventArgs e)
|
|
{
|
|
menuItemDblClkCenterView.Checked = true;
|
|
menuItemDblClkZoomView.Checked = false;
|
|
}
|
|
|
|
private void menuItemDblClkZoomView_Click(object sender, EventArgs e)
|
|
{
|
|
menuItemDblClkCenterView.Checked = false;
|
|
menuItemDblClkZoomView.Checked = true;
|
|
}
|
|
}
|
|
} |