diff --git a/data/resources/StringResources.resx b/data/resources/StringResources.resx
index 98bace9f94..8e9516f3a6 100644
--- a/data/resources/StringResources.resx
+++ b/data/resources/StringResources.resx
@@ -5934,6 +5934,9 @@ Shows the full callstack of the error.
Object Graph
+
+ Parallel Stack
+
Run to cursor
diff --git a/data/resources/image/BitmapResources/BitmapResources.res b/data/resources/image/BitmapResources/BitmapResources.res
index eaf2aa2126..e9c1149a08 100644
--- a/data/resources/image/BitmapResources/BitmapResources.res
+++ b/data/resources/image/BitmapResources/BitmapResources.res
@@ -247,6 +247,7 @@ PadIcons.LocalVariables = PadIcons\LocalVariables.png
PadIcons.Threads = PadIcons\Threads.png
PadIcons.Exceptions = PadIcons\Exceptions.png
PadIcons.XPathQuery = PadIcons\XPathQuery.png
+PadIcons.Parallel = PadIcons\Parallel.png
#SharpQuery icons
Icons.16x16.SharpQuery.DataBaseRoot = SharpQueryIcons\Icons.16x16.SharpQuery.Database.png
diff --git a/data/resources/image/BitmapResources/PadIcons/Parallel.png b/data/resources/image/BitmapResources/PadIcons/Parallel.png
new file mode 100644
index 0000000000..eeac4da65a
Binary files /dev/null and b/data/resources/image/BitmapResources/PadIcons/Parallel.png differ
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin
index 3d4cc96e78..80db53ae4d 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin
+++ b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin
@@ -123,6 +123,13 @@
icon = "PadIcons.LocalVariables"
class = "ICSharpCode.SharpDevelop.Gui.Pads.ObjectGraphPad"
defaultPosition = "Bottom, Hidden" />
+
+
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
index 6361e00f64..142e3e98a3 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
+++ b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
@@ -1,426 +1,451 @@
-
-
-
- Debug
- AnyCPU
- 9.0.30729
- 2.0
- {EC06F96A-AEEC-49D6-B03D-AB87C6EB674C}
- Library
- Debugger.AddIn
- Debugger.AddIn
- 4
- False
- False
- False
- False
- Auto
- 116916224
- AnyCPU
- 4096
- false
- Always
- v4.0
- "C:\Program Files\SharpDevelop\3.0\bin\..\AddIns\AddIns\Misc\SourceAnalysis\Settings.SourceAnalysis"
- C:\Dokumente und Einstellungen\HP\Anwendungsdaten\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis
- http://localhost/Debugger.AddIn/
- true
- Web
- true
- Foreground
- 7
- Days
- false
- false
- true
- 0
- 1.0.0.%2a
- true
- false
- true
- ..\..\..\..\AddIns\Debugger\
-
-
-
-
- true
- Full
- false
- DEBUG;TRACE
-
-
- pdbonly
- true
- TRACE
-
-
- Program
- ..\..\..\..\bin\SharpDevelop.exe
-
-
-
-
- 3.0
-
-
- 3.0
-
-
- 3.0
-
-
-
- 3.5
-
-
-
-
-
-
-
- 3.0
-
-
- 3.0
-
-
- 4.0
-
-
-
-
- CallStackPad.xaml
- Code
-
-
- ConditionCell.xaml
-
-
-
- SimpleListViewControl.xaml
- Code
-
-
-
- WatchList.xaml
-
-
- WatchInputBox.xaml
- Code
-
-
-
-
-
-
-
-
-
- UserControl
-
-
- DebuggingOptionsPanel.cs
-
-
- UserControl
-
-
- DebuggingSymbolsPanel.cs
-
-
-
-
-
-
- UserControl
-
-
-
-
-
- Component
-
-
-
- Form
-
-
- Form
-
-
- DebuggeeExceptionForm.cs
-
-
- Form
-
-
- DebuggerEventForm.cs
-
-
- EditBreakpointScriptWindow.xaml
- Code
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- PositionedGraphNodeControl.xaml
- Code
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ObjectGraphControl.xaml
- Code
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- TextVisualizerWindow.xaml
- Code
-
-
-
-
-
-
-
-
-
-
- DebuggeeExceptionForm.cs
-
-
-
-
-
- DebuggingOptionsPanel.cs
-
-
- DebuggingSymbolsPanel.cs
-
-
- DebuggerEventForm.cs
-
-
-
- Properties\GlobalAssemblyInfo.cs
-
-
-
-
-
- Component
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- GridVisualizerWindow.xaml
- Code
-
-
-
-
-
-
-
-
-
-
- ObjectGraphWindow.xaml
-
-
-
-
-
-
-
-
-
-
- Never
-
-
- {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}
- ICSharpCode.AvalonEdit
- False
-
-
- {2748AD25-9C63-4E12-877B-4DCE96FBED54}
- ICSharpCode.SharpDevelop
- False
-
-
- {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
- ICSharpCode.Core
- False
-
-
- {7E4A7172-7FF5-48D0-B719-7CD959DD1AC9}
- ICSharpCode.Core.Presentation
- False
-
-
- {1D18D788-F7EE-4585-A23B-34DC8EC63CB8}
- Debugger.Core
- False
- False
-
-
-
- {924EE450-603D-49C1-A8E5-4AFAA31CE6F3}
- ICSharpCode.SharpDevelop.Dom
- False
-
-
- {8035765F-D51F-4A0C-A746-2FD100E19419}
- ICSharpCode.SharpDevelop.Widgets
- False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}
- NRefactory
- False
-
-
- {E73BB233-D88B-44A7-A98F-D71EE158381D}
- Aga.Controls
- False
-
-
- {857CA1A3-FC88-4BE0-AB6A-D1EE772AB288}
- ICSharpCode.Core.WinForms
- False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- False
- .NET Framework Client Profile
- false
-
-
- False
- .NET Framework 2.0 %28x86%29
- true
-
-
- False
- .NET Framework 3.0 %28x86%29
- false
-
-
- False
- .NET Framework 3.5
- false
-
-
- False
- .NET Framework 3.5 SP1
- false
-
-
-
-
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {EC06F96A-AEEC-49D6-B03D-AB87C6EB674C}
+ Library
+ Debugger.AddIn
+ Debugger.AddIn
+ 4
+ False
+ False
+ False
+ False
+ Auto
+ 116916224
+ AnyCPU
+ 4096
+ false
+ Always
+ v4.0
+ "C:\Program Files\SharpDevelop\3.0\bin\..\AddIns\AddIns\Misc\SourceAnalysis\Settings.SourceAnalysis"
+ C:\Dokumente und Einstellungen\HP\Anwendungsdaten\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis
+ http://localhost/Debugger.AddIn/
+ true
+ Web
+ true
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ true
+ false
+ true
+ ..\..\..\..\AddIns\Debugger\
+
+
+
+
+ true
+ Full
+ false
+ DEBUG;TRACE
+
+
+ pdbonly
+ true
+ TRACE
+
+
+ Program
+ ..\..\..\..\bin\SharpDevelop.exe
+
+
+
+ ..\..\..\Libraries\GraphSharp\GraphSharp.dll
+
+
+ ..\..\..\Libraries\GraphSharp\GraphSharp.Controls.dll
+
+
+ ..\..\..\Libraries\GraphSharp\Microsoft.Contracts.dll
+
+
+
+ 3.0
+
+
+ 3.0
+
+
+ 3.0
+
+
+ ..\..\..\Libraries\GraphSharp\QuickGraph.dll
+
+
+
+ 3.5
+
+
+
+
+
+
+
+ 3.0
+
+
+ 3.0
+
+
+ 4.0
+
+
+
+
+ CallStackPad.xaml
+ Code
+
+
+ ConditionCell.xaml
+
+
+
+ SimpleListViewControl.xaml
+ Code
+
+
+
+ WatchList.xaml
+
+
+ DrawSurface.xaml
+ Code
+
+
+
+
+ ThreadStack.xaml
+ Code
+
+
+ WatchInputBox.xaml
+ Code
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ DebuggingOptionsPanel.cs
+
+
+ UserControl
+
+
+ DebuggingSymbolsPanel.cs
+
+
+
+
+
+
+ UserControl
+
+
+
+
+
+ Component
+
+
+
+ Form
+
+
+ Form
+
+
+ DebuggeeExceptionForm.cs
+
+
+ Form
+
+
+ DebuggerEventForm.cs
+
+
+ EditBreakpointScriptWindow.xaml
+ Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PositionedGraphNodeControl.xaml
+ Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ObjectGraphControl.xaml
+ Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TextVisualizerWindow.xaml
+ Code
+
+
+
+
+
+
+
+
+
+
+ DebuggeeExceptionForm.cs
+
+
+
+
+
+ DebuggingOptionsPanel.cs
+
+
+ DebuggingSymbolsPanel.cs
+
+
+ DebuggerEventForm.cs
+
+
+
+ Properties\GlobalAssemblyInfo.cs
+
+
+
+
+
+ Component
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GridVisualizerWindow.xaml
+ Code
+
+
+
+
+
+
+
+
+
+
+ ObjectGraphWindow.xaml
+
+
+
+
+
+
+
+
+
+
+ Never
+
+
+ {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}
+ ICSharpCode.AvalonEdit
+ False
+
+
+ {2748AD25-9C63-4E12-877B-4DCE96FBED54}
+ ICSharpCode.SharpDevelop
+ False
+
+
+ {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
+ ICSharpCode.Core
+ False
+
+
+ {7E4A7172-7FF5-48D0-B719-7CD959DD1AC9}
+ ICSharpCode.Core.Presentation
+ False
+
+
+ {1D18D788-F7EE-4585-A23B-34DC8EC63CB8}
+ Debugger.Core
+ False
+ False
+
+
+
+ {924EE450-603D-49C1-A8E5-4AFAA31CE6F3}
+ ICSharpCode.SharpDevelop.Dom
+ False
+
+
+ {8035765F-D51F-4A0C-A746-2FD100E19419}
+ ICSharpCode.SharpDevelop.Widgets
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}
+ NRefactory
+ False
+
+
+ {E73BB233-D88B-44A7-A98F-D71EE158381D}
+ Aga.Controls
+ False
+
+
+ {857CA1A3-FC88-4BE0-AB6A-D1EE772AB288}
+ ICSharpCode.Core.WinForms
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ .NET Framework Client Profile
+ false
+
+
+ False
+ .NET Framework 2.0 %28x86%29
+ true
+
+
+ False
+ .NET Framework 3.0 %28x86%29
+ false
+
+
+ False
+ .NET Framework 3.5
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml
new file mode 100644
index 0000000000..843236079d
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml.cs
new file mode 100644
index 0000000000..6d58b1ccf2
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml.cs
@@ -0,0 +1,105 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace Debugger.AddIn.Pads.ParallelPad
+{
+ public partial class DrawSurface : UserControl
+ {
+ Point dragStartedPoint;
+
+ TransformGroup group = new TransformGroup();
+ ScaleTransform zoom = new ScaleTransform();
+ TranslateTransform translate = new TranslateTransform();
+
+ public DrawSurface()
+ {
+ InitializeComponent();
+
+ group.Children.Add(zoom);
+ group.Children.Add(translate);
+ drawingSurface.RenderTransform = group;
+
+ this.MouseLeftButtonDown += DrawSurface_PreviewMouseLeftButtonDown;
+ this.MouseLeftButtonUp += DrawSurface_MouseLeftButtonUp;
+ }
+
+ public void SetGraph(ParallelStacksGraph graph)
+ {
+ this.ParallelStacksLayout.Graph = graph;
+ this.ParallelStacksLayout.Relayout();
+ }
+
+ #region Pan
+
+ void DrawSurface_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.OriginalSource is Slider || e.OriginalSource is Button)
+ return;
+
+ dragStartedPoint = e.GetPosition(drawingSurface);
+ drawingSurface.CaptureMouse();
+ this.PreviewMouseMove += DrawSurface_PreviewMouseMove;
+ e.Handled = true;
+ }
+
+ void DrawSurface_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ if (e.OriginalSource is Slider || e.OriginalSource is Button)
+ return;
+
+ drawingSurface.ReleaseMouseCapture();
+ this.PreviewMouseMove -= DrawSurface_PreviewMouseMove;
+ Cursor = Cursors.Arrow;
+ }
+
+ void DrawSurface_PreviewMouseMove(object sender, MouseEventArgs e)
+ {
+ if (!drawingSurface.IsMouseCaptured) return;
+
+ if (e.LeftButton == MouseButtonState.Pressed)
+ {
+ if (e.OriginalSource is Slider || e.OriginalSource is Button)
+ return;
+
+ Cursor = Cursors.ScrollAll;
+ Vector v = dragStartedPoint - e.GetPosition(drawingSurface);
+ translate.X = v.X / 5;
+ translate.Y = v.Y / 5;
+ e.Handled = true;
+ }
+ }
+
+ #endregion
+
+ #region Zoom
+
+ void SliderControl_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
+ {
+ if (e.OldValue == 0)
+ return;
+
+ double value = (e.NewValue / 5d) * 100;
+
+ this.PercentText.Text = string.Format("{0}%", value);
+
+ // zoom canvas
+ zoom.ScaleX = e.NewValue / 5d;
+ zoom.ScaleY = e.NewValue / 5d;
+ zoom.CenterX = drawingSurface.ActualWidth / 2d;
+ zoom.CenterY = drawingSurface.ActualHeight / 2d;
+ }
+
+ void Reset_Click(object sender, RoutedEventArgs e)
+ {
+ this.SliderControl.Value = 5;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs
new file mode 100644
index 0000000000..1c4983eb2c
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs
@@ -0,0 +1,357 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Dynamic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+using Debugger.AddIn.TreeModel;
+using ICSharpCode.Core;
+using ICSharpCode.SharpDevelop.Debugging;
+using ICSharpCode.SharpDevelop.Gui.Pads;
+using ICSharpCode.SharpDevelop.Services;
+
+namespace Debugger.AddIn.Pads.ParallelPad
+{
+ public class ParallelStackPad : DebuggerPad
+ {
+ private DrawSurface surface;
+ private Process debuggedProcess;
+
+ private List currentThreadStacks = new List();
+
+ protected override void InitializeComponents()
+ {
+ surface = new DrawSurface();
+ }
+
+ public override object Control {
+ get {
+ return surface;
+ }
+ }
+
+ protected override void SelectProcess(Process process)
+ {
+ if (debuggedProcess != null) {
+ debuggedProcess.Paused -= debuggedProcess_Paused;
+ }
+ debuggedProcess = process;
+ if (debuggedProcess != null) {
+ debuggedProcess.Paused += debuggedProcess_Paused;
+ }
+
+ DebuggerService.DebugStarted += OnReset;
+ DebuggerService.DebugStopped += OnReset;
+
+ RefreshPad();
+ }
+
+ void OnReset(object sender, EventArgs e)
+ {
+ currentThreadStacks.Clear();
+ }
+
+ void debuggedProcess_Paused(object sender, ProcessEventArgs e)
+ {
+ RefreshPad();
+ }
+
+ public override void RefreshPad()
+ {
+ if (debuggedProcess == null || debuggedProcess.IsRunning) {
+ return;
+ }
+
+ using(new PrintTimes("Threads refresh")) {
+ try {
+ OnReset(null, EventArgs.Empty);
+ // create all simple ThreadStacks
+ foreach (Thread thread in debuggedProcess.Threads) {
+ if (debuggedProcess.IsPaused) {
+ Utils.DoEvents(debuggedProcess);
+ }
+
+ CreateThreadStack(thread);
+ }
+
+ CreateCommonStacks();
+ }
+ catch(AbortedBecauseDebuggeeResumedException) { }
+ catch(System.Exception) {
+ if (debuggedProcess == null || debuggedProcess.HasExited) {
+ // Process unexpectedly exited
+ } else {
+ throw;
+ }
+ }
+
+ // paint the ThreadStaks
+ graph = new ParallelStacksGraph();
+ foreach (var stack in this.currentThreadStacks)
+ {
+ if (stack == null)
+ continue;
+ if (stack.ThreadStackParent != null &&
+ (stack.ThreadStackChildren == null || stack.ThreadStackChildren.Length == 0))
+ continue;
+
+ graph.AddVertex(stack);
+
+ // add the children
+ AddChildren(stack);
+ }
+
+ surface.SetGraph(graph);
+ }
+ }
+
+ ParallelStacksGraph graph;
+ void AddChildren(ThreadStack parent)
+ {
+ if(parent.ThreadStackChildren == null || parent.ThreadStackChildren.Length == 0)
+ return;
+
+ foreach (var ts in parent.ThreadStackChildren)
+ {
+ if (ts == null) continue;
+
+ graph.AddVertex(ts);
+ graph.AddEdge(new ParallelStacksEdge(parent, ts));
+
+ if (ts.ThreadStackChildren == null || ts.ThreadStackChildren.Length == 0)
+ continue;
+
+ AddChildren(ts);
+ }
+ }
+
+ private void CreateCommonStacks()
+ {
+ // stack.ItemCollection order
+ // 0 -> top of stack = S.C
+ // 1 -> ............ = S.B
+ // .......................
+ // n -> bottom of stack = [External Methods]
+
+ // ThreadStacks with common frame
+ var commonFrameThreads = new Dictionary>();
+
+ bool isOver = false;
+ while (true) {
+
+ for (int i = currentThreadStacks.Count - 1; i >=0; --i) {
+ var stack = currentThreadStacks[i];
+ if (stack.ItemCollection.Count == 0) {
+ currentThreadStacks.Remove(stack);
+ continue;
+ }
+ }
+ //get all thread stacks with common start frame
+ foreach (var stack in currentThreadStacks) {
+ int count = stack.ItemCollection.Count;
+ dynamic frame = stack.ItemCollection[count - 1];
+ string fullname = frame.MethodName;
+
+ if (!commonFrameThreads.ContainsKey(fullname))
+ commonFrameThreads.Add(fullname, new List());
+
+ if (!commonFrameThreads[fullname].Contains(stack))
+ commonFrameThreads[fullname].Add(stack);
+ }
+
+ // for every list of common stacks, find split place and add them to currentThreadStacks
+ foreach (var frameName in commonFrameThreads.Keys) {
+ var listOfCurrentStacks = commonFrameThreads[frameName];
+
+ if (listOfCurrentStacks.Count == 1) // just skip the parents
+ {
+ isOver = true; // we finish when all are pseodo-parents: no more spliting
+ continue;
+ }
+
+ isOver = false;
+
+ // find the frameIndex where we can split
+ int frameIndex = 0;
+ string fn = string.Empty;
+ bool canContinue = true;
+
+ while(canContinue) {
+ for (int i = 0; i < listOfCurrentStacks.Count; ++i) {
+ var stack = listOfCurrentStacks[i];
+ if (stack.ItemCollection.Count == frameIndex)
+ {
+ canContinue = false;
+ break;
+ }
+ dynamic item = stack.ItemCollection[stack.ItemCollection.Count - frameIndex - 1];
+
+ string currentName = item.MethodName;
+
+ if (i == 0) {
+ fn = currentName;
+ continue;
+ }
+
+ if (fn == currentName)
+ continue;
+ else {
+ canContinue = false;
+ break;
+ }
+ }
+ if (canContinue)
+ frameIndex++;
+ }
+
+ // 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];
+ int indexToRemove = stack.ItemCollection.Count - 1;
+
+ #if DEBUG
+ dynamic d_item = stack.ItemCollection[indexToRemove];
+ string name = d_item.MethodName;
+ #endif
+ if (i == 0)
+ parentItems.Push(stack.ItemCollection[indexToRemove]);
+ if (i == j)
+ threadIds.AddRange(stack.ThreadIds);
+
+ stack.ItemCollection.RemoveAt(indexToRemove);
+ }
+ j++;
+ frameIndex--;
+ }
+ // 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);
+ }
+
+ // create new parent stack
+ ThreadStack commonParent = new ThreadStack();
+ commonParent.ThreadIds = threadIds;
+ commonParent.ItemCollection = parentItems.ToObservable();
+ commonParent.Process = debuggedProcess;
+ commonParent.IsSelected = commonParent.ThreadIds.Contains(debuggedProcess.SelectedThread.ID);
+ // add new children
+ foreach (var stack in listOfCurrentStacks) {
+ if (stack.ItemCollection.Count == 0)
+ {
+ currentThreadStacks.Remove(stack);
+ continue;
+ }
+ dynamic item = stack.ItemCollection[stack.ItemCollection.Count - 1];
+ stack.ThreadStackParent = commonParent;
+ string currentName = item.MethodName;
+ var newList = new List();
+ newList.Add(stack);
+
+ commonFrameThreads.Add(currentName, newList);
+ }
+
+ commonParent.ThreadStackChildren = listOfCurrentStacks.ToArray();
+ commonFrameThreads[frameName].Clear();
+ commonFrameThreads[frameName].Add(commonParent);
+ currentThreadStacks.Add(commonParent);
+
+ // exit and retry
+ break;
+ }
+
+ if (isOver)
+ break;
+ }
+ }
+
+ private void CreateThreadStack(Thread thread)
+ {
+ var items = CreateItems(thread);
+ if (items.Count == 0)
+ return;
+
+ ThreadStack threadStack = new ThreadStack();
+ threadStack.ThreadIds = new List();
+ threadStack.ThreadIds.Add(thread.ID);
+ threadStack.Process = debuggedProcess;
+ currentThreadStacks.Add(threadStack);
+
+ threadStack.ItemCollection = items;
+ if (debuggedProcess.SelectedThread != null)
+ threadStack.IsSelected = threadStack.ThreadIds.Contains(debuggedProcess.SelectedThread.ID);
+ }
+
+ private ObservableCollection CreateItems(Thread thread)
+ {
+ bool lastItemIsExternalMethod = false;
+ var result = new ObservableCollection();
+ foreach (StackFrame frame in thread.GetCallstack(100)) {
+ dynamic obj = new ExpandoObject();
+ string fullName;
+ if (frame.HasSymbols) {
+ // Show the method in the list
+ fullName = frame.GetMethodName();
+ lastItemIsExternalMethod = false;
+ obj.FontWeight = FontWeights.Normal;
+ obj.Foreground = Brushes.Black;
+ } else {
+ // Show [External methods] in the list
+ if (lastItemIsExternalMethod) continue;
+ fullName = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ExternalMethods").Trim();
+ obj.FontWeight = FontWeights.Normal;
+ obj.Foreground = Brushes.Gray;
+ lastItemIsExternalMethod = true;
+ }
+
+ obj.Image = null;
+ obj.MethodName = fullName;
+
+ result.Add(obj);
+ }
+
+ Utils.DoEvents(debuggedProcess);
+
+ return result;
+ }
+ }
+
+ internal static class StackFrameExtensions
+ {
+ internal static string GetMethodName(this StackFrame frame)
+ {
+ StringBuilder name = new StringBuilder();
+ name.Append(frame.MethodInfo.DeclaringType.FullName);
+ name.Append('.');
+ name.Append(frame.MethodInfo.Name);
+
+ return name.ToString();
+ }
+ }
+
+ internal static class ParallelStackExtensions
+ {
+ internal static ObservableCollection ToObservable(this Stack stack)
+ {
+ if (stack == null)
+ throw new NullReferenceException("Stack is null!");
+
+ var result = new ObservableCollection();
+ while (stack.Count > 0)
+ result.Add(stack.Pop());
+
+ return result;
+ }
+ }
+}
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStacksGraph.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStacksGraph.cs
new file mode 100644
index 0000000000..b8d0d905df
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStacksGraph.cs
@@ -0,0 +1,37 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+using System;
+using QuickGraph;
+using GraphSharp.Algorithms.Layout;
+using GraphSharp.Algorithms.Layout.Contextual;
+using GraphSharp.Algorithms.Layout.Simple.Tree;
+using GraphSharp.Controls;
+
+namespace Debugger.AddIn.Pads.ParallelPad
+{
+ public class ParallelStacksEdge : QuickGraph.Edge
+ {
+ public ParallelStacksEdge(ThreadStack source, ThreadStack target) : base(source, target)
+ { }
+ }
+
+ public class ParallelStacksGraph : BidirectionalGraph
+ {
+ public ParallelStacksGraph()
+ { }
+ }
+
+ public class ParallelStacksGraphLayout : GraphLayout
+ {
+ public ParallelStacksGraphLayout()
+ {
+ var par = new SimpleTreeLayoutParameters();
+ par.LayerGap = 30;
+ par.VertexGap = 50;
+ par.Direction = LayoutDirection.BottomToTop;
+ par.SpanningTreeGeneration = SpanningTreeGeneration.DFS;
+
+ this.LayoutParameters = par;
+ }
+ }
+}
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml
new file mode 100644
index 0000000000..8a21fbebe2
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs
new file mode 100644
index 0000000000..89ea9c79a4
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs
@@ -0,0 +1,154 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Dynamic;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace Debugger.AddIn.Pads.ParallelPad
+{
+ public partial class ThreadStack : UserControl
+ {
+ public static SolidColorBrush SelectedBrush = new SolidColorBrush(Color.FromRgb(84, 169, 255));
+
+ public static readonly DependencyProperty IsSelectedProperty =
+ DependencyProperty.Register("IsSelected", typeof(bool), typeof(ThreadStack),
+ new FrameworkPropertyMetadata());
+
+ private ObservableCollection itemCollection = new ObservableCollection();
+
+ public ThreadStack()
+ {
+ InitializeComponent();
+ }
+
+ internal bool IsAdded { get; set; }
+
+ public Process Process { get; set; }
+
+ public bool IsSelected {
+ get { return (bool)GetValue(IsSelectedProperty); }
+ set {
+ if (value) {
+ BorderParent.BorderBrush = SelectedBrush;
+ BorderParent.BorderThickness = new Thickness(5);
+ }
+ else {
+ BorderParent.BorderBrush = Brushes.Black;
+ BorderParent.BorderThickness = new Thickness(3);
+ }
+
+ SetValue(IsSelectedProperty, value);
+
+ SelectParent(value);
+ }
+ }
+
+ public ThreadStack ThreadStackParent { get; set; }
+
+ public ThreadStack[] ThreadStackChildren { get; set; }
+
+ public List ThreadIds { get; set; }
+
+ public ObservableCollection ItemCollection {
+ get {
+ return itemCollection;
+ }
+
+ set {
+ itemCollection = value;
+ this.datagrid.ItemsSource = itemCollection;
+
+ if (ThreadIds.Count > 1)
+ this.HeaderText.Text = ThreadIds.Count.ToString() + " Threads";
+ else
+ this.HeaderText.Text = "1 Thread";
+ }
+ }
+
+ private void SelectParent(bool isSelected)
+ {
+ var ts = this.ThreadStackParent;
+ while(ts != null) {
+ ts.IsSelected = isSelected;
+ ts = ts.ThreadStackParent;
+ }
+ }
+
+ void Datagrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
+ {
+ if (Process.IsRunning) return;
+
+ dynamic selectedItem = datagrid.SelectedItem;
+ if (selectedItem != null) {
+ if (ThreadIds.Count > 1) {
+ datagrid.ContextMenu = CreateContextMenu(selectedItem);
+ datagrid.ContextMenu.IsOpen = true;
+ }
+ else
+ {
+ SelectFrame(ThreadIds[0], selectedItem);
+ }
+ }
+ }
+
+ void SelectFrame(uint threadId, ExpandoObject selectedItem)
+ {
+ var thread = Process.Threads.Find(t => t.ID == threadId);
+ if (thread == null)
+ return;
+ dynamic obj = selectedItem;
+ Process.SelectedThread = thread;
+ foreach(var frame in thread.Callstack)
+ {
+ if (frame.GetMethodName() == obj.MethodName)
+ {
+ Process.SelectedThread.SelectedStackFrame = frame;
+ break;
+ }
+ }
+
+ Process.OnPaused();
+ }
+
+ void Datagrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ if (Process.IsRunning) return;
+
+ dynamic selectedItem = datagrid.SelectedItem;
+ if (selectedItem == null)
+ return;
+
+ datagrid.ContextMenu = CreateContextMenu(selectedItem);
+ datagrid.ContextMenu.IsOpen = true;
+ }
+
+ ContextMenu CreateContextMenu(ExpandoObject item)
+ {
+ dynamic obj = item;
+
+ var menu = new ContextMenu();
+ foreach (var id in ThreadIds)
+ {
+ MenuItem m = new MenuItem();
+ m.IsCheckable = true;
+ m.IsChecked = id == Process.SelectedThread.ID;
+ m.Checked += delegate(object sender, RoutedEventArgs e) {
+ var menuItem = e.OriginalSource as MenuItem;
+ SelectFrame((uint)menuItem.Tag, item);
+ };
+ m.Tag = id;
+ m.Header = id.ToString() + ":" + obj.MethodName;
+
+ menu.Items.Add(m);
+ }
+
+ return menu;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/RunningThreadsPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/RunningThreadsPad.cs
index 07998ff443..8feb17ef46 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Pads/RunningThreadsPad.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/RunningThreadsPad.cs
@@ -3,7 +3,6 @@
using System;
using System.Dynamic;
-using System.Threading;
using System.Windows;
using System.Windows.Data;
@@ -133,9 +132,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
thread.NameChanged += delegate {
RefreshItem(obj);
};
- thread.Exited += delegate {
- RemoveThread(obj);
- };
+ thread.Exited += (s, e) => RemoveThread(e.Thread);
}
void RefreshItem(ExpandoObject obj)
@@ -143,6 +140,9 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
dynamic item = obj;
var thread = item.Tag as Thread;
+ if (thread == null)
+ return;
+
item.ID = thread.ID;
item.Tag = thread;
StackFrame location = null;
@@ -156,19 +156,19 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
}
switch (thread.Priority) {
- case ThreadPriority.Highest:
+ case System.Threading.ThreadPriority.Highest:
item.Priority = ResourceService.GetString("MainWindow.Windows.Debug.Threads.Priority.Highest");
break;
- case ThreadPriority.AboveNormal:
+ case System.Threading.ThreadPriority.AboveNormal:
item.Priority = ResourceService.GetString("MainWindow.Windows.Debug.Threads.Priority.AboveNormal");
break;
- case ThreadPriority.Normal:
+ case System.Threading.ThreadPriority.Normal:
item.Priority = ResourceService.GetString("MainWindow.Windows.Debug.Threads.Priority.Normal");
break;
- case ThreadPriority.BelowNormal:
+ case System.Threading.ThreadPriority.BelowNormal:
item.Priority = ResourceService.GetString("MainWindow.Windows.Debug.Threads.Priority.BelowNormal");
break;
- case ThreadPriority.Lowest:
+ case System.Threading.ThreadPriority.Lowest:
item.Priority = ResourceService.GetString("MainWindow.Windows.Debug.Threads.Priority.Lowest");
break;
default:
@@ -180,6 +180,9 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
void RemoveThread(Thread thread)
{
+ if (thread == null || thread.HasExited)
+ return;
+
foreach (dynamic item in runningThreadsList.ItemCollection) {
if (thread.ID == item.ID) {
runningThreadsList.ItemCollection.Remove(item);
diff --git a/src/AddIns/Debugger/Debugger.Core/ThreadCollection.cs b/src/AddIns/Debugger/Debugger.Core/ThreadCollection.cs
index 9437a92a9f..500b52aa46 100644
--- a/src/AddIns/Debugger/Debugger.Core/ThreadCollection.cs
+++ b/src/AddIns/Debugger/Debugger.Core/ThreadCollection.cs
@@ -17,6 +17,20 @@ namespace Debugger
set { selected = value; }
}
+ public Thread Find(Predicate predicate)
+ {
+ if (predicate == null)
+ return null;
+
+ foreach (var thread in this)
+ {
+ if (predicate(thread))
+ return thread;
+ }
+
+ return null;
+ }
+
internal bool Contains(ICorDebugThread corThread)
{
foreach(Thread thread in this) {
diff --git a/src/Main/StartUp/Project/Resources/BitmapResources.resources b/src/Main/StartUp/Project/Resources/BitmapResources.resources
index 00029d6f0b..f3ce3bcf6a 100644
Binary files a/src/Main/StartUp/Project/Resources/BitmapResources.resources and b/src/Main/StartUp/Project/Resources/BitmapResources.resources differ