mirror of https://github.com/icsharpcode/ILSpy.git
26 changed files with 2242 additions and 11 deletions
@ -0,0 +1,120 @@ |
|||||||
|
// 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.Text; |
||||||
|
|
||||||
|
using Debugger; |
||||||
|
using ICSharpCode.NRefactory.Ast; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Models.TreeModel |
||||||
|
{ |
||||||
|
public partial class Utils |
||||||
|
{ |
||||||
|
public static IEnumerable<TreeNode> LazyGetChildNodesOfArray(Expression expression, ArrayDimensions dimensions) |
||||||
|
{ |
||||||
|
if (dimensions.TotalElementCount == 0) |
||||||
|
return new TreeNode[] { new TreeNode(null, "(empty)", null, null, null) }; |
||||||
|
|
||||||
|
return new ArrayRangeNode(expression, dimensions, dimensions).ChildNodes; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> This is a partent node for all elements within a given bounds </summary>
|
||||||
|
public class ArrayRangeNode: TreeNode |
||||||
|
{ |
||||||
|
const int MaxElementCount = 100; |
||||||
|
|
||||||
|
Expression arrayTarget; |
||||||
|
ArrayDimensions bounds; |
||||||
|
ArrayDimensions originalBounds; |
||||||
|
|
||||||
|
public ArrayRangeNode(Expression arrayTarget, ArrayDimensions bounds, ArrayDimensions originalBounds) |
||||||
|
{ |
||||||
|
this.arrayTarget = arrayTarget; |
||||||
|
this.bounds = bounds; |
||||||
|
this.originalBounds = originalBounds; |
||||||
|
|
||||||
|
this.Name = GetName(); |
||||||
|
this.ChildNodes = LazyGetChildren(); |
||||||
|
} |
||||||
|
|
||||||
|
string GetName() |
||||||
|
{ |
||||||
|
StringBuilder name = new StringBuilder(); |
||||||
|
bool isFirst = true; |
||||||
|
name.Append("["); |
||||||
|
for(int i = 0; i < bounds.Count; i++) { |
||||||
|
if (!isFirst) name.Append(", "); |
||||||
|
isFirst = false; |
||||||
|
ArrayDimension dim = bounds[i]; |
||||||
|
ArrayDimension originalDim = originalBounds[i]; |
||||||
|
|
||||||
|
if (dim.Count == 0) { |
||||||
|
throw new DebuggerException("Empty dimension"); |
||||||
|
} else if (dim.Count == 1) { |
||||||
|
name.Append(dim.LowerBound.ToString()); |
||||||
|
} else if (dim.Equals(originalDim)) { |
||||||
|
name.Append("*"); |
||||||
|
} else { |
||||||
|
name.Append(dim.LowerBound); |
||||||
|
name.Append(".."); |
||||||
|
name.Append(dim.UpperBound); |
||||||
|
} |
||||||
|
} |
||||||
|
name.Append("]"); |
||||||
|
return name.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
static string GetName(int[] indices) |
||||||
|
{ |
||||||
|
StringBuilder sb = new StringBuilder(indices.Length * 4); |
||||||
|
sb.Append("["); |
||||||
|
bool isFirst = true; |
||||||
|
foreach(int index in indices) { |
||||||
|
if (!isFirst) sb.Append(", "); |
||||||
|
sb.Append(index.ToString()); |
||||||
|
isFirst = false; |
||||||
|
} |
||||||
|
sb.Append("]"); |
||||||
|
return sb.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
IEnumerable<TreeNode> LazyGetChildren() |
||||||
|
{ |
||||||
|
// The whole array is small - just add all elements as childs
|
||||||
|
if (bounds.TotalElementCount <= MaxElementCount) { |
||||||
|
foreach(int[] indices in bounds.Indices) { |
||||||
|
string imageName; |
||||||
|
var image = ExpressionNode.GetImageForArrayIndexer(out imageName); |
||||||
|
var expression = new ExpressionNode(image, GetName(indices), arrayTarget.AppendIndexer(indices)); |
||||||
|
expression.ImageName = imageName; |
||||||
|
yield return expression; |
||||||
|
} |
||||||
|
yield break; |
||||||
|
} |
||||||
|
|
||||||
|
// Find a dimension of size at least 2
|
||||||
|
int splitDimensionIndex = bounds.Count - 1; |
||||||
|
for(int i = 0; i < bounds.Count; i++) { |
||||||
|
if (bounds[i].Count > 1) { |
||||||
|
splitDimensionIndex = i; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
ArrayDimension splitDim = bounds[splitDimensionIndex]; |
||||||
|
|
||||||
|
// Split the dimension
|
||||||
|
int elementsPerSegment = 1; |
||||||
|
while (splitDim.Count > elementsPerSegment * MaxElementCount) { |
||||||
|
elementsPerSegment *= MaxElementCount; |
||||||
|
} |
||||||
|
for(int i = splitDim.LowerBound; i <= splitDim.UpperBound; i += elementsPerSegment) { |
||||||
|
List<ArrayDimension> newDims = new List<ArrayDimension>(bounds); |
||||||
|
newDims[splitDimensionIndex] = new ArrayDimension(i, Math.Min(i + elementsPerSegment - 1, splitDim.UpperBound)); |
||||||
|
yield return new ArrayRangeNode(arrayTarget, new ArrayDimensions(newDims), originalBounds); |
||||||
|
} |
||||||
|
yield break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,153 @@ |
|||||||
|
// 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.Collections; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Reflection; |
||||||
|
|
||||||
|
using Debugger; |
||||||
|
using Debugger.MetaData; |
||||||
|
using ICSharpCode.Core; |
||||||
|
using ICSharpCode.NRefactory.Ast; |
||||||
|
using ILSpy.Debugger.Services; |
||||||
|
using ILSpy.Debugger.Services.Debugger; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Models.TreeModel |
||||||
|
{ |
||||||
|
public partial class Utils |
||||||
|
{ |
||||||
|
public static IEnumerable<TreeNode> LazyGetChildNodesOfObject(Expression targetObject, DebugType shownType) |
||||||
|
{ |
||||||
|
MemberInfo[] publicStatic = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); |
||||||
|
MemberInfo[] publicInstance = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); |
||||||
|
MemberInfo[] nonPublicStatic = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly); |
||||||
|
MemberInfo[] nonPublicInstance = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); |
||||||
|
|
||||||
|
DebugType baseType = (DebugType)shownType.BaseType; |
||||||
|
if (baseType != null) { |
||||||
|
yield return new TreeNode( |
||||||
|
ImageService.GetImage("Icons.16x16.Class"), |
||||||
|
"BaseClass", |
||||||
|
baseType.Name, |
||||||
|
baseType.FullName, |
||||||
|
baseType.FullName == "System.Object" ? null : Utils.LazyGetChildNodesOfObject(targetObject, baseType) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if (nonPublicInstance.Length > 0) { |
||||||
|
yield return new TreeNode( |
||||||
|
null, |
||||||
|
"NonPublicMembers", |
||||||
|
string.Empty, |
||||||
|
string.Empty, |
||||||
|
Utils.LazyGetMembersOfObject(targetObject, nonPublicInstance) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if (publicStatic.Length > 0 || nonPublicStatic.Length > 0) { |
||||||
|
IEnumerable<TreeNode> childs = Utils.LazyGetMembersOfObject(targetObject, publicStatic); |
||||||
|
if (nonPublicStatic.Length > 0) { |
||||||
|
TreeNode nonPublicStaticNode = new TreeNode( |
||||||
|
null, |
||||||
|
"NonPublicStaticMembers", |
||||||
|
string.Empty, |
||||||
|
string.Empty, |
||||||
|
Utils.LazyGetMembersOfObject(targetObject, nonPublicStatic) |
||||||
|
); |
||||||
|
childs = Utils.PrependNode(nonPublicStaticNode, childs); |
||||||
|
} |
||||||
|
yield return new TreeNode( |
||||||
|
null, |
||||||
|
"StaticMembers", |
||||||
|
string.Empty, |
||||||
|
string.Empty, |
||||||
|
childs |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
DebugType iListType = (DebugType)shownType.GetInterface(typeof(IList).FullName); |
||||||
|
if (iListType != null) { |
||||||
|
yield return new IListNode(targetObject); |
||||||
|
} else { |
||||||
|
DebugType iEnumerableType, itemType; |
||||||
|
if (shownType.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) { |
||||||
|
yield return new IEnumerableNode(targetObject, itemType); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
foreach(TreeNode node in LazyGetMembersOfObject(targetObject, publicInstance)) { |
||||||
|
yield return node; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static IEnumerable<TreeNode> LazyGetMembersOfObject(Expression expression, MemberInfo[] members) |
||||||
|
{ |
||||||
|
List<TreeNode> nodes = new List<TreeNode>(); |
||||||
|
foreach(MemberInfo memberInfo in members) { |
||||||
|
string imageName; |
||||||
|
var image = ExpressionNode.GetImageForMember((IDebugMemberInfo)memberInfo, out imageName); |
||||||
|
var exp = new ExpressionNode(image, memberInfo.Name, expression.AppendMemberReference((IDebugMemberInfo)memberInfo)); |
||||||
|
exp.ImageName = imageName; |
||||||
|
nodes.Add(exp); |
||||||
|
} |
||||||
|
nodes.Sort(); |
||||||
|
return nodes; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static IEnumerable<TreeNode> LazyGetItemsOfIList(Expression targetObject) |
||||||
|
{ |
||||||
|
// This is needed for expanding IEnumerable<T>
|
||||||
|
targetObject = new CastExpression( |
||||||
|
new TypeReference(typeof(IList).FullName), |
||||||
|
targetObject, |
||||||
|
CastType.Cast |
||||||
|
); |
||||||
|
int count = 0; |
||||||
|
GetValueException error = null; |
||||||
|
try { |
||||||
|
count = GetIListCount(targetObject); |
||||||
|
} catch (GetValueException e) { |
||||||
|
// Cannot yield a value in the body of a catch clause (CS1631)
|
||||||
|
error = e; |
||||||
|
} |
||||||
|
if (error != null) { |
||||||
|
yield return new TreeNode(null, "(error)", error.Message, null, null); |
||||||
|
} else if (count == 0) { |
||||||
|
yield return new TreeNode(null, "(empty)", null, null, null); |
||||||
|
} else { |
||||||
|
for(int i = 0; i < count; i++) { |
||||||
|
string imageName; |
||||||
|
var image = ExpressionNode.GetImageForArrayIndexer(out imageName); |
||||||
|
var expression = new ExpressionNode(image, "[" + i + "]", targetObject.AppendIndexer(i)); |
||||||
|
expression.ImageName = imageName; |
||||||
|
yield return expression; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates System.Collections.ICollection.Count property on given object.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="GetValueException">Evaluating System.Collections.ICollection.Count on targetObject failed.</exception>
|
||||||
|
public static int GetIListCount(Expression targetObject) |
||||||
|
{ |
||||||
|
Value list = targetObject.Evaluate(WindowsDebugger.CurrentProcess); |
||||||
|
var iCollectionInterface = list.Type.GetInterface(typeof(ICollection).FullName); |
||||||
|
if (iCollectionInterface == null) |
||||||
|
throw new GetValueException(targetObject, targetObject.PrettyPrint() + " does not implement System.Collections.ICollection"); |
||||||
|
PropertyInfo countProperty = iCollectionInterface.GetProperty("Count"); |
||||||
|
// Do not get string representation since it can be printed in hex
|
||||||
|
return (int)list.GetPropertyValue(countProperty).PrimitiveValue; |
||||||
|
} |
||||||
|
|
||||||
|
public static IEnumerable<TreeNode> PrependNode(TreeNode node, IEnumerable<TreeNode> rest) |
||||||
|
{ |
||||||
|
yield return node; |
||||||
|
if (rest != null) { |
||||||
|
foreach(TreeNode absNode in rest) { |
||||||
|
yield return absNode; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,420 @@ |
|||||||
|
// 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.ComponentModel; |
||||||
|
using System.Globalization; |
||||||
|
using System.Reflection; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Text; |
||||||
|
using System.Windows.Media; |
||||||
|
|
||||||
|
using Debugger; |
||||||
|
using Debugger.Interop.CorDebug; |
||||||
|
using Debugger.MetaData; |
||||||
|
using ICSharpCode.NRefactory.Ast; |
||||||
|
using ILSpy.Debugger.Services; |
||||||
|
using ILSpy.Debugger.Services.Debugger; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Models.TreeModel |
||||||
|
{ |
||||||
|
public class ExpressionNode: TreeNode, ISetText, INotifyPropertyChanged |
||||||
|
{ |
||||||
|
bool evaluated; |
||||||
|
|
||||||
|
Expression expression; |
||||||
|
bool canSetText; |
||||||
|
GetValueException error; |
||||||
|
string fullText; |
||||||
|
|
||||||
|
public bool Evaluated { |
||||||
|
get { return evaluated; } |
||||||
|
set { evaluated = value; } |
||||||
|
} |
||||||
|
|
||||||
|
public Expression Expression { |
||||||
|
get { return expression; } |
||||||
|
} |
||||||
|
|
||||||
|
public override bool CanSetText { |
||||||
|
get { |
||||||
|
if (!evaluated) EvaluateExpression(); |
||||||
|
return canSetText; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public GetValueException Error { |
||||||
|
get { |
||||||
|
if (!evaluated) EvaluateExpression(); |
||||||
|
return error; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public string FullText { |
||||||
|
get { return fullText; } |
||||||
|
} |
||||||
|
|
||||||
|
public override string Text { |
||||||
|
get { |
||||||
|
if (!evaluated) EvaluateExpression(); |
||||||
|
return base.Text; |
||||||
|
} |
||||||
|
set { |
||||||
|
if (value != base.Text) { |
||||||
|
base.Text = value; |
||||||
|
NotifyPropertyChanged("Text"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override string FullName { |
||||||
|
get { |
||||||
|
if (!evaluated) EvaluateExpression(); |
||||||
|
|
||||||
|
return this.expression.PrettyPrint() ?? Name.Trim(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override string Type { |
||||||
|
get { |
||||||
|
if (!evaluated) EvaluateExpression(); |
||||||
|
return base.Type; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override IEnumerable<TreeNode> ChildNodes { |
||||||
|
get { |
||||||
|
if (!evaluated) EvaluateExpression(); |
||||||
|
return base.ChildNodes; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override bool HasChildNodes { |
||||||
|
get { |
||||||
|
if (!evaluated) EvaluateExpression(); |
||||||
|
return base.HasChildNodes; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> Used to determine available VisualizerCommands </summary>
|
||||||
|
private DebugType expressionType; |
||||||
|
/// <summary> Used to determine available VisualizerCommands </summary>
|
||||||
|
private bool valueIsNull = true; |
||||||
|
|
||||||
|
private IEnumerable<IVisualizerCommand> visualizerCommands; |
||||||
|
public override IEnumerable<IVisualizerCommand> VisualizerCommands { |
||||||
|
get { |
||||||
|
if (visualizerCommands == null) { |
||||||
|
visualizerCommands = getAvailableVisualizerCommands(); |
||||||
|
} |
||||||
|
return visualizerCommands; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private IEnumerable<IVisualizerCommand> getAvailableVisualizerCommands() |
||||||
|
{ |
||||||
|
if (!evaluated) EvaluateExpression(); |
||||||
|
|
||||||
|
if (this.expressionType == null) { |
||||||
|
// no visualizers if EvaluateExpression failed
|
||||||
|
yield break; |
||||||
|
} |
||||||
|
if (this.valueIsNull) { |
||||||
|
// no visualizers if evaluated value is null
|
||||||
|
yield break; |
||||||
|
} |
||||||
|
/*if (this.expressionType.IsPrimitive || this.expressionType.IsSystemDotObject() || this.expressionType.IsEnum()) { |
||||||
|
// no visualizers for primitive types
|
||||||
|
yield break; |
||||||
|
}*/ |
||||||
|
|
||||||
|
yield break; |
||||||
|
// foreach (var descriptor in VisualizerDescriptors.GetAllDescriptors()) {
|
||||||
|
// if (descriptor.IsVisualizerAvailable(this.expressionType)) {
|
||||||
|
// yield return descriptor.CreateVisualizerCommand(this.Expression);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} |
||||||
|
|
||||||
|
public ExpressionNode(ImageSource image, string name, Expression expression) |
||||||
|
{ |
||||||
|
this.ImageSource = image; |
||||||
|
this.Name = name; |
||||||
|
this.expression = expression; |
||||||
|
} |
||||||
|
|
||||||
|
void EvaluateExpression() |
||||||
|
{ |
||||||
|
evaluated = true; |
||||||
|
|
||||||
|
Value val; |
||||||
|
try { |
||||||
|
val = expression.Evaluate(WindowsDebugger.DebuggedProcess); |
||||||
|
} catch (GetValueException e) { |
||||||
|
error = e; |
||||||
|
this.Text = e.Message; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.canSetText = val.Type.IsPrimitive; |
||||||
|
|
||||||
|
this.expressionType = val.Type; |
||||||
|
this.Type = val.Type.Name; |
||||||
|
this.valueIsNull = val.IsNull; |
||||||
|
|
||||||
|
// Note that these return enumerators so they are lazy-evaluated
|
||||||
|
if (val.IsNull) { |
||||||
|
} else if (val.Type.IsPrimitive || val.Type.FullName == typeof(string).FullName) { // Must be before IsClass
|
||||||
|
} else if (val.Type.IsArray) { // Must be before IsClass
|
||||||
|
if (val.ArrayLength > 0) |
||||||
|
this.ChildNodes = Utils.LazyGetChildNodesOfArray(this.Expression, val.ArrayDimensions); |
||||||
|
} else if (val.Type.IsClass || val.Type.IsValueType) { |
||||||
|
if (val.Type.FullNameWithoutGenericArguments == typeof(List<>).FullName) { |
||||||
|
if ((int)val.GetMemberValue("_size").PrimitiveValue > 0) |
||||||
|
this.ChildNodes = Utils.LazyGetItemsOfIList(this.expression); |
||||||
|
} else { |
||||||
|
this.ChildNodes = Utils.LazyGetChildNodesOfObject(this.Expression, val.Type); |
||||||
|
} |
||||||
|
} else if (val.Type.IsPointer) { |
||||||
|
Value deRef = val.Dereference(); |
||||||
|
if (deRef != null) { |
||||||
|
this.ChildNodes = new ExpressionNode [] { new ExpressionNode(this.ImageSource, "*" + this.Name, this.Expression.AppendDereference()) }; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (true) { |
||||||
|
TreeNode info = ICorDebug.GetDebugInfoRoot(val.AppDomain, val.CorValue); |
||||||
|
this.ChildNodes = Utils.PrependNode(info, this.ChildNodes); |
||||||
|
} |
||||||
|
|
||||||
|
// Do last since it may expire the object
|
||||||
|
if (val.Type.IsInteger) { |
||||||
|
fullText = FormatInteger(val.PrimitiveValue); |
||||||
|
} else if (val.Type.IsPointer) { |
||||||
|
fullText = String.Format("0x{0:X}", val.PointerAddress); |
||||||
|
} else if ((val.Type.FullName == typeof(string).FullName || |
||||||
|
val.Type.FullName == typeof(char).FullName) && !val.IsNull) { |
||||||
|
try { |
||||||
|
fullText = '"' + Escape(val.InvokeToString()) + '"'; |
||||||
|
} catch (GetValueException e) { |
||||||
|
error = e; |
||||||
|
fullText = e.Message; |
||||||
|
return; |
||||||
|
} |
||||||
|
} else if ((val.Type.IsClass || val.Type.IsValueType) && !val.IsNull) { |
||||||
|
try { |
||||||
|
fullText = val.InvokeToString(); |
||||||
|
} catch (GetValueException e) { |
||||||
|
error = e; |
||||||
|
fullText = e.Message; |
||||||
|
return; |
||||||
|
} |
||||||
|
} else { |
||||||
|
fullText = val.AsString(); |
||||||
|
} |
||||||
|
|
||||||
|
this.Text = (fullText.Length > 256) ? fullText.Substring(0, 256) + "..." : fullText; |
||||||
|
} |
||||||
|
|
||||||
|
string Escape(string source) |
||||||
|
{ |
||||||
|
return source.Replace("\n", "\\n") |
||||||
|
.Replace("\t", "\\t") |
||||||
|
.Replace("\r", "\\r") |
||||||
|
.Replace("\0", "\\0") |
||||||
|
.Replace("\b", "\\b") |
||||||
|
.Replace("\a", "\\a") |
||||||
|
.Replace("\f", "\\f") |
||||||
|
.Replace("\v", "\\v") |
||||||
|
.Replace("\"", "\\\""); |
||||||
|
} |
||||||
|
|
||||||
|
string FormatInteger(object i) |
||||||
|
{ |
||||||
|
if (true) |
||||||
|
return i.ToString(); |
||||||
|
|
||||||
|
string hex = null; |
||||||
|
for(int len = 1;; len *= 2) { |
||||||
|
hex = string.Format("{0:X" + len + "}", i); |
||||||
|
if (hex.Length == len) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (true) { |
||||||
|
return "0x" + hex; |
||||||
|
} else { |
||||||
|
if (ShowAsHex(i)) { |
||||||
|
return String.Format("{0} (0x{1})", i, hex); |
||||||
|
} else { |
||||||
|
return i.ToString(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool ShowAsHex(object i) |
||||||
|
{ |
||||||
|
ulong val; |
||||||
|
if (i is sbyte || i is short || i is int || i is long) { |
||||||
|
unchecked { val = (ulong)Convert.ToInt64(i); } |
||||||
|
if (val > (ulong)long.MaxValue) |
||||||
|
val = ~val + 1; |
||||||
|
} else { |
||||||
|
val = Convert.ToUInt64(i); |
||||||
|
} |
||||||
|
if (val >= 0x10000) |
||||||
|
return true; |
||||||
|
|
||||||
|
int ones = 0; // How many 1s there is
|
||||||
|
int runs = 0; // How many runs of 1s there is
|
||||||
|
int size = 0; // Size of the integer in bits
|
||||||
|
while(val != 0) { // There is at least one 1
|
||||||
|
while((val & 1) == 0) { // Skip 0s
|
||||||
|
val = val >> 1; |
||||||
|
size++; |
||||||
|
} |
||||||
|
while((val & 1) == 1) { // Skip 1s
|
||||||
|
val = val >> 1; |
||||||
|
size++; |
||||||
|
ones++; |
||||||
|
} |
||||||
|
runs++; |
||||||
|
} |
||||||
|
|
||||||
|
return size >= 7 && runs <= (size + 7) / 8; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool SetText(string newText) |
||||||
|
{ |
||||||
|
string fullName = FullName; |
||||||
|
|
||||||
|
Value val = null; |
||||||
|
try { |
||||||
|
val = this.Expression.Evaluate(WindowsDebugger.DebuggedProcess); |
||||||
|
if (val.Type.IsInteger && newText.StartsWith("0x")) { |
||||||
|
try { |
||||||
|
val.PrimitiveValue = long.Parse(newText.Substring(2), NumberStyles.HexNumber); |
||||||
|
} catch (FormatException) { |
||||||
|
throw new NotSupportedException(); |
||||||
|
} catch (OverflowException) { |
||||||
|
throw new NotSupportedException(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
val.PrimitiveValue = newText; |
||||||
|
} |
||||||
|
this.Text = newText; |
||||||
|
return true; |
||||||
|
} catch (NotSupportedException) { |
||||||
|
string format = "Can not convert {0} to {1}"; |
||||||
|
string msg = string.Format(format, newText, val.Type.PrimitiveType); |
||||||
|
System.Windows.MessageBox.Show(msg); |
||||||
|
} catch (COMException) { |
||||||
|
// COMException (0x80131330): Cannot perfrom SetValue on non-leaf frames.
|
||||||
|
// Happens if trying to set value after exception is breaked
|
||||||
|
System.Windows.MessageBox.Show("UnknownError"); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public static ImageSource GetImageForThis(out string imageName) |
||||||
|
{ |
||||||
|
imageName = "Icons.16x16.Parameter"; |
||||||
|
return ImageService.GetImage(imageName); |
||||||
|
} |
||||||
|
|
||||||
|
public static ImageSource GetImageForParameter(out string imageName) |
||||||
|
{ |
||||||
|
imageName = "Icons.16x16.Parameter"; |
||||||
|
return ImageService.GetImage(imageName); |
||||||
|
} |
||||||
|
|
||||||
|
public static ImageSource GetImageForLocalVariable(out string imageName) |
||||||
|
{ |
||||||
|
imageName = "Icons.16x16.Local"; |
||||||
|
return ImageService.GetImage(imageName); |
||||||
|
} |
||||||
|
|
||||||
|
public static ImageSource GetImageForArrayIndexer(out string imageName) |
||||||
|
{ |
||||||
|
imageName = "Icons.16x16.Field"; |
||||||
|
return ImageService.GetImage(imageName); |
||||||
|
} |
||||||
|
|
||||||
|
public static ImageSource GetImageForMember(IDebugMemberInfo memberInfo, out string imageName) |
||||||
|
{ |
||||||
|
string name = string.Empty; |
||||||
|
if (memberInfo.IsPublic) { |
||||||
|
} else if (memberInfo.IsAssembly) { |
||||||
|
name += "Internal"; |
||||||
|
} else if (memberInfo.IsFamily) { |
||||||
|
name += "Protected"; |
||||||
|
} else if (memberInfo.IsPrivate) { |
||||||
|
name += "Private"; |
||||||
|
} |
||||||
|
if (memberInfo is FieldInfo) { |
||||||
|
name += "Field"; |
||||||
|
} else if (memberInfo is PropertyInfo) { |
||||||
|
name += "Property"; |
||||||
|
} else if (memberInfo is MethodInfo) { |
||||||
|
name += "Method"; |
||||||
|
} else { |
||||||
|
throw new DebuggerException("Unknown member type " + memberInfo.GetType().FullName); |
||||||
|
} |
||||||
|
|
||||||
|
imageName = "Icons.16x16." + name; |
||||||
|
return ImageService.GetImage(imageName); |
||||||
|
} |
||||||
|
|
||||||
|
// public ContextMenuStrip GetContextMenu()
|
||||||
|
// {
|
||||||
|
// if (this.Error != null) return GetErrorContextMenu();
|
||||||
|
//
|
||||||
|
// ContextMenuStrip menu = new ContextMenuStrip();
|
||||||
|
//
|
||||||
|
// ToolStripMenuItem copyItem;
|
||||||
|
// copyItem = new ToolStripMenuItem();
|
||||||
|
// copyItem.Text = ResourceService.GetString("MainWindow.Windows.Debug.LocalVariables.CopyToClipboard");
|
||||||
|
// copyItem.Checked = false;
|
||||||
|
// copyItem.Click += delegate {
|
||||||
|
// ClipboardWrapper.SetText(fullText);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// ToolStripMenuItem hexView;
|
||||||
|
// hexView = new ToolStripMenuItem();
|
||||||
|
// hexView.Text = ResourceService.GetString("MainWindow.Windows.Debug.LocalVariables.ShowInHexadecimal");
|
||||||
|
// hexView.Checked = DebuggingOptions.Instance.ShowValuesInHexadecimal;
|
||||||
|
// hexView.Click += delegate {
|
||||||
|
// // refresh all pads that use ValueNode for display
|
||||||
|
// DebuggingOptions.Instance.ShowValuesInHexadecimal = !DebuggingOptions.Instance.ShowValuesInHexadecimal;
|
||||||
|
// // always check if instance is null, might be null if pad is not opened
|
||||||
|
// if (LocalVarPad.Instance != null)
|
||||||
|
// LocalVarPad.Instance.RefreshPad();
|
||||||
|
// if (WatchPad.Instance != null)
|
||||||
|
// WatchPad.Instance.RefreshPad();
|
||||||
|
// };
|
||||||
|
|
||||||
|
// menu.Items.AddRange(new ToolStripItem[] {
|
||||||
|
// copyItem,
|
||||||
|
// //hexView
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// return menu;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public static WindowsDebugger WindowsDebugger { |
||||||
|
get { |
||||||
|
return (WindowsDebugger)DebuggerService.CurrentDebugger; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; |
||||||
|
|
||||||
|
private void NotifyPropertyChanged(string info) |
||||||
|
{ |
||||||
|
if (PropertyChanged != null) |
||||||
|
{ |
||||||
|
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(info)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,153 @@ |
|||||||
|
// 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.Collections.Generic; |
||||||
|
using Debugger.Interop.CorDebug; |
||||||
|
using Debugger.MetaData; |
||||||
|
using Debugger; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Models.TreeModel |
||||||
|
{ |
||||||
|
public class ICorDebug |
||||||
|
{ |
||||||
|
public class InfoNode: TreeNode |
||||||
|
{ |
||||||
|
List<TreeNode> children; |
||||||
|
|
||||||
|
public InfoNode(string name, string text): this(name, text, null) |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public InfoNode(string name, string text, List<TreeNode> children) |
||||||
|
{ |
||||||
|
this.Name = name; |
||||||
|
this.Text = text; |
||||||
|
this.ChildNodes = children; |
||||||
|
this.children = children; |
||||||
|
} |
||||||
|
|
||||||
|
public void AddChild(string name, string text) |
||||||
|
{ |
||||||
|
if (children == null) { |
||||||
|
children = new List<TreeNode>(); |
||||||
|
this.ChildNodes = children; |
||||||
|
} |
||||||
|
children.Add(new InfoNode(name, text)); |
||||||
|
} |
||||||
|
|
||||||
|
public void AddChild(string name, string text, List<TreeNode> subChildren) |
||||||
|
{ |
||||||
|
if (children == null) { |
||||||
|
children = new List<TreeNode>(); |
||||||
|
this.ChildNodes = children; |
||||||
|
} |
||||||
|
children.Add(new InfoNode(name, text, subChildren)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static InfoNode GetDebugInfoRoot(AppDomain appDomain, ICorDebugValue corValue) |
||||||
|
{ |
||||||
|
return new InfoNode("ICorDebug", "", GetDebugInfo(appDomain, corValue)); |
||||||
|
} |
||||||
|
|
||||||
|
public static List<TreeNode> GetDebugInfo(AppDomain appDomain, ICorDebugValue corValue) |
||||||
|
{ |
||||||
|
List<TreeNode> items = new List<TreeNode>(); |
||||||
|
|
||||||
|
if (corValue is ICorDebugValue) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugValue", ""); |
||||||
|
info.AddChild("Address", corValue.GetAddress().ToString("X8")); |
||||||
|
info.AddChild("Type", ((CorElementType)corValue.GetTheType()).ToString()); |
||||||
|
info.AddChild("Size", corValue.GetSize().ToString()); |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
if (corValue is ICorDebugValue2) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugValue2", ""); |
||||||
|
ICorDebugValue2 corValue2 = (ICorDebugValue2)corValue; |
||||||
|
string fullname; |
||||||
|
try { |
||||||
|
fullname = DebugType.CreateFromCorType(appDomain, corValue2.GetExactType()).FullName; |
||||||
|
} catch (DebuggerException e) { |
||||||
|
fullname = e.Message; |
||||||
|
} |
||||||
|
info.AddChild("ExactType", fullname); |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
if (corValue is ICorDebugGenericValue) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugGenericValue", ""); |
||||||
|
try { |
||||||
|
byte[] bytes = ((ICorDebugGenericValue)corValue).GetRawValue(); |
||||||
|
for(int i = 0; i < bytes.Length; i += 8) { |
||||||
|
string val = ""; |
||||||
|
for(int j = i; j < bytes.Length && j < i + 8; j++) { |
||||||
|
val += bytes[j].ToString("X2") + " "; |
||||||
|
} |
||||||
|
info.AddChild("Value" + i.ToString("X2"), val); |
||||||
|
} |
||||||
|
} catch (System.ArgumentException) { |
||||||
|
info.AddChild("Value", "N/A"); |
||||||
|
} |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
if (corValue is ICorDebugReferenceValue) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugReferenceValue", ""); |
||||||
|
ICorDebugReferenceValue refValue = (ICorDebugReferenceValue)corValue; |
||||||
|
info.AddChild("IsNull", (refValue.IsNull() != 0).ToString()); |
||||||
|
if (refValue.IsNull() == 0) { |
||||||
|
info.AddChild("Value", refValue.GetValue().ToString("X8")); |
||||||
|
if (refValue.Dereference() != null) { |
||||||
|
info.AddChild("Dereference", "", GetDebugInfo(appDomain, refValue.Dereference())); |
||||||
|
} else { |
||||||
|
info.AddChild("Dereference", "N/A"); |
||||||
|
} |
||||||
|
} |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
if (corValue is ICorDebugHeapValue) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugHeapValue", ""); |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
if (corValue is ICorDebugHeapValue2) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugHeapValue2", ""); |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
if (corValue is ICorDebugObjectValue) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugObjectValue", ""); |
||||||
|
ICorDebugObjectValue objValue = (ICorDebugObjectValue)corValue; |
||||||
|
info.AddChild("Class", objValue.GetClass().GetToken().ToString("X8")); |
||||||
|
info.AddChild("IsValueClass", (objValue.IsValueClass() != 0).ToString()); |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
if (corValue is ICorDebugObjectValue2) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugObjectValue2", ""); |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
if (corValue is ICorDebugBoxValue) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugBoxValue", ""); |
||||||
|
ICorDebugBoxValue boxValue = (ICorDebugBoxValue)corValue; |
||||||
|
info.AddChild("Object", "", GetDebugInfo(appDomain, boxValue.GetObject())); |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
if (corValue is ICorDebugStringValue) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugStringValue", ""); |
||||||
|
ICorDebugStringValue stringValue = (ICorDebugStringValue)corValue; |
||||||
|
info.AddChild("Length", stringValue.GetLength().ToString()); |
||||||
|
info.AddChild("String", stringValue.GetString()); |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
if (corValue is ICorDebugArrayValue) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugArrayValue", ""); |
||||||
|
info.AddChild("...", "..."); |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
if (corValue is ICorDebugHandleValue) { |
||||||
|
InfoNode info = new InfoNode("ICorDebugHandleValue", ""); |
||||||
|
ICorDebugHandleValue handleValue = (ICorDebugHandleValue)corValue; |
||||||
|
info.AddChild("HandleType", handleValue.GetHandleType().ToString()); |
||||||
|
items.Add(info); |
||||||
|
} |
||||||
|
|
||||||
|
return items; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
// 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 Debugger.MetaData; |
||||||
|
using ICSharpCode.NRefactory.Ast; |
||||||
|
using ILSpy.Debugger.Services.Debugger; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Models.TreeModel |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// IEnumerable node in the variable tree.
|
||||||
|
/// </summary>
|
||||||
|
public class IEnumerableNode : TreeNode |
||||||
|
{ |
||||||
|
Expression targetObject; |
||||||
|
Expression debugListExpression; |
||||||
|
|
||||||
|
public IEnumerableNode(Expression targetObject, DebugType itemType) |
||||||
|
{ |
||||||
|
this.targetObject = targetObject; |
||||||
|
|
||||||
|
this.Name = "IEnumerable"; |
||||||
|
this.Text = "Expanding will enumerate the IEnumerable"; |
||||||
|
DebugType debugListType; |
||||||
|
this.debugListExpression = DebuggerHelpers.CreateDebugListExpression(targetObject, itemType, out debugListType); |
||||||
|
this.ChildNodes = Utils.LazyGetItemsOfIList(this.debugListExpression); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
// 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 ICSharpCode.NRefactory.Ast; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Models.TreeModel |
||||||
|
{ |
||||||
|
public class IListNode : TreeNode |
||||||
|
{ |
||||||
|
Expression targetObject; |
||||||
|
int count; |
||||||
|
|
||||||
|
public IListNode(Expression targetObject) |
||||||
|
{ |
||||||
|
this.targetObject = targetObject; |
||||||
|
|
||||||
|
this.Name = "IList"; |
||||||
|
this.count = Utils.GetIListCount(this.targetObject); |
||||||
|
this.ChildNodes = Utils.LazyGetItemsOfIList(this.targetObject); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool HasChildNodes { |
||||||
|
get { return this.count > 0; } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
// 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; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Models.TreeModel |
||||||
|
{ |
||||||
|
public interface ISetText |
||||||
|
{ |
||||||
|
bool CanSetText { get; } |
||||||
|
|
||||||
|
bool SetText(string text); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
// 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; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Models.TreeModel |
||||||
|
{ |
||||||
|
public class SavedTreeNode : TreeNode |
||||||
|
{ |
||||||
|
public override bool CanSetText { |
||||||
|
get { return true; } |
||||||
|
} |
||||||
|
|
||||||
|
public SavedTreeNode(ImageSource image, string fullname, string text) |
||||||
|
{ |
||||||
|
base.ImageSource = image; |
||||||
|
FullName = fullname; |
||||||
|
Text = text; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool SetText(string newValue) { |
||||||
|
Text = newValue; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
// 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.Collections.Generic; |
||||||
|
using Debugger; |
||||||
|
using Debugger.MetaData; |
||||||
|
using ICSharpCode.NRefactory.Ast; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Models.TreeModel |
||||||
|
{ |
||||||
|
public class StackFrameNode: TreeNode |
||||||
|
{ |
||||||
|
StackFrame stackFrame; |
||||||
|
|
||||||
|
public StackFrame StackFrame { |
||||||
|
get { return stackFrame; } |
||||||
|
} |
||||||
|
|
||||||
|
public StackFrameNode(StackFrame stackFrame) |
||||||
|
{ |
||||||
|
this.stackFrame = stackFrame; |
||||||
|
|
||||||
|
this.Name = stackFrame.MethodInfo.Name; |
||||||
|
this.ChildNodes = LazyGetChildNodes(); |
||||||
|
} |
||||||
|
|
||||||
|
IEnumerable<TreeNode> LazyGetChildNodes() |
||||||
|
{ |
||||||
|
foreach(DebugParameterInfo par in stackFrame.MethodInfo.GetParameters()) { |
||||||
|
string imageName; |
||||||
|
var image = ExpressionNode.GetImageForParameter(out imageName); |
||||||
|
var expression = new ExpressionNode(image, par.Name, par.GetExpression()); |
||||||
|
expression.ImageName = imageName; |
||||||
|
yield return expression; |
||||||
|
} |
||||||
|
foreach(DebugLocalVariableInfo locVar in stackFrame.MethodInfo.GetLocalVariables(this.StackFrame.IP)) { |
||||||
|
string imageName; |
||||||
|
var image = ExpressionNode.GetImageForLocalVariable(out imageName); |
||||||
|
var expression = new ExpressionNode(image, locVar.Name, locVar.GetExpression()); |
||||||
|
expression.ImageName = imageName; |
||||||
|
yield return expression; |
||||||
|
} |
||||||
|
if (stackFrame.Thread.CurrentException != null) { |
||||||
|
yield return new ExpressionNode(null, "__exception", new IdentifierExpression("__exception")); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
// 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.ComponentModel; |
||||||
|
using System.Linq; |
||||||
|
using System.Windows.Media; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Models.TreeModel |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A node in the variable tree.
|
||||||
|
/// The node is imutable.
|
||||||
|
/// </summary>
|
||||||
|
public class TreeNode : ITreeNode |
||||||
|
{ |
||||||
|
string text = string.Empty; |
||||||
|
|
||||||
|
IEnumerable<TreeNode> childNodes = null; |
||||||
|
|
||||||
|
public ImageSource ImageSource { get; protected set; } |
||||||
|
|
||||||
|
public string Name { get; set; } |
||||||
|
|
||||||
|
public string ImageName { get; set; } |
||||||
|
|
||||||
|
public virtual string FullName { |
||||||
|
get { return Name; } |
||||||
|
set { Name = value; } |
||||||
|
} |
||||||
|
|
||||||
|
public virtual string Text |
||||||
|
{ |
||||||
|
get { return text; } |
||||||
|
set { text = value; } |
||||||
|
} |
||||||
|
|
||||||
|
public virtual string Type { get; protected set; } |
||||||
|
|
||||||
|
public virtual IEnumerable<TreeNode> ChildNodes { |
||||||
|
get { return childNodes; } |
||||||
|
protected set { childNodes = value; } |
||||||
|
} |
||||||
|
|
||||||
|
IEnumerable<ITreeNode> ITreeNode.ChildNodes { |
||||||
|
get { return childNodes; } |
||||||
|
} |
||||||
|
|
||||||
|
public virtual bool HasChildNodes { |
||||||
|
get { return childNodes != null; } |
||||||
|
} |
||||||
|
|
||||||
|
public virtual bool CanSetText { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
public virtual IEnumerable<IVisualizerCommand> VisualizerCommands { |
||||||
|
get { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public virtual bool HasVisualizerCommands { |
||||||
|
get { |
||||||
|
return (VisualizerCommands != null) && (VisualizerCommands.Count() > 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsPinned { get; set; } |
||||||
|
|
||||||
|
public TreeNode() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public TreeNode(ImageSource iconImage, string name, string text, string type, IEnumerable<TreeNode> childNodes) |
||||||
|
{ |
||||||
|
this.ImageSource = iconImage; |
||||||
|
this.Name = name; |
||||||
|
this.text = text; |
||||||
|
this.Type = type; |
||||||
|
this.childNodes = childNodes; |
||||||
|
} |
||||||
|
|
||||||
|
public int CompareTo(ITreeNode other) |
||||||
|
{ |
||||||
|
return this.FullName.CompareTo(other.FullName); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual bool SetText(string newValue) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
// 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.Threading; |
||||||
|
|
||||||
|
using Debugger; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Models.TreeModel |
||||||
|
{ |
||||||
|
public static partial class Utils |
||||||
|
{ |
||||||
|
/// <param name="process">Process on which to track debuggee state</param>
|
||||||
|
public static void DoEvents(Process process) |
||||||
|
{ |
||||||
|
if (process == null) return; |
||||||
|
DebuggeeState oldState = process.DebuggeeState; |
||||||
|
WpfDoEvents(); |
||||||
|
DebuggeeState newState = process.DebuggeeState; |
||||||
|
if (oldState != newState) { |
||||||
|
//LoggingService.Info("Aborted because debuggee resumed");
|
||||||
|
throw new AbortedBecauseDebuggeeResumedException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void WpfDoEvents() |
||||||
|
{ |
||||||
|
DispatcherFrame frame = new DispatcherFrame(); |
||||||
|
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => frame.Continue = false)); |
||||||
|
Dispatcher.PushFrame(frame); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class AbortedBecauseDebuggeeResumedException: System.Exception |
||||||
|
{ |
||||||
|
public AbortedBecauseDebuggeeResumedException(): base() |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class PrintTimes: PrintTime |
||||||
|
{ |
||||||
|
public PrintTimes(string text): base(text + " - end") |
||||||
|
{ |
||||||
|
//LoggingService.InfoFormatted("{0} - start", text);
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class PrintTime: IDisposable |
||||||
|
{ |
||||||
|
string text; |
||||||
|
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); |
||||||
|
|
||||||
|
public PrintTime(string text) |
||||||
|
{ |
||||||
|
this.text = text; |
||||||
|
stopwatch.Start(); |
||||||
|
} |
||||||
|
|
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
stopwatch.Stop(); |
||||||
|
//LoggingService.InfoFormatted("{0} ({1} ms)", text, stopwatch.ElapsedMilliseconds);
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,111 @@ |
|||||||
|
// 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 Debugger.Interop.CorDebug; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Reflection; |
||||||
|
using Debugger; |
||||||
|
using Debugger.MetaData; |
||||||
|
using ICSharpCode.NRefactory.Ast; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Services.Debugger |
||||||
|
{ |
||||||
|
public static class DebuggerHelpers |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Creates an expression which, when evaluated, creates a List<T> in the debugee
|
||||||
|
/// filled with contents of IEnumerable<T> from the debugee.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="iEnumerableVariable">Expression for IEnumerable variable in the debugee.</param>
|
||||||
|
/// <param name="itemType">
|
||||||
|
/// The generic argument of IEnumerable<T> that <paramref name="iEnumerableVariable"/> implements.</param>
|
||||||
|
public static Expression CreateDebugListExpression(Expression iEnumerableVariable, DebugType itemType, out DebugType listType) |
||||||
|
{ |
||||||
|
// is using itemType.AppDomain ok?
|
||||||
|
listType = DebugType.CreateFromType(itemType.AppDomain, typeof(System.Collections.Generic.List<>), itemType); |
||||||
|
var iEnumerableType = DebugType.CreateFromType(itemType.AppDomain, typeof(IEnumerable<>), itemType); |
||||||
|
// explicitely cast the variable to IEnumerable<T>, where T is itemType
|
||||||
|
Expression iEnumerableVariableExplicitCast = new CastExpression(iEnumerableType.GetTypeReference() , iEnumerableVariable, CastType.Cast); |
||||||
|
return new ObjectCreateExpression(listType.GetTypeReference(), iEnumerableVariableExplicitCast.ToList()); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets underlying address of object in the debuggee.
|
||||||
|
/// </summary>
|
||||||
|
public static ulong GetObjectAddress(this Value val) |
||||||
|
{ |
||||||
|
if (val.IsNull) return 0; |
||||||
|
ICorDebugReferenceValue refVal = val.CorReferenceValue; |
||||||
|
return refVal.GetValue(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if this type is enum.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsEnum(this DebugType type) |
||||||
|
{ |
||||||
|
return (type.BaseType != null) && (type.BaseType.FullName == "System.Enum"); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true is this type is just System.Object.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsSystemDotObject(this DebugType type) |
||||||
|
{ |
||||||
|
return type.FullName == "System.Object"; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates expression and gets underlying address of object in the debuggee.
|
||||||
|
/// </summary>
|
||||||
|
public static ulong GetObjectAddress(this Expression expr) |
||||||
|
{ |
||||||
|
return expr.Evaluate(WindowsDebugger.CurrentProcess).GetObjectAddress(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// System.Runtime.CompilerServices.GetHashCode method, for obtaining non-overriden hash codes from debuggee.
|
||||||
|
/// </summary>
|
||||||
|
private static DebugMethodInfo hashCodeMethod; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes RuntimeHelpers.GetHashCode on given value, that is a default hashCode ignoring user overrides.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Valid value.</param>
|
||||||
|
/// <returns>Hash code of the object in the debugee.</returns>
|
||||||
|
public static int InvokeDefaultGetHashCode(this Value value) |
||||||
|
{ |
||||||
|
if (hashCodeMethod == null || hashCodeMethod.Process.HasExited) { |
||||||
|
DebugType typeRuntimeHelpers = DebugType.CreateFromType(value.AppDomain, typeof(System.Runtime.CompilerServices.RuntimeHelpers)); |
||||||
|
hashCodeMethod = (DebugMethodInfo)typeRuntimeHelpers.GetMethod("GetHashCode", BindingFlags.Public | BindingFlags.Static); |
||||||
|
if (hashCodeMethod == null) { |
||||||
|
throw new DebuggerException("Cannot obtain method System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode"); |
||||||
|
} |
||||||
|
} |
||||||
|
// David: I had hard time finding out how to invoke static method.
|
||||||
|
// value.InvokeMethod is nice for instance methods.
|
||||||
|
// what about MethodInfo.Invoke() ?
|
||||||
|
// also, there could be an overload that takes 1 parameter instead of array
|
||||||
|
Value defaultHashCode = Eval.InvokeMethod(DebuggerHelpers.hashCodeMethod, null, new Value[]{value}); |
||||||
|
|
||||||
|
//MethodInfo method = value.Type.GetMember("GetHashCode", BindingFlags.Method | BindingFlags.IncludeSuperType) as MethodInfo;
|
||||||
|
//string hashCode = value.InvokeMethod(method, null).AsString;
|
||||||
|
return (int)defaultHashCode.PrimitiveValue; |
||||||
|
} |
||||||
|
|
||||||
|
public static Value EvalPermanentReference(this Expression expr) |
||||||
|
{ |
||||||
|
return expr.Evaluate(WindowsDebugger.CurrentProcess).GetPermanentReference(); |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsNull(this Expression expr) |
||||||
|
{ |
||||||
|
return expr.Evaluate(WindowsDebugger.CurrentProcess).IsNull; |
||||||
|
} |
||||||
|
|
||||||
|
public static DebugType GetType(this Expression expr) |
||||||
|
{ |
||||||
|
return expr.Evaluate(WindowsDebugger.CurrentProcess).Type; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
// 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; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Services.Debugger |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// ListHelper wraps System.Collection.Generic.List methods to return the original list,
|
||||||
|
/// instead of returning 'void', so we can write eg. list.Sorted().First()
|
||||||
|
/// </summary>
|
||||||
|
public static class ListHelper |
||||||
|
{ |
||||||
|
public static List<T> Sorted<T>(this List<T> list, IComparer<T> comparer) |
||||||
|
{ |
||||||
|
list.Sort(comparer); |
||||||
|
return list; |
||||||
|
} |
||||||
|
|
||||||
|
public static List<T> Sorted<T>(this List<T> list) |
||||||
|
{ |
||||||
|
list.Sort(); |
||||||
|
return list; |
||||||
|
} |
||||||
|
|
||||||
|
public static List<T> ToList<T>(this T singleItem) |
||||||
|
{ |
||||||
|
var newList = new List<T>(); |
||||||
|
newList.Add(singleItem); |
||||||
|
return newList; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,81 @@ |
|||||||
|
// 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.IO; |
||||||
|
using System.Reflection; |
||||||
|
using System.Runtime.Remoting; |
||||||
|
using System.Security.Policy; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Services.Debugger |
||||||
|
{ |
||||||
|
[Serializable] |
||||||
|
class RemotingConfigurationHelpper |
||||||
|
{ |
||||||
|
public string path; |
||||||
|
|
||||||
|
public RemotingConfigurationHelpper(string path) |
||||||
|
{ |
||||||
|
this.path = path; |
||||||
|
} |
||||||
|
|
||||||
|
public static string GetLoadedAssemblyPath(string assemblyName) |
||||||
|
{ |
||||||
|
string path = null; |
||||||
|
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { |
||||||
|
try { |
||||||
|
string fullFilename = assembly.Location; |
||||||
|
if (Path.GetFileName(fullFilename).Equals(assemblyName, StringComparison.OrdinalIgnoreCase)) { |
||||||
|
path = Path.GetDirectoryName(fullFilename); |
||||||
|
break; |
||||||
|
} |
||||||
|
} catch (NotSupportedException) { |
||||||
|
// assembly.Location throws NotSupportedException for assemblies emitted using
|
||||||
|
// Reflection.Emit by custom controls used in the forms designer
|
||||||
|
} |
||||||
|
} |
||||||
|
if (path == null) { |
||||||
|
throw new Exception("Assembly " + assemblyName + " is not loaded"); |
||||||
|
} |
||||||
|
return path; |
||||||
|
} |
||||||
|
|
||||||
|
public void Configure() |
||||||
|
{ |
||||||
|
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve; |
||||||
|
|
||||||
|
RemotingConfiguration.Configure(Path.Combine(path, "Client.config"), false); |
||||||
|
|
||||||
|
string baseDir = Directory.GetDirectoryRoot(AppDomain.CurrentDomain.BaseDirectory); |
||||||
|
string relDirs = AppDomain.CurrentDomain.BaseDirectory + ";" + path; |
||||||
|
AppDomain serverAppDomain = AppDomain.CreateDomain("Debugging server", |
||||||
|
new Evidence(AppDomain.CurrentDomain.Evidence), |
||||||
|
baseDir, |
||||||
|
relDirs, |
||||||
|
AppDomain.CurrentDomain.ShadowCopyFiles); |
||||||
|
serverAppDomain.DoCallBack(new CrossAppDomainDelegate(ConfigureServer)); |
||||||
|
} |
||||||
|
|
||||||
|
private void ConfigureServer() |
||||||
|
{ |
||||||
|
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve; |
||||||
|
RemotingConfiguration.Configure(Path.Combine(path, "Server.config"), false); |
||||||
|
} |
||||||
|
|
||||||
|
Assembly AssemblyResolve(object sender, ResolveEventArgs args) |
||||||
|
{ |
||||||
|
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { |
||||||
|
try { |
||||||
|
string fullFilename = assembly.Location; |
||||||
|
if (Path.GetFileNameWithoutExtension(fullFilename).Equals(args.Name, StringComparison.OrdinalIgnoreCase) || |
||||||
|
assembly.FullName == args.Name) { |
||||||
|
return assembly; |
||||||
|
} |
||||||
|
} catch (NotSupportedException) { |
||||||
|
// assembly.Location throws NotSupportedException for assemblies emitted using
|
||||||
|
// Reflection.Emit by custom controls used in the forms designer
|
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
// 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; |
||||||
|
using Debugger.MetaData; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Services.Debugger |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Helper for obtaining information about DebugType.
|
||||||
|
/// </summary>
|
||||||
|
public static class TypeResolverExtension |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Gets generic interface this type implements.
|
||||||
|
/// The generic argument of the interface does not have to be specified.
|
||||||
|
/// If you know the generic argument, use DebugType.GetInterface.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fullNamePrefix">Eg. "System.Collections.Generic.IList"</param>
|
||||||
|
public static DebugType GetGenericInterface(this DebugType type, string fullNamePrefix) |
||||||
|
{ |
||||||
|
foreach(DebugType inter in type.GetInterfaces()) { |
||||||
|
if (inter.FullName.StartsWith(fullNamePrefix) && inter.GetGenericArguments().Length > 0) { |
||||||
|
return inter; |
||||||
|
} |
||||||
|
} |
||||||
|
// not found, search BaseType
|
||||||
|
return type.BaseType == null ? null : (DebugType)type.BaseType.GetInterface(fullNamePrefix); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves implementation of System.Collections.Generic.IList on this type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="iListType">Result found implementation of System.Collections.Generic.IList.</param>
|
||||||
|
/// <param name="itemType">The only generic argument of <paramref name="implementation"/></param>
|
||||||
|
/// <returns>True if found, false otherwise.</returns>
|
||||||
|
public static bool ResolveIListImplementation(this DebugType type, out DebugType iListType, out DebugType itemType) |
||||||
|
{ |
||||||
|
return type.ResolveGenericInterfaceImplementation( |
||||||
|
"System.Collections.Generic.IList", out iListType, out itemType); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves implementation of System.Collections.Generic.IEnumerable on this type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="iEnumerableType">Result found implementation of System.Collections.Generic.IEnumerable.</param>
|
||||||
|
/// <param name="itemType">The only generic argument of <paramref name="implementation"/></param>
|
||||||
|
/// <returns>True if found, false otherwise.</returns>
|
||||||
|
public static bool ResolveIEnumerableImplementation(this DebugType type, out DebugType iEnumerableType, out DebugType itemType) |
||||||
|
{ |
||||||
|
return type.ResolveGenericInterfaceImplementation( |
||||||
|
"System.Collections.Generic.IEnumerable", out iEnumerableType, out itemType); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves implementation of a single-generic-argument interface on this type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fullNamePrefix">Interface name to search for (eg. "System.Collections.Generic.IList")</param>
|
||||||
|
/// <param name="implementation">Result found implementation.</param>
|
||||||
|
/// <param name="itemType">The only generic argument of <paramref name="implementation"/></param>
|
||||||
|
/// <returns>True if found, false otherwise.</returns>
|
||||||
|
public static bool ResolveGenericInterfaceImplementation(this DebugType type, string fullNamePrefix, out DebugType implementation, out DebugType itemType) |
||||||
|
{ |
||||||
|
if (type == null) |
||||||
|
throw new ArgumentNullException("type"); |
||||||
|
|
||||||
|
implementation = null; |
||||||
|
itemType = null; |
||||||
|
|
||||||
|
implementation = type.GetGenericInterface(fullNamePrefix); |
||||||
|
if (implementation != null) { |
||||||
|
if (implementation.GetGenericArguments().Length == 1) { |
||||||
|
itemType = (DebugType)implementation.GetGenericArguments()[0]; |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,727 @@ |
|||||||
|
// 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.Diagnostics; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Text; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Media; |
||||||
|
|
||||||
|
using Debugger; |
||||||
|
using Debugger.Interop.CorPublish; |
||||||
|
using ICSharpCode.NRefactory; |
||||||
|
using ICSharpCode.NRefactory.Ast; |
||||||
|
using ICSharpCode.NRefactory.Visitors; |
||||||
|
using ILSpy.Debugger.Bookmarks; |
||||||
|
using ILSpy.Debugger.Models.TreeModel; |
||||||
|
using ILSpy.Debugger.Services.Debugger; |
||||||
|
using CorDbg = Debugger; |
||||||
|
using Process = Debugger.Process; |
||||||
|
|
||||||
|
namespace ILSpy.Debugger.Services |
||||||
|
{ |
||||||
|
public class WindowsDebugger : IDebugger |
||||||
|
{ |
||||||
|
enum StopAttachedProcessDialogResult { |
||||||
|
Detach = 0, |
||||||
|
Terminate = 1, |
||||||
|
Cancel = 2 |
||||||
|
} |
||||||
|
|
||||||
|
bool useRemotingForThreadInterop = false; |
||||||
|
bool attached; |
||||||
|
|
||||||
|
NDebugger debugger; |
||||||
|
|
||||||
|
ICorPublish corPublish; |
||||||
|
|
||||||
|
Process debuggedProcess; |
||||||
|
|
||||||
|
//DynamicTreeDebuggerRow currentTooltipRow;
|
||||||
|
//Expression currentTooltipExpression;
|
||||||
|
|
||||||
|
public event EventHandler<ProcessEventArgs> ProcessSelected; |
||||||
|
|
||||||
|
public NDebugger DebuggerCore { |
||||||
|
get { |
||||||
|
return debugger; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Process DebuggedProcess { |
||||||
|
get { |
||||||
|
return debuggedProcess; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static Process CurrentProcess { |
||||||
|
get { |
||||||
|
WindowsDebugger dbgr = DebuggerService.CurrentDebugger as WindowsDebugger; |
||||||
|
if (dbgr != null && dbgr.DebuggedProcess != null) { |
||||||
|
return dbgr.DebuggedProcess; |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool BreakAtBeginning { |
||||||
|
get; |
||||||
|
set; |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual void OnProcessSelected(ProcessEventArgs e) |
||||||
|
{ |
||||||
|
if (ProcessSelected != null) { |
||||||
|
ProcessSelected(this, e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool ServiceInitialized { |
||||||
|
get { |
||||||
|
return debugger != null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public WindowsDebugger() |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
#region IDebugger Members
|
||||||
|
|
||||||
|
string errorDebugging = "Error.Debugging"; |
||||||
|
string errorNotDebugging = "Error.NotDebugging"; |
||||||
|
string errorProcessRunning = "Error.ProcessRunning"; |
||||||
|
string errorProcessPaused = "Error.ProcessPaused"; |
||||||
|
string errorCannotStepNoActiveFunction = "Threads.CannotStepNoActiveFunction"; |
||||||
|
|
||||||
|
public bool IsDebugging { |
||||||
|
get { |
||||||
|
return ServiceInitialized && debuggedProcess != null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsAttached { |
||||||
|
get { |
||||||
|
return ServiceInitialized && attached; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsProcessRunning { |
||||||
|
get { |
||||||
|
return IsDebugging && debuggedProcess.IsRunning; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Start(ProcessStartInfo processStartInfo) |
||||||
|
{ |
||||||
|
if (IsDebugging) { |
||||||
|
MessageBox.Show(errorDebugging); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (!ServiceInitialized) { |
||||||
|
InitializeService(); |
||||||
|
} |
||||||
|
|
||||||
|
string version = debugger.GetProgramVersion(processStartInfo.FileName); |
||||||
|
|
||||||
|
if (version.StartsWith("v1.0")) { |
||||||
|
MessageBox.Show("Net10NotSupported"); |
||||||
|
} else if (version.StartsWith("v1.1")) { |
||||||
|
MessageBox.Show("Net1.1NotSupported"); |
||||||
|
// } else if (string.IsNullOrEmpty(version)) {
|
||||||
|
// // Not a managed assembly
|
||||||
|
// MessageBox.Show(".Error.BadAssembly}");
|
||||||
|
} else if (debugger.IsKernelDebuggerEnabled) { |
||||||
|
MessageBox.Show("KernelDebuggerEnabled"); |
||||||
|
} else { |
||||||
|
attached = false; |
||||||
|
if (DebugStarting != null) |
||||||
|
DebugStarting(this, EventArgs.Empty); |
||||||
|
|
||||||
|
try { |
||||||
|
Process process = debugger.Start(processStartInfo.FileName, |
||||||
|
processStartInfo.WorkingDirectory, |
||||||
|
processStartInfo.Arguments); |
||||||
|
SelectProcess(process); |
||||||
|
} catch (System.Exception e) { |
||||||
|
// COMException: The request is not supported. (Exception from HRESULT: 0x80070032)
|
||||||
|
// COMException: The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log for more detail. (Exception from HRESULT: 0x800736B1)
|
||||||
|
// COMException: The requested operation requires elevation. (Exception from HRESULT: 0x800702E4)
|
||||||
|
// COMException: The directory name is invalid. (Exception from HRESULT: 0x8007010B)
|
||||||
|
// BadImageFormatException: is not a valid Win32 application. (Exception from HRESULT: 0x800700C1)
|
||||||
|
// UnauthorizedAccessException: Отказано в доступе. (Исключение из HRESULT: 0x80070005 (E_ACCESSDENIED))
|
||||||
|
if (e is COMException || e is BadImageFormatException || e is UnauthorizedAccessException) { |
||||||
|
string msg = "CannotStartProcess"; |
||||||
|
msg += " " + e.Message; |
||||||
|
// TODO: Remove
|
||||||
|
if (e is COMException && ((uint)((COMException)e).ErrorCode == 0x80070032)) { |
||||||
|
msg += Environment.NewLine + Environment.NewLine; |
||||||
|
msg += "64-bit debugging is not supported. Please set Project -> Project Options... -> Compiling -> Target CPU to 32bit."; |
||||||
|
} |
||||||
|
MessageBox.Show(msg); |
||||||
|
|
||||||
|
if (DebugStopped != null) |
||||||
|
DebugStopped(this, EventArgs.Empty); |
||||||
|
} else { |
||||||
|
throw; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Attach(System.Diagnostics.Process existingProcess) |
||||||
|
{ |
||||||
|
if (existingProcess == null) |
||||||
|
return; |
||||||
|
|
||||||
|
if (IsDebugging) { |
||||||
|
MessageBox.Show(errorDebugging); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (!ServiceInitialized) { |
||||||
|
InitializeService(); |
||||||
|
} |
||||||
|
|
||||||
|
string version = debugger.GetProgramVersion(existingProcess.MainModule.FileName); |
||||||
|
if (version.StartsWith("v1.0")) { |
||||||
|
MessageBox.Show("Net10NotSupported"); |
||||||
|
} else { |
||||||
|
if (DebugStarting != null) |
||||||
|
DebugStarting(this, EventArgs.Empty); |
||||||
|
|
||||||
|
try { |
||||||
|
Process process = debugger.Attach(existingProcess); |
||||||
|
attached = true; |
||||||
|
SelectProcess(process); |
||||||
|
} catch (System.Exception e) { |
||||||
|
// CORDBG_E_DEBUGGER_ALREADY_ATTACHED
|
||||||
|
if (e is COMException || e is UnauthorizedAccessException) { |
||||||
|
string msg = "CannotAttachToProcess"; |
||||||
|
MessageBox.Show(msg + " " + e.Message); |
||||||
|
|
||||||
|
if (DebugStopped != null) |
||||||
|
DebugStopped(this, EventArgs.Empty); |
||||||
|
} else { |
||||||
|
throw; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Detach() |
||||||
|
{ |
||||||
|
if (debuggedProcess == null) |
||||||
|
return; |
||||||
|
|
||||||
|
debugger.Detach(); |
||||||
|
} |
||||||
|
|
||||||
|
public void StartWithoutDebugging(ProcessStartInfo processStartInfo) |
||||||
|
{ |
||||||
|
System.Diagnostics.Process.Start(processStartInfo); |
||||||
|
} |
||||||
|
|
||||||
|
public void Stop() |
||||||
|
{ |
||||||
|
if (!IsDebugging) { |
||||||
|
MessageBox.Show(errorNotDebugging, "Stop"); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (IsAttached) { |
||||||
|
Detach(); |
||||||
|
} else { |
||||||
|
debuggedProcess.Terminate(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ExecutionControl:
|
||||||
|
|
||||||
|
public void Break() |
||||||
|
{ |
||||||
|
if (!IsDebugging) { |
||||||
|
MessageBox.Show(errorNotDebugging, "Break"); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (!IsProcessRunning) { |
||||||
|
MessageBox.Show(errorProcessPaused, "Break"); |
||||||
|
return; |
||||||
|
} |
||||||
|
debuggedProcess.Break(); |
||||||
|
} |
||||||
|
|
||||||
|
public void Continue() |
||||||
|
{ |
||||||
|
if (!IsDebugging) { |
||||||
|
MessageBox.Show(errorNotDebugging, "Continue"); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (IsProcessRunning) { |
||||||
|
MessageBox.Show(errorProcessRunning, "Continue"); |
||||||
|
return; |
||||||
|
} |
||||||
|
debuggedProcess.AsyncContinue(); |
||||||
|
} |
||||||
|
|
||||||
|
// Stepping:
|
||||||
|
|
||||||
|
public void StepInto() |
||||||
|
{ |
||||||
|
if (!IsDebugging) { |
||||||
|
MessageBox.Show(errorNotDebugging, "StepInto"); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) { |
||||||
|
MessageBox.Show(errorCannotStepNoActiveFunction, "StepInto"); |
||||||
|
} else { |
||||||
|
debuggedProcess.SelectedStackFrame.AsyncStepInto(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void StepOver() |
||||||
|
{ |
||||||
|
if (!IsDebugging) { |
||||||
|
MessageBox.Show(errorNotDebugging, "StepOver"); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) { |
||||||
|
MessageBox.Show(errorCannotStepNoActiveFunction, "StepOver"); |
||||||
|
} else { |
||||||
|
debuggedProcess.SelectedStackFrame.AsyncStepOver(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void StepOut() |
||||||
|
{ |
||||||
|
if (!IsDebugging) { |
||||||
|
MessageBox.Show(errorNotDebugging, "StepOut"); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) { |
||||||
|
MessageBox.Show(errorCannotStepNoActiveFunction, "StepOut"); |
||||||
|
} else { |
||||||
|
debuggedProcess.SelectedStackFrame.AsyncStepOut(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public event EventHandler DebugStarting; |
||||||
|
public event EventHandler DebugStarted; |
||||||
|
public event EventHandler DebugStopped; |
||||||
|
public event EventHandler IsProcessRunningChanged; |
||||||
|
|
||||||
|
protected virtual void OnIsProcessRunningChanged(EventArgs e) |
||||||
|
{ |
||||||
|
if (IsProcessRunningChanged != null) { |
||||||
|
IsProcessRunningChanged(this, e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets variable of given name.
|
||||||
|
/// Returns null if unsuccessful. Can throw GetValueException.
|
||||||
|
/// <exception cref="GetValueException">Thrown when evaluation fails. Exception message explains reason.</exception>
|
||||||
|
/// </summary>
|
||||||
|
public Value GetValueFromName(string variableName) |
||||||
|
{ |
||||||
|
if (!CanEvaluate) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return ExpressionEvaluator.Evaluate(variableName, SupportedLanguage.CSharp, debuggedProcess.SelectedStackFrame); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets Expression for given variable. Can throw GetValueException.
|
||||||
|
/// <exception cref="GetValueException">Thrown when getting expression fails. Exception message explains reason.</exception>
|
||||||
|
/// </summary>
|
||||||
|
public ICSharpCode.NRefactory.Ast.Expression GetExpression(string variableName) |
||||||
|
{ |
||||||
|
if (!CanEvaluate) { |
||||||
|
throw new GetValueException("Cannot evaluate now - debugged process is either null or running or has no selected stack frame"); |
||||||
|
} |
||||||
|
return ExpressionEvaluator.ParseExpression(variableName, SupportedLanguage.CSharp); |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsManaged(int processId) |
||||||
|
{ |
||||||
|
corPublish = new CorpubPublishClass(); |
||||||
|
CorDbg.Interop.TrackedComObjects.Track(corPublish); |
||||||
|
|
||||||
|
ICorPublishProcess process = corPublish.GetProcess((uint)processId); |
||||||
|
if (process != null) { |
||||||
|
return process.IsManaged() != 0; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current value of the variable as string that can be displayed in tooltips.
|
||||||
|
/// Returns null if unsuccessful.
|
||||||
|
/// </summary>
|
||||||
|
public string GetValueAsString(string variableName) |
||||||
|
{ |
||||||
|
try { |
||||||
|
Value val = GetValueFromName(variableName); |
||||||
|
if (val == null) return null; |
||||||
|
return val.AsString(); |
||||||
|
} catch (GetValueException) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool CanEvaluate |
||||||
|
{ |
||||||
|
get { |
||||||
|
return debuggedProcess != null && !debuggedProcess.IsRunning && debuggedProcess.SelectedStackFrame != null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the tooltip control that shows the value of given variable.
|
||||||
|
/// Return null if no tooltip is available.
|
||||||
|
/// </summary>
|
||||||
|
public object GetTooltipControl(Location logicalPosition, string variableName) |
||||||
|
{ |
||||||
|
try { |
||||||
|
var tooltipExpression = GetExpression(variableName); |
||||||
|
string imageName; |
||||||
|
var image = ExpressionNode.GetImageForLocalVariable(out imageName); |
||||||
|
ExpressionNode expressionNode = new ExpressionNode(image, variableName, tooltipExpression); |
||||||
|
expressionNode.ImageName = imageName; |
||||||
|
return null; |
||||||
|
// return new DebuggerTooltipControl(logicalPosition, expressionNode);
|
||||||
|
} catch (GetValueException) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public ITreeNode GetNode(string variable, string currentImageName = null) |
||||||
|
{ |
||||||
|
try { |
||||||
|
var expression = GetExpression(variable); |
||||||
|
string imageName; |
||||||
|
ImageSource image; |
||||||
|
if (string.IsNullOrEmpty(currentImageName)) { |
||||||
|
image = ExpressionNode.GetImageForLocalVariable(out imageName); |
||||||
|
} |
||||||
|
else { |
||||||
|
image = ImageService.GetImage(currentImageName); |
||||||
|
imageName = currentImageName; |
||||||
|
} |
||||||
|
ExpressionNode expressionNode = new ExpressionNode(image, variable, expression); |
||||||
|
expressionNode.ImageName = imageName; |
||||||
|
return expressionNode; |
||||||
|
} catch (GetValueException) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool CanSetInstructionPointer(string filename, int line, int column) |
||||||
|
{ |
||||||
|
if (debuggedProcess != null && debuggedProcess.IsPaused && debuggedProcess.SelectedStackFrame != null) { |
||||||
|
SourcecodeSegment seg = debuggedProcess.SelectedStackFrame.CanSetIP(filename, line, column); |
||||||
|
return seg != null; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool SetInstructionPointer(string filename, int line, int column) |
||||||
|
{ |
||||||
|
if (CanSetInstructionPointer(filename, line, column)) { |
||||||
|
SourcecodeSegment seg = debuggedProcess.SelectedStackFrame.SetIP(filename, line, column); |
||||||
|
return seg != null; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
Stop(); |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public event EventHandler Initialize; |
||||||
|
|
||||||
|
public void InitializeService() |
||||||
|
{ |
||||||
|
if (useRemotingForThreadInterop) { |
||||||
|
// This needs to be called before instance of NDebugger is created
|
||||||
|
string path = RemotingConfigurationHelpper.GetLoadedAssemblyPath("Debugger.Core.dll"); |
||||||
|
new RemotingConfigurationHelpper(path).Configure(); |
||||||
|
} |
||||||
|
|
||||||
|
debugger = new NDebugger(); |
||||||
|
|
||||||
|
//debugger.Options = DebuggingOptions.Instance;
|
||||||
|
|
||||||
|
debugger.DebuggerTraceMessage += debugger_TraceMessage; |
||||||
|
debugger.Processes.Added += debugger_ProcessStarted; |
||||||
|
debugger.Processes.Removed += debugger_ProcessExited; |
||||||
|
|
||||||
|
DebuggerService.BreakPointAdded += delegate (object sender, BreakpointBookmarkEventArgs e) { |
||||||
|
AddBreakpoint(e.BreakpointBookmark); |
||||||
|
}; |
||||||
|
|
||||||
|
foreach (BreakpointBookmark b in DebuggerService.Breakpoints) { |
||||||
|
AddBreakpoint(b); |
||||||
|
} |
||||||
|
|
||||||
|
if (Initialize != null) { |
||||||
|
Initialize(this, null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool Compare(byte[] a, byte[] b) |
||||||
|
{ |
||||||
|
if (a.Length != b.Length) return false; |
||||||
|
for(int i = 0; i < a.Length; i++) { |
||||||
|
if (a[i] != b[i]) return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void AddBreakpoint(BreakpointBookmark bookmark) |
||||||
|
{ |
||||||
|
Breakpoint breakpoint = debugger.Breakpoints.Add(bookmark.TypeName, null, bookmark.LineNumber, 0, bookmark.IsEnabled); |
||||||
|
// Action setBookmarkColor = delegate {
|
||||||
|
// if (debugger.Processes.Count == 0) {
|
||||||
|
// bookmark.IsHealthy = true;
|
||||||
|
// bookmark.Tooltip = null;
|
||||||
|
// } else if (!breakpoint.IsSet) {
|
||||||
|
// bookmark.IsHealthy = false;
|
||||||
|
// bookmark.Tooltip = "Breakpoint was not found in any loaded modules";
|
||||||
|
// } else if (breakpoint.OriginalLocation.CheckSum == null) {
|
||||||
|
// bookmark.IsHealthy = true;
|
||||||
|
// bookmark.Tooltip = null;
|
||||||
|
// } else {
|
||||||
|
// byte[] fileMD5;
|
||||||
|
// IEditable file = FileService.GetOpenFile(bookmark.FileName) as IEditable;
|
||||||
|
// if (file != null) {
|
||||||
|
// byte[] fileContent = Encoding.UTF8.GetBytesWithPreamble(file.Text);
|
||||||
|
// fileMD5 = new MD5CryptoServiceProvider().ComputeHash(fileContent);
|
||||||
|
// } else {
|
||||||
|
// fileMD5 = new MD5CryptoServiceProvider().ComputeHash(File.ReadAllBytes(bookmark.FileName));
|
||||||
|
// }
|
||||||
|
// if (Compare(fileMD5, breakpoint.OriginalLocation.CheckSum)) {
|
||||||
|
// bookmark.IsHealthy = true;
|
||||||
|
// bookmark.Tooltip = null;
|
||||||
|
// } else {
|
||||||
|
// bookmark.IsHealthy = false;
|
||||||
|
// bookmark.Tooltip = "Check sum or file does not match to the original";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// event handlers on bookmark and breakpoint don't need deregistration
|
||||||
|
bookmark.IsEnabledChanged += delegate { |
||||||
|
breakpoint.Enabled = bookmark.IsEnabled; |
||||||
|
}; |
||||||
|
breakpoint.Set += delegate { |
||||||
|
//setBookmarkColor();
|
||||||
|
}; |
||||||
|
|
||||||
|
//setBookmarkColor();
|
||||||
|
|
||||||
|
EventHandler<CollectionItemEventArgs<Process>> bp_debugger_ProcessStarted = (sender, e) => { |
||||||
|
//setBookmarkColor();
|
||||||
|
// User can change line number by inserting or deleting lines
|
||||||
|
breakpoint.Line = bookmark.LineNumber; |
||||||
|
}; |
||||||
|
EventHandler<CollectionItemEventArgs<Process>> bp_debugger_ProcessExited = (sender, e) => { |
||||||
|
//setBookmarkColor();
|
||||||
|
}; |
||||||
|
|
||||||
|
EventHandler<BreakpointEventArgs> bp_debugger_BreakpointHit = |
||||||
|
new EventHandler<BreakpointEventArgs>( |
||||||
|
delegate(object sender, BreakpointEventArgs e) |
||||||
|
{ |
||||||
|
//LoggingService.Debug(bookmark.Action + " " + bookmark.ScriptLanguage + " " + bookmark.Condition);
|
||||||
|
|
||||||
|
switch (bookmark.Action) { |
||||||
|
case BreakpointAction.Break: |
||||||
|
break; |
||||||
|
case BreakpointAction.Condition: |
||||||
|
// if (Evaluate(bookmark.Condition, bookmark.ScriptLanguage))
|
||||||
|
// DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAtBecause}") + "\n", bookmark.LineNumber, bookmark.FileName, bookmark.Condition));
|
||||||
|
// else
|
||||||
|
// this.debuggedProcess.AsyncContinue();
|
||||||
|
break; |
||||||
|
case BreakpointAction.Trace: |
||||||
|
//DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAt}") + "\n", bookmark.LineNumber, bookmark.FileName));
|
||||||
|
break; |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
BookmarkEventHandler bp_bookmarkManager_Removed = null; |
||||||
|
bp_bookmarkManager_Removed = (sender, e) => { |
||||||
|
if (bookmark == e.Bookmark) { |
||||||
|
debugger.Breakpoints.Remove(breakpoint); |
||||||
|
|
||||||
|
// unregister the events
|
||||||
|
debugger.Processes.Added -= bp_debugger_ProcessStarted; |
||||||
|
debugger.Processes.Removed -= bp_debugger_ProcessExited; |
||||||
|
breakpoint.Hit -= bp_debugger_BreakpointHit; |
||||||
|
BookmarkManager.Removed -= bp_bookmarkManager_Removed; |
||||||
|
} |
||||||
|
}; |
||||||
|
// register the events
|
||||||
|
debugger.Processes.Added += bp_debugger_ProcessStarted; |
||||||
|
debugger.Processes.Removed += bp_debugger_ProcessExited; |
||||||
|
breakpoint.Hit += bp_debugger_BreakpointHit; |
||||||
|
BookmarkManager.Removed += bp_bookmarkManager_Removed; |
||||||
|
} |
||||||
|
|
||||||
|
bool Evaluate(string code, string language) |
||||||
|
{ |
||||||
|
try { |
||||||
|
SupportedLanguage supportedLanguage = (SupportedLanguage)Enum.Parse(typeof(SupportedLanguage), language, true); |
||||||
|
Value val = ExpressionEvaluator.Evaluate(code, supportedLanguage, debuggedProcess.SelectedStackFrame); |
||||||
|
|
||||||
|
if (val != null && val.Type.IsPrimitive && val.PrimitiveValue is bool) |
||||||
|
return (bool)val.PrimitiveValue; |
||||||
|
else |
||||||
|
return false; |
||||||
|
} catch (GetValueException e) { |
||||||
|
string errorMessage = "Error while evaluating breakpoint condition " + code + ":\n" + e.Message + "\n"; |
||||||
|
//DebuggerService.PrintDebugMessage(errorMessage);
|
||||||
|
//WorkbenchSingleton.SafeThreadAsyncCall(MessageService.ShowWarning, errorMessage);
|
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void LogMessage(object sender, MessageEventArgs e) |
||||||
|
{ |
||||||
|
//DebuggerService.PrintDebugMessage(e.Message);
|
||||||
|
} |
||||||
|
|
||||||
|
void debugger_TraceMessage(object sender, MessageEventArgs e) |
||||||
|
{ |
||||||
|
//LoggingService.Debug("Debugger: " + e.Message);
|
||||||
|
} |
||||||
|
|
||||||
|
void debugger_ProcessStarted(object sender, CollectionItemEventArgs<Process> e) |
||||||
|
{ |
||||||
|
if (debugger.Processes.Count == 1) { |
||||||
|
if (DebugStarted != null) { |
||||||
|
DebugStarted(this, EventArgs.Empty); |
||||||
|
} |
||||||
|
} |
||||||
|
e.Item.LogMessage += LogMessage; |
||||||
|
} |
||||||
|
|
||||||
|
void debugger_ProcessExited(object sender, CollectionItemEventArgs<Process> e) |
||||||
|
{ |
||||||
|
if (debugger.Processes.Count == 0) { |
||||||
|
if (DebugStopped != null) { |
||||||
|
DebugStopped(this, e); |
||||||
|
} |
||||||
|
SelectProcess(null); |
||||||
|
} else { |
||||||
|
SelectProcess(debugger.Processes[0]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void SelectProcess(Process process) |
||||||
|
{ |
||||||
|
if (debuggedProcess != null) { |
||||||
|
debuggedProcess.Paused -= debuggedProcess_DebuggingPaused; |
||||||
|
debuggedProcess.ExceptionThrown -= debuggedProcess_ExceptionThrown; |
||||||
|
debuggedProcess.Resumed -= debuggedProcess_DebuggingResumed; |
||||||
|
} |
||||||
|
debuggedProcess = process; |
||||||
|
if (debuggedProcess != null) { |
||||||
|
debuggedProcess.Paused += debuggedProcess_DebuggingPaused; |
||||||
|
debuggedProcess.ExceptionThrown += debuggedProcess_ExceptionThrown; |
||||||
|
debuggedProcess.Resumed += debuggedProcess_DebuggingResumed; |
||||||
|
|
||||||
|
debuggedProcess.BreakAtBeginning = BreakAtBeginning; |
||||||
|
} |
||||||
|
// reset
|
||||||
|
BreakAtBeginning = false; |
||||||
|
|
||||||
|
JumpToCurrentLine(); |
||||||
|
OnProcessSelected(new ProcessEventArgs(process)); |
||||||
|
} |
||||||
|
|
||||||
|
void debuggedProcess_DebuggingPaused(object sender, ProcessEventArgs e) |
||||||
|
{ |
||||||
|
OnIsProcessRunningChanged(EventArgs.Empty); |
||||||
|
|
||||||
|
//using(new PrintTimes("Jump to current line")) {
|
||||||
|
JumpToCurrentLine(); |
||||||
|
//}
|
||||||
|
// TODO update tooltip
|
||||||
|
/*if (currentTooltipRow != null && currentTooltipRow.IsShown) { |
||||||
|
using(new PrintTimes("Update tooltip")) { |
||||||
|
try { |
||||||
|
Utils.DoEvents(debuggedProcess); |
||||||
|
AbstractNode updatedNode = ValueNode.Create(currentTooltipExpression); |
||||||
|
currentTooltipRow.SetContentRecursive(updatedNode); |
||||||
|
} catch (AbortedBecauseDebuggeeResumedException) { |
||||||
|
} |
||||||
|
} |
||||||
|
}*/ |
||||||
|
} |
||||||
|
|
||||||
|
void debuggedProcess_DebuggingResumed(object sender, CorDbg.ProcessEventArgs e) |
||||||
|
{ |
||||||
|
OnIsProcessRunningChanged(EventArgs.Empty); |
||||||
|
DebuggerService.RemoveCurrentLineMarker(); |
||||||
|
} |
||||||
|
|
||||||
|
void debuggedProcess_ExceptionThrown(object sender, CorDbg.ExceptionEventArgs e) |
||||||
|
{ |
||||||
|
if (!e.IsUnhandled) { |
||||||
|
// Ignore the exception
|
||||||
|
e.Process.AsyncContinue(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
JumpToCurrentLine(); |
||||||
|
|
||||||
|
StringBuilder stacktraceBuilder = new StringBuilder(); |
||||||
|
|
||||||
|
// Need to intercept now so that we can evaluate properties
|
||||||
|
if (e.Process.SelectedThread.InterceptCurrentException()) { |
||||||
|
stacktraceBuilder.AppendLine(e.Exception.ToString()); |
||||||
|
string stackTrace; |
||||||
|
try { |
||||||
|
stackTrace = e.Exception.GetStackTrace("--- End of inner exception stack trace ---"); |
||||||
|
} catch (GetValueException) { |
||||||
|
stackTrace = e.Process.SelectedThread.GetStackTrace("at {0} in {1}:line {2}", "at {0}"); |
||||||
|
} |
||||||
|
stacktraceBuilder.Append(stackTrace); |
||||||
|
} else { |
||||||
|
// For example, happens on stack overflow
|
||||||
|
stacktraceBuilder.AppendLine("CannotInterceptException"); |
||||||
|
stacktraceBuilder.AppendLine(e.Exception.ToString()); |
||||||
|
stacktraceBuilder.Append(e.Process.SelectedThread.GetStackTrace("at {0} in {1}:line {2}", "at {0}")); |
||||||
|
} |
||||||
|
|
||||||
|
string title = e.IsUnhandled ? "Unhandled" : "Handled"; |
||||||
|
string message = string.Format("Message {0} {1}", e.Exception.Type, e.Exception.Message); |
||||||
|
|
||||||
|
MessageBox.Show(message + stacktraceBuilder.ToString(), title); |
||||||
|
} |
||||||
|
|
||||||
|
public void JumpToCurrentLine() |
||||||
|
{ |
||||||
|
DebuggerService.RemoveCurrentLineMarker(); |
||||||
|
if (debuggedProcess != null) { |
||||||
|
SourcecodeSegment nextStatement = debuggedProcess.NextStatement; |
||||||
|
if (nextStatement != null) { |
||||||
|
DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void ShowAttachDialog() |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue