diff --git a/gui/Data/EventBoard.cs b/gui/Data/EventBoard.cs index 0a61b300..6d172fd0 100644 --- a/gui/Data/EventBoard.cs +++ b/gui/Data/EventBoard.cs @@ -134,8 +134,12 @@ public override void CollectDescriptionFilter(HashSet filter) public override void Add(SamplingNode node) { base.Add(node); - Self += node.Sampled; - SelfPercent += node.SelfPercent; + + if (!node.ExcludeFromSelf) + { + Self += node.Sampled; + SelfPercent += node.SelfPercent; + } if (!node.ExcludeFromTotal) { @@ -157,14 +161,13 @@ public Board(TNode node) AddRange(items.Values); } - void Add(Dictionary items, TNode node) + void AddInternal(Dictionary items, TNode node) { - TDescription description = node.Description; - if (description != null) + if (node.Description != null) { TItem item; - Object key = description.GetSharedKey(); + Object key = node.Description.GetSharedKey(); if (!items.TryGetValue(key, out item)) { @@ -174,9 +177,16 @@ void Add(Dictionary items, TNode node) item.Add(node); } + } + + void Add(Dictionary items, TNode node) + { + if (node.ShadowNodes != null) + node.ShadowNodes.ForEach(shadow => AddInternal(items, shadow as TNode)); + else + AddInternal(items, node); - foreach (TNode child in node.Children) - Add(items, child); + node.Children?.ForEach(child => Add(items, child as TNode)); } } } diff --git a/gui/Data/EventTree.cs b/gui/Data/EventTree.cs index 127d7b5a..621bfc73 100644 --- a/gui/Data/EventTree.cs +++ b/gui/Data/EventTree.cs @@ -15,7 +15,7 @@ public abstract class BaseTreeNode : INotifyPropertyChanged private bool isSelected; private Visibility visible; - public BaseTreeNode Parent { get; private set; } + public BaseTreeNode Parent { get; set; } public BaseTreeNode RootParent { get; private set; } public List Children { get; private set; } @@ -27,6 +27,7 @@ public abstract class BaseTreeNode : INotifyPropertyChanged public abstract String ToolTipName { get; } public virtual List Tags { get { return null; } set { } } + public virtual List ShadowNodes { get { return null; } } public event PropertyChangedEventHandler PropertyChanged; private void Raise(string propertyName) @@ -45,7 +46,19 @@ public double Ratio } public double TotalPercent { get { return RootParent != null ? (100.0 * Duration / RootParent.Duration) : 100.0; } } - public double SelfPercent { get { return RootParent != null ? (100.0 * SelfDuration / RootParent.Duration) : (SelfDuration / Duration); } } + public double SelfPercent + { + get + { + if (RootParent != null) + return (100.0 * SelfDuration / RootParent.Duration); + + if (Duration > 0.0) + return SelfDuration / Duration; + + return 0.0; + } + } public abstract String Path { get; } @@ -177,7 +190,8 @@ public abstract class TreeNode : BaseTreeNode public override String Name { get { return Description.Name; } } public override String ToolTipName { get { return String.Format("{0}\n{1}", Description.FullName, Description.Path); } } - public bool ExcludeFromTotal { get; private set; } + public bool ExcludeFromTotal { get; set; } = false; + public bool ExcludeFromSelf { get; set; } = false; public TreeNode(TreeNode root, TDescription desc, double duration) : base(root, duration) diff --git a/gui/Data/FrameCollection.cs b/gui/Data/FrameCollection.cs index f414c159..9fe4154b 100644 --- a/gui/Data/FrameCollection.cs +++ b/gui/Data/FrameCollection.cs @@ -13,6 +13,7 @@ namespace Profiler.Data public class ThreadData { + public ThreadDescription Description { get; set; } public List Events { get; set; } public List Callstacks { get; set; } public List SysCalls { get; set; } @@ -21,8 +22,9 @@ public class ThreadData public TagsPack TagsPack { get; set; } public bool IsDirty { get; set; } - public ThreadData() + public ThreadData(ThreadDescription desc) { + Description = desc; Events = new List(); } @@ -97,6 +99,7 @@ public class FrameGroup public EventDescriptionBoard Board { get; set; } public ISamplingBoard SamplingBoard { get; set; } public List Threads { get; set; } + public List Cores { get; set; } public List Fibers { get; set; } public ThreadData MainThread { get { return Threads[Board.MainThreadIndex]; } } public SummaryPack Summary { get; set; } @@ -104,6 +107,8 @@ public class FrameGroup public List Responses { get; set; } + public bool IsCoreDataGenerated { get; set; } + public List GetThreads(ThreadDescription.Source origin) { List threads = new List(); @@ -125,7 +130,7 @@ public FrameGroup(EventDescriptionBoard board) Threads = new List(board.Threads.Count); foreach (ThreadDescription desc in board.Threads) - Threads.Add(new ThreadData()); + Threads.Add(new ThreadData(desc)); Fibers = new List(); Responses = new List(); @@ -143,7 +148,7 @@ public void AddFrame(EventFrame frame) { while (threadIndex >= Threads.Count) { - Threads.Add(new ThreadData()); + Threads.Add(new ThreadData(Board.Threads[threadIndex])); } Threads[threadIndex].Events.Add(frame); } @@ -154,53 +159,66 @@ public void AddFrame(EventFrame frame) { while (fiberIndex >= Fibers.Count) { - Fibers.Add(new ThreadData()); + Fibers.Add(new ThreadData(null)); } Fibers[fiberIndex].Events.Add(frame); } } + } + private void GenerateDummyCoreThreads() + { + if (Cores == null) + { + Cores = new List(Board.CPUCoreCount); + foreach (KeyValuePair pair in Synchronization.SyncMap) + { + foreach (SyncInterval interval in pair.Value) + { + while (Cores.Count <= interval.Core) + { + ThreadDescription desc = new ThreadDescription() { Name = String.Format("CPU Core {0:00}", Cores.Count), ThreadID = UInt64.MaxValue, Origin = ThreadDescription.Source.Core }; + Cores.Add(AddThread(desc)); + } + } + } + } } - void GenerateCoreThreads() + public void GenerateRealCoreThreads() { - List> cores = new List>(Board.CPUCoreCount); - - foreach (KeyValuePair pair in Synchronization.SyncMap) + if (Cores != null && !IsCoreDataGenerated) { - ThreadDescription threadDesc = null; - Board.ThreadDescriptions.TryGetValue(pair.Key, out threadDesc); - - if (threadDesc != null && threadDesc.IsIdle) - continue; + foreach (KeyValuePair pair in Synchronization.SyncMap) + { + ThreadDescription threadDesc = null; + Board.ThreadDescriptions.TryGetValue(pair.Key, out threadDesc); - EventDescription eventDesc = new EventDescription(threadDesc != null ? String.Format("{0}:0x{1:X}", threadDesc.FullName, threadDesc.ThreadID) : pair.Key.ToString("X")); - if (threadDesc != null && threadDesc.ProcessID != Board.ProcessID) - eventDesc.Color = Colors.SlateGray; + if (threadDesc != null && threadDesc.IsIdle) + continue; - foreach (SyncInterval interval in pair.Value) - { - byte core = interval.Core; + EventDescription eventDesc = new EventDescription(threadDesc != null ? String.Format("{0}:0x{1:X}", threadDesc.FullName, threadDesc.ThreadID) : pair.Key.ToString("X")); + if (threadDesc != null && threadDesc.ProcessID != Board.ProcessID) + eventDesc.Color = Colors.SlateGray; - while (cores.Count <= core) - { - ThreadDescription desc = new ThreadDescription() { Name = String.Format("CPU Core {0:00}", cores.Count), ThreadID = UInt64.MaxValue, Origin = ThreadDescription.Source.Core }; - cores.Add(new KeyValuePair(desc, AddThread(desc))); - } + foreach (SyncInterval interval in pair.Value) + { + byte core = interval.Core; - Entry entry = new Entry(eventDesc, interval.Start, interval.Finish); + Entry entry = new Entry(eventDesc, interval.Start, interval.Finish); - EventFrame frame = new EventFrame(new FrameHeader(interval, cores[interval.Core].Key.ThreadIndex), new List() { entry }, this); - entry.Frame = frame; + EventFrame frame = new EventFrame(new FrameHeader(interval, Cores[interval.Core].Description.ThreadIndex), new List() { entry }, this); + entry.Frame = frame; - cores[interval.Core].Value.Events.Add(frame); + Cores[interval.Core].Events.Add(frame); + } } - } - cores.ForEach(core => core.Value.Events.Sort()); + Cores.ForEach(core => core.Events.Sort()); + IsCoreDataGenerated = true; + } } - private void GenerateMiscThreads() { @@ -255,7 +273,7 @@ public void AddSynchronization(SynchronizationMap syncMap) } } - GenerateCoreThreads(); + GenerateDummyCoreThreads(); GenerateMiscThreads(); } @@ -268,7 +286,7 @@ public void AddFiberSynchronization(FiberSynchronization fiberSync) int index = fiberSync.FiberIndex; while (index >= Fibers.Count) { - Fibers.Add(new ThreadData()); + Fibers.Add(new ThreadData(null)); } Fibers[index].FiberSync = fiberSync; @@ -404,7 +422,7 @@ public ThreadData AddThread(ThreadDescription desc) desc.ThreadIndex = index; if (desc.ThreadID != UInt64.MaxValue) Board.ThreadID2ThreadIndex.Add(desc.ThreadID, index); - ThreadData threadData = new ThreadData(); + ThreadData threadData = new ThreadData(desc); Threads.Add(threadData); Board.Threads.Add(desc); return threadData; diff --git a/gui/Data/SamplingFrame.cs b/gui/Data/SamplingFrame.cs index f52621c6..f24c1aa3 100644 --- a/gui/Data/SamplingFrame.cs +++ b/gui/Data/SamplingFrame.cs @@ -212,6 +212,9 @@ public class SamplingNode : TreeNode public UInt64 Address { get; private set; } public override String Name { get { return Description.Name; } } + private List shadowNodes = new List(); + public override List ShadowNodes { get { return shadowNodes; } } + // Participated in sampling process public uint Passed { get; private set; } @@ -231,7 +234,7 @@ public uint Sampled //public SamplingNode Parent { get; private set; } - SamplingNode(SamplingNode root, SamplingDescription desc, UInt64 address, UInt32 passed) + SamplingNode(SamplingNode root, SamplingDescription desc, UInt32 passed) : base(root, desc, passed) { Passed = passed; @@ -247,7 +250,7 @@ public static SamplingNode Create(BinaryReader reader, SamplingDescriptionBoard UInt32 passed = reader.ReadUInt32(); - SamplingNode node = new SamplingNode(root, desc, address, passed); + SamplingNode node = new SamplingNode(root, desc, passed); UInt32 childrenCount = reader.ReadUInt32(); for (UInt32 i = 0; i < childrenCount; ++i) @@ -274,13 +277,15 @@ void AppendMerge(Callstack callstack, int index, SamplingNode root) { if (IsSimilar(node.Description, desc)) { + node.shadowNodes.Add(new SamplingNode(rootNode, desc, 1) { Parent = this, ChildrenDuration = index == callstack.Count - 1 ? 0 : 1 }); ++node.Passed; node.AppendMerge(callstack, index + 1, rootNode); return; } } - SamplingNode child = new SamplingNode(rootNode, desc, desc.Address, 1); + SamplingNode child = new SamplingNode(rootNode, desc, 1); + child.shadowNodes.Add(new SamplingNode(rootNode, desc, 1) { Parent = child, ChildrenDuration = index == callstack.Count - 1 ? 0 : 1 }); AddChild(child); child.AppendMerge(callstack, index + 1, rootNode); } @@ -302,7 +307,7 @@ void Update() public static SamplingNode Create(List callstacks) { - SamplingNode node = new SamplingNode(null, null, 0, (uint)callstacks.Count); + SamplingNode node = new SamplingNode(null, null, (uint)callstacks.Count); callstacks.ForEach(c => node.AppendMerge(c, 0, node)); node.Update(); node.Sort(); diff --git a/gui/ThreadView/EventThreadView.xaml.cs b/gui/ThreadView/EventThreadView.xaml.cs index 9ba1fcfe..401c96f6 100644 --- a/gui/ThreadView/EventThreadView.xaml.cs +++ b/gui/ThreadView/EventThreadView.xaml.cs @@ -187,12 +187,11 @@ List GenerateThreadRows(FrameGroup group) for (int i = 0; i < Math.Min(group.Board.Threads.Count, group.Threads.Count); ++i) { - ThreadDescription thread = group.Board.Threads[i]; + ThreadData data = group.Threads[i]; + ThreadDescription thread = data.Description; if (thread.IsIdle) continue; - - ThreadData data = group.Threads[i]; bool threadHasData = false; if (data.Events != null) @@ -209,6 +208,9 @@ List GenerateThreadRows(FrameGroup group) } } + if (thread.Origin == ThreadDescription.Source.Core) + threadHasData = true; + if (threadHasData) { EventsThreadRow row = new EventsThreadRow(group, thread, data); @@ -232,6 +234,7 @@ List GenerateThreadRows(FrameGroup group) return SortRows(eventThreads); } + enum ProcessGroup { None, @@ -383,13 +386,15 @@ ThreadData GenerateSamplingThread(FrameGroup group, ThreadData thread) }; frames.Add(new EventFrame(header, entries, group)); - ThreadData result = new ThreadData() + ThreadData result = new ThreadData(null) { Events = frames }; return result; } + List coreRows = new List(); + void InitThreadList(FrameGroup group) { List rows = new List(); @@ -407,13 +412,50 @@ void InitThreadList(FrameGroup group) ThreadRow cpuCoreChart = GenerateCoreChart(group); if (cpuCoreChart != null) + { + cpuCoreChart.IsExpanded = false; + cpuCoreChart.ExpandChanged += CpuCoreChart_ExpandChanged; rows.Add(cpuCoreChart); - rows.AddRange(GenerateThreadRows(group)); + } + + List threadRows = GenerateThreadRows(group); + foreach (EventsThreadRow row in threadRows) + { + if (row.Description.Origin == ThreadDescription.Source.Core) + { + row.IsVisible = false; + coreRows.Add(row); + } + } + rows.AddRange(threadRows); } ThreadViewControl.InitRows(rows, group != null ? group.Board.TimeSlice : null); } + private void CpuCoreChart_ExpandChanged(ThreadRow row) + { + if (row.IsExpanded) + { + if (!Group.IsCoreDataGenerated) + { + bool isVisible = row.IsExpanded; + Task.Run(() => + { + row.SetBusy(true); + Group.GenerateRealCoreThreads(); + ThreadViewControl.ReinitRows(coreRows); + row.SetBusy(false); + Application.Current.Dispatcher.Invoke(new Action(() => coreRows.ForEach(core => core.IsVisible = isVisible))); + }); + return; + } + } + + coreRows.ForEach(core => core.IsVisible = row.IsExpanded); + ThreadViewControl.UpdateRows(); + } + public void Highlight(EventFrame frame, IDurable focus) { Group = frame.Group; diff --git a/gui/ThreadView/SamplingThreadView.xaml.cs b/gui/ThreadView/SamplingThreadView.xaml.cs index c2bb9d74..fbb2b4a8 100644 --- a/gui/ThreadView/SamplingThreadView.xaml.cs +++ b/gui/ThreadView/SamplingThreadView.xaml.cs @@ -67,7 +67,7 @@ void InitThreadList(SamplingFrame frame) BuildEntryList(entries, root, 0.0); EventFrame eventFrame = new EventFrame(new FrameHeader() { Start = 0, Finish = Durable.MsToTick(root.Duration) }, entries, frame.Group); - ThreadData threadData = new ThreadData() { Events = new List { eventFrame } }; + ThreadData threadData = new ThreadData(null) { Events = new List { eventFrame } }; EventsThreadRow row = new EventsThreadRow(frame.Group, new ThreadDescription() { Name = "Sampling Node" }, threadData); row.EventNodeHover += Row_EventNodeHover; rows.Add(row); diff --git a/gui/ThreadView/ThreadView.xaml.cs b/gui/ThreadView/ThreadView.xaml.cs index 5464e0bc..4e600303 100644 --- a/gui/ThreadView/ThreadView.xaml.cs +++ b/gui/ThreadView/ThreadView.xaml.cs @@ -64,6 +64,7 @@ public void UpdateRows() ThreadList.Margin = new Thickness(0, 0, 3, 0); double offset = 0.0; bool isAlternative = false; + int rowCount = 0; for (int threadIndex = 0; threadIndex < Rows.Count; ++threadIndex) { ThreadRow row = Rows[threadIndex]; @@ -87,12 +88,13 @@ public void UpdateRows() border.Child = row.Header; border.Background = isAlternative ? OptickAlternativeBackground : OptickBackground; - Grid.SetRow(border, threadIndex); + Grid.SetRow(border, rowCount); ThreadList.Children.Add(border); } isAlternative = !isAlternative; offset += row.Height; + rowCount += 1; } surface.Height = offset; @@ -112,21 +114,37 @@ public void InitRows(List rows, IDurable timeslice) Scroll.Width = surface.ActualWidth * RenderSettings.dpiScaleX; rows.ForEach(row => row.BuildMesh(surface, Scroll)); rows.ForEach(row => row.ExpandChanged += Row_ExpandChanged); - rows.ForEach(row => row.VisibilityChanged += Row_ExpandChanged); + rows.ForEach(row => row.VisibilityChanged += Row_VisibilityChanged); } UpdateRows(); } + public void ReinitRows(List rows) + { + rows.ForEach(row => row.BuildMesh(surface, Scroll)); + UpdateRowsAsync(); + } + private void Row_ExpandChanged(ThreadRow row) { //Task.Run(()=> //{ row.BuildMesh(surface, Scroll); - Application.Current.Dispatcher.BeginInvoke(new Action(() => UpdateRows() )); + UpdateRowsAsync(); //}); } + private void UpdateRowsAsync() + { + Application.Current.Dispatcher.BeginInvoke(new Action(() => UpdateRows())); + } + + private void Row_VisibilityChanged(ThreadRow row) + { + //throw new NotImplementedException(); + } + private void InitBackgroundMesh() { if (BackgroundMesh != null)