405 lines
No EOL
14 KiB
C#
405 lines
No EOL
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
|
|
using Time = System.Double;
|
|
|
|
namespace ProfileEventViewer
|
|
{
|
|
public class Filters
|
|
{
|
|
public string OriginalFilter { get; set; }
|
|
|
|
private List<string> m_Filters = new List<string>();
|
|
|
|
public bool IsValid { get { return m_Filters.Count != 0; } }
|
|
|
|
public Filters()
|
|
{
|
|
SetFilter(string.Empty);
|
|
}
|
|
|
|
public void SetFilter(string filter)
|
|
{
|
|
m_Filters.Clear();
|
|
m_Filters.AddRange(ParseFilter(filter).Select(f => f.ToUpperInvariant()));
|
|
OriginalFilter = filter;
|
|
}
|
|
|
|
private IEnumerable<string> ParseFilter(string filter)
|
|
{
|
|
int lastIndex = 0;
|
|
bool inQuotes = false;
|
|
for (int index = 0; index < filter.Length; ++index)
|
|
{
|
|
char c = filter[index];
|
|
if (c == '"')
|
|
{
|
|
if (lastIndex < index - 1)
|
|
{
|
|
string substr = filter.Substring(lastIndex, index - lastIndex);
|
|
if (inQuotes)
|
|
yield return substr;
|
|
else
|
|
foreach (string subFilter in substr.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries))
|
|
yield return subFilter;
|
|
}
|
|
|
|
lastIndex = index + 1;
|
|
|
|
inQuotes = !inQuotes;
|
|
}
|
|
}
|
|
|
|
if (lastIndex != filter.Length)
|
|
{
|
|
string substr = filter.Substring(lastIndex, filter.Length - lastIndex);
|
|
if (inQuotes)
|
|
yield return substr;
|
|
else
|
|
foreach (string subFilter in substr.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries))
|
|
yield return subFilter;
|
|
}
|
|
}
|
|
|
|
public bool IsAllowed(string str)
|
|
{
|
|
string upperStr = str.ToUpperInvariant();
|
|
return m_Filters.Any(filter => upperStr.Contains(filter));
|
|
}
|
|
}
|
|
|
|
public class ClockTime
|
|
{
|
|
private List<TimeExtent> m_TimeExtents = new List<TimeExtent>();
|
|
|
|
private List<Time> m_Mins = new List<Time>();
|
|
private List<Time> m_Maxs = new List<Time>();
|
|
|
|
private struct TimeExtentExtremum
|
|
{
|
|
public Time Time;
|
|
public bool IsMinExtent;
|
|
}
|
|
|
|
private class MinExtentComparer : IComparer<TimeExtent>
|
|
{
|
|
public int Compare(TimeExtent x, TimeExtent y) { return Math.Sign(x.Min - y.Min); }
|
|
}
|
|
|
|
private class MaxExtentComparer : IComparer<TimeExtent>
|
|
{
|
|
public int Compare(TimeExtent x, TimeExtent y) { return Math.Sign(x.Max - y.Max); }
|
|
}
|
|
|
|
private class MinMaxExtentComparer : IComparer<TimeExtent>
|
|
{
|
|
public int Compare(TimeExtent x, TimeExtent y) { return Math.Sign(x.Max - y.Min); }
|
|
}
|
|
|
|
private class MaxMinExtentComparer : IComparer<TimeExtent>
|
|
{
|
|
public int Compare(TimeExtent x, TimeExtent y) { return Math.Sign(x.Min - y.Max); }
|
|
}
|
|
|
|
public void Merge(ClockTime clockTime)
|
|
{
|
|
foreach (TimeExtent timeExtent in clockTime.m_TimeExtents)
|
|
Merge(timeExtent.Min, timeExtent.Max);
|
|
}
|
|
|
|
public void Merge(Time minExtent, Time maxExtent)
|
|
{
|
|
if (maxExtent <= minExtent)
|
|
System.Diagnostics.Debugger.Break();
|
|
|
|
m_Mins.Add(minExtent);
|
|
m_Maxs.Add(maxExtent);
|
|
}
|
|
|
|
// Returns indices as List<T>.BinarySearch would return them.
|
|
private void GetMinAndMaxInsertionIndicesFromExtent(TimeExtent extent, out int outMinIndex, out int outMaxIndex)
|
|
{
|
|
int minMaxIndex = m_TimeExtents.BinarySearch(extent, new MinMaxExtentComparer());
|
|
int positiveMinMaxIndex = (minMaxIndex >= 0 ? minMaxIndex : ~minMaxIndex);
|
|
int minIndex = m_TimeExtents.BinarySearch(positiveMinMaxIndex, m_TimeExtents.Count - positiveMinMaxIndex, extent, new MinExtentComparer());
|
|
|
|
int maxIndex = m_TimeExtents.BinarySearch(positiveMinMaxIndex, m_TimeExtents.Count - positiveMinMaxIndex, extent, new MaxExtentComparer());
|
|
int positiveMaxIndex = (maxIndex >= 0 ? maxIndex : ~maxIndex);
|
|
|
|
int maxMinIndex = m_TimeExtents.BinarySearch(positiveMaxIndex, m_TimeExtents.Count - positiveMaxIndex, extent, new MaxMinExtentComparer());
|
|
|
|
if (minIndex >= 0)
|
|
outMinIndex = minIndex;
|
|
else if (minMaxIndex >= 0)
|
|
outMinIndex = minMaxIndex;
|
|
else if (minIndex == minMaxIndex)
|
|
outMinIndex = minMaxIndex;
|
|
else
|
|
outMinIndex = positiveMinMaxIndex;
|
|
|
|
if (maxIndex >= 0)
|
|
outMaxIndex = maxIndex;
|
|
else if (maxMinIndex >= 0)
|
|
outMaxIndex = maxMinIndex;
|
|
else if (maxIndex == maxMinIndex)
|
|
outMaxIndex = maxIndex;
|
|
else
|
|
outMaxIndex = positiveMaxIndex;
|
|
|
|
if ((outMinIndex >= 0 ? outMinIndex : ~outMinIndex) > (outMaxIndex >= 0 ? outMaxIndex : ~outMaxIndex))
|
|
System.Diagnostics.Debugger.Break();
|
|
}
|
|
|
|
public void CoalesceResults()
|
|
{
|
|
// It's faster to use unsorted lists to gather times and then sort them rather than sorting at insertion time (either using lists or sorted containers)
|
|
// Also, using a single List<TimeExtentExtremum> is also slower, because sorting it takes more time (and I'm not sure why! (but it must have something to do with the complex sorting condition))
|
|
m_Mins.Sort();
|
|
m_Maxs.Sort();
|
|
|
|
List<TimeExtentExtremum> timeExtentPoints = new List<TimeExtentExtremum>(m_Mins.Count + m_Maxs.Count);
|
|
|
|
// Merge them!
|
|
var minIt = m_Mins.GetEnumerator();
|
|
var maxIt = m_Maxs.GetEnumerator();
|
|
|
|
bool minIsValid = minIt.MoveNext();
|
|
bool maxIsValid = maxIt.MoveNext();
|
|
|
|
while (minIsValid && maxIsValid)
|
|
{
|
|
if (minIt.Current <= maxIt.Current)
|
|
{
|
|
timeExtentPoints.Add(new TimeExtentExtremum() { Time = minIt.Current, IsMinExtent = true });
|
|
minIsValid = minIt.MoveNext();
|
|
}
|
|
else
|
|
{
|
|
timeExtentPoints.Add(new TimeExtentExtremum() { Time = maxIt.Current, IsMinExtent = false });
|
|
maxIsValid = maxIt.MoveNext();
|
|
}
|
|
}
|
|
|
|
// Ignore remaining mins, add remaining maxs
|
|
if (maxIsValid)
|
|
{
|
|
do
|
|
{
|
|
timeExtentPoints.Add(new TimeExtentExtremum() { Time = maxIt.Current, IsMinExtent = false });
|
|
} while (maxIt.MoveNext());
|
|
}
|
|
|
|
Time minValue = Time.MaxValue;
|
|
int count = 0;
|
|
|
|
int index = timeExtentPoints.FindIndex(p => p.IsMinExtent);
|
|
if (index >= 0)
|
|
{
|
|
for (; index < timeExtentPoints.Count; ++index)
|
|
{
|
|
TimeExtentExtremum p = timeExtentPoints[index];
|
|
|
|
if (p.IsMinExtent)
|
|
{
|
|
if (++count == 1)
|
|
minValue = p.Time;
|
|
}
|
|
else
|
|
{
|
|
if (--count == 0 && minValue != p.Time)
|
|
m_TimeExtents.Add(new TimeExtent() { Min = minValue, Max = p.Time });
|
|
}
|
|
}
|
|
}
|
|
|
|
m_Mins.Clear();
|
|
m_Maxs.Clear();
|
|
|
|
#if DEBUG
|
|
// For debugging purposes... but it is pretty slow
|
|
for (int i = 1; i < m_TimeExtents.Count; ++i)
|
|
{
|
|
if (m_TimeExtents[i].Min <= m_TimeExtents[i - 1].Min ||
|
|
m_TimeExtents[i].Max <= m_TimeExtents[i - 1].Max ||
|
|
m_TimeExtents[i].Min <= m_TimeExtents[i - 1].Max ||
|
|
m_TimeExtents[i].Max <= m_TimeExtents[i - 1].Min)
|
|
System.Diagnostics.Debugger.Break();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
public Time GetTime()
|
|
{
|
|
return m_TimeExtents.Aggregate(0, (Time t, TimeExtent extent) => t + extent.Length);
|
|
}
|
|
|
|
public Time GetTimeWithinRange(Time start, Time end)
|
|
{
|
|
return GetClockTimeWithinRange(start, end).GetTime();
|
|
}
|
|
|
|
public ClockTime GetClockTimeWithinRange(Time start, Time end)
|
|
{
|
|
ClockTime newClockTime = new ClockTime();
|
|
TimeExtent extent = new TimeExtent { Min = start, Max = end };
|
|
int outMinIndex, outMaxIndex;
|
|
GetMinAndMaxInsertionIndicesFromExtent(extent, out outMinIndex, out outMaxIndex);
|
|
|
|
int minIndex = (outMinIndex >= 0 ? outMinIndex : ~outMinIndex);
|
|
int maxIndex = (outMaxIndex >= 0 ? outMaxIndex : ~outMaxIndex - 1);
|
|
|
|
if (minIndex > maxIndex)
|
|
return newClockTime;
|
|
|
|
List<TimeExtent> timeExtents = m_TimeExtents.GetRange(minIndex, maxIndex - minIndex + 1);
|
|
if (timeExtents[0].Min < start)
|
|
timeExtents[0] = new TimeExtent() { Min = start, Max = timeExtents[0].Max };
|
|
if (timeExtents[timeExtents.Count - 1].Max > end)
|
|
timeExtents[timeExtents.Count - 1] = new TimeExtent() { Min = timeExtents[timeExtents.Count - 1].Min, Max = end };
|
|
|
|
newClockTime.m_TimeExtents = timeExtents;
|
|
|
|
return newClockTime;
|
|
}
|
|
}
|
|
|
|
public class ListViewInfo
|
|
{
|
|
public string ProfileName { get; set; }
|
|
public UInt32 ProfileId; // Not a property: not serialized.
|
|
public Time TotalTime { get; set; }
|
|
public UInt32 CallCount { get; set; }
|
|
public Time MeanTime { get { return TotalTime / CallCount; } }
|
|
public Time ClockTime { get; set; } // Cached value of the ClockTimeObject
|
|
public Time MinTimeExtent { get; set; }
|
|
public Time MaxTimeExtent { get; set; }
|
|
public Time MinTime { get; set; }
|
|
public Time MaxTime { get; set; }
|
|
|
|
// This is not a property so it is not serialized.
|
|
private ClockTime m_ClockTimeObject = null;
|
|
public ClockTime GetClockTimeObject() { return m_ClockTimeObject; }
|
|
public void SetClockTimeObject(ClockTime clockTimeObject) { m_ClockTimeObject = clockTimeObject; }
|
|
}
|
|
|
|
public class SortInfo
|
|
{
|
|
public bool Ascending { get; set; }
|
|
public int Column { get; set; }
|
|
}
|
|
|
|
public class ListViewCache
|
|
{
|
|
private List<ListViewItem> m_ListViewItems = new List<ListViewItem>();
|
|
private int m_StartIndex = -1;
|
|
|
|
public bool IsIndexInCache(int index)
|
|
{
|
|
int cacheIndex = index - m_StartIndex;
|
|
return (cacheIndex >= 0 && cacheIndex < m_ListViewItems.Count);
|
|
}
|
|
|
|
public bool IsRangeInCache(int startIndex, int endIndex)
|
|
{
|
|
return (startIndex >= m_StartIndex && endIndex <= m_StartIndex + m_ListViewItems.Count);
|
|
}
|
|
|
|
public ListViewItem GetListViewItem(int index)
|
|
{
|
|
return m_ListViewItems[index - m_StartIndex];
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
m_StartIndex = -1;
|
|
m_ListViewItems.Clear();
|
|
}
|
|
|
|
public void Populate(IEnumerable<ListViewItem> itemsToCache, int startIndex)
|
|
{
|
|
Clear();
|
|
|
|
m_StartIndex = startIndex;
|
|
m_ListViewItems.AddRange(itemsToCache);
|
|
}
|
|
}
|
|
|
|
// Inspired by http://www.codeproject.com/Articles/42068/Merge-Sort
|
|
public class MergeSort
|
|
{
|
|
public static void Sort<T>(IList<T> list) where T : IComparable
|
|
{
|
|
Sort(list, (T a, T b) => a.CompareTo(b));
|
|
}
|
|
|
|
public static void Sort<T>(IList<T> list, Comparison<T> comparison)
|
|
{
|
|
int count = list.Count;
|
|
RecursiveMergeSort(list, count, comparison);
|
|
}
|
|
|
|
private static void RecursiveMergeSort<T>(IList<T> toSort, int n, Comparison<T> comparison)
|
|
{
|
|
// Recursion stop condition
|
|
if (n == 2)
|
|
{
|
|
int compareValue = comparison(toSort[0], toSort[1]);
|
|
if (compareValue > 0)
|
|
{
|
|
T tempT = toSort[0];
|
|
toSort[0] = toSort[1];
|
|
toSort[1] = tempT;
|
|
}
|
|
}
|
|
else if (n > 2)
|
|
{
|
|
int m = n / 2;
|
|
|
|
IList<T> leftList = new List<T>((n / 2) + 2);
|
|
IList<T> rightList = new List<T>((n / 2) + 2);
|
|
|
|
// Initialize the 2 Sub-Arrays (The first relevant values)
|
|
for (int i = 0; i < m; i = i + 1)
|
|
leftList.Add(toSort[i]);
|
|
|
|
for (int j = m; j < n; j++)
|
|
rightList.Add(toSort[j]);
|
|
|
|
// 2 Recursive Calling, Sorting Sub-Arrays
|
|
RecursiveMergeSort(leftList, m, comparison);
|
|
RecursiveMergeSort(rightList, n - m, comparison);
|
|
|
|
// Merging the Sorted Sub-Arrays into the main Array
|
|
int p = 0;
|
|
int q = 0;
|
|
int k = 0;
|
|
|
|
for (; k < n && p < m && q < (n - m); ++k)
|
|
{
|
|
if (comparison(leftList[p], rightList[q]) <= 0)
|
|
toSort[k] = leftList[p++];
|
|
else
|
|
toSort[k] = rightList[q++];
|
|
}
|
|
|
|
if (p < m)
|
|
{
|
|
for (; k < n; ++k, ++p)
|
|
toSort[k] = leftList[p];
|
|
}
|
|
else
|
|
{
|
|
for (; k < n; ++k, ++q)
|
|
toSort[k] = rightList[q];
|
|
}
|
|
|
|
System.Diagnostics.Debug.Assert(k == n);
|
|
System.Diagnostics.Debug.Assert(p == m);
|
|
System.Diagnostics.Debug.Assert(q == (n - m));
|
|
}
|
|
}
|
|
}
|
|
} |