mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 03:28:20 +02:00
1173 lines
32 KiB
Beef
1173 lines
32 KiB
Beef
using Beefy.widgets;
|
|
using Beefy.theme.dark;
|
|
using Beefy.gfx;
|
|
using System.Collections.Generic;
|
|
using System;
|
|
using Beefy.events;
|
|
using Beefy;
|
|
using System.Threading;
|
|
using System.Diagnostics;
|
|
using Beefy.utils;
|
|
|
|
namespace BeefPerf
|
|
{
|
|
class FindPanel : Widget
|
|
{
|
|
public class FindListViewItem : DarkVirtualListViewItem
|
|
{
|
|
public override void Draw(Graphics g)
|
|
{
|
|
base.Draw(g);
|
|
}
|
|
|
|
public override void DrawAll(Graphics g)
|
|
{
|
|
base.DrawAll(g);
|
|
}
|
|
|
|
public override bool Selected
|
|
{
|
|
set
|
|
{
|
|
if (value)
|
|
{
|
|
var findPanel = ((FindListView)mListView).mFindPanel;
|
|
findPanel.ItemSelected(this);
|
|
}
|
|
base.Selected = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class FindListView : DarkVirtualListView
|
|
{
|
|
public FindPanel mFindPanel;
|
|
public bool mIconsDirty;
|
|
|
|
public this(FindPanel findPanel)
|
|
{
|
|
mFindPanel = findPanel;
|
|
}
|
|
|
|
protected override ListViewItem CreateListViewItem()
|
|
{
|
|
mIconsDirty = true;
|
|
return new FindListViewItem();
|
|
}
|
|
|
|
public override void PopulateVirtualItem(DarkVirtualListViewItem listViewItem)
|
|
{
|
|
base.PopulateVirtualItem(listViewItem);
|
|
listViewItem.mOnMouseDown.Add(new => mFindPanel.ValueClicked);
|
|
|
|
if (listViewItem.mVirtualIdx >= mFindPanel.mSearchState.mFoundEntries.Count)
|
|
{
|
|
listViewItem.mLabel = new String("--- Results Trimmed ---");
|
|
listViewItem.mTextColor = 0xFF808080;
|
|
var subItem = listViewItem.CreateSubItem(1);
|
|
subItem.mLabel = new String();
|
|
subItem = listViewItem.CreateSubItem(2);
|
|
subItem.mLabel = new String();
|
|
return;
|
|
}
|
|
|
|
var client = mFindPanel.PerfView.mSession;
|
|
var foundEntry = mFindPanel.mSearchState.mFoundEntries[listViewItem.mVirtualIdx];
|
|
|
|
listViewItem.mLabel = new String(foundEntry.mZoneStr);
|
|
|
|
var subItem = listViewItem.CreateSubItem(1);
|
|
subItem.mOnMouseDown.Add(new => mFindPanel.ValueClicked);
|
|
subItem.mLabel = new String();
|
|
BpClient.TimeToStr(client.TicksToUS(foundEntry.mStartTick - client.mFirstTick), subItem.mLabel);
|
|
|
|
subItem = listViewItem.CreateSubItem(2);
|
|
subItem.mOnMouseDown.Add(new => mFindPanel.ValueClicked);
|
|
subItem.mLabel = new String();
|
|
if (foundEntry.mEndTick != 0)
|
|
client.ElapsedTicksToStr(foundEntry.mEndTick - foundEntry.mStartTick, subItem.mLabel);
|
|
|
|
var track = mFindPanel.PerfView.mSession.mThreads[foundEntry.mTrackIdx];
|
|
subItem = listViewItem.CreateSubItem(3);
|
|
subItem.mOnMouseDown.Add(new => mFindPanel.ValueClicked);
|
|
subItem.mLabel = new String();
|
|
track.GetName(subItem.mLabel);
|
|
}
|
|
|
|
public void UpdateItem(FindListViewItem listViewItem)
|
|
{
|
|
if (listViewItem.mVirtualIdx >= mFindPanel.mSearchState.mFoundEntries.Count)
|
|
{
|
|
listViewItem.mLabel.Set("???");
|
|
return;
|
|
}
|
|
|
|
var client = mFindPanel.PerfView.mSession;
|
|
var foundEntry = mFindPanel.mSearchState.mFoundEntries[listViewItem.mVirtualIdx];
|
|
|
|
listViewItem.mLabel.Set(foundEntry.mZoneStr);
|
|
|
|
var subItem = listViewItem.GetSubItem(1);
|
|
subItem.mLabel.Clear();
|
|
BpClient.TimeToStr(client.TicksToUS(foundEntry.mStartTick - client.mFirstTick), subItem.mLabel);
|
|
|
|
subItem = listViewItem.GetSubItem(2);
|
|
subItem.mLabel.Clear();
|
|
if (foundEntry.mEndTick == 0)
|
|
subItem.mLabel.Clear();
|
|
else
|
|
client.ElapsedTicksToStr(foundEntry.mEndTick - foundEntry.mStartTick, subItem.mLabel);
|
|
}
|
|
|
|
public override void ChangeSort(SortType sortType)
|
|
{
|
|
mSortType = sortType;
|
|
if (mFindPanel.mSearchState == null)
|
|
return;
|
|
if (mFindPanel.mSearchState.mTrimmedResults)
|
|
mFindPanel.mNeedsRehup = true;
|
|
else
|
|
mFindPanel.mNeedsResort = true;
|
|
}
|
|
|
|
public override void KeyDown(KeyCode keyCode, bool isRepeat)
|
|
{
|
|
base.KeyDown(keyCode, isRepeat);
|
|
|
|
if (keyCode == (KeyCode)'D')
|
|
{
|
|
for (int32 i < (int32)mFindPanel.mSearchState.mFoundEntries.Count)
|
|
{
|
|
//Debug.WriteLine("{0} {1}", i, mFindPanel.mSearchState.mFoundEntries[i].mZoneStr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct TrackLoc : IHashable
|
|
{
|
|
public int64 mTick;
|
|
public int32 mDepth;
|
|
|
|
public this(int64 tick, int32 depth)
|
|
{
|
|
mTick = tick;
|
|
mDepth = depth;
|
|
}
|
|
|
|
public int GetHashCode()
|
|
{
|
|
return (int)mTick;
|
|
}
|
|
}
|
|
|
|
class FindEntry
|
|
{
|
|
public int64 mStartTick;
|
|
public int64 mEndTick;
|
|
public int32 mDepth;
|
|
public String mZoneStr ~ delete _;
|
|
public int32 mTrackIdx;
|
|
public bool mTimeDirty;
|
|
public bool mInDictionary;
|
|
}
|
|
|
|
class TrackState
|
|
{
|
|
public int64 mLastEntryEndTick;
|
|
public int64 mPrevLastEntryEndTick;
|
|
public int64 mPrevEndTick;
|
|
public Dictionary<TrackLoc, FindEntry> mFindPositions = new Dictionary<TrackLoc, FindEntry>() ~ delete _;
|
|
}
|
|
|
|
class SearchState
|
|
{
|
|
public int32 mCurThreadIdx;
|
|
public int32 mCurThreadStreamIdx = -1;
|
|
|
|
public TextSearcher mTextSearch = new TextSearcher() ~ delete _;
|
|
public TextSearcher mTrackSearch = new TextSearcher() ~ delete _;
|
|
public int64 mStartTick;
|
|
public int64 mEndTick;
|
|
public int32 mMinDepth;
|
|
public bool mIncludeZones;
|
|
public bool mIncludeEvents;
|
|
|
|
public List<FindEntry> mFoundEntries = new List<FindEntry>() ~ DeleteContainerAndItems!(_);
|
|
public List<FindEntry> mSortedEntries ~ delete _;
|
|
public List<FindEntry> mPendingDeleteEntries = new List<FindEntry>() ~ DeleteContainerAndItems!(_);
|
|
public FindEntry mHighestEntry;
|
|
|
|
public List<TrackState> mTrackStates = new List<TrackState>() ~ DeleteContainerAndItems!(_);
|
|
public bool mTrimmedResults;
|
|
}
|
|
|
|
FindListView mListView;
|
|
|
|
bool mSelectionDirty;
|
|
|
|
public DarkEditWidget mEntryEdit;
|
|
public DarkComboBox mTrackEdit;
|
|
public DarkComboBox mTimeFromEdit;
|
|
public DarkComboBox mTimeToEdit;
|
|
public DarkCheckBox mFormatCheckbox;
|
|
public DarkCheckBox mZonesCheckbox;
|
|
public DarkCheckBox mEventsCheckbox;
|
|
SearchState mSearchState ~ delete _;
|
|
|
|
public bool mNeedsRestartSearch;
|
|
public bool mNeedsRehup;
|
|
public bool mNeedsResort;
|
|
int32 mMaxFoundEntries = 100000;
|
|
bool mSorting;
|
|
WaitEvent mSortDoneHandle = new WaitEvent() ~ delete _;
|
|
bool mSearchIncomplete;
|
|
|
|
const int cColumnName = 0;
|
|
const int cColumnStart = 1;
|
|
const int cColumnLength = 2;
|
|
const int cColumnTrack = 2;
|
|
|
|
int mDrawWaitCount = 0; // We draw when this is > 0
|
|
bool mFailed;
|
|
|
|
public this()
|
|
{
|
|
mEntryEdit = new DarkEditWidget();
|
|
AddWidget(mEntryEdit);
|
|
mEntryEdit.mOnKeyDown.Add(new => KeyDownHandler);
|
|
mEntryEdit.mOnContentChanged.Add(new => FindContentChanged);
|
|
|
|
mTrackEdit = new DarkComboBox();
|
|
mTrackEdit.MakeEditable();
|
|
AddWidget(mTrackEdit);
|
|
mTrackEdit.mEditWidget.mOnContentChanged.Add(new => FindContentChanged);
|
|
mTrackEdit.mPopulateMenuAction.Add(new => PopulateTrackList);
|
|
|
|
mTimeFromEdit = new DarkComboBox();
|
|
mTimeFromEdit.MakeEditable();
|
|
AddWidget(mTimeFromEdit);
|
|
mTimeFromEdit.mEditWidget.mOnContentChanged.Add(new => FindContentChanged);
|
|
mTimeFromEdit.mPopulateMenuAction.Add(new => PopulateTimeFrom);
|
|
|
|
mTimeToEdit = new DarkComboBox();
|
|
mTimeToEdit.MakeEditable();
|
|
AddWidget(mTimeToEdit);
|
|
mTimeToEdit.mEditWidget.mOnContentChanged.Add(new => FindContentChanged);
|
|
mTimeToEdit.mPopulateMenuAction.Add(new => PopulateTimeTo);
|
|
|
|
mFormatCheckbox = new DarkCheckBox();
|
|
AddWidget(mFormatCheckbox);
|
|
mFormatCheckbox.Checked = true;
|
|
mFormatCheckbox.Label = "Format Strings";
|
|
mFormatCheckbox.mOnMouseUp.Add(new (evt) => { FindOptionsChanged(); });
|
|
|
|
mZonesCheckbox = new DarkCheckBox();
|
|
AddWidget(mZonesCheckbox);
|
|
mZonesCheckbox.Checked = true;
|
|
mZonesCheckbox.Label = "Zones";
|
|
mZonesCheckbox.mOnMouseUp.Add(new (evt) => { FindOptionsChanged(); });
|
|
|
|
mEventsCheckbox = new DarkCheckBox();
|
|
mEventsCheckbox.Checked = true;
|
|
mEventsCheckbox.Label = "Events";
|
|
AddWidget(mEventsCheckbox);
|
|
mEventsCheckbox.mOnMouseUp.Add(new (evt) => { FindOptionsChanged(); });
|
|
|
|
mListView = new FindListView(this);
|
|
mListView.mOnLostFocus.Add(new (evt) => mListView.GetRoot().SelectItemExclusively(null));
|
|
mListView.mSortType.mColumn = 2;
|
|
mListView.mSortType.mReverse = true;
|
|
AddWidget(mListView);
|
|
|
|
mListView.AddColumn(200, "Name");
|
|
mListView.AddColumn(150, "Start");
|
|
mListView.AddColumn(150, "Length");
|
|
mListView.AddColumn(150, "Track");
|
|
mListView.InitScrollbars(false, true);
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
FinishSorting();
|
|
}
|
|
|
|
PerfView PerfView
|
|
{
|
|
get
|
|
{
|
|
return gApp.mBoard.mPerfView;
|
|
}
|
|
}
|
|
|
|
void PopulateTrackList(Menu menu)
|
|
{
|
|
var perfView = PerfView;
|
|
List<String> threadNames = scope List<String>();
|
|
|
|
for (var thread in perfView.mSession.mThreads)
|
|
{
|
|
var threadName = new String();
|
|
thread.GetName(threadName);
|
|
threadNames.Add(threadName);
|
|
}
|
|
|
|
threadNames.Sort(scope (lhs, rhs) => String.Compare(lhs, rhs, true));
|
|
for (var threadName in threadNames)
|
|
{
|
|
#unwarn
|
|
var menuItem = menu.AddItem(threadName);
|
|
menuItem.mOnMenuItemSelected.Add(new (item) =>
|
|
{
|
|
var str = scope String();
|
|
str.Append('=');
|
|
str.Append(((Menu)item).mLabel);
|
|
if (str.Contains(' '))
|
|
{
|
|
str.Insert(1, '\"');
|
|
str.Append('"');
|
|
}
|
|
mTrackEdit.mEditWidget.SetText(str);
|
|
});
|
|
}
|
|
ClearAndDeleteItems(threadNames);
|
|
}
|
|
|
|
void SetVisibleLeft()
|
|
{
|
|
var perfView = PerfView;
|
|
var str = scope String();
|
|
perfView.mSession.TicksToStr(perfView.mTickOffset, str);
|
|
mTimeFromEdit.mEditWidget.SetText(str);
|
|
}
|
|
|
|
void SetVisibleRight()
|
|
{
|
|
var perfView = PerfView;
|
|
var str = scope String();
|
|
perfView.mSession.TicksToStr(perfView.mTickOffset + (int64)(perfView.mWidth / perfView.mTickScale), str);
|
|
mTimeToEdit.mEditWidget.SetText(str);
|
|
}
|
|
|
|
void SetEndRight()
|
|
{
|
|
//var perfView = PerfView;
|
|
//var str = scope String();
|
|
//perfView.mClient.ElapsedTicksToStr(perfView.mClient.mCurTick - perfView.mClient.mFirstTick, str);
|
|
//mTimeToEdit.mEditWidget.SetText(str);
|
|
mTimeToEdit.mEditWidget.SetText("");
|
|
}
|
|
|
|
void PopulateTimeFrom(Menu menu)
|
|
{
|
|
var menuItem = menu.AddItem("Start");
|
|
menuItem.mOnMenuItemSelected.Add(new (item) => { mTimeFromEdit.mEditWidget.SetText("0s"); });
|
|
menuItem = menu.AddItem("Entire Range");
|
|
menuItem.mOnMenuItemSelected.Add(new (item) =>
|
|
{
|
|
mTimeFromEdit.mEditWidget.SetText("0s");
|
|
SetEndRight();
|
|
});
|
|
menuItem = menu.AddItem("Visible Left");
|
|
menuItem.mOnMenuItemSelected.Add(new (item) =>
|
|
{
|
|
SetVisibleLeft();
|
|
});
|
|
menuItem = menu.AddItem("Visible Range");
|
|
menuItem.mOnMenuItemSelected.Add(new (item) =>
|
|
{
|
|
SetVisibleLeft();
|
|
SetVisibleRight();
|
|
});
|
|
}
|
|
|
|
void PopulateTimeTo(Menu menu)
|
|
{
|
|
var menuItem = menu.AddItem("End");
|
|
menuItem.mOnMenuItemSelected.Add(new (item) =>
|
|
{
|
|
SetEndRight();
|
|
});
|
|
menuItem = menu.AddItem("Visible Right");
|
|
menuItem.mOnMenuItemSelected.Add(new (item) =>
|
|
{
|
|
SetVisibleRight();
|
|
});
|
|
}
|
|
|
|
void KeyDownHandler(KeyDownEvent evt)
|
|
{
|
|
//if (evt.mKeycode == KeyCode.Escape)
|
|
//Close();
|
|
|
|
if (evt.mKeyCode == KeyCode.Return)
|
|
{
|
|
//StartSearch();
|
|
}
|
|
}
|
|
|
|
void FindContentChanged(EditEvent editEvent)
|
|
{
|
|
mNeedsRestartSearch = true;
|
|
}
|
|
|
|
void FindOptionsChanged()
|
|
{
|
|
mNeedsRestartSearch = true;
|
|
}
|
|
|
|
public void ItemSelected(FindListViewItem item)
|
|
{
|
|
int32 selectedIdx = item.mVirtualIdx;
|
|
if (selectedIdx >= mSearchState.mFoundEntries.Count)
|
|
return; // "-- Results Trimmed --" entry
|
|
var foundEntry = mSearchState.mFoundEntries[selectedIdx];
|
|
|
|
if (foundEntry.mEndTick == 0)
|
|
{
|
|
PerfView.mSelection = .Event(foundEntry.mTrackIdx, foundEntry.mStartTick);
|
|
}
|
|
else
|
|
{
|
|
BPSelection selection;
|
|
selection.mTickStart = foundEntry.mStartTick;
|
|
selection.mTickEnd = foundEntry.mEndTick;
|
|
selection.mDepth = foundEntry.mDepth;
|
|
selection.mThreadIdx = foundEntry.mTrackIdx;
|
|
|
|
PerfView.mSelection = .Entry(selection);
|
|
gApp.mProfilePanel.Show(PerfView, selection);
|
|
}
|
|
}
|
|
|
|
public void ValueClicked(MouseEvent theEvent)
|
|
{
|
|
DarkVirtualListViewItem clickedItem = (DarkVirtualListViewItem)theEvent.mSender;
|
|
DarkVirtualListViewItem item = (DarkVirtualListViewItem)clickedItem.GetSubItem(0);
|
|
|
|
mListView.GetRoot().SelectItemExclusively(item);
|
|
mListView.SetFocus();
|
|
|
|
if (theEvent.mBtn == 0)
|
|
{
|
|
int32 selectedIdx = item.mVirtualIdx;
|
|
var foundEntry = mSearchState.mFoundEntries[selectedIdx];
|
|
if (theEvent.mBtnCount > 1)
|
|
{
|
|
PerfView.EnsureVisible(foundEntry.mTrackIdx, foundEntry.mDepth);
|
|
PerfView.ZoomTo(foundEntry.mStartTick, foundEntry.mEndTick);
|
|
}
|
|
|
|
if (foundEntry.mEndTick != 0)
|
|
{
|
|
BPSelection selection;
|
|
selection.mTickStart = foundEntry.mStartTick;
|
|
selection.mTickEnd = foundEntry.mEndTick;
|
|
selection.mDepth = foundEntry.mDepth;
|
|
selection.mThreadIdx = foundEntry.mTrackIdx;
|
|
|
|
PerfView.mSelection = .Entry(selection);
|
|
gApp.mProfilePanel.Show(PerfView, selection);
|
|
}
|
|
|
|
//IDEApp.sApp.mDebugger.mSelectedCallStackIdx = selectedIdx;
|
|
//IDEApp.sApp.ShowPCLocation(selectedIdx, false, true);
|
|
//IDEApp.sApp.StackPositionChanged();
|
|
}
|
|
|
|
//UpdateIcons();
|
|
}
|
|
|
|
public override void Resize(float x, float y, float width, float height)
|
|
{
|
|
base.Resize(x, y, width, height);
|
|
|
|
var font = DarkTheme.sDarkTheme.mSmallFont;
|
|
String findLabel = "Find";
|
|
String trackLabel = "Thread";
|
|
float findLabelWidth = font.GetWidth(findLabel) + 27;
|
|
float trackLabelWidth = font.GetWidth(trackLabel) + 12;
|
|
float spacingWidth = 8;
|
|
float availWidth = Math.Max(mWidth - findLabelWidth - spacingWidth - trackLabelWidth, 0);
|
|
|
|
mEntryEdit.Resize(findLabelWidth, 0, availWidth * 0.65f, 20);
|
|
mTrackEdit.Resize(mEntryEdit.mX + mEntryEdit.mWidth + spacingWidth + trackLabelWidth, 0, availWidth * 0.35f, 20);
|
|
|
|
String timeFromLabel = "Time From";
|
|
String timeToLabel = "Time To";
|
|
float timeFromLabelWidth = font.GetWidth(timeFromLabel) + 12;
|
|
float timeToLabelWidth = font.GetWidth(timeToLabel) + 12;
|
|
|
|
float timeEditWidth = 140;
|
|
mTimeFromEdit.Resize(timeFromLabelWidth, 20, timeEditWidth, 20);
|
|
mTimeToEdit.Resize(mTimeFromEdit.mX + timeEditWidth + spacingWidth + timeToLabelWidth, 20, timeEditWidth, 20);
|
|
|
|
float formatWidth = font.GetWidth(mFormatCheckbox.mLabel) + 28;
|
|
float zonesWidth = font.GetWidth(mZonesCheckbox.mLabel) + 28;
|
|
mFormatCheckbox.Resize(mTimeToEdit.mX + timeEditWidth + 8, 20, 20, 20);
|
|
mZonesCheckbox.Resize(mFormatCheckbox.mX + formatWidth, 20, 20, 20);
|
|
mEventsCheckbox.Resize(mZonesCheckbox.mX + zonesWidth, 20, 20, 20);
|
|
|
|
mListView.ResizeClamped(0, 40, width, height - 40);
|
|
}
|
|
|
|
public override void Draw(Graphics g)
|
|
{
|
|
base.Draw(g);
|
|
|
|
//g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Bkg), 0, 0, mWidth, mHeight);
|
|
|
|
var font = DarkTheme.sDarkTheme.mSmallFont;
|
|
String findLabel = "Find";
|
|
String trackLabel = "Thread";
|
|
float findLabelWidth = font.GetWidth(findLabel);
|
|
float trackLabelWidth = font.GetWidth(trackLabel);
|
|
|
|
g.SetFont(DarkTheme.sDarkTheme.mSmallFont);
|
|
g.DrawString(findLabel, mEntryEdit.mX - findLabelWidth - 6, 0);
|
|
g.DrawString(trackLabel, mTrackEdit.mX - trackLabelWidth - 6, 0);
|
|
|
|
String timeFromLabel = "Time From";
|
|
String timeToLabel = "Time To";
|
|
float timeFromLabelWidth = font.GetWidth(timeFromLabel);
|
|
float timeToLabelWidth = font.GetWidth(timeToLabel);
|
|
g.DrawString(timeFromLabel, mTimeFromEdit.mX - timeFromLabelWidth- 6, 20);
|
|
g.DrawString(timeToLabel, mTimeToEdit.mX - timeToLabelWidth - 6, 20);
|
|
}
|
|
|
|
public void Show(PerfView perfView)
|
|
{
|
|
mSelectionDirty = true;
|
|
}
|
|
|
|
void GetData()
|
|
{
|
|
mListView.GetRoot().Clear();
|
|
|
|
}
|
|
|
|
bool SearchIn(int32 trackIdx, int32 streamDataIdx)
|
|
{
|
|
var client = PerfView.mSession;
|
|
BpTrack track = client.mThreads[trackIdx];
|
|
BpStreamData streamData = track.mStreamDataList[streamDataIdx];
|
|
var trackState = mSearchState.mTrackStates[trackIdx];
|
|
var curEntryEndTick = ref trackState.mLastEntryEndTick;
|
|
|
|
bool isRecording = false;
|
|
//bool isFirstDrawn = true;
|
|
|
|
if (mSearchState.mStartTick == 0)
|
|
isRecording = true;
|
|
|
|
if ((mSearchState.mStartTick != 0) && (streamData.mSplitTick > 0) && (streamData.mSplitTick < mSearchState.mStartTick))
|
|
return false; // All data is to the left
|
|
//if ((mSearchState.mEndTick != 0) && (streamData.mSplitTick < mSearchState.mStartTick))
|
|
//return false; // All data is to the right
|
|
|
|
if ((mSearchState.mEndTick != 0) && (streamData.mStartTick > mSearchState.mEndTick))
|
|
return true;
|
|
|
|
BPStateContext stateCtx = scope BPStateContext(client, streamData);
|
|
|
|
List<BPEntry> entryStack = scope List<BPEntry>();
|
|
int32 stackDepth = 0;
|
|
|
|
String tempDynStr = scope String();
|
|
String tempStr = scope String();
|
|
|
|
FindEntry tempFindEntry = new FindEntry();
|
|
defer
|
|
{
|
|
tempFindEntry.mZoneStr = null;
|
|
delete tempFindEntry;
|
|
}
|
|
|
|
CmdLoop: while (true)
|
|
{
|
|
switch (stateCtx.GetNextEvent())
|
|
{
|
|
case let .Enter(startTick, strIdx):
|
|
#unwarn
|
|
int stackPos = stackDepth++;
|
|
//if ((!isFirstDrawn) && (startTick < streamData.mStartTick))
|
|
//continue; // Would have already been handled by a previous streamData
|
|
|
|
if (!mSearchState.mIncludeZones)
|
|
continue;
|
|
|
|
if ((mSearchState.mStartTick != 0) && (startTick >= mSearchState.mStartTick))
|
|
{
|
|
isRecording = true;
|
|
}
|
|
|
|
if (isRecording)
|
|
{
|
|
BPEntry entry;
|
|
entry.mStartTick = startTick;
|
|
entry.mZoneNameId = strIdx;
|
|
//stateCtx.MoveToParamData();
|
|
entry.mParamsReadPos = stateCtx.ReadPos;
|
|
entryStack.Add(entry);
|
|
}
|
|
case let .Leave(endTick):
|
|
stackDepth--;
|
|
if (isRecording)
|
|
{
|
|
if (entryStack.Count == 0)
|
|
{
|
|
continue;
|
|
}
|
|
#unwarn
|
|
let entry = entryStack.PopBack();
|
|
#unwarn
|
|
if (endTick < curEntryEndTick)
|
|
continue;
|
|
|
|
if ((mSearchState.mEndTick != 0) && (entry.mStartTick > mSearchState.mEndTick))
|
|
continue;
|
|
|
|
bool isOld = ((entry.mStartTick <= streamData.mStartTick) && (stackDepth < stateCtx.mSplitCarryoverCount));
|
|
if (!stateCtx.mTimeInferred)
|
|
{
|
|
if (endTick <= curEntryEndTick)
|
|
continue;
|
|
|
|
if ((streamData.mSplitTick == 0) || (endTick <= streamData.mSplitTick)) // Don't include entries that span past the end of this stream
|
|
curEntryEndTick = endTick;
|
|
|
|
if (isOld)
|
|
continue;
|
|
}
|
|
|
|
#unwarn
|
|
int32 stackPos = stackDepth;
|
|
|
|
String str;
|
|
int32 paramsSize;
|
|
int32 paramReadPos = entry.mParamsReadPos;
|
|
if (entry.mZoneNameId < 0)
|
|
{
|
|
int32 nameLen = -entry.mZoneNameId;
|
|
str = tempDynStr;
|
|
str.Reference((char8*)stateCtx.mReadStart + paramReadPos - nameLen, nameLen, 0);
|
|
paramsSize = -1;
|
|
}
|
|
else
|
|
{
|
|
let zoneName = client.mZoneNames[entry.mZoneNameId];
|
|
str = zoneName.mName;
|
|
paramsSize = zoneName.mParamsSize;
|
|
}
|
|
|
|
if (paramsSize != 0)
|
|
{
|
|
tempStr.Clear();
|
|
stateCtx.FormatStr(paramReadPos, paramsSize, str, tempStr);
|
|
str = tempStr;
|
|
}
|
|
|
|
FindEntry findEntry = tempFindEntry;
|
|
findEntry.mStartTick = entry.mStartTick;
|
|
findEntry.mEndTick = endTick;
|
|
findEntry.mZoneStr = str;
|
|
findEntry.mTrackIdx = trackIdx;
|
|
findEntry.mDepth = stackDepth;
|
|
|
|
bool isOverflow = mSearchState.mFoundEntries.Count >= mMaxFoundEntries;
|
|
|
|
if (isOverflow)
|
|
{
|
|
// Can we be sure this entry wouldn't be included in the list after sorting and trimming?
|
|
if (EntryCompare(findEntry, mSearchState.mHighestEntry) >= 0)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (mSearchState.mTextSearch.Matches(str))
|
|
{
|
|
FindEntry* findEntryPtr = null;
|
|
|
|
if ((endTick >= client.mCurTick) || // If it extends off the back
|
|
(isOld) ||
|
|
(entry.mStartTick <= trackState.mPrevLastEntryEndTick)) // Was already started on the previous update
|
|
{
|
|
TrackLoc* trackLoc;
|
|
if (!trackState.mFindPositions.TryAdd(TrackLoc(entry.mStartTick, stackDepth), out trackLoc, out findEntryPtr))
|
|
{
|
|
findEntry = *findEntryPtr;
|
|
findEntry.mEndTick = endTick;
|
|
findEntry.mTimeDirty = true;
|
|
continue;
|
|
}
|
|
|
|
if (isOld)
|
|
continue;
|
|
}
|
|
|
|
findEntry.mZoneStr = new String(findEntry.mZoneStr);
|
|
mSearchState.mFoundEntries.Add(findEntry);
|
|
|
|
if (!isOverflow)
|
|
{
|
|
if ((mSearchState.mHighestEntry == null) || (EntryCompare(findEntry, mSearchState.mHighestEntry) >= 0))
|
|
{
|
|
// We have a new "highest entry"
|
|
mSearchState.mHighestEntry = findEntry;
|
|
}
|
|
}
|
|
|
|
// Create a pending entry...
|
|
tempFindEntry = new FindEntry();
|
|
|
|
if (findEntryPtr != null)
|
|
{
|
|
findEntry.mInDictionary = true;
|
|
*findEntryPtr = findEntry;
|
|
}
|
|
}
|
|
}
|
|
case let .Event(tick, name, details):
|
|
if (tick <= curEntryEndTick)
|
|
continue;
|
|
|
|
if (!mSearchState.mIncludeEvents)
|
|
continue;
|
|
|
|
if ((mSearchState.mStartTick != 0) && (tick < mSearchState.mStartTick))
|
|
continue;
|
|
if ((mSearchState.mEndTick != 0) && (tick > mSearchState.mEndTick))
|
|
continue;
|
|
|
|
curEntryEndTick = tick;
|
|
|
|
var nameStr = scope String();
|
|
nameStr.Reference(name);
|
|
var detailsStr = scope String();
|
|
detailsStr.Reference(details);
|
|
|
|
if ((mSearchState.mTextSearch.Matches(nameStr)) || (mSearchState.mTextSearch.Matches(detailsStr)))
|
|
{
|
|
let maxDetailChars = 128;
|
|
let strLen = nameStr.Length + " : ".Length + Math.Min(detailsStr.Length, maxDetailChars + 3);
|
|
int numDetailChars = Math.Min(detailsStr.Length, maxDetailChars);
|
|
var foundStr = new String(strLen);
|
|
foundStr.Append(nameStr, " : ");
|
|
|
|
for (int i < numDetailChars)
|
|
{
|
|
char8 c = details[i];
|
|
if ((int)c < 32)
|
|
foundStr.Append(' ');
|
|
else
|
|
foundStr.Append(c);
|
|
}
|
|
|
|
if (detailsStr.Length > maxDetailChars)
|
|
{
|
|
foundStr.Append("...");
|
|
}
|
|
|
|
var findEntry = new FindEntry();
|
|
findEntry.mZoneStr = foundStr;
|
|
findEntry.mStartTick = tick;
|
|
findEntry.mTrackIdx = trackIdx;
|
|
findEntry.mDepth = -1;
|
|
mSearchState.mFoundEntries.Add(findEntry);
|
|
}
|
|
|
|
case .EndOfStream:
|
|
break CmdLoop;
|
|
default:
|
|
}
|
|
}
|
|
|
|
//isFirstDrawn = false;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
void StartSearch()
|
|
{
|
|
//Debug.WriteLine("StartSearch");
|
|
mListView.GetRoot().Clear();
|
|
|
|
var error = scope String();
|
|
|
|
Clear();
|
|
|
|
var perfView = PerfView;
|
|
if (perfView == null)
|
|
return;
|
|
|
|
bool hadError = true;
|
|
do
|
|
{
|
|
var client = perfView.mSession;
|
|
|
|
mSearchState = new SearchState();
|
|
|
|
var str = scope String();
|
|
mEntryEdit.GetText(str);
|
|
if (mSearchState.mTextSearch.Init(str, error) case .Err)
|
|
break;
|
|
|
|
str.Clear();
|
|
mTrackEdit.mEditWidget.GetText(str);
|
|
if (mSearchState.mTrackSearch.Init(str, error) case .Err)
|
|
break;
|
|
|
|
str.Clear();
|
|
mTimeFromEdit.mEditWidget.GetText(str);
|
|
if (!str.IsWhiteSpace)
|
|
{
|
|
if (client.ParseTime(str) case .Ok(let ticks))
|
|
mSearchState.mStartTick = ticks;
|
|
else
|
|
{
|
|
error.Set("Invalid 'Time From' value");
|
|
break;
|
|
}
|
|
}
|
|
|
|
str.Clear();
|
|
mTimeToEdit.mEditWidget.GetText(str);
|
|
if (!str.IsWhiteSpace)
|
|
{
|
|
if (client.ParseTime(str) case .Ok(let ticks))
|
|
mSearchState.mEndTick = ticks;
|
|
else
|
|
{
|
|
error.Set("Invalid 'Time To' value");
|
|
break;
|
|
}
|
|
}
|
|
|
|
mSearchState.mIncludeZones = mZonesCheckbox.Checked;
|
|
mSearchState.mIncludeEvents = mEventsCheckbox.Checked;
|
|
|
|
hadError = false;
|
|
}
|
|
|
|
if (hadError)
|
|
{
|
|
if (error.IsEmpty)
|
|
error.Append("Error");
|
|
|
|
var listViewItem = (DarkListViewItem)mListView.GetRoot().CreateChildItem();
|
|
listViewItem.Label = error;
|
|
listViewItem.mTextColor = 0xFFFF8080;
|
|
|
|
mFailed = true;
|
|
}
|
|
|
|
SearchIteration();
|
|
}
|
|
|
|
int EntryCompare(FindEntry lhs, FindEntry rhs)
|
|
{
|
|
int64 result = 0;
|
|
switch (mListView.mSortType.mColumn)
|
|
{
|
|
case 0:
|
|
result = String.Compare(lhs.mZoneStr, rhs.mZoneStr, true);
|
|
if (result == 0)
|
|
result = lhs.mStartTick - rhs.mStartTick;
|
|
case 1:
|
|
result = lhs.mStartTick - rhs.mStartTick;
|
|
case 2:
|
|
int64 lhsLen = lhs.mEndTick - lhs.mStartTick;
|
|
int64 rhsLen = rhs.mEndTick - rhs.mStartTick;
|
|
result = lhsLen - rhsLen;
|
|
case 3:
|
|
var lhsTrack = PerfView.mSession.mThreads[lhs.mTrackIdx];
|
|
var rhsTrack = PerfView.mSession.mThreads[rhs.mTrackIdx];
|
|
result = BpTrack.Compare(lhsTrack, rhsTrack);
|
|
if (result == 0)
|
|
result = lhs.mStartTick - rhs.mStartTick;
|
|
}
|
|
if (mListView.mSortType.mReverse)
|
|
result = -result;
|
|
return (int)result;
|
|
}
|
|
|
|
void SortProc()
|
|
{
|
|
//Debug.WriteLine("SortProc start");
|
|
|
|
mSearchState.mSortedEntries = new List<FindEntry>();
|
|
mSearchState.mFoundEntries.CopyTo(mSearchState.mSortedEntries);
|
|
|
|
mSearchState.mSortedEntries.Sort(scope => EntryCompare);
|
|
|
|
mSortDoneHandle.Set();
|
|
|
|
//Debug.WriteLine("SortProc end");
|
|
}
|
|
|
|
bool SearchIteration()
|
|
{
|
|
if (mFailed)
|
|
return false;
|
|
mSearchIncomplete = false;
|
|
if (mSorting)
|
|
mSearchIncomplete = true;
|
|
|
|
if (mSearchState.mTextSearch.IsEmpty)
|
|
return true;
|
|
|
|
var stopwatch = scope Stopwatch();
|
|
stopwatch.Start();
|
|
|
|
bool refreshItems = false;
|
|
if (mSearchState.mSortedEntries != null)
|
|
{
|
|
Debug.Assert(mSearchState.mSortedEntries.Count == mSearchState.mFoundEntries.Count);
|
|
delete mSearchState.mFoundEntries;
|
|
mSearchState.mFoundEntries = mSearchState.mSortedEntries;
|
|
mSearchState.mSortedEntries = null;
|
|
mSearchState.mHighestEntry = null;
|
|
|
|
while (mSearchState.mFoundEntries.Count > mMaxFoundEntries)
|
|
{
|
|
var entry = mSearchState.mFoundEntries.PopBack();
|
|
if (entry.mInDictionary)
|
|
mSearchState.mPendingDeleteEntries.Add(entry);
|
|
else
|
|
delete entry;
|
|
mSearchState.mTrimmedResults = true;
|
|
}
|
|
if (mSearchState.mFoundEntries.Count > 0)
|
|
mSearchState.mHighestEntry = mSearchState.mFoundEntries.Back;
|
|
refreshItems = true;
|
|
}
|
|
|
|
int32 prevFindCount = (int32)mSearchState.mFoundEntries.Count;
|
|
|
|
var client = PerfView.mSession;
|
|
if (mSearchState.mCurThreadIdx >= client.mThreads.Count)
|
|
{
|
|
mSearchState.mCurThreadIdx = 0;
|
|
mSearchState.mCurThreadStreamIdx = -1;
|
|
}
|
|
bool didEarlyBreak = false;
|
|
TrackLoop: while (mSearchState.mCurThreadIdx < client.mThreads.Count)
|
|
{
|
|
int32 threadIdx = mSearchState.mCurThreadIdx;
|
|
while (threadIdx >= mSearchState.mTrackStates.Count)
|
|
mSearchState.mTrackStates.Add(new TrackState());
|
|
|
|
var track = client.mThreads[threadIdx];
|
|
var trackState = mSearchState.mTrackStates[threadIdx];
|
|
|
|
//int32 streamDataIdx = track.mStreamDataList.Count - 1;
|
|
if (mSearchState.mCurThreadStreamIdx == -1)
|
|
{
|
|
trackState.mPrevLastEntryEndTick = trackState.mLastEntryEndTick;
|
|
mSearchState.mCurThreadStreamIdx = (int32)track.mStreamDataList.Count - 1;
|
|
while (mSearchState.mCurThreadStreamIdx > 0)
|
|
{
|
|
mSearchState.mCurThreadStreamIdx--;
|
|
var streamData = track.mStreamDataList[mSearchState.mCurThreadStreamIdx];
|
|
if ((streamData.mEndTick < trackState.mLastEntryEndTick) ||
|
|
(streamData.mEndTick == trackState.mPrevEndTick))
|
|
{
|
|
mSearchState.mCurThreadStreamIdx++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Test
|
|
//mSearchState.mCurThreadStreamIdx = 0;
|
|
}
|
|
|
|
bool includeTrack = true;
|
|
if (!mSearchState.mTrackSearch.IsEmpty)
|
|
{
|
|
var trackName = scope String(128);
|
|
track.GetName(trackName);
|
|
includeTrack = mSearchState.mTrackSearch.Matches(trackName);
|
|
}
|
|
|
|
if (includeTrack)
|
|
{
|
|
while (mSearchState.mCurThreadStreamIdx < track.mStreamDataList.Count)
|
|
{
|
|
SearchIn(threadIdx, mSearchState.mCurThreadStreamIdx);
|
|
BpStreamData streamData = track.mStreamDataList[mSearchState.mCurThreadStreamIdx];
|
|
trackState.mPrevEndTick = streamData.mEndTick;
|
|
mSearchState.mCurThreadStreamIdx++;
|
|
|
|
if ((mSearchState.mFoundEntries.Count >= mMaxFoundEntries * 2) ||
|
|
(stopwatch.ElapsedMilliseconds > (int)(gApp.mTimePerFrame * 1000)))
|
|
{
|
|
didEarlyBreak = true;
|
|
mSearchIncomplete = true;
|
|
break TrackLoop;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!didEarlyBreak)
|
|
{
|
|
mSearchState.mCurThreadIdx++;
|
|
mSearchState.mCurThreadStreamIdx = -1;
|
|
}
|
|
}
|
|
|
|
int width;
|
|
int height;
|
|
gApp.GetDesktopResolution(out width, out height);
|
|
|
|
//TODO: We really want to set this to the maximum number of possibly visible lines
|
|
//int32 scrollLinePos = (int32)((height - mListView.mScrollContent.mY) / mListView.mFont.GetLineSpacing());
|
|
int numNewEntries = mSearchState.mFoundEntries.Count - prevFindCount;
|
|
|
|
if ((numNewEntries != 0) || (mNeedsResort))
|
|
{
|
|
//mSortThread = new Thread(new => SortProc);
|
|
//mSortThread.Start(false);
|
|
mSorting = true;
|
|
ThreadPool.QueueUserWorkItem(new => SortProc);
|
|
|
|
mNeedsResort = false;
|
|
mSearchIncomplete = true;
|
|
}
|
|
|
|
// Add entries
|
|
if (numNewEntries != 0)
|
|
{
|
|
if (mListView.GetRoot().GetChildCount() == 0)
|
|
{
|
|
var listViewItem = (DarkVirtualListViewItem)mListView.GetRoot().CreateChildItem();
|
|
listViewItem.mVirtualHeadItem = listViewItem;
|
|
mListView.PopulateVirtualItem(listViewItem);
|
|
}
|
|
|
|
var listViewItem = (DarkVirtualListViewItem)mListView.GetRoot().GetChildAtIndex(0);
|
|
listViewItem.mVirtualCount = Math.Min((int32)mSearchState.mFoundEntries.Count, mMaxFoundEntries);
|
|
if (mSearchState.mTrimmedResults)
|
|
listViewItem.mVirtualCount++; // Show "trimmed results" message at bottom
|
|
}
|
|
|
|
for (int itemIdx < mListView.GetRoot().GetChildCount())
|
|
{
|
|
var listViewItem = (FindListViewItem)mListView.GetRoot().GetChildAtIndex((int32)itemIdx);
|
|
if (listViewItem.mVirtualIdx >= mSearchState.mFoundEntries.Count)
|
|
continue;
|
|
var foundEntry = mSearchState.mFoundEntries[listViewItem.mVirtualIdx];
|
|
if ((foundEntry.mTimeDirty) || (refreshItems))
|
|
{
|
|
mListView.UpdateItem(listViewItem);
|
|
if (foundEntry.mTimeDirty)
|
|
{
|
|
foundEntry.mTimeDirty = false;
|
|
if (mListView.mSortType.mColumn == cColumnLength)
|
|
mNeedsResort = true;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void RefreshData()
|
|
{
|
|
mListView.GetRoot().Clear();
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
if (mSelectionDirty)
|
|
{
|
|
RefreshData();
|
|
mSelectionDirty = false;
|
|
}
|
|
|
|
if (mSorting)
|
|
{
|
|
if (mSortDoneHandle.WaitFor(0))
|
|
{
|
|
mSorting = false;
|
|
mSortDoneHandle.Reset();
|
|
}
|
|
}
|
|
|
|
if ((mNeedsRestartSearch) && (!mSorting))
|
|
{
|
|
StartSearch();
|
|
mNeedsRestartSearch = false;
|
|
}
|
|
|
|
if ((mNeedsRehup) && (!mSorting))
|
|
{
|
|
if (mSearchState != null)
|
|
{
|
|
mSearchState.mHighestEntry = null;
|
|
ClearAndDeleteItems(mSearchState.mFoundEntries);
|
|
DeleteAndNullify!(mSearchState.mSortedEntries);
|
|
for (var trackState in mSearchState.mTrackStates)
|
|
{
|
|
trackState.mFindPositions.Clear();
|
|
trackState.mLastEntryEndTick = 0;
|
|
trackState.mPrevLastEntryEndTick = 0;
|
|
}
|
|
mListView.GetRoot().Clear();
|
|
}
|
|
mNeedsRehup = false;
|
|
}
|
|
|
|
if ((mSearchState != null) && (!mSorting))
|
|
{
|
|
if (gApp.mIsUpdateBatchStart)
|
|
SearchIteration();
|
|
}
|
|
|
|
if (mSearchIncomplete)
|
|
mDrawWaitCount = Math.Min(mDrawWaitCount + 1, 8);
|
|
else
|
|
mDrawWaitCount = Math.Max(mDrawWaitCount - 1, 0);
|
|
}
|
|
|
|
void ClearMem()
|
|
{
|
|
|
|
}
|
|
|
|
void FinishSorting()
|
|
{
|
|
if (mSorting)
|
|
{
|
|
mSortDoneHandle.WaitFor();
|
|
mSorting = false;
|
|
mSortDoneHandle.Reset();
|
|
}
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
mFailed = false;
|
|
//Debug.WriteLine("FinePanel.Clear");
|
|
FinishSorting();
|
|
|
|
//Debug.WriteLine("FinePanel.Clear - deleting search state");
|
|
DeleteAndNullify!(mSearchState);
|
|
mListView.GetRoot().Clear();
|
|
}
|
|
|
|
public override void DrawAll(Graphics g)
|
|
{
|
|
base.DrawAll(g);
|
|
|
|
if (mDrawWaitCount > 0)
|
|
{
|
|
BPUtils.DrawWait(g, 1, mEntryEdit.mY + 0);
|
|
}
|
|
}
|
|
}
|
|
}
|