14 changed files with 1386 additions and 434 deletions
After Width: | Height: | Size: 3.9 KiB |
@ -0,0 +1,133 @@
@@ -0,0 +1,133 @@
|
||||
<?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" |
||||
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="100%" |
||||
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="40" /> |
||||
<ColumnDefinition |
||||
Width="*" /> |
||||
</Grid.ColumnDefinitions> |
||||
<Canvas |
||||
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> |
||||
<Canvas x:Name="drawingSurface" Grid.Column="1"> |
||||
<local:ParallelStacksGraphLayout |
||||
x:Name="ParallelStacksLayout" |
||||
LayoutAlgorithmType = "Tree" |
||||
OverlapRemovalAlgorithmType = "FSA"/> |
||||
</Canvas> |
||||
</Grid> |
||||
</UserControl> |
@ -0,0 +1,105 @@
@@ -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<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,357 @@
@@ -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<ThreadStack> currentThreadStacks = new List<ThreadStack>(); |
||||
|
||||
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<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; |
||||
|
||||
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>(); |
||||
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<ThreadStack>(); |
||||
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<uint>(); |
||||
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<ExpandoObject> CreateItems(Thread thread) |
||||
{ |
||||
bool lastItemIsExternalMethod = false; |
||||
var result = new ObservableCollection<ExpandoObject>(); |
||||
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<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; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,37 @@
@@ -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<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() |
||||
{ |
||||
var par = new SimpleTreeLayoutParameters(); |
||||
par.LayerGap = 30; |
||||
par.VertexGap = 50; |
||||
par.Direction = LayoutDirection.BottomToTop; |
||||
par.SpanningTreeGeneration = SpanningTreeGeneration.DFS; |
||||
|
||||
this.LayoutParameters = par; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,113 @@
@@ -0,0 +1,113 @@
|
||||
<?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"> |
||||
<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" |
||||
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.Columns> |
||||
<DataGridTemplateColumn> |
||||
<DataGridTemplateColumn.CellTemplate> |
||||
<DataTemplate> |
||||
<Border BorderBrush="Gray" BorderThickness="0,1,1,0" Width="25" Height="22"> |
||||
<Image VerticalAlignment="Center" 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,154 @@
@@ -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<ExpandoObject> itemCollection = new ObservableCollection<ExpandoObject>(); |
||||
|
||||
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<uint> ThreadIds { get; set; } |
||||
|
||||
public ObservableCollection<ExpandoObject> 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; |
||||
} |
||||
} |
||||
} |
Binary file not shown.
Loading…
Reference in new issue