Browse Source

Stepping in IL debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
00d01f24ba
  1. 75
      Debugger/Debugger.Core/Breakpoint.cs
  2. 27
      Debugger/Debugger.Core/SourcecodeSegment.cs
  3. 32
      Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs
  4. 53
      Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs
  5. 8
      Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj
  6. 7
      Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs
  7. 132
      Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs
  8. 1
      Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml
  9. 22
      Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs
  10. 60
      ICSharpCode.Decompiler/Disassembler/CodeMappings.cs
  11. 8
      ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
  12. 14
      ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  13. 12
      ILSpy/Commands/RoutedUICommands.cs
  14. 4
      ILSpy/ILSpy.csproj
  15. BIN
      ILSpy/Images/ContinueDebugging.png
  16. BIN
      ILSpy/Images/StepInto.png
  17. BIN
      ILSpy/Images/StepOut.png
  18. BIN
      ILSpy/Images/StepOver.png
  19. 36
      ILSpy/MainWindow.xaml
  20. 81
      ILSpy/MainWindow.xaml.cs
  21. 12
      ILSpy/TextView/DecompilerTextView.cs
  22. 2
      ILSpy/TreeNodes/TypeTreeNode.cs

75
Debugger/Debugger.Core/Breakpoint.cs

@ -19,9 +19,9 @@ namespace Debugger
int column; int column;
bool enabled; bool enabled;
SourcecodeSegment originalLocation; protected SourcecodeSegment originalLocation;
List<ICorDebugFunctionBreakpoint> corBreakpoints = new List<ICorDebugFunctionBreakpoint>(); protected List<ICorDebugFunctionBreakpoint> corBreakpoints = new List<ICorDebugFunctionBreakpoint>();
public event EventHandler<BreakpointEventArgs> Hit; public event EventHandler<BreakpointEventArgs> Hit;
public event EventHandler<BreakpointEventArgs> Set; public event EventHandler<BreakpointEventArgs> Set;
@ -29,6 +29,7 @@ namespace Debugger
[Debugger.Tests.Ignore] [Debugger.Tests.Ignore]
public NDebugger Debugger { public NDebugger Debugger {
get { return debugger; } get { return debugger; }
protected set { debugger = value; }
} }
public string FileName { public string FileName {
@ -46,6 +47,7 @@ namespace Debugger
public int Column { public int Column {
get { return column; } get { return column; }
protected set { column = value; }
} }
public bool Enabled { public bool Enabled {
@ -62,8 +64,8 @@ namespace Debugger
get { return originalLocation; } get { return originalLocation; }
} }
public bool IsSet { public bool IsSet {
get { get {
return corBreakpoints.Count > 0; return corBreakpoints.Count > 0;
} }
} }
@ -94,6 +96,8 @@ namespace Debugger
this.corBreakpoints.Add(corBreakpoint); this.corBreakpoints.Add(corBreakpoint);
} }
public Breakpoint() { }
public Breakpoint(NDebugger debugger, string fileName, byte[] checkSum, int line, int column, bool enabled) public Breakpoint(NDebugger debugger, string fileName, byte[] checkSum, int line, int column, bool enabled)
{ {
this.debugger = debugger; this.debugger = debugger;
@ -104,7 +108,7 @@ namespace Debugger
this.enabled = enabled; this.enabled = enabled;
} }
internal bool IsOwnerOf(ICorDebugBreakpoint breakpoint) internal bool IsOwnerOf(ICorDebugBreakpoint breakpoint)
{ {
foreach(ICorDebugFunctionBreakpoint corFunBreakpoint in corBreakpoints) { foreach(ICorDebugFunctionBreakpoint corFunBreakpoint in corBreakpoints) {
if (((ICorDebugBreakpoint)corFunBreakpoint).Equals(breakpoint)) return true; if (((ICorDebugBreakpoint)corFunBreakpoint).Equals(breakpoint)) return true;
@ -116,18 +120,18 @@ namespace Debugger
{ {
foreach(ICorDebugFunctionBreakpoint corBreakpoint in corBreakpoints) { foreach(ICorDebugFunctionBreakpoint corBreakpoint in corBreakpoints) {
#if DEBUG #if DEBUG
// Get repro // Get repro
corBreakpoint.Activate(0); corBreakpoint.Activate(0);
#else #else
try { try {
corBreakpoint.Activate(0); corBreakpoint.Activate(0);
} catch(COMException e) { } catch(COMException e) {
// Sometimes happens, but we had not repro yet. // Sometimes happens, but we had not repro yet.
// 0x80131301: Process was terminated. // 0x80131301: Process was terminated.
if ((uint)e.ErrorCode == 0x80131301) if ((uint)e.ErrorCode == 0x80131301)
continue; continue;
throw; throw;
} }
#endif #endif
} }
corBreakpoints.Clear(); corBreakpoints.Clear();
@ -138,9 +142,9 @@ namespace Debugger
corBreakpoints.Clear(); corBreakpoints.Clear();
} }
internal bool SetBreakpoint(Module module) internal virtual bool SetBreakpoint(Module module)
{ {
if (this.fileName == null) if (this.fileName == null)
return false; return false;
SourcecodeSegment segment = SourcecodeSegment.Resolve(module, FileName, CheckSum, Line, Column); SourcecodeSegment segment = SourcecodeSegment.Resolve(module, FileName, CheckSum, Line, Column);
@ -165,6 +169,41 @@ namespace Debugger
} }
} }
public class ILBreakpoint : Breakpoint
{
public ILBreakpoint(NDebugger debugger, int line, uint metadataToken, int ilOffset, bool enabled)
{
this.Debugger = debugger;
this.Line = line;
this.MetadataToken = metadataToken;
this.ILOffset = ilOffset;
this.Enabled = enabled;
}
public uint MetadataToken { get; private set; }
public int ILOffset { get; private set; }
internal override bool SetBreakpoint(Module module)
{
SourcecodeSegment segment = SourcecodeSegment.CreateForIL(module, this.Line, (int)MetadataToken, ILOffset);
try {
ICorDebugFunctionBreakpoint corBreakpoint = segment.CorFunction.GetILCode().CreateBreakpoint((uint)segment.ILStart);
corBreakpoint.Activate(Enabled ? 1 : 0);
corBreakpoints.Add(corBreakpoint);
OnSet(new BreakpointEventArgs(this));
return true;
}
catch (COMException) {
return false;
}
}
}
[Serializable] [Serializable]
public class BreakpointEventArgs : DebuggerEventArgs public class BreakpointEventArgs : DebuggerEventArgs
{ {

27
Debugger/Debugger.Core/SourcecodeSegment.cs

@ -16,6 +16,7 @@ namespace Debugger
Module module; Module module;
string filename; string filename;
string typename;
byte[] checkSum; byte[] checkSum;
int startLine; int startLine;
int startColumn; int startColumn;
@ -35,6 +36,10 @@ namespace Debugger
get { return filename; } get { return filename; }
} }
public string Typename {
get { return typename; }
}
public byte[] CheckSum { public byte[] CheckSum {
get { return checkSum; } get { return checkSum; }
} }
@ -336,7 +341,27 @@ namespace Debugger
public override string ToString() public override string ToString()
{ {
return string.Format("{0}:{1},{2}-{3},{4}", Path.GetFileName(this.Filename), this.startLine, this.startColumn, this.endLine, this.endColumn); return string.Format("{0}:{1},{2}-{3},{4}",
Path.GetFileName(this.Filename ?? string.Empty),
this.startLine, this.startColumn, this.endLine, this.endColumn);
}
public static SourcecodeSegment CreateForIL(Module module, int line, int metadataToken, int iLOffset)
{
SourcecodeSegment segment = new SourcecodeSegment();
segment.module = module;
segment.typename = null;
segment.checkSum = null;
segment.startLine = line;
segment.startColumn = 0;
segment.endLine = line;
segment.endColumn = 0;
segment.corFunction = module.CorModule.GetFunctionFromToken((uint)metadataToken);
segment.ilStart = iLOffset;
segment.ilEnd = iLOffset;
segment.stepRanges = null;
return segment;
} }
} }
} }

32
Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs

@ -27,31 +27,23 @@ using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
using ILSpy.Debugger.Bookmarks; using ILSpy.Debugger.Bookmarks;
using ILSpy.Debugger.Services; using ILSpy.Debugger.Services;
using Mono.Cecil;
namespace ILSpy.Debugger.AvalonEdit namespace ILSpy.Debugger.AvalonEdit
{ {
public class IconBarMargin : AbstractMargin, IDisposable public class IconBarMargin : AbstractMargin, IDisposable
{ {
static string currentTypeName; static TypeDefinition currentTypeName;
public static string CurrentTypeName { public static TypeDefinition CurrentType {
get { get { return currentTypeName; }
return currentTypeName; }
set { currentTypeName = value; } set { currentTypeName = value; }
} }
#region OnTextViewChanged
/// <inheritdoc/>
protected override void OnTextViewChanged(TextView oldTextView, TextView newTextView)
{
InvalidateVisual();
}
public virtual void Dispose() public virtual void Dispose()
{ {
this.TextView = null; // detach from TextView (will also detach from manager) this.TextView = null; // detach from TextView (will also detach from manager)
} }
#endregion
/// <inheritdoc/> /// <inheritdoc/>
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
@ -80,7 +72,7 @@ namespace ILSpy.Debugger.AvalonEdit
// create a dictionary line number => first bookmark // create a dictionary line number => first bookmark
Dictionary<int, BookmarkBase> bookmarkDict = new Dictionary<int, BookmarkBase>(); Dictionary<int, BookmarkBase> bookmarkDict = new Dictionary<int, BookmarkBase>();
foreach (var bm in BookmarkManager.Bookmarks) { foreach (var bm in BookmarkManager.Bookmarks) {
if (bm.TypeName != IconBarMargin.CurrentTypeName) if (IconBarMargin.CurrentType == null || bm.TypeName != IconBarMargin.CurrentType.FullName)
continue; continue;
int line = bm.LineNumber; int line = bm.LineNumber;
@ -139,7 +131,9 @@ namespace ILSpy.Debugger.AvalonEdit
{ {
BookmarkBase result = null; BookmarkBase result = null;
foreach (BookmarkBase bm in BookmarkManager.Bookmarks) { foreach (BookmarkBase bm in BookmarkManager.Bookmarks) {
if (bm.LineNumber == line && bm.TypeName == IconBarMargin.CurrentTypeName) { if (bm.LineNumber == line &&
IconBarMargin.CurrentType != null &&
bm.TypeName == IconBarMargin.CurrentType.FullName) {
if (result == null || bm.ZOrder > result.ZOrder) if (result == null || bm.ZOrder > result.ZOrder)
result = bm; result = bm;
} }
@ -225,17 +219,17 @@ namespace ILSpy.Debugger.AvalonEdit
IBookmark bm = GetBookmarkFromLine(line); IBookmark bm = GetBookmarkFromLine(line);
if (bm != null) { if (bm != null) {
bm.MouseUp(e); bm.MouseUp(e);
if (!string.IsNullOrEmpty(IconBarMargin.CurrentTypeName)) { if (IconBarMargin.CurrentType != null) {
DebuggerService.ToggleBreakpointAt(IconBarMargin.CurrentTypeName, line); DebuggerService.ToggleBreakpointAt(IconBarMargin.CurrentType.FullName, line);
} }
InvalidateVisual(); InvalidateVisual();
if (e.Handled) if (e.Handled)
return; return;
} }
if (e.ChangedButton == MouseButton.Left && TextView != null) { if (e.ChangedButton == MouseButton.Left && TextView != null) {
if (!string.IsNullOrEmpty(IconBarMargin.CurrentTypeName)) { if (IconBarMargin.CurrentType != null) {
// no bookmark on the line: create a new breakpoint // no bookmark on the line: create a new breakpoint
DebuggerService.ToggleBreakpointAt(IconBarMargin.CurrentTypeName, line); DebuggerService.ToggleBreakpointAt(IconBarMargin.CurrentType.FullName, line);
} }
} }
InvalidateVisual(); InvalidateVisual();

53
Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs

@ -17,40 +17,19 @@ namespace ILSpy.Debugger.Bookmarks
static int endLine; static int endLine;
static int endColumn; static int endColumn;
// public static void SetPosition(IViewContent viewContent, int makerStartLine, int makerStartColumn, int makerEndLine, int makerEndColumn) public static void SetPosition(string typeName, int makerStartLine, int makerStartColumn, int makerEndLine, int makerEndColumn)
// { {
// ITextEditorProvider tecp = viewContent as ITextEditorProvider; Remove();
// if (tecp != null)
// SetPosition(tecp.TextEditor.FileName, tecp.TextEditor.Document, makerStartLine, makerStartColumn, makerEndLine, makerEndColumn); startLine = makerStartLine;
// else startColumn = makerStartColumn;
// Remove(); endLine = makerEndLine;
// } endColumn = makerEndColumn;
//
// public static void SetPosition(FileName fileName, IDocument document, int makerStartLine, int makerStartColumn, int makerEndLine, int makerEndColumn) instance = new CurrentLineBookmark(typeName, new Location(startColumn, startLine));
// { BookmarkManager.AddMark(instance);
// Remove(); }
//
// startLine = makerStartLine;
// startColumn = makerStartColumn;
// endLine = makerEndLine;
// endColumn = makerEndColumn;
//
// if (startLine < 1 || startLine > document.TotalNumberOfLines)
// return;
// if (endLine < 1 || endLine > document.TotalNumberOfLines) {
// endLine = startLine;
// endColumn = int.MaxValue;
// }
// if (startColumn < 1)
// startColumn = 1;
//
// IDocumentLine line = document.GetLine(startLine);
// if (endColumn < 1 || endColumn > line.Length)
// endColumn = line.Length;
// instance = new CurrentLineBookmark(fileName, new Location(startColumn, startLine));
// BookmarkManager.AddMark(instance);
// }
//
public static void Remove() public static void Remove()
{ {
if (instance != null) { if (instance != null) {
@ -60,9 +39,7 @@ namespace ILSpy.Debugger.Bookmarks
} }
public override bool CanToggle { public override bool CanToggle {
get { get { return false; }
return false;
}
} }
public override int ZOrder { public override int ZOrder {
@ -79,7 +56,7 @@ namespace ILSpy.Debugger.Bookmarks
} }
public override bool CanDragDrop { public override bool CanDragDrop {
get { return true; } get { return false; }
} }
public override void Drop(int lineNumber) public override void Drop(int lineNumber)

8
Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj

@ -108,6 +108,14 @@
<Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project> <Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project>
<Name>NRefactory</Name> <Name>NRefactory</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj">
<Project>{984CC812-9470-4A13-AFF9-CC44068D666C}</Project>
<Name>ICSharpCode.Decompiler</Name>
</ProjectReference>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
</ProjectReference>
<ProjectReference Include="..\Debugger.Core\Debugger.Core.csproj"> <ProjectReference Include="..\Debugger.Core\Debugger.Core.csproj">
<Project>{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}</Project> <Project>{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}</Project>
<Name>Debugger.Core</Name> <Name>Debugger.Core</Name>

7
Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs

@ -182,12 +182,9 @@ namespace ILSpy.Debugger.Services
CurrentLineBookmark.Remove(); CurrentLineBookmark.Remove();
} }
public static void JumpToCurrentLine(string SourceFullFilename, int StartLine, int StartColumn, int EndLine, int EndColumn) public static void JumpToCurrentLine(string typeName, int startLine, int startColumn, int endLine, int endColumn)
{ {
// IViewContent viewContent = FileService.OpenFile(SourceFullFilename); CurrentLineBookmark.SetPosition(typeName, startLine, startColumn, endLine, endColumn);
// if (viewContent is ITextEditorProvider)
// ((ITextEditorProvider)viewContent).TextEditor.JumpTo(StartLine, StartColumn);
// CurrentLineBookmark.SetPosition(viewContent, StartLine, StartColumn, EndLine, EndColumn);
} }
#region Tool tips #region Tool tips

132
Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs

@ -9,12 +9,14 @@ using System.Windows.Media;
using Debugger; using Debugger;
using Debugger.Interop.CorPublish; using Debugger.Interop.CorPublish;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.NRefactory; using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast; using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors; using ICSharpCode.NRefactory.Visitors;
using ILSpy.Debugger.Bookmarks; using ILSpy.Debugger.Bookmarks;
using ILSpy.Debugger.Models.TreeModel; using ILSpy.Debugger.Models.TreeModel;
using ILSpy.Debugger.Services.Debugger; using ILSpy.Debugger.Services.Debugger;
using Mono.Cecil.Cil;
using CorDbg = Debugger; using CorDbg = Debugger;
using Process = Debugger.Process; using Process = Debugger.Process;
@ -486,7 +488,13 @@ namespace ILSpy.Debugger.Services
void AddBreakpoint(BreakpointBookmark bookmark) void AddBreakpoint(BreakpointBookmark bookmark)
{ {
Breakpoint breakpoint = debugger.Breakpoints.Add(bookmark.TypeName, null, bookmark.LineNumber, 0, bookmark.IsEnabled); uint token;
ILCodeMapping map = CodeMappings.GetInstructionByTypeAndLine(bookmark.TypeName, bookmark.LineNumber, out token);
if (map != null) {
Breakpoint breakpoint = new ILBreakpoint(debugger, bookmark.LineNumber, token, map.ILInstruction.Offset , bookmark.IsEnabled);
debugger.Breakpoints.Add(breakpoint);
// Action setBookmarkColor = delegate { // Action setBookmarkColor = delegate {
// if (debugger.Processes.Count == 0) { // if (debugger.Processes.Count == 0) {
// bookmark.IsHealthy = true; // bookmark.IsHealthy = true;
@ -515,64 +523,65 @@ namespace ILSpy.Debugger.Services
// } // }
// } // }
// }; // };
// event handlers on bookmark and breakpoint don't need deregistration // event handlers on bookmark and breakpoint don't need deregistration
bookmark.IsEnabledChanged += delegate { bookmark.IsEnabledChanged += delegate {
breakpoint.Enabled = bookmark.IsEnabled; breakpoint.Enabled = bookmark.IsEnabled;
}; };
breakpoint.Set += delegate { breakpoint.Set += delegate {
//setBookmarkColor(); //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(); //setBookmarkColor();
};
EventHandler<CollectionItemEventArgs<Process>> bp_debugger_ProcessStarted = (sender, e) => {
EventHandler<BreakpointEventArgs> bp_debugger_BreakpointHit = //setBookmarkColor();
new EventHandler<BreakpointEventArgs>( // User can change line number by inserting or deleting lines
delegate(object sender, BreakpointEventArgs e) breakpoint.Line = bookmark.LineNumber;
{ };
//LoggingService.Debug(bookmark.Action + " " + bookmark.ScriptLanguage + " " + bookmark.Condition); EventHandler<CollectionItemEventArgs<Process>> bp_debugger_ProcessExited = (sender, e) => {
//setBookmarkColor();
switch (bookmark.Action) { };
case BreakpointAction.Break:
break; EventHandler<BreakpointEventArgs> bp_debugger_BreakpointHit =
case BreakpointAction.Condition: 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)) // 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)); // DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAtBecause}") + "\n", bookmark.LineNumber, bookmark.FileName, bookmark.Condition));
// else // else
// this.debuggedProcess.AsyncContinue(); // this.debuggedProcess.AsyncContinue();
break; break;
case BreakpointAction.Trace: case BreakpointAction.Trace:
//DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAt}") + "\n", bookmark.LineNumber, bookmark.FileName)); //DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAt}") + "\n", bookmark.LineNumber, bookmark.FileName));
break; break;
} }
}); });
BookmarkEventHandler bp_bookmarkManager_Removed = null; BookmarkEventHandler bp_bookmarkManager_Removed = null;
bp_bookmarkManager_Removed = (sender, e) => { bp_bookmarkManager_Removed = (sender, e) => {
if (bookmark == e.Bookmark) { if (bookmark == e.Bookmark) {
debugger.Breakpoints.Remove(breakpoint); debugger.Breakpoints.Remove(breakpoint);
// unregister the events // unregister the events
debugger.Processes.Added -= bp_debugger_ProcessStarted; debugger.Processes.Added -= bp_debugger_ProcessStarted;
debugger.Processes.Removed -= bp_debugger_ProcessExited; debugger.Processes.Removed -= bp_debugger_ProcessExited;
breakpoint.Hit -= bp_debugger_BreakpointHit; breakpoint.Hit -= bp_debugger_BreakpointHit;
BookmarkManager.Removed -= bp_bookmarkManager_Removed; BookmarkManager.Removed -= bp_bookmarkManager_Removed;
} }
}; };
// register the events // register the events
debugger.Processes.Added += bp_debugger_ProcessStarted; debugger.Processes.Added += bp_debugger_ProcessStarted;
debugger.Processes.Removed += bp_debugger_ProcessExited; debugger.Processes.Removed += bp_debugger_ProcessExited;
breakpoint.Hit += bp_debugger_BreakpointHit; breakpoint.Hit += bp_debugger_BreakpointHit;
BookmarkManager.Removed += bp_bookmarkManager_Removed; BookmarkManager.Removed += bp_bookmarkManager_Removed;
}
} }
bool Evaluate(string code, string language) bool Evaluate(string code, string language)
@ -652,7 +661,7 @@ namespace ILSpy.Debugger.Services
OnIsProcessRunningChanged(EventArgs.Empty); OnIsProcessRunningChanged(EventArgs.Empty);
//using(new PrintTimes("Jump to current line")) { //using(new PrintTimes("Jump to current line")) {
JumpToCurrentLine(); JumpToCurrentLine();
//} //}
// TODO update tooltip // TODO update tooltip
/*if (currentTooltipRow != null && currentTooltipRow.IsShown) { /*if (currentTooltipRow != null && currentTooltipRow.IsShown) {
@ -711,10 +720,15 @@ namespace ILSpy.Debugger.Services
public void JumpToCurrentLine() public void JumpToCurrentLine()
{ {
DebuggerService.RemoveCurrentLineMarker(); DebuggerService.RemoveCurrentLineMarker();
if (debuggedProcess != null) {
SourcecodeSegment nextStatement = debuggedProcess.NextStatement; if (debuggedProcess != null && debuggedProcess.SelectedStackFrame != null) {
if (nextStatement != null) { uint token = (uint)debuggedProcess.SelectedStackFrame.MethodInfo.MetadataToken;
DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn); int ilOffset = debuggedProcess.SelectedStackFrame.IP;
int line;
string typeName;
CodeMappings.GetSourceCodeFromMetadataTokenAndOffset(token, ilOffset, out typeName, out line);
if (typeName != null) {
DebuggerService.JumpToCurrentLine(typeName, line, 0, line, 0);
} }
} }
} }

1
Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml

@ -16,6 +16,7 @@
Height="0.17*" /> Height="0.17*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<ListView <ListView
MouseDoubleClick="RunningProcesses_MouseDoubleClick"
x:Name="RunningProcesses"> x:Name="RunningProcesses">
<ListView.View> <ListView.View>
<GridView> <GridView>

22
Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs

@ -85,12 +85,7 @@ namespace ILSpy.Debugger.UI
RunningProcesses.ItemsSource = list; RunningProcesses.ItemsSource = list;
} }
void OnLoaded(object sender, RoutedEventArgs e) void Attach()
{
RefreshProcessList();
}
void AttachButton_Click(object sender, RoutedEventArgs e)
{ {
if (this.RunningProcesses.SelectedItem == null) if (this.RunningProcesses.SelectedItem == null)
return; return;
@ -101,6 +96,16 @@ namespace ILSpy.Debugger.UI
this.DialogResult = true; this.DialogResult = true;
} }
void OnLoaded(object sender, RoutedEventArgs e)
{
RefreshProcessList();
}
void AttachButton_Click(object sender, RoutedEventArgs e)
{
Attach();
}
void CancelButton_Click(object sender, RoutedEventArgs e) void CancelButton_Click(object sender, RoutedEventArgs e)
{ {
this.Close(); this.Close();
@ -110,5 +115,10 @@ namespace ILSpy.Debugger.UI
{ {
RefreshProcessList(); RefreshProcessList();
} }
void RunningProcesses_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Attach();
}
} }
} }

60
ICSharpCode.Decompiler/Disassembler/CodeMappings.cs

@ -17,7 +17,7 @@ namespace ICSharpCode.Decompiler.Disassembler
{ {
public string TypeName { get; set; } public string TypeName { get; set; }
public int MetadataToken { get; set; } public uint MetadataToken { get; set; }
public List<ILCodeMapping> MethodCodeMappings { get; set; } public List<ILCodeMapping> MethodCodeMappings { get; set; }
@ -63,4 +63,62 @@ namespace ICSharpCode.Decompiler.Disassembler
return -1; return -1;
} }
} }
public static class CodeMappings
{
static Dictionary<string, List<MethodMapping>> ilCodeMappings = new Dictionary<string, List<MethodMapping>>();
/// <summary>
/// Stores the source codes mappings: IL &lt;-&gt; editor lines
/// </summary>
public static Dictionary<string, List<MethodMapping>> ILSourceCodeMappings {
get { return ilCodeMappings; }
set { ilCodeMappings = value; }
}
public static ILCodeMapping GetInstructionByTypeAndLine(string typeName, int lineNumber, out uint metadataToken)
{
if (!ilCodeMappings.ContainsKey(typeName)) {
metadataToken = 0;
return null;
}
if (lineNumber <= 0) {
metadataToken = 0;
return null;
}
var methodMappings = ilCodeMappings[typeName];
foreach (var maping in methodMappings) {
var ilMap = maping.MethodCodeMappings.Find(m => m.SourceCodeLine == lineNumber);
if (ilMap != null) {
metadataToken = maping.MetadataToken;
return ilMap;
}
}
metadataToken = 0;
return null;
}
public static void GetSourceCodeFromMetadataTokenAndOffset(uint token, int ilOffset, out string typeName, out int line)
{
typeName = null;
line = 0;
foreach (var typename in ilCodeMappings.Keys) {
var mapping = ilCodeMappings[typename].Find(m => m.MetadataToken == token);
if (mapping == null)
continue;
var ilCodeMapping = mapping.MethodCodeMappings.Find(cm => cm.ILInstruction.Offset == ilOffset);
if (ilCodeMapping == null)
continue;
typeName = typename;
line = ilCodeMapping.SourceCodeLine;
break;
}
}
}
} }

8
ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs

@ -49,11 +49,11 @@ namespace ICSharpCode.Decompiler.Disassembler
{ {
// create mappings // create mappings
MethodMapping currentMethodMapping = null; MethodMapping currentMethodMapping = null;
if (ReflectionDisassembler.ILSourceCodeMappings.ContainsKey(body.Method.DeclaringType.FullName)) { if (CodeMappings.ILSourceCodeMappings.ContainsKey(body.Method.DeclaringType.FullName)) {
var mapping = ReflectionDisassembler.ILSourceCodeMappings[body.Method.DeclaringType.FullName]; var mapping = CodeMappings.ILSourceCodeMappings[body.Method.DeclaringType.FullName];
if (mapping.Find(map => map.MetadataToken == body.Method.MetadataToken.ToInt32()) == null) { if (mapping.Find(map => (int)map.MetadataToken == body.Method.MetadataToken.ToInt32()) == null) {
currentMethodMapping = new MethodMapping() { currentMethodMapping = new MethodMapping() {
MetadataToken = body.Method.MetadataToken.ToInt32(), MetadataToken = (uint)body.Method.MetadataToken.ToInt32(),
TypeName = body.Method.DeclaringType.FullName, TypeName = body.Method.DeclaringType.FullName,
MethodCodeMappings = new List<ILCodeMapping>() MethodCodeMappings = new List<ILCodeMapping>()
}; };

14
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -36,16 +36,6 @@ namespace ICSharpCode.Decompiler.Disassembler
bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings) bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings)
MethodBodyDisassembler methodBodyDisassembler; MethodBodyDisassembler methodBodyDisassembler;
static Dictionary<string, List<MethodMapping>> ilCodeMappings = new Dictionary<string, List<MethodMapping>>();
/// <summary>
/// Stores the source codes mappings: IL &lt;-&gt; editor lines
/// </summary>
public static Dictionary<string, List<MethodMapping>> ILSourceCodeMappings {
get { return ilCodeMappings; }
set { ilCodeMappings = value; }
}
public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken) public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken)
{ {
if (output == null) if (output == null)
@ -112,8 +102,8 @@ namespace ICSharpCode.Decompiler.Disassembler
void DisassembleMethodInternal(MethodDefinition method) void DisassembleMethodInternal(MethodDefinition method)
{ {
// create mappings for types that were not disassebled // create mappings for types that were not disassebled
if (!ilCodeMappings.ContainsKey(method.DeclaringType.FullName)) { if (!CodeMappings.ILSourceCodeMappings.ContainsKey(method.DeclaringType.FullName)) {
ilCodeMappings.Add(method.DeclaringType.FullName, new List<MethodMapping>()); CodeMappings.ILSourceCodeMappings.Add(method.DeclaringType.FullName, new List<MethodMapping>());
} }
// .method public hidebysig specialname // .method public hidebysig specialname

12
ILSpy/Commands/RoutedUICommands.cs

@ -27,10 +27,22 @@ namespace ICSharpCode.ILSpy.Commands
{ {
AttachToProcess = new RoutedUICommand("Attach to running process...", "AttachToProcess", typeof(RoutedUICommands)); AttachToProcess = new RoutedUICommand("Attach to running process...", "AttachToProcess", typeof(RoutedUICommands));
DetachFromProcess = new RoutedUICommand("Detach from process...", "DetachFromProcess", typeof(RoutedUICommands)); DetachFromProcess = new RoutedUICommand("Detach from process...", "DetachFromProcess", typeof(RoutedUICommands));
ContinueDebugging = new RoutedUICommand("Continue debugging", "ContinueDebugging", typeof(RoutedUICommands));
StepInto = new RoutedUICommand("Step into", "StepInto", typeof(RoutedUICommands));
StepOver = new RoutedUICommand("Step over", "StepOver", typeof(RoutedUICommands));
StepOut = new RoutedUICommand("Step out", "StepOut", typeof(RoutedUICommands));
} }
public static RoutedUICommand AttachToProcess { get; private set; } public static RoutedUICommand AttachToProcess { get; private set; }
public static RoutedUICommand DetachFromProcess { get; private set; } public static RoutedUICommand DetachFromProcess { get; private set; }
public static RoutedUICommand ContinueDebugging { get; private set; }
public static RoutedUICommand StepInto { get; private set; }
public static RoutedUICommand StepOver { get; private set; }
public static RoutedUICommand StepOut { get; private set; }
} }
} }

4
ILSpy/ILSpy.csproj

@ -115,6 +115,10 @@
<Resource Include="Images\Delete.png" /> <Resource Include="Images\Delete.png" />
<Resource Include="Images\bug.png" /> <Resource Include="Images\bug.png" />
<None Include="app.config" /> <None Include="app.config" />
<Resource Include="Images\ContinueDebugging.png" />
<Resource Include="Images\StepInto.png" />
<Resource Include="Images\StepOut.png" />
<Resource Include="Images\StepOver.png" />
<None Include="Properties\AssemblyInfo.template.cs" /> <None Include="Properties\AssemblyInfo.template.cs" />
<Compile Include="Properties\WPFAssemblyInfo.cs" /> <Compile Include="Properties\WPFAssemblyInfo.cs" />
<Compile Include="MainWindow.xaml.cs"> <Compile Include="MainWindow.xaml.cs">

BIN
ILSpy/Images/ContinueDebugging.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

BIN
ILSpy/Images/StepInto.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

BIN
ILSpy/Images/StepOut.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

BIN
ILSpy/Images/StepOver.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

36
ILSpy/MainWindow.xaml

@ -27,6 +27,18 @@
<CommandBinding <CommandBinding
Command="routedCommands:RoutedUICommands.DetachFromProcess" Command="routedCommands:RoutedUICommands.DetachFromProcess"
Executed="DetachFromProcessExecuted" /> Executed="DetachFromProcessExecuted" />
<CommandBinding
Command="routedCommands:RoutedUICommands.ContinueDebugging"
Executed="ContinueDebuggingExecuted" />
<CommandBinding
Command="routedCommands:RoutedUICommands.StepInto"
Executed="StepIntoExecuted" />
<CommandBinding
Command="routedCommands:RoutedUICommands.StepOver"
Executed="StepOverExecuted" />
<CommandBinding
Command="routedCommands:RoutedUICommands.StepOut"
Executed="StepOutExecuted" />
<CommandBinding <CommandBinding
Command="Open" Command="Open"
Executed="OpenCommandExecuted" /> Executed="OpenCommandExecuted" />
@ -71,7 +83,29 @@
<Image Width="16" Height="16" Source="Images/bug.png" /> <Image Width="16" Height="16" Source="Images/bug.png" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem x:Name="DetachMenuItem" IsEnabled="False" Header="Detach from running application" Command="routedCommands:RoutedUICommands.DetachFromProcess"/> <MenuItem x:Name="ContinueDebuggingMenuItem" IsEnabled="False" Header="Continue debugging" InputGestureText="F5" Command="routedCommands:RoutedUICommands.ContinueDebugging">
<MenuItem.Icon>
<Image Width="16" Height="16" Source="Images/ContinueDebugging.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="StepIntoMenuItem" IsEnabled="False" Header="Step into" InputGestureText="F11" Command="routedCommands:RoutedUICommands.StepInto">
<MenuItem.Icon>
<Image Width="16" Height="16" Source="Images/StepInto.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="StepOverMenuItem" IsEnabled="False" Header="Step over" InputGestureText="F10" Command="routedCommands:RoutedUICommands.StepOver">
<MenuItem.Icon>
<Image Width="16" Height="16" Source="Images/StepOver.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="StepOutMenuItem" IsEnabled="False" Header="Step out" Command="routedCommands:RoutedUICommands.StepOut">
<MenuItem.Icon>
<Image Width="16" Height="16" Source="Images/StepOut.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="DetachMenuItem" IsEnabled="False"
Header="Detach from running application"
Command="routedCommands:RoutedUICommands.DetachFromProcess"/>
</MenuItem> </MenuItem>
<MenuItem Header="_Help"> <MenuItem Header="_Help">
<MenuItem Header="_About" Click="AboutClick" /> <MenuItem Header="_About" Click="AboutClick" />

81
ILSpy/MainWindow.xaml.cs

@ -322,6 +322,8 @@ namespace ICSharpCode.ILSpy
treeView.ScrollIntoView(lastNode); treeView.ScrollIntoView(lastNode);
} }
#region Debugger commands
void RefreshCommandExecuted(object sender, ExecutedRoutedEventArgs e) void RefreshCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{ {
e.Handled = true; e.Handled = true;
@ -332,23 +334,84 @@ namespace ICSharpCode.ILSpy
void AttachToProcessExecuted(object sender, ExecutedRoutedEventArgs e) void AttachToProcessExecuted(object sender, ExecutedRoutedEventArgs e)
{ {
var window = new AttachToProcessWindow(); if (!AttachToProcessWindow.Debugger.IsDebugging) {
window.Owner = this; var window = new AttachToProcessWindow();
if (window.ShowDialog() == true) window.Owner = this;
{ if (window.ShowDialog() == true)
AttachMenuItem.IsEnabled = AttachButton.IsEnabled = false; {
DetachMenuItem.IsEnabled = true; AttachMenuItem.IsEnabled = AttachButton.IsEnabled = false;
ContinueDebuggingMenuItem.IsEnabled =
StepIntoMenuItem.IsEnabled =
StepOverMenuItem.IsEnabled =
StepOutMenuItem.IsEnabled =
DetachMenuItem.IsEnabled = true;
}
} }
} }
void DetachFromProcessExecuted(object sender, ExecutedRoutedEventArgs e) void DetachFromProcessExecuted(object sender, ExecutedRoutedEventArgs e)
{ {
AttachToProcessWindow.Debugger.Detach(); if (AttachToProcessWindow.Debugger.IsDebugging){
AttachToProcessWindow.Debugger.Detach();
AttachMenuItem.IsEnabled = AttachButton.IsEnabled = true;
ContinueDebuggingMenuItem.IsEnabled =
StepIntoMenuItem.IsEnabled =
StepOverMenuItem.IsEnabled =
StepOutMenuItem.IsEnabled =
DetachMenuItem.IsEnabled = false;
}
}
void ContinueDebuggingExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (AttachToProcessWindow.Debugger.IsDebugging)
AttachToProcessWindow.Debugger.Continue();
}
void StepIntoExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (AttachToProcessWindow.Debugger.IsDebugging)
AttachToProcessWindow.Debugger.StepInto();
}
void StepOverExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (AttachToProcessWindow.Debugger.IsDebugging)
AttachToProcessWindow.Debugger.StepOver();
}
void StepOutExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (AttachToProcessWindow.Debugger.IsDebugging)
AttachToProcessWindow.Debugger.StepOut();
}
protected override void OnKeyUp(KeyEventArgs e)
{
switch (e.Key) {
case Key.F5:
ContinueDebuggingExecuted(null, null);
e.Handled = true;
break;
case Key.System:
StepOverExecuted(null, null);
e.Handled = true;
break;
case Key.F11:
StepIntoExecuted(null, null);
e.Handled = true;
break;
default:
// do nothing
break;
}
AttachMenuItem.IsEnabled = AttachButton.IsEnabled = true; base.OnKeyUp(e);
DetachMenuItem.IsEnabled = false;
} }
#endregion
void ExitClick(object sender, RoutedEventArgs e) void ExitClick(object sender, RoutedEventArgs e)
{ {
Close(); Close();

12
ILSpy/TextView/DecompilerTextView.cs

@ -80,6 +80,16 @@ namespace ICSharpCode.ILSpy.TextView
// add margin // add margin
iconMargin = new IconBarMargin(); iconMargin = new IconBarMargin();
textEditor.TextArea.LeftMargins.Add(iconMargin); textEditor.TextArea.LeftMargins.Add(iconMargin);
textEditor.TextArea.TextView.VisualLinesChanged += (s, e) => iconMargin.InvalidateVisual();
BookmarkManager.Added += BookmarkManager_Added;
BookmarkManager.Removed += (s, e) => iconMargin.InvalidateVisual();
}
void BookmarkManager_Added(object sender, BookmarkEventArgs e)
{
if (e.Bookmark is CurrentLineBookmark) {
iconMargin.InvalidateVisual();
}
} }
#endregion #endregion
@ -196,7 +206,7 @@ namespace ICSharpCode.ILSpy.TextView
/// </summary> /// </summary>
public void Decompile(ILSpy.Language language, IEnumerable<ILSpyTreeNodeBase> treeNodes, DecompilationOptions options) public void Decompile(ILSpy.Language language, IEnumerable<ILSpyTreeNodeBase> treeNodes, DecompilationOptions options)
{ {
IconBarMargin.CurrentTypeName = string.Empty; IconBarMargin.CurrentType = null;
// Some actions like loading an assembly list cause several selection changes in the tree view, // Some actions like loading an assembly list cause several selection changes in the tree view,
// and each of those will start a decompilation action. // and each of those will start a decompilation action.
bool isDecompilationScheduled = this.nextDecompilationRun != null; bool isDecompilationScheduled = this.nextDecompilationRun != null;

2
ILSpy/TreeNodes/TypeTreeNode.cs

@ -122,7 +122,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
IconBarMargin.CurrentTypeName = type.FullName; IconBarMargin.CurrentType = type;
language.DecompileType(type, output, options); language.DecompileType(type, output, options);
} }

Loading…
Cancel
Save