Browse Source
Conflicts: data/resources/StringResources.resx data/resources/image/BitmapResources/BitmapResources.res src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj src/AddIns/Debugger/Debugger.AddIn/Pads/CallStackPad.xaml.cs src/AddIns/Debugger/Debugger.AddIn/Pads/RunningThreadsPad.cs src/Main/StartUp/Project/Resources/BitmapResources.resourcespull/15/head
28 changed files with 1831 additions and 199 deletions
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 733 B |
@ -0,0 +1,92 @@ |
|||||||
|
// 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.Controls; |
||||||
|
using ICSharpCode.Core; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Gui.Pads |
||||||
|
{ |
||||||
|
public sealed class ShowZoomControlCommand : AbstractCheckableMenuCommand |
||||||
|
{ |
||||||
|
ParallelStackPad pad; |
||||||
|
|
||||||
|
public override object Owner { |
||||||
|
get { return base.Owner; } |
||||||
|
set { |
||||||
|
if (!(value is ParallelStackPad)) |
||||||
|
throw new Exception("Owner has to be a ParallelStackPad"); |
||||||
|
pad = value as ParallelStackPad; |
||||||
|
base.Owner = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsChecked { |
||||||
|
get { return pad.IsZoomControlVisible; } |
||||||
|
set { pad.IsZoomControlVisible = value; } |
||||||
|
} |
||||||
|
|
||||||
|
public override void Run() |
||||||
|
{ |
||||||
|
IsChecked = !IsChecked; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public sealed class ToggleMethodViewCommand : AbstractCheckableMenuCommand |
||||||
|
{ |
||||||
|
ParallelStackPad pad; |
||||||
|
|
||||||
|
public override object Owner { |
||||||
|
get { return base.Owner; } |
||||||
|
set { |
||||||
|
if (!(value is ParallelStackPad)) |
||||||
|
throw new Exception("Owner has to be a AbstractConsolePad"); |
||||||
|
pad = value as ParallelStackPad; |
||||||
|
base.Owner = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsChecked { |
||||||
|
get { return pad.IsMethodView; } |
||||||
|
set { pad.IsMethodView = value; } |
||||||
|
} |
||||||
|
|
||||||
|
public override void Run() |
||||||
|
{ |
||||||
|
IsChecked = !IsChecked; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public sealed class ParallelStacksViewCommand : AbstractComboBoxCommand |
||||||
|
{ |
||||||
|
ParallelStackPad pad; |
||||||
|
ComboBox box; |
||||||
|
|
||||||
|
protected override void OnOwnerChanged(EventArgs e) |
||||||
|
{ |
||||||
|
this.pad = this.Owner as ParallelStackPad; |
||||||
|
if (this.pad == null) |
||||||
|
return; |
||||||
|
|
||||||
|
box = this.ComboBox as ComboBox; |
||||||
|
|
||||||
|
if (this.box == null) |
||||||
|
return; |
||||||
|
|
||||||
|
foreach (var name in Enum.GetNames(typeof(ParallelStacksView))) |
||||||
|
box.Items.Add(name); |
||||||
|
|
||||||
|
box.SelectedIndex = 0; |
||||||
|
|
||||||
|
base.OnOwnerChanged(e); |
||||||
|
} |
||||||
|
|
||||||
|
public override void Run() |
||||||
|
{ |
||||||
|
if (this.pad != null && this.box != null) { |
||||||
|
pad.ParallelStacksView = (ParallelStacksView)Enum.Parse(typeof(ParallelStacksView), box.SelectedValue.ToString()); |
||||||
|
} |
||||||
|
base.Run(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,147 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<UserControl |
||||||
|
x:Class="Debugger.AddIn.Pads.ParallelPad.DrawSurface" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Debugger.AddIn.Pads.ParallelPad" |
||||||
|
xmlns:visControls="clr-namespace:Debugger.AddIn.Visualizers.Controls" |
||||||
|
Background="White"> |
||||||
|
<UserControl.Resources> |
||||||
|
<LinearGradientBrush |
||||||
|
x:Key="SilverBrushKey" |
||||||
|
EndPoint="0,1" |
||||||
|
StartPoint="0,0"> |
||||||
|
<LinearGradientBrush.GradientStops> |
||||||
|
<GradientStop |
||||||
|
Offset="0.7" |
||||||
|
Color="White" /> |
||||||
|
<GradientStop |
||||||
|
Offset="1" |
||||||
|
Color="LightGray" /> |
||||||
|
</LinearGradientBrush.GradientStops> |
||||||
|
</LinearGradientBrush> |
||||||
|
<LinearGradientBrush |
||||||
|
x:Key="SilverPressedBrushKey" |
||||||
|
EndPoint="0,1" |
||||||
|
StartPoint="0,0"> |
||||||
|
<LinearGradientBrush.GradientStops> |
||||||
|
<GradientStop |
||||||
|
Offset="0.3" |
||||||
|
Color="White" /> |
||||||
|
<GradientStop |
||||||
|
Offset="0" |
||||||
|
Color="LightGray" /> |
||||||
|
</LinearGradientBrush.GradientStops> |
||||||
|
</LinearGradientBrush> |
||||||
|
<ControlTemplate |
||||||
|
x:Key="ButtonTemplate" |
||||||
|
TargetType="Button"> |
||||||
|
<Border |
||||||
|
Width="28" |
||||||
|
Height="17" |
||||||
|
Name="TheBorder" |
||||||
|
CornerRadius="2,2,2,2" |
||||||
|
BorderThickness="1" |
||||||
|
BorderBrush="LightGray"> |
||||||
|
<Canvas> |
||||||
|
<TextBlock |
||||||
|
Canvas.Top="2" |
||||||
|
Canvas.Left="1" |
||||||
|
Text="Reset" |
||||||
|
FontSize="9" |
||||||
|
Foreground="LightGray" /> |
||||||
|
</Canvas> |
||||||
|
</Border> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger |
||||||
|
Property="ButtonBase.IsMouseOver" |
||||||
|
Value="True"> |
||||||
|
<Setter |
||||||
|
TargetName="TheBorder" |
||||||
|
Property="Background" |
||||||
|
Value="{StaticResource SilverBrushKey}" /> |
||||||
|
<Setter |
||||||
|
TargetName="TheBorder" |
||||||
|
Property="BorderBrush" |
||||||
|
Value="Silver" /> |
||||||
|
</Trigger> |
||||||
|
<Trigger |
||||||
|
Property="ButtonBase.IsPressed" |
||||||
|
Value="True"> |
||||||
|
<Setter |
||||||
|
TargetName="TheBorder" |
||||||
|
Property="Background" |
||||||
|
Value="{StaticResource SilverPressedBrushKey}" /> |
||||||
|
<Setter |
||||||
|
TargetName="TheBorder" |
||||||
|
Property="BorderBrush" |
||||||
|
Value="Silver" /> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
</UserControl.Resources> |
||||||
|
<Grid> |
||||||
|
<Grid.ColumnDefinitions> |
||||||
|
<ColumnDefinition |
||||||
|
Width="Auto" /> |
||||||
|
<ColumnDefinition |
||||||
|
Width="*" /> |
||||||
|
</Grid.ColumnDefinitions> |
||||||
|
<Canvas x:Name="ZoomControl" Width="40" Visibility="Hidden" |
||||||
|
Margin="3,3"> |
||||||
|
<TextBlock |
||||||
|
Foreground="LightGray" |
||||||
|
x:Name="PercentText" |
||||||
|
HorizontalAlignment="Center">100%</TextBlock> |
||||||
|
<Line |
||||||
|
X1="0" |
||||||
|
Y1="20" |
||||||
|
X2="28" |
||||||
|
Y2="20" |
||||||
|
StrokeThickness="2" |
||||||
|
Stroke="LightGray" /> |
||||||
|
<Slider |
||||||
|
Canvas.Top="23" |
||||||
|
Name="SliderControl" |
||||||
|
Ticks="1,2,3,4,5,6,7,8,9,10" |
||||||
|
Opacity=".4" |
||||||
|
Value="5" |
||||||
|
Interval="1" |
||||||
|
TickPlacement="BottomRight" |
||||||
|
Minimum="1" |
||||||
|
Maximum="10" |
||||||
|
Height="100" |
||||||
|
Width="30" |
||||||
|
Orientation="Vertical" |
||||||
|
ValueChanged="SliderControl_ValueChanged" /> |
||||||
|
<Line |
||||||
|
X1="0" |
||||||
|
Y1="125" |
||||||
|
X2="28" |
||||||
|
Y2="125" |
||||||
|
StrokeThickness="2" |
||||||
|
Stroke="LightGray" /> |
||||||
|
<Button |
||||||
|
Canvas.Top="130" |
||||||
|
Name="Reset" |
||||||
|
Click="Reset_Click" |
||||||
|
Template="{StaticResource ButtonTemplate}" /> |
||||||
|
</Canvas> |
||||||
|
|
||||||
|
<visControls:DragScrollViewer |
||||||
|
x:Name="drawingSurface" |
||||||
|
Grid.Column="1"> |
||||||
|
<Grid |
||||||
|
x:Name="ContentControl" |
||||||
|
VerticalAlignment="Center" |
||||||
|
HorizontalAlignment="Center"> |
||||||
|
<local:ParallelStacksGraphLayout |
||||||
|
x:Name="ParallelStacksLayout" |
||||||
|
ShowAllStates="False" |
||||||
|
LayoutAlgorithmType="Tree" |
||||||
|
IsAnimationEnabled="False" |
||||||
|
EdgeRoutingAlgorithmType="Automatic" |
||||||
|
EdgeRoutingConstraint="Automatic" |
||||||
|
OverlapRemovalConstraint="Automatic" |
||||||
|
OverlapRemovalAlgorithmType="FSA" /> |
||||||
|
</Grid> |
||||||
|
</visControls:DragScrollViewer> |
||||||
|
</Grid> |
||||||
|
</UserControl> |
||||||
@ -0,0 +1,71 @@ |
|||||||
|
// 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; |
||||||
|
using System.Windows.Shapes; |
||||||
|
|
||||||
|
using Microsoft.Windows.Themes; |
||||||
|
|
||||||
|
namespace Debugger.AddIn.Pads.ParallelPad |
||||||
|
{ |
||||||
|
public partial class DrawSurface : UserControl |
||||||
|
{ |
||||||
|
private ScaleTransform zoom = new ScaleTransform(); |
||||||
|
|
||||||
|
public DrawSurface() |
||||||
|
{ |
||||||
|
InitializeComponent(); |
||||||
|
|
||||||
|
ContentControl.LayoutTransform = zoom; |
||||||
|
} |
||||||
|
|
||||||
|
public void SetGraph(ParallelStacksGraph graph) |
||||||
|
{ |
||||||
|
this.ParallelStacksLayout.Graph = graph; |
||||||
|
|
||||||
|
if (graph == null) |
||||||
|
this.ParallelStacksLayout.CancelLayout(); |
||||||
|
else |
||||||
|
this.ParallelStacksLayout.Relayout(); |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsZoomControlVisible { |
||||||
|
get { return ZoomControl.Visibility == Visibility.Visible; } |
||||||
|
set { |
||||||
|
if (value) |
||||||
|
ZoomControl.Visibility = Visibility.Visible; |
||||||
|
else |
||||||
|
ZoomControl.Visibility = Visibility.Hidden; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#region Zoom
|
||||||
|
|
||||||
|
void SliderControl_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> 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
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,679 @@ |
|||||||
|
// 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; |
||||||
|
using Debugger.AddIn.Pads.ParallelPad; |
||||||
|
using Debugger.AddIn.TreeModel; |
||||||
|
using ICSharpCode.AvalonEdit.Rendering; |
||||||
|
using ICSharpCode.Core; |
||||||
|
using ICSharpCode.Core.Presentation; |
||||||
|
using ICSharpCode.NRefactory; |
||||||
|
using ICSharpCode.SharpDevelop.Bookmarks; |
||||||
|
using ICSharpCode.SharpDevelop.Debugging; |
||||||
|
using ICSharpCode.SharpDevelop.Editor; |
||||||
|
using ICSharpCode.SharpDevelop.Gui.Pads; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Gui.Pads |
||||||
|
{ |
||||||
|
public enum ParallelStacksView |
||||||
|
{ |
||||||
|
Threads, |
||||||
|
Tasks |
||||||
|
} |
||||||
|
|
||||||
|
public class ParallelStackPad : DebuggerPad |
||||||
|
{ |
||||||
|
private DrawSurface surface; |
||||||
|
private Process debuggedProcess; |
||||||
|
private ParallelStacksGraph graph; |
||||||
|
private List<ThreadStack> currentThreadStacks = new List<ThreadStack>(); |
||||||
|
private ParallelStacksView parallelStacksView; |
||||||
|
private StackFrame selectedFrame; |
||||||
|
private bool isMethodView; |
||||||
|
|
||||||
|
#region Overrides
|
||||||
|
|
||||||
|
protected override void InitializeComponents() |
||||||
|
{ |
||||||
|
surface = new DrawSurface(); |
||||||
|
|
||||||
|
panel.Children.Add(surface); |
||||||
|
} |
||||||
|
|
||||||
|
protected override void SelectProcess(Process process) |
||||||
|
{ |
||||||
|
if (debuggedProcess != null) { |
||||||
|
debuggedProcess.Paused -= OnProcessPaused; |
||||||
|
} |
||||||
|
debuggedProcess = process; |
||||||
|
if (debuggedProcess != null) { |
||||||
|
debuggedProcess.Paused += OnProcessPaused; |
||||||
|
} |
||||||
|
|
||||||
|
DebuggerService.DebugStarted += OnReset; |
||||||
|
DebuggerService.DebugStopped += OnReset; |
||||||
|
|
||||||
|
RefreshPad(); |
||||||
|
} |
||||||
|
|
||||||
|
public override void RefreshPad() |
||||||
|
{ |
||||||
|
if (debuggedProcess == null || debuggedProcess.IsRunning) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
LoggingService.InfoFormatted("Start refresh: {0}" + Environment.NewLine, parallelStacksView); |
||||||
|
|
||||||
|
currentThreadStacks.Clear(); |
||||||
|
|
||||||
|
using(new PrintTimes("Create stacks")) { |
||||||
|
try { |
||||||
|
// create all simple ThreadStacks
|
||||||
|
foreach (Thread thread in debuggedProcess.Threads) { |
||||||
|
if (debuggedProcess.IsPaused) { |
||||||
|
Utils.DoEvents(debuggedProcess); |
||||||
|
} |
||||||
|
CreateThreadStack(thread); |
||||||
|
} |
||||||
|
} |
||||||
|
catch(AbortedBecauseDebuggeeResumedException) { } |
||||||
|
catch(System.Exception) { |
||||||
|
if (debuggedProcess == null || debuggedProcess.HasExited) { |
||||||
|
// Process unexpectedly exited
|
||||||
|
} else { |
||||||
|
throw; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
using(new PrintTimes("Run algorithm")) { |
||||||
|
if (isMethodView) |
||||||
|
{ |
||||||
|
// build method view for threads
|
||||||
|
CreateMethodViewStacks(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// normal view
|
||||||
|
CreateCommonStacks(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
using(new PrintTimes("Graph refresh")) { |
||||||
|
// paint the ThreadStaks
|
||||||
|
graph = new ParallelStacksGraph(); |
||||||
|
foreach (var stack in this.currentThreadStacks.FindAll(ts => ts.ThreadStackParents == null )) |
||||||
|
{ |
||||||
|
graph.AddVertex(stack); |
||||||
|
|
||||||
|
// add the children
|
||||||
|
AddChildren(stack); |
||||||
|
} |
||||||
|
|
||||||
|
surface.SetGraph(graph); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected override ToolBar BuildToolBar() |
||||||
|
{ |
||||||
|
return ToolBarService.CreateToolBar(this, "/SharpDevelop/Pads/ParallelStacksPad/ToolBar"); |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Properties
|
||||||
|
|
||||||
|
public ParallelStacksView ParallelStacksView { |
||||||
|
get { return parallelStacksView; } |
||||||
|
set { |
||||||
|
parallelStacksView = value; |
||||||
|
RefreshPad(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsMethodView { |
||||||
|
get { return isMethodView; } |
||||||
|
set { |
||||||
|
isMethodView = value; |
||||||
|
RefreshPad(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsZoomControlVisible { |
||||||
|
get { return surface.IsZoomControlVisible; } |
||||||
|
set { surface.IsZoomControlVisible = value; } |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Methods
|
||||||
|
|
||||||
|
private void OnReset(object sender, EventArgs e) |
||||||
|
{ |
||||||
|
currentThreadStacks.Clear(); |
||||||
|
selectedFrame = null; |
||||||
|
|
||||||
|
// remove all
|
||||||
|
BookmarkManager.RemoveAll(b => b is SelectedFrameBookmark); |
||||||
|
} |
||||||
|
|
||||||
|
private void OnProcessPaused(object sender, ProcessEventArgs e) |
||||||
|
{ |
||||||
|
RefreshPad(); |
||||||
|
} |
||||||
|
|
||||||
|
private void AddChildren(ThreadStack parent) |
||||||
|
{ |
||||||
|
if(parent.ThreadStackChildren == null || parent.ThreadStackChildren.Count == 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.Count == 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<string, List<ThreadStack>>(); |
||||||
|
|
||||||
|
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 + stack.Level.ToString(); |
||||||
|
|
||||||
|
if (!commonFrameThreads.ContainsKey(fullname)) |
||||||
|
commonFrameThreads.Add(fullname, new List<ThreadStack>()); |
||||||
|
|
||||||
|
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<uint>(); |
||||||
|
var parentItems = new Stack<ExpandoObject>(); |
||||||
|
|
||||||
|
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]); |
||||||
|
|
||||||
|
stack.ItemCollection.RemoveAt(indexToRemove); |
||||||
|
} |
||||||
|
|
||||||
|
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.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, threadIds.ToArray()); |
||||||
|
commonParent.ItemCollection = parentItems.ToObservable(); |
||||||
|
commonParent.Process = debuggedProcess; |
||||||
|
commonParent.StackSelected += OnThreadStackSelected; |
||||||
|
commonParent.FrameSelected += OnFrameSelected; |
||||||
|
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]; |
||||||
|
// add the parent to the parent
|
||||||
|
if (stack.ThreadStackParents != null) { |
||||||
|
// remove stack from it's parent because it will have the commonParent as parent
|
||||||
|
stack.ThreadStackParents[0].ThreadStackChildren.Remove(stack); |
||||||
|
commonParent.ThreadStackParents = stack.ThreadStackParents.Clone(); |
||||||
|
commonParent.ThreadStackParents[0].ThreadStackChildren.Add(commonParent); |
||||||
|
// set level
|
||||||
|
commonParent.Level = stack.Level - 1; |
||||||
|
} |
||||||
|
else |
||||||
|
stack.ThreadStackParents = new List<ThreadStack>(); |
||||||
|
|
||||||
|
// update thread ids
|
||||||
|
if (commonParent.ThreadStackParents != null) { |
||||||
|
commonParent.ThreadStackParents[0].UpdateThreadIds( |
||||||
|
parallelStacksView == ParallelStacksView.Tasks, |
||||||
|
commonParent.ThreadIds.ToArray()); |
||||||
|
} |
||||||
|
|
||||||
|
stack.ThreadStackParents.Clear(); |
||||||
|
stack.ThreadStackParents.Add(commonParent); |
||||||
|
string currentName = item.MethodName + stack.Level.ToString();; |
||||||
|
|
||||||
|
// add name or add to list
|
||||||
|
if (!commonFrameThreads.ContainsKey(currentName)) { |
||||||
|
var newList = new List<ThreadStack>(); |
||||||
|
newList.Add(stack); |
||||||
|
commonFrameThreads.Add(currentName, newList); |
||||||
|
} |
||||||
|
else { |
||||||
|
var list = commonFrameThreads[currentName]; |
||||||
|
list.Add(stack); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
commonParent.ThreadStackChildren = listOfCurrentStacks.Clone(); |
||||||
|
commonFrameThreads[frameName].Clear(); |
||||||
|
commonFrameThreads[frameName].Add(commonParent); |
||||||
|
currentThreadStacks.Add(commonParent); |
||||||
|
|
||||||
|
// exit and retry
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (isOver || currentThreadStacks.Count == 0) |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void CreateMethodViewStacks() |
||||||
|
{ |
||||||
|
var list = |
||||||
|
new List<Tuple<ObservableCollection<ExpandoObject>, ObservableCollection<ExpandoObject>, List<uint>>>(); |
||||||
|
|
||||||
|
// find all threadstacks that contains the selected frame
|
||||||
|
for (int i = currentThreadStacks.Count - 1; i >= 0; --i) { |
||||||
|
var tuple = currentThreadStacks[i].ItemCollection.SplitStack(selectedFrame, currentThreadStacks[i].ThreadIds); |
||||||
|
if (tuple != null) |
||||||
|
list.Add(tuple); |
||||||
|
} |
||||||
|
|
||||||
|
currentThreadStacks.Clear(); |
||||||
|
|
||||||
|
// common
|
||||||
|
ThreadStack common = new ThreadStack(); |
||||||
|
var observ = new ObservableCollection<ExpandoObject>(); |
||||||
|
bool dummy = false; |
||||||
|
dynamic obj = CreateItemForFrame(selectedFrame, ref dummy); |
||||||
|
obj.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source; |
||||||
|
observ.Add(obj); |
||||||
|
common.ItemCollection = observ; |
||||||
|
common.StackSelected += OnThreadStackSelected; |
||||||
|
common.FrameSelected += OnFrameSelected; |
||||||
|
common.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, selectedFrame.Thread.ID); |
||||||
|
common.Process = debuggedProcess; |
||||||
|
common.ThreadStackChildren = new List<ThreadStack>(); |
||||||
|
common.ThreadStackParents = new List<ThreadStack>(); |
||||||
|
|
||||||
|
// for all thread stacks, split them in 2 :
|
||||||
|
// one that invokes the method frame, second that get's invoked by current method frame
|
||||||
|
foreach (var tuple in list) { |
||||||
|
// add top
|
||||||
|
if (tuple.Item1.Count > 0) |
||||||
|
{ |
||||||
|
ThreadStack topStack = new ThreadStack(); |
||||||
|
topStack.ItemCollection = tuple.Item1; |
||||||
|
topStack.StackSelected += OnThreadStackSelected; |
||||||
|
topStack.FrameSelected += OnFrameSelected; |
||||||
|
topStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray()); |
||||||
|
topStack.Process = debuggedProcess; |
||||||
|
topStack.ThreadStackParents = new List<ThreadStack>(); |
||||||
|
topStack.ThreadStackParents.Add(common); |
||||||
|
|
||||||
|
currentThreadStacks.Add(topStack); |
||||||
|
common.ThreadStackChildren.Add(topStack); |
||||||
|
} |
||||||
|
|
||||||
|
// add bottom
|
||||||
|
if(tuple.Item2.Count > 0) |
||||||
|
{ |
||||||
|
ThreadStack bottomStack = new ThreadStack(); |
||||||
|
bottomStack.ItemCollection = tuple.Item2; |
||||||
|
bottomStack.StackSelected += OnThreadStackSelected; |
||||||
|
bottomStack.FrameSelected += OnFrameSelected; |
||||||
|
bottomStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray()); |
||||||
|
bottomStack.Process = debuggedProcess; |
||||||
|
bottomStack.ThreadStackChildren = new List<ThreadStack>(); |
||||||
|
bottomStack.ThreadStackChildren.Add(common); |
||||||
|
common.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray()); |
||||||
|
common.ThreadStackParents.Add(bottomStack); |
||||||
|
currentThreadStacks.Add(bottomStack); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
currentThreadStacks.Add(common); |
||||||
|
common.IsSelected = true; |
||||||
|
} |
||||||
|
|
||||||
|
private void CreateThreadStack(Thread thread) |
||||||
|
{ |
||||||
|
var items = CreateItems(thread); |
||||||
|
if (items == null || items.Count == 0) |
||||||
|
return; |
||||||
|
|
||||||
|
ThreadStack threadStack = new ThreadStack(); |
||||||
|
threadStack.StackSelected += OnThreadStackSelected; |
||||||
|
threadStack.FrameSelected += OnFrameSelected; |
||||||
|
threadStack.Process = debuggedProcess; |
||||||
|
threadStack.ItemCollection = items; |
||||||
|
threadStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, thread.ID); |
||||||
|
|
||||||
|
if (debuggedProcess.SelectedThread != null) { |
||||||
|
threadStack.IsSelected = threadStack.ThreadIds.Contains(debuggedProcess.SelectedThread.ID); |
||||||
|
if (selectedFrame == null) |
||||||
|
selectedFrame = debuggedProcess.SelectedStackFrame; |
||||||
|
} |
||||||
|
|
||||||
|
currentThreadStacks.Add(threadStack); |
||||||
|
} |
||||||
|
|
||||||
|
private ObservableCollection<ExpandoObject> CreateItems(Thread thread) |
||||||
|
{ |
||||||
|
bool lastItemIsExternalMethod = false; |
||||||
|
int noTasks = 0; |
||||||
|
var result = new ObservableCollection<ExpandoObject>(); |
||||||
|
var callstack = thread.GetCallstack(100); |
||||||
|
|
||||||
|
if (parallelStacksView == ParallelStacksView.Threads) { |
||||||
|
foreach (StackFrame frame in callstack) { |
||||||
|
dynamic obj = CreateItemForFrame(frame, ref lastItemIsExternalMethod); |
||||||
|
|
||||||
|
if (obj != null) |
||||||
|
result.Add(obj); |
||||||
|
} |
||||||
|
} else { |
||||||
|
for (int i = 0 ; i < callstack.Length; ++i) { |
||||||
|
StackFrame frame = callstack[i]; |
||||||
|
dynamic obj = CreateItemForFrame(frame, ref lastItemIsExternalMethod); |
||||||
|
|
||||||
|
if (frame.MethodInfo.FullName.IndexOf("System.Threading.Tasks.Task.ExecuteEntry") != -1) { |
||||||
|
noTasks++; |
||||||
|
} |
||||||
|
|
||||||
|
if (noTasks == 1) { |
||||||
|
if (frame.HasSymbols) { |
||||||
|
// create thread stack for the items collected until now
|
||||||
|
ThreadStack threadStack = new ThreadStack(); |
||||||
|
threadStack.StackSelected += OnThreadStackSelected; |
||||||
|
threadStack.FrameSelected += OnFrameSelected; |
||||||
|
threadStack.Process = debuggedProcess; |
||||||
|
threadStack.ItemCollection = result.Clone(); |
||||||
|
threadStack.UpdateThreadIds(true, frame.Thread.ID); |
||||||
|
|
||||||
|
if (debuggedProcess.SelectedThread != null) { |
||||||
|
threadStack.IsSelected = threadStack.ThreadIds.Contains(debuggedProcess.SelectedThread.ID); |
||||||
|
if (selectedFrame == null) |
||||||
|
selectedFrame = debuggedProcess.SelectedStackFrame; |
||||||
|
} |
||||||
|
|
||||||
|
currentThreadStacks.Add(threadStack); |
||||||
|
// reset
|
||||||
|
result.Clear(); |
||||||
|
noTasks = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (obj != null) |
||||||
|
result.Add(obj); |
||||||
|
} |
||||||
|
|
||||||
|
Utils.DoEvents(debuggedProcess); |
||||||
|
// return null if we are dealing with a simple thread
|
||||||
|
return noTasks == 0 ? null : result; |
||||||
|
} |
||||||
|
|
||||||
|
Utils.DoEvents(debuggedProcess); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
private ExpandoObject CreateItemForFrame(StackFrame frame, ref bool lastItemIsExternalMethod) |
||||||
|
{ |
||||||
|
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) return null; |
||||||
|
fullName = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ExternalMethods").Trim(); |
||||||
|
obj.FontWeight = FontWeights.Normal; |
||||||
|
obj.Foreground = Brushes.Gray; |
||||||
|
lastItemIsExternalMethod = true; |
||||||
|
} |
||||||
|
|
||||||
|
if (frame.Thread.SelectedStackFrame != null && |
||||||
|
frame.Thread.ID == debuggedProcess.SelectedThread.ID && |
||||||
|
frame.Thread.SelectedStackFrame.IP == frame.IP && |
||||||
|
frame.Thread.SelectedStackFrame.GetMethodName() == frame.GetMethodName()) { |
||||||
|
obj.Image = PresentationResourceService.GetImage("Bookmarks.CurrentLine").Source; |
||||||
|
obj.IsRunningStackFrame = true; |
||||||
|
} else { |
||||||
|
if (selectedFrame != null && frame.Thread.ID == selectedFrame.Thread.ID && |
||||||
|
frame.GetMethodName() == selectedFrame.GetMethodName()) |
||||||
|
obj.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source; |
||||||
|
else |
||||||
|
obj.Image = null; |
||||||
|
obj.IsRunningStackFrame = false; |
||||||
|
} |
||||||
|
|
||||||
|
obj.MethodName = fullName; |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
private void ToggleSelectedFrameBookmark(Location location) |
||||||
|
{ |
||||||
|
// remove all
|
||||||
|
BookmarkManager.RemoveAll(b => b is SelectedFrameBookmark); |
||||||
|
|
||||||
|
ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveContent as ITextEditorProvider; |
||||||
|
if (provider != null) { |
||||||
|
ITextEditor editor = provider.TextEditor; |
||||||
|
BookmarkManager.AddMark(new SelectedFrameBookmark(editor.FileName, location)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void OnThreadStackSelected(object sender, EventArgs e) |
||||||
|
{ |
||||||
|
foreach (var ts in this.currentThreadStacks) { |
||||||
|
if (ts.IsSelected) |
||||||
|
ts.IsSelected = false; |
||||||
|
ts.ClearImages(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void OnFrameSelected(object sender, FrameSelectedEventArgs e) |
||||||
|
{ |
||||||
|
selectedFrame = e.Item; |
||||||
|
|
||||||
|
ToggleSelectedFrameBookmark(e.Location); |
||||||
|
|
||||||
|
if (isMethodView) |
||||||
|
RefreshPad(); |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
} |
||||||
|
|
||||||
|
internal static class StackFrameExtensions |
||||||
|
{ |
||||||
|
internal static string GetMethodName(this StackFrame frame) |
||||||
|
{ |
||||||
|
if (frame == null) |
||||||
|
return null; |
||||||
|
|
||||||
|
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 List<T> Clone<T>(this List<T> listToClone) |
||||||
|
{ |
||||||
|
if (listToClone == null) |
||||||
|
return null; |
||||||
|
|
||||||
|
var result = new List<T>(); |
||||||
|
foreach (var item in listToClone) |
||||||
|
result.Add(item); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
internal static ObservableCollection<T> Clone<T>(this ObservableCollection<T> collectionToClone) |
||||||
|
{ |
||||||
|
if (collectionToClone == null) |
||||||
|
return null; |
||||||
|
|
||||||
|
var result = new ObservableCollection<T>(); |
||||||
|
foreach (var item in collectionToClone) |
||||||
|
result.Add(item); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
internal static ObservableCollection<T> ToObservable<T>(this Stack<T> stack) |
||||||
|
{ |
||||||
|
if (stack == null) |
||||||
|
throw new NullReferenceException("Stack is null!"); |
||||||
|
|
||||||
|
var result = new ObservableCollection<T>(); |
||||||
|
while (stack.Count > 0) |
||||||
|
result.Add(stack.Pop()); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
internal static Tuple<ObservableCollection<T>, ObservableCollection<T>, List<uint>> |
||||||
|
SplitStack<T>(this ObservableCollection<T> source, StackFrame frame, List<uint> threadIds) |
||||||
|
{ |
||||||
|
var bottom = new ObservableCollection<T>(); |
||||||
|
var top = new ObservableCollection<T>(); |
||||||
|
|
||||||
|
int found = 0; |
||||||
|
|
||||||
|
foreach (dynamic item in source) |
||||||
|
{ |
||||||
|
if (item.MethodName == frame.GetMethodName()) |
||||||
|
found = 1; |
||||||
|
|
||||||
|
if (found >= 1) { |
||||||
|
if(found > 1) |
||||||
|
bottom.Add(item); |
||||||
|
|
||||||
|
found++; |
||||||
|
} |
||||||
|
else |
||||||
|
top.Add(item); |
||||||
|
} |
||||||
|
|
||||||
|
var result = |
||||||
|
new Tuple<ObservableCollection<T>, ObservableCollection<T>, List<uint>>(top, bottom, threadIds); |
||||||
|
|
||||||
|
return found > 1 ? result : null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,38 @@ |
|||||||
|
// 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<ThreadStack> |
||||||
|
{ |
||||||
|
public ParallelStacksEdge(ThreadStack source, ThreadStack target) : base(source, target) |
||||||
|
{ } |
||||||
|
} |
||||||
|
|
||||||
|
public class ParallelStacksGraph : BidirectionalGraph<ThreadStack, ParallelStacksEdge> |
||||||
|
{ |
||||||
|
public ParallelStacksGraph() |
||||||
|
{ } |
||||||
|
} |
||||||
|
|
||||||
|
public class ParallelStacksGraphLayout : GraphLayout<ThreadStack, ParallelStacksEdge, ParallelStacksGraph> |
||||||
|
{ |
||||||
|
public ParallelStacksGraphLayout() |
||||||
|
{ |
||||||
|
// TODO : Replace the current tree layout with EfficientSugiyama layout when Direction is available for this type of layout
|
||||||
|
var par = new SimpleTreeLayoutParameters(); |
||||||
|
par.LayerGap = 30; |
||||||
|
par.VertexGap = 50; |
||||||
|
par.Direction = LayoutDirection.BottomToTop; |
||||||
|
par.SpanningTreeGeneration = SpanningTreeGeneration.DFS; |
||||||
|
|
||||||
|
this.LayoutParameters = par; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
// 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.Media; |
||||||
|
using ICSharpCode.Core; |
||||||
|
using ICSharpCode.NRefactory; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using ICSharpCode.SharpDevelop.Bookmarks; |
||||||
|
using ICSharpCode.SharpDevelop.Editor; |
||||||
|
|
||||||
|
namespace Debugger.AddIn.Pads.ParallelPad |
||||||
|
{ |
||||||
|
public class SelectedFrameBookmark : SDMarkerBookmark |
||||||
|
{ |
||||||
|
public static readonly IImage SelectedFrameImage = new ResourceServiceImage("Icons.48x48.CurrentFrame"); |
||||||
|
|
||||||
|
public SelectedFrameBookmark(FileName fileName, Location location) : base(fileName, location) |
||||||
|
{ |
||||||
|
this.IsVisibleInBookmarkPad = false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public override bool CanToggle { |
||||||
|
get { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public override IImage Image { |
||||||
|
get { |
||||||
|
return SelectedFrameImage; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="markerService"><inheritdoc/></param>
|
||||||
|
/// <returns><inheritdoc/></returns>
|
||||||
|
protected override ITextMarker CreateMarker(ITextMarkerService markerService) |
||||||
|
{ |
||||||
|
IDocumentLine line = this.Document.GetLine(this.LineNumber); |
||||||
|
ITextMarker marker = markerService.Create(line.Offset, line.Length); |
||||||
|
marker.BackgroundColor = Color.FromRgb(162, 208, 80); |
||||||
|
marker.ForegroundColor = Colors.White; |
||||||
|
return marker; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,132 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<UserControl |
||||||
|
x:Class="Debugger.AddIn.Pads.ParallelPad.ThreadStack" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||||
|
Width="Auto" |
||||||
|
Height="Auto" |
||||||
|
Background="Transparent"> |
||||||
|
<UserControl.Resources> |
||||||
|
<LinearGradientBrush x:Key="RowBackground" StartPoint="0.5,0" EndPoint="0.5,1"> |
||||||
|
<GradientStop Color="#F7F7F7" Offset="0.2"/> |
||||||
|
<GradientStop Color="#EAEAEA" Offset=".5"/> |
||||||
|
<GradientStop Color="#E5E5E5" Offset=".8"/> |
||||||
|
</LinearGradientBrush> |
||||||
|
</UserControl.Resources> |
||||||
|
<Border |
||||||
|
x:Name="BorderParent" |
||||||
|
BorderBrush="Black" |
||||||
|
BorderThickness="3" |
||||||
|
CornerRadius="5" |
||||||
|
Height="Auto"> |
||||||
|
<StackPanel |
||||||
|
Background="Transparent" |
||||||
|
Margin="-5"> |
||||||
|
<Border |
||||||
|
Height="23" |
||||||
|
Margin="5,5,5,0" |
||||||
|
CornerRadius="5,5,0,0" |
||||||
|
BorderBrush="Transparent"> |
||||||
|
<Border.Background> |
||||||
|
<LinearGradientBrush |
||||||
|
StartPoint="0,0.5" |
||||||
|
EndPoint="1,0.5"> |
||||||
|
<GradientStop |
||||||
|
Color="#FFE2F6FE" |
||||||
|
Offset="0" /> |
||||||
|
<GradientStop |
||||||
|
Color="White" |
||||||
|
Offset="1" /> |
||||||
|
</LinearGradientBrush> |
||||||
|
</Border.Background> |
||||||
|
<TextBlock |
||||||
|
VerticalAlignment="Center" |
||||||
|
x:Name="HeaderText" |
||||||
|
FontFamily="Khmer UI" |
||||||
|
FontSize="12" |
||||||
|
Margin="2" |
||||||
|
HorizontalAlignment="Center" /> |
||||||
|
</Border> |
||||||
|
<DataGrid |
||||||
|
Background="Transparent" |
||||||
|
Margin="5,0,5,5" |
||||||
|
x:Name="datagrid" |
||||||
|
VerticalScrollBarVisibility="Disabled" |
||||||
|
HorizontalScrollBarVisibility="Disabled" |
||||||
|
GridLinesVisibility="None" |
||||||
|
RowHeight="18" |
||||||
|
SelectionMode="Single" |
||||||
|
SelectionUnit="FullRow" |
||||||
|
ItemsSource="{Binding}" |
||||||
|
AutoGenerateColumns="False" |
||||||
|
CanUserAddRows="False" |
||||||
|
HeadersVisibility="None" |
||||||
|
BorderThickness="0" |
||||||
|
MouseDoubleClick="Datagrid_MouseDoubleClick" |
||||||
|
MouseRightButtonUp="Datagrid_MouseRightButtonUp"> |
||||||
|
<DataGrid.CellStyle> |
||||||
|
<Style |
||||||
|
TargetType="{x:Type DataGridCell}"> |
||||||
|
<Setter Property="Background" Value="White"/> |
||||||
|
<Setter |
||||||
|
Property="Focusable" |
||||||
|
Value="false" /> |
||||||
|
<Setter |
||||||
|
Property="BorderThickness" |
||||||
|
Value="0" /> |
||||||
|
<Style.Triggers> |
||||||
|
<Trigger |
||||||
|
Property="IsSelected" |
||||||
|
Value="True"> |
||||||
|
<!-- disable selection highlight --> |
||||||
|
<Setter |
||||||
|
Property="Foreground" |
||||||
|
Value="Black" /> |
||||||
|
<Setter |
||||||
|
Property="Background" |
||||||
|
Value="{x:Null}" /> |
||||||
|
</Trigger> |
||||||
|
</Style.Triggers> |
||||||
|
</Style> |
||||||
|
</DataGrid.CellStyle> |
||||||
|
<DataGrid.RowStyle> |
||||||
|
<Style TargetType="{x:Type DataGridRow}"> |
||||||
|
<Setter Property="Background" Value="White"></Setter> |
||||||
|
<Style.Triggers> |
||||||
|
<Trigger Property="IsMouseOver" Value="True"> |
||||||
|
<Setter Property="Background" Value="{StaticResource RowBackground}" /> |
||||||
|
</Trigger> |
||||||
|
</Style.Triggers> |
||||||
|
</Style> |
||||||
|
</DataGrid.RowStyle> |
||||||
|
<DataGrid.Columns> |
||||||
|
<DataGridTemplateColumn> |
||||||
|
<DataGridTemplateColumn.CellTemplate> |
||||||
|
<DataTemplate> |
||||||
|
<Border BorderBrush="Gray" BorderThickness="0,1,1,0" Width="25" Height="22"> |
||||||
|
<Image VerticalAlignment="Center" Margin="0,-5,0,0" Width="14" Height="14" HorizontalAlignment="Center" |
||||||
|
Source="{Binding Image}" /> |
||||||
|
</Border> |
||||||
|
</DataTemplate> |
||||||
|
</DataGridTemplateColumn.CellTemplate> |
||||||
|
</DataGridTemplateColumn> |
||||||
|
<DataGridTemplateColumn |
||||||
|
Width="Auto"> |
||||||
|
<DataGridTemplateColumn.CellTemplate> |
||||||
|
<DataTemplate> |
||||||
|
<Border BorderBrush="Gray" BorderThickness="0,1,0,0"> |
||||||
|
<TextBlock Margin="5,0,10,0" |
||||||
|
VerticalAlignment="Center" |
||||||
|
FontFamily="Khmer UI" |
||||||
|
FontSize="12" |
||||||
|
Text="{Binding Path=MethodName}" |
||||||
|
FontWeight="{Binding Path=FontWeight}" |
||||||
|
Foreground="{Binding Path=Foreground}" |
||||||
|
/> |
||||||
|
</Border> |
||||||
|
</DataTemplate> |
||||||
|
</DataGridTemplateColumn.CellTemplate> |
||||||
|
</DataGridTemplateColumn> |
||||||
|
</DataGrid.Columns> |
||||||
|
</DataGrid> |
||||||
|
</StackPanel> |
||||||
|
</Border> |
||||||
|
</UserControl> |
||||||
@ -0,0 +1,340 @@ |
|||||||
|
// 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; |
||||||
|
|
||||||
|
using ICSharpCode.Core.Presentation; |
||||||
|
using ICSharpCode.NRefactory; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using ICSharpCode.SharpDevelop.Gui.Pads; |
||||||
|
|
||||||
|
namespace Debugger.AddIn.Pads.ParallelPad |
||||||
|
{ |
||||||
|
public class FrameSelectedEventArgs : EventArgs |
||||||
|
{ |
||||||
|
public StackFrame Item { |
||||||
|
get; |
||||||
|
private set; |
||||||
|
} |
||||||
|
|
||||||
|
public Location Location { |
||||||
|
get; |
||||||
|
private set; |
||||||
|
} |
||||||
|
|
||||||
|
public FrameSelectedEventArgs(StackFrame item, Location location) |
||||||
|
{ |
||||||
|
Item = item; |
||||||
|
Location = location; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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()); |
||||||
|
|
||||||
|
public event EventHandler StackSelected; |
||||||
|
|
||||||
|
public event EventHandler<FrameSelectedEventArgs> FrameSelected; |
||||||
|
|
||||||
|
private ObservableCollection<ExpandoObject> itemCollection = new ObservableCollection<ExpandoObject>(); |
||||||
|
|
||||||
|
private ToolTip toolTip = new ToolTip(); |
||||||
|
private List<uint> threadIds = new List<uint>(); |
||||||
|
|
||||||
|
public ThreadStack() |
||||||
|
{ |
||||||
|
InitializeComponent(); |
||||||
|
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 { |
||||||
|
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 List<ThreadStack> ThreadStackParents { get; set; } |
||||||
|
|
||||||
|
public List<ThreadStack> ThreadStackChildren { get; set; } |
||||||
|
|
||||||
|
public List<uint> ThreadIds { |
||||||
|
get { |
||||||
|
return threadIds; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public ObservableCollection<ExpandoObject> ItemCollection { |
||||||
|
get { |
||||||
|
return itemCollection; |
||||||
|
} |
||||||
|
|
||||||
|
set { |
||||||
|
itemCollection = value; |
||||||
|
this.datagrid.ItemsSource = itemCollection; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
|
||||||
|
public void UpdateThreadIds(bool isTask, params uint[] threadIds) |
||||||
|
{ |
||||||
|
var list = new List<uint>(); |
||||||
|
foreach (uint id in threadIds) { |
||||||
|
if (!this.threadIds.Contains(id)) { |
||||||
|
list.Add(id); |
||||||
|
} |
||||||
|
} |
||||||
|
this.threadIds.AddRange(list); |
||||||
|
|
||||||
|
if (this.threadIds.Count > 1) { |
||||||
|
string suffix = isTask ? " Tasks" : " Threads"; |
||||||
|
this.HeaderText.Text = this.threadIds.Count.ToString() + suffix; |
||||||
|
} |
||||||
|
else { |
||||||
|
this.HeaderText.Text = isTask ? "1 Task" : "1 Thread"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void ClearImages() |
||||||
|
{ |
||||||
|
foreach(dynamic item in itemCollection) { |
||||||
|
if (!item.IsRunningStackFrame) |
||||||
|
item.Image = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Methods
|
||||||
|
|
||||||
|
private void SelectParent(bool isSelected) |
||||||
|
{ |
||||||
|
if (this.ThreadStackParents == null || this.ThreadStackParents.Count == 0) |
||||||
|
return; |
||||||
|
|
||||||
|
foreach (var ts in this.ThreadStackParents) |
||||||
|
if (ts != null) |
||||||
|
ts.IsSelected = isSelected; |
||||||
|
} |
||||||
|
|
||||||
|
void datagrid_PreviewMouseMove(object sender, MouseEventArgs e) |
||||||
|
{ |
||||||
|
var result = VisualTreeHelper.HitTest(this, e.GetPosition(this)); |
||||||
|
if (result != null) |
||||||
|
{ |
||||||
|
var row = TryFindParent<DataGridRow>(result.VisualHit); |
||||||
|
if (row != null) |
||||||
|
{ |
||||||
|
datagrid.SelectedItem = row.DataContext; |
||||||
|
e.Handled = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private 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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void SelectFrame(uint threadId, ExpandoObject selectedItem) |
||||||
|
{ |
||||||
|
if (selectedItem == null) |
||||||
|
return; |
||||||
|
|
||||||
|
var thread = Process.Threads.Find(t => t.ID == threadId); |
||||||
|
if (thread == null) |
||||||
|
return; |
||||||
|
|
||||||
|
if (StackSelected != null) |
||||||
|
StackSelected(this, EventArgs.Empty); |
||||||
|
|
||||||
|
this.IsSelected = true; |
||||||
|
|
||||||
|
dynamic obj = selectedItem; |
||||||
|
|
||||||
|
foreach(var frame in thread.Callstack) |
||||||
|
{ |
||||||
|
if (frame.GetMethodName() == obj.MethodName) |
||||||
|
{ |
||||||
|
if (!obj.IsRunningStackFrame) |
||||||
|
obj.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source; |
||||||
|
|
||||||
|
SourcecodeSegment nextStatement = frame.NextStatement; |
||||||
|
if (nextStatement != null) { |
||||||
|
var location = new Location(nextStatement.StartColumn, nextStatement.StartLine); |
||||||
|
FileService.JumpToFilePosition( |
||||||
|
nextStatement.Filename, location.Line, location.Column); |
||||||
|
|
||||||
|
if (FrameSelected != null) |
||||||
|
FrameSelected(this, new FrameSelectedEventArgs(frame, location)); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private 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; |
||||||
|
} |
||||||
|
|
||||||
|
private 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.Click += 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; |
||||||
|
} |
||||||
|
|
||||||
|
private void OnToolTipOpening(object sender, ToolTipEventArgs e) |
||||||
|
{ |
||||||
|
if (Process.IsRunning) |
||||||
|
return; |
||||||
|
|
||||||
|
StackPanel panel = new StackPanel(); |
||||||
|
|
||||||
|
dynamic selectedItem = datagrid.SelectedItem; |
||||||
|
if (selectedItem == null) { |
||||||
|
panel.Children.Add(new TextBlock { Text = "No item selected" }); |
||||||
|
this.toolTip.Content = panel; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
foreach(var thread in Process.Threads) |
||||||
|
{ |
||||||
|
if (ThreadIds.Contains(thread.ID)) |
||||||
|
{ |
||||||
|
foreach (var frame in thread.Callstack) |
||||||
|
{ |
||||||
|
if (selectedItem.MethodName == frame.GetMethodName()) |
||||||
|
{ |
||||||
|
TextBlock tb = new TextBlock(); |
||||||
|
tb.Text = thread.ID + ": " + CallStackPadContent.GetFullName(frame); |
||||||
|
panel.Children.Add(tb); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
this.toolTip.Content = panel; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Static Methods
|
||||||
|
|
||||||
|
private static T TryFindParent<T>(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<T>(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
|
||||||
|
} |
||||||
|
} |
||||||
Binary file not shown.
Loading…
Reference in new issue