diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs index 7630cc34c4..57c1a0248c 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs @@ -23,9 +23,11 @@ namespace Debugger.AddIn.Pads.ParallelPad { private DrawSurface surface; private Process debuggedProcess; - + private ParallelStacksGraph graph; private List currentThreadStacks = new List(); + #region Overrides + protected override void InitializeComponents() { surface = new DrawSurface(); @@ -52,16 +54,6 @@ namespace Debugger.AddIn.Pads.ParallelPad RefreshPad(); } - - void OnReset(object sender, EventArgs e) - { - currentThreadStacks.Clear(); - } - - void debuggedProcess_Paused(object sender, ProcessEventArgs e) - { - RefreshPad(); - } public override void RefreshPad() { @@ -101,7 +93,7 @@ namespace Debugger.AddIn.Pads.ParallelPad if (stack == null) continue; if (stack.ThreadStackParent != null && - (stack.ThreadStackChildren == null || stack.ThreadStackChildren.Length == 0)) + (stack.ThreadStackChildren == null || stack.ThreadStackChildren.Count == 0)) continue; graph.AddVertex(stack); @@ -113,12 +105,24 @@ namespace Debugger.AddIn.Pads.ParallelPad surface.SetGraph(graph); } } + + #endregion + + #region Private Methods - ParallelStacksGraph graph; + private void OnReset(object sender, EventArgs e) + { + currentThreadStacks.Clear(); + } + + private void debuggedProcess_Paused(object sender, ProcessEventArgs e) + { + RefreshPad(); + } - void AddChildren(ThreadStack parent) + private void AddChildren(ThreadStack parent) { - if(parent.ThreadStackChildren == null || parent.ThreadStackChildren.Length == 0) + if(parent.ThreadStackChildren == null || parent.ThreadStackChildren.Count == 0) return; foreach (var ts in parent.ThreadStackChildren) @@ -128,7 +132,7 @@ namespace Debugger.AddIn.Pads.ParallelPad graph.AddVertex(ts); graph.AddEdge(new ParallelStacksEdge(parent, ts)); - if (ts.ThreadStackChildren == null || ts.ThreadStackChildren.Length == 0) + if (ts.ThreadStackChildren == null || ts.ThreadStackChildren.Count == 0) continue; AddChildren(ts); @@ -160,7 +164,7 @@ namespace Debugger.AddIn.Pads.ParallelPad foreach (var stack in currentThreadStacks) { int count = stack.ItemCollection.Count; dynamic frame = stack.ItemCollection[count - 1]; - string fullname = frame.MethodName; + string fullname = frame.MethodName + stack.Level.ToString(); if (!commonFrameThreads.ContainsKey(fullname)) commonFrameThreads.Add(fullname, new List()); @@ -217,7 +221,7 @@ namespace Debugger.AddIn.Pads.ParallelPad // remove last [frameIndex] and create a new ThreadStack as the parent of what remained in the children var threadIds = new List(); var parentItems = new Stack(); - int j = 0; + while (frameIndex > 0) { for (int i = 0 ; i < listOfCurrentStacks.Count; ++i) { var stack = listOfCurrentStacks[i]; @@ -229,24 +233,31 @@ namespace Debugger.AddIn.Pads.ParallelPad #endif if (i == 0) parentItems.Push(stack.ItemCollection[indexToRemove]); - if (i == j) - threadIds.AddRange(stack.ThreadIds); stack.ItemCollection.RemoveAt(indexToRemove); } - j++; + frameIndex--; } + + // update thread ids + for (int i = 0 ; i < listOfCurrentStacks.Count; ++i) + threadIds.AddRange(listOfCurrentStacks[i].ThreadIds); + // remove stacks with no items for (int i = listOfCurrentStacks.Count - 1; i >= 0; --i) { var stack = listOfCurrentStacks[i]; if (stack.ItemCollection.Count == 0) listOfCurrentStacks.Remove(stack); } + + // increase the Level + for (int i = 0 ; i < listOfCurrentStacks.Count; ++i) + listOfCurrentStacks[i].Level++; // create new parent stack ThreadStack commonParent = new ThreadStack(); - commonParent.ThreadIds = threadIds; + commonParent.UpdateThreadIds(threadIds.ToArray()); commonParent.ItemCollection = parentItems.ToObservable(); commonParent.Process = debuggedProcess; commonParent.FrameSelected += threadStack_FrameSelected; @@ -259,15 +270,48 @@ namespace Debugger.AddIn.Pads.ParallelPad continue; } dynamic item = stack.ItemCollection[stack.ItemCollection.Count - 1]; + // add the parent to the parent + if (stack.ThreadStackParent != null) { + // remove stack from it's parent because it will have the commonParent as parent + stack.ThreadStackParent.ThreadStackChildren.Remove(stack); + commonParent.ThreadStackParent = stack.ThreadStackParent; + commonParent.ThreadStackParent.ThreadStackChildren.Add(commonParent); + // set level + commonParent.Level = stack.Level - 1; + } + + // update thread ids + var parent = commonParent.ThreadStackParent; + if (parent != null) { + if (parent.ThreadIds != null) + { + var list = new List(); + foreach (uint id in commonParent.ThreadIds) { + if (!parent.ThreadIds.Contains(id)) { + list.Add(id); + } + } + + parent.UpdateThreadIds(list.ToArray()); + } + } + stack.ThreadStackParent = commonParent; - string currentName = item.MethodName; - var newList = new List(); - newList.Add(stack); + string currentName = item.MethodName + stack.Level.ToString();; - commonFrameThreads.Add(currentName, newList); + // add name or add to list + if (!commonFrameThreads.ContainsKey(currentName)) { + var newList = new List(); + newList.Add(stack); + commonFrameThreads.Add(currentName, newList); + } + else { + var list = commonFrameThreads[currentName]; + list.Add(stack); + } } - commonParent.ThreadStackChildren = listOfCurrentStacks.ToArray(); + commonParent.ThreadStackChildren = listOfCurrentStacks.Clone(); commonFrameThreads[frameName].Clear(); commonFrameThreads[frameName].Add(commonParent); currentThreadStacks.Add(commonParent); @@ -289,8 +333,7 @@ namespace Debugger.AddIn.Pads.ParallelPad ThreadStack threadStack = new ThreadStack(); threadStack.FrameSelected += threadStack_FrameSelected; - threadStack.ThreadIds = new List(); - threadStack.ThreadIds.Add(thread.ID); + threadStack.UpdateThreadIds(thread.ID); threadStack.Process = debuggedProcess; currentThreadStacks.Add(threadStack); @@ -299,7 +342,7 @@ namespace Debugger.AddIn.Pads.ParallelPad threadStack.IsSelected = threadStack.ThreadIds.Contains(debuggedProcess.SelectedThread.ID); } - void threadStack_FrameSelected(object sender, EventArgs e) + private void threadStack_FrameSelected(object sender, EventArgs e) { foreach (var ts in this.currentThreadStacks) { ts.IsSelected = false; @@ -346,6 +389,8 @@ namespace Debugger.AddIn.Pads.ParallelPad return result; } + + #endregion } internal static class StackFrameExtensions @@ -366,6 +411,18 @@ namespace Debugger.AddIn.Pads.ParallelPad internal static class ParallelStackExtensions { + internal static List Clone(this List listToClone) + { + if (listToClone == null) + return null; + + List result = new List(); + foreach (var item in listToClone) + result.Add(item); + + return result; + } + internal static ObservableCollection ToObservable(this Stack stack) { if (stack == null) diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs index 8c056a4fc9..8a360a8c25 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Dynamic; using System.Windows; using System.Windows.Controls; @@ -25,22 +26,28 @@ namespace Debugger.AddIn.Pads.ParallelPad DependencyProperty.Register("IsSelected", typeof(bool), typeof(ThreadStack), new FrameworkPropertyMetadata()); - public event EventHandler FrameSelected; private ObservableCollection itemCollection = new ObservableCollection(); private ToolTip toolTip = new ToolTip(); + private List threadIds = new List(); public ThreadStack() { InitializeComponent(); - ToolTip = toolTip; - ToolTipOpening += new ToolTipEventHandler(OnToolTipOpening); + datagrid.ToolTip = toolTip; + datagrid.ToolTipOpening += OnToolTipOpening; + datagrid.PreviewMouseMove += new MouseEventHandler(datagrid_PreviewMouseMove); + datagrid.MouseLeave += delegate { toolTip.IsOpen = false; }; } + #region Public Properties + public Process Process { get; set; } + public int Level { get; set; } + public bool IsSelected { get { return (bool)GetValue(IsSelectedProperty); } set { @@ -61,9 +68,13 @@ namespace Debugger.AddIn.Pads.ParallelPad public ThreadStack ThreadStackParent { get; set; } - public ThreadStack[] ThreadStackChildren { get; set; } + public List ThreadStackChildren { get; set; } - public List ThreadIds { get; set; } + public List ThreadIds { + get { + return threadIds; + } + } public ObservableCollection ItemCollection { get { @@ -73,14 +84,23 @@ namespace Debugger.AddIn.Pads.ParallelPad set { itemCollection = value; this.datagrid.ItemsSource = itemCollection; - - if (ThreadIds.Count > 1) - this.HeaderText.Text = ThreadIds.Count.ToString() + " Threads"; - else - this.HeaderText.Text = "1 Thread"; } } + #endregion + + #region Public Methods + + public void UpdateThreadIds(params uint[] threadIds) + { + this.threadIds.AddRange(threadIds); + + if (this.threadIds.Count > 1) + this.HeaderText.Text = this.threadIds.Count.ToString() + " Threads"; + else + this.HeaderText.Text = "1 Thread"; + } + public void ClearImages() { foreach(dynamic item in itemCollection) { @@ -88,7 +108,11 @@ namespace Debugger.AddIn.Pads.ParallelPad } } - void SelectParent(bool isSelected) + #endregion + + #region Private Methods + + private void SelectParent(bool isSelected) { var ts = this.ThreadStackParent; while(ts != null) { @@ -97,7 +121,24 @@ namespace Debugger.AddIn.Pads.ParallelPad } } - void Datagrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) + void datagrid_PreviewMouseMove(object sender, MouseEventArgs e) + { + var result = VisualTreeHelper.HitTest(this, e.GetPosition(this)); + if (result != null) + { + var row = TryFindParent(result.VisualHit); + if (row != null) + { + datagrid.SelectedItem = row.DataContext; + if (toolTip.IsOpen) + toolTip.IsOpen = false; + toolTip.IsOpen = true; + e.Handled = true; + } + } + } + + private void Datagrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) { if (Process.IsRunning) return; @@ -114,7 +155,7 @@ namespace Debugger.AddIn.Pads.ParallelPad } } - void SelectFrame(uint threadId, ExpandoObject selectedItem) + private void SelectFrame(uint threadId, ExpandoObject selectedItem) { if (selectedItem == null) return; @@ -142,7 +183,7 @@ namespace Debugger.AddIn.Pads.ParallelPad } } - void Datagrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e) + private void Datagrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e) { if (Process.IsRunning) return; @@ -154,7 +195,7 @@ namespace Debugger.AddIn.Pads.ParallelPad datagrid.ContextMenu.IsOpen = true; } - ContextMenu CreateContextMenu(ExpandoObject item) + private ContextMenu CreateContextMenu(ExpandoObject item) { dynamic obj = item; @@ -177,7 +218,7 @@ namespace Debugger.AddIn.Pads.ParallelPad return menu; } - void OnToolTipOpening(object sender, ToolTipEventArgs e) + private void OnToolTipOpening(object sender, ToolTipEventArgs e) { StackPanel panel = new StackPanel(); @@ -196,7 +237,6 @@ namespace Debugger.AddIn.Pads.ParallelPad { if (selectedItem.MethodName == frame.GetMethodName()) { - // TODO : get method parameter values TextBlock tb = new TextBlock(); tb.Text = thread.ID + ": " + CallStackPadContent.GetFullName(frame); panel.Children.Add(tb); @@ -207,5 +247,53 @@ namespace Debugger.AddIn.Pads.ParallelPad this.toolTip.Content = panel; } + + #endregion + + #region Static Methods + + private static T TryFindParent(DependencyObject child) where T : DependencyObject + { + if (child is T) return child as T; + + DependencyObject parentObject = GetParentObject(child); + if (parentObject == null) return null; + + var parent = parentObject as T; + if (parent != null && parent is T) + { + return parent; + } + else + { + return TryFindParent(parentObject); + } + } + + private static DependencyObject GetParentObject(DependencyObject child) + { + if (child == null) return null; + + ContentElement contentElement = child as ContentElement; + if (contentElement != null) + { + DependencyObject parent = ContentOperations.GetParent(contentElement); + if (parent != null) return parent; + + FrameworkContentElement fce = contentElement as FrameworkContentElement; + return fce != null ? fce.Parent : null; + } + + FrameworkElement frameworkElement = child as FrameworkElement; + if (frameworkElement != null) + { + DependencyObject parent = frameworkElement.Parent; + if (parent != null) return parent; + } + + return VisualTreeHelper.GetParent(child); + } + + #endregion } } \ No newline at end of file