From 16d7bcde97b37f86eca414f5c07a613d759f4b51 Mon Sep 17 00:00:00 2001 From: Eusebiu Marcu Date: Fri, 25 Feb 2011 01:55:25 +0200 Subject: [PATCH] map code for assemblies without symbols --- Debugger/Debugger.Core/Breakpoint.cs | 6 +- Debugger/Debugger.Core/SourcecodeSegment.cs | 66 ++++++++--- Debugger/Debugger.Core/StackFrame.cs | 10 +- .../AvalonEdit/IconBarMargin.cs | 15 ++- .../Bookmarks/CurrentLineBookmark.cs | 4 + .../Services/Debugger/WindowsDebugger.cs | 108 ++++++++++++++---- .../ToolTips/DebuggerTooltipControl.xaml.cs | 9 ++ .../ToolTips/TextEditorListener.cs | 3 + ILSpy/MainWindow.xaml.cs | 16 +-- 9 files changed, 182 insertions(+), 55 deletions(-) diff --git a/Debugger/Debugger.Core/Breakpoint.cs b/Debugger/Debugger.Core/Breakpoint.cs index daad88595..898ce31e6 100644 --- a/Debugger/Debugger.Core/Breakpoint.cs +++ b/Debugger/Debugger.Core/Breakpoint.cs @@ -187,7 +187,8 @@ namespace Debugger internal override bool SetBreakpoint(Module module) { SourcecodeSegment segment = SourcecodeSegment.CreateForIL(module, this.Line, (int)MetadataToken, ILOffset); - + if (segment == null) + return false; try { ICorDebugFunctionBreakpoint corBreakpoint = segment.CorFunction.GetILCode().CreateBreakpoint((uint)segment.ILStart); corBreakpoint.Activate(Enabled ? 1 : 0); @@ -197,8 +198,7 @@ namespace Debugger OnSet(new BreakpointEventArgs(this)); return true; - } - catch (COMException) { + } catch { return false; } } diff --git a/Debugger/Debugger.Core/SourcecodeSegment.cs b/Debugger/Debugger.Core/SourcecodeSegment.cs index da433bc88..d00568c54 100644 --- a/Debugger/Debugger.Core/SourcecodeSegment.cs +++ b/Debugger/Debugger.Core/SourcecodeSegment.cs @@ -104,7 +104,7 @@ namespace Debugger yield return symUrl; yield break; } - + if (Path.IsPathRooted(symUrl)) { Dictionary returned = new Dictionary(); @@ -212,7 +212,7 @@ namespace Debugger segment.corFunction = module.CorModule.GetFunctionFromToken(symMethod.GetToken()); segment.ilStart = (int)sqPoint.Offset; segment.ilEnd = (int)sqPoint.Offset; - segment.stepRanges = null; + segment.stepRanges = null; return segment; } } @@ -341,27 +341,57 @@ namespace Debugger public override string ToString() { - return string.Format("{0}:{1},{2}-{3},{4}", - Path.GetFileName(this.Filename ?? string.Empty), + return string.Format("{0}:{1},{2}-{3},{4}", + Path.GetFileName(this.Filename ?? string.Empty), this.startLine, this.startColumn, this.endLine, this.endColumn); } + #region ILSpy + 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; + try { + 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; + } catch { + return null; + } } + + public static SourcecodeSegment ResolveForIL(Module module, ICorDebugFunction corFunction, int line, int offset, int[] ranges) + { + try { + 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 = corFunction; + segment.ilStart = offset; + segment.ilEnd = offset; + segment.stepRanges = ranges; + + return segment; + } catch { + return null; + } + } + + #endregion } } diff --git a/Debugger/Debugger.Core/StackFrame.cs b/Debugger/Debugger.Core/StackFrame.cs index 2f0858c23..75be28c06 100644 --- a/Debugger/Debugger.Core/StackFrame.cs +++ b/Debugger/Debugger.Core/StackFrame.cs @@ -139,9 +139,13 @@ namespace Debugger } } + public int[] ILRanges { get; set; } + + public int SourceCodeLine { get; set; } + SourcecodeSegment GetSegmentForOffet(int offset) { - return SourcecodeSegment.Resolve(this.MethodInfo.DebugModule, corFunction, offset); + return SourcecodeSegment.ResolveForIL(this.MethodInfo.DebugModule, corFunction, SourceCodeLine, offset, ILRanges); } /// Step into next instruction @@ -187,10 +191,6 @@ namespace Debugger void AsyncStep(bool stepIn) { - if (this.MethodInfo.DebugModule.HasSymbols == false) { - throw new DebuggerException("Unable to step. No symbols loaded."); - } - SourcecodeSegment nextSt = NextStatement; if (nextSt == null) { throw new DebuggerException("Unable to step. Next statement not aviable"); diff --git a/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs b/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs index 98876d212..c39a8af46 100644 --- a/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs +++ b/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs @@ -25,6 +25,7 @@ using System.Windows.Media; using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Utils; +using ICSharpCode.Decompiler; using ILSpy.Debugger.Bookmarks; using ILSpy.Debugger.Services; using Mono.Cecil; @@ -237,15 +238,27 @@ namespace ILSpy.Debugger.AvalonEdit BookmarkBase bm = GetBookmarkFromLine(line); if (bm != null) { bm.MouseUp(e); + if (bm.CanToggle) { BookmarkManager.RemoveMark(bm); + InvalidateVisual(); } - InvalidateVisual(); + if (e.Handled) return; } if (e.ChangedButton == MouseButton.Left) { if (CurrentType != null) { + + // check if the codemappings exists for this line + var storage = CodeMappings.GetStorage(DebuggerService.CurrentDebugger.Language); + uint token; + if (storage.GetInstructionByTypeAndLine(CurrentType.FullName, line, out token) == null) { + MessageBox.Show(string.Format("Missing code mappings for {0} at line {1}", CurrentType.FullName, line), + "Code mappings", MessageBoxButton.OK, MessageBoxImage.Information); + return; + } + // no bookmark on the line: create a new breakpoint DebuggerService.ToggleBreakpointAt( CurrentType.FullName, diff --git a/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs b/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs index d54b2d675..d1551f7f6 100644 --- a/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs +++ b/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs @@ -13,6 +13,10 @@ namespace ILSpy.Debugger.Bookmarks { static CurrentLineBookmark instance; + public static CurrentLineBookmark Instance { + get { return instance; } + } + static int startLine; static int startColumn; static int endLine; diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs index ef25e5223..89c43c54d 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs @@ -1,8 +1,10 @@ // 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.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows; @@ -45,6 +47,12 @@ namespace ILSpy.Debugger.Services //DynamicTreeDebuggerRow currentTooltipRow; //Expression currentTooltipExpression; + private ConcurrentDictionary> CodeMappingsStorage { + get { + return CodeMappings.GetStorage(Language); + } + } + public event EventHandler ProcessSelected; public NDebugger DebuggerCore { @@ -274,16 +282,43 @@ namespace ILSpy.Debugger.Services // Stepping: + SourceCodeMapping GetNextCodeMapping() + { + uint token; + var instruction = CodeMappingsStorage.GetInstructionByTypeAndLine( + CurrentLineBookmark.Instance.TypeName, + CurrentLineBookmark.Instance.LineNumber, out token); + + var val = CodeMappingsStorage[CurrentLineBookmark.Instance.TypeName]; + + var mapping = val.Find(m => m.MetadataToken == token); + + return mapping.MethodCodeMappings.FirstOrDefault(s => s.ILInstructionOffset.From <= instruction.ILInstructionOffset.To); + } + public void StepInto() { if (!IsDebugging) { MessageBox.Show(errorNotDebugging, "StepInto"); return; } - if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) { + + // use most recent stack frame because we don't have the symbols + if (debuggedProcess.SelectedThread == null || + debuggedProcess.SelectedThread.MostRecentStackFrame == null || + debuggedProcess.IsRunning) { MessageBox.Show(errorCannotStepNoActiveFunction, "StepInto"); } else { - debuggedProcess.SelectedStackFrame.AsyncStepInto(); + var map = GetNextCodeMapping(); + if (map == null) { + CurrentLineBookmark.Remove(); + Continue(); + } else { + var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; + frame.SourceCodeLine = map.SourceCodeLine; + frame.ILRanges = (new List { map.ILInstructionOffset.From, map.ILInstructionOffset.To }).ToArray(); + frame.AsyncStepInto(); + } } } @@ -292,11 +327,24 @@ namespace ILSpy.Debugger.Services if (!IsDebugging) { MessageBox.Show(errorNotDebugging, "StepOver"); return; + } - if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) { + // use most recent stack frame because we don't have the symbols + if (debuggedProcess.SelectedThread == null || + debuggedProcess.SelectedThread.MostRecentStackFrame == null || + debuggedProcess.IsRunning) { MessageBox.Show(errorCannotStepNoActiveFunction, "StepOver"); } else { - debuggedProcess.SelectedStackFrame.AsyncStepOver(); + var map = GetNextCodeMapping(); + if (map == null) { + CurrentLineBookmark.Remove(); + Continue(); + } else { + var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; + frame.SourceCodeLine = map.SourceCodeLine; + frame.ILRanges = (new List { map.ILInstructionOffset.From, map.ILInstructionOffset.To }).ToArray(); + frame.AsyncStepOver(); + } } } @@ -306,10 +354,23 @@ namespace ILSpy.Debugger.Services MessageBox.Show(errorNotDebugging, "StepOut"); return; } - if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) { + + // use most recent stack frame because we don't have the symbols + if (debuggedProcess.SelectedThread == null || + debuggedProcess.SelectedThread.MostRecentStackFrame == null || + debuggedProcess.IsRunning) { MessageBox.Show(errorCannotStepNoActiveFunction, "StepOut"); } else { - debuggedProcess.SelectedStackFrame.AsyncStepOut(); + var map = GetNextCodeMapping(); + if (map == null) { + CurrentLineBookmark.Remove(); + Continue(); + } else { + var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; + frame.SourceCodeLine = map.SourceCodeLine; + frame.ILRanges = (new List { map.ILInstructionOffset.From, map.ILInstructionOffset.To }).ToArray(); + frame.AsyncStepOut(); + } } } @@ -335,7 +396,7 @@ namespace ILSpy.Debugger.Services if (!CanEvaluate) { return null; } - return ExpressionEvaluator.Evaluate(variableName, SupportedLanguage.CSharp, debuggedProcess.SelectedStackFrame); + return ExpressionEvaluator.Evaluate(variableName, SupportedLanguage.CSharp, debuggedProcess.SelectedThread.MostRecentStackFrame); } /// @@ -381,7 +442,10 @@ namespace ILSpy.Debugger.Services public bool CanEvaluate { get { - return debuggedProcess != null && !debuggedProcess.IsRunning && debuggedProcess.SelectedStackFrame != null; + return debuggedProcess != null && + !debuggedProcess.IsRunning && + debuggedProcess.SelectedThread != null && + debuggedProcess.SelectedThread.MostRecentStackFrame != null; } } @@ -429,8 +493,9 @@ namespace ILSpy.Debugger.Services 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); + if (debuggedProcess != null && debuggedProcess.IsPaused && + debuggedProcess.SelectedThread != null && debuggedProcess.SelectedThread.MostRecentStackFrame != null) { + SourcecodeSegment seg = debuggedProcess.SelectedThread.MostRecentStackFrame.CanSetIP(filename, line, column); return seg != null; } else { return false; @@ -440,7 +505,7 @@ namespace ILSpy.Debugger.Services public bool SetInstructionPointer(string filename, int line, int column) { if (CanSetInstructionPointer(filename, line, column)) { - SourcecodeSegment seg = debuggedProcess.SelectedStackFrame.SetIP(filename, line, column); + SourcecodeSegment seg = debuggedProcess.SelectedThread.MostRecentStackFrame.SetIP(filename, line, column); return seg != null; } else { return false; @@ -498,12 +563,10 @@ namespace ILSpy.Debugger.Services { Breakpoint breakpoint = null; - var storage = CodeMappings.GetStorage(Language); - if (Language == bookmark.Language) { uint token; SourceCodeMapping map = - storage.GetInstructionByTypeAndLine( + CodeMappingsStorage.GetInstructionByTypeAndLine( bookmark.TypeName, bookmark.LineNumber, out token); if (map != null) { @@ -613,7 +676,7 @@ namespace ILSpy.Debugger.Services { try { SupportedLanguage supportedLanguage = (SupportedLanguage)Enum.Parse(typeof(SupportedLanguage), language, true); - Value val = ExpressionEvaluator.Evaluate(code, supportedLanguage, debuggedProcess.SelectedStackFrame); + Value val = ExpressionEvaluator.Evaluate(code, supportedLanguage, debuggedProcess.SelectedThread.MostRecentStackFrame); if (val != null && val.Type.IsPrimitive && val.PrimitiveValue is bool) return (bool)val.PrimitiveValue; @@ -746,14 +809,19 @@ namespace ILSpy.Debugger.Services { DebuggerService.RemoveCurrentLineMarker(); - if (debuggedProcess != null && debuggedProcess.SelectedStackFrame != null) { - var storage = CodeMappings.GetStorage(Language); + if (debuggedProcess != null && debuggedProcess.SelectedThread != null) { + + // use most recent stack frame because we don't have the symbols + var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; + + if (frame == null) + return; - uint token = (uint)debuggedProcess.SelectedStackFrame.MethodInfo.MetadataToken; - int ilOffset = debuggedProcess.SelectedStackFrame.IP; + uint token = (uint)frame.MethodInfo.MetadataToken; + int ilOffset = frame.IP; int line; string typeName; - storage.GetSourceCodeFromMetadataTokenAndOffset(token, ilOffset, out typeName, out line); + CodeMappingsStorage.GetSourceCodeFromMetadataTokenAndOffset(token, ilOffset, out typeName, out line); if (typeName != null) DebuggerService.JumpToCurrentLine(typeName, line, 0, line, 0); } diff --git a/Debugger/ILSpy.Debugger/ToolTips/DebuggerTooltipControl.xaml.cs b/Debugger/ILSpy.Debugger/ToolTips/DebuggerTooltipControl.xaml.cs index dff6e3357..6cecdf846 100644 --- a/Debugger/ILSpy.Debugger/ToolTips/DebuggerTooltipControl.xaml.cs +++ b/Debugger/ILSpy.Debugger/ToolTips/DebuggerTooltipControl.xaml.cs @@ -227,17 +227,26 @@ namespace ILSpy.Debugger.Tooltips private void handleScroll(object sender, ScrollChangedEventArgs e) { + if (this.lazyGrid == null) + return; + btnUp.IsEnabled = !this.lazyGrid.IsScrolledToStart; btnDown.IsEnabled = !this.lazyGrid.IsScrolledToEnd; } void BtnUp_Click(object sender, RoutedEventArgs e) { + if (this.lazyGrid == null) + return; + this.lazyGrid.ScrollViewer.ScrollUp(1); } void BtnDown_Click(object sender, RoutedEventArgs e) { + if (this.lazyGrid == null) + return; + this.lazyGrid.ScrollViewer.ScrollDown(1); } diff --git a/Debugger/ILSpy.Debugger/ToolTips/TextEditorListener.cs b/Debugger/ILSpy.Debugger/ToolTips/TextEditorListener.cs index 612060006..10fc927f0 100644 --- a/Debugger/ILSpy.Debugger/ToolTips/TextEditorListener.cs +++ b/Debugger/ILSpy.Debugger/ToolTips/TextEditorListener.cs @@ -76,6 +76,9 @@ namespace ILSpy.Debugger.ToolTips toolTip.IsOpen = false; TryCloseExistingPopup(true); + + if (popup != null) + popup.IsOpen = false; } void OnMouseHoverStopped(MouseEventArgs e) diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index b95b636a2..7a9cdf91d 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -298,10 +298,6 @@ namespace ICSharpCode.ILSpy } } - #endregion - - #region Debugger commands - void RefreshCommandExecuted(object sender, ExecutedRoutedEventArgs e) { if (!DebuggerService.CurrentDebugger.IsDebugging) { @@ -312,6 +308,10 @@ namespace ICSharpCode.ILSpy } } + #endregion + + #region Debugger commands + void AttachToProcessExecuted(object sender, ExecutedRoutedEventArgs e) { if (!DebuggerService.CurrentDebugger.IsDebugging) { @@ -335,7 +335,7 @@ namespace ICSharpCode.ILSpy void DetachFromProcessExecuted(object sender, ExecutedRoutedEventArgs e) { - if (DebuggerService.CurrentDebugger.IsDebugging){ + if (DebuggerService.CurrentDebugger.IsDebugging && !DebuggerService.CurrentDebugger.IsProcessRunning){ DebuggerService.CurrentDebugger.Detach(); EnableDebuggerUI(true); @@ -351,19 +351,19 @@ namespace ICSharpCode.ILSpy void StepIntoExecuted(object sender, ExecutedRoutedEventArgs e) { - if (DebuggerService.CurrentDebugger.IsDebugging) + if (DebuggerService.CurrentDebugger.IsDebugging && !DebuggerService.CurrentDebugger.IsProcessRunning) DebuggerService.CurrentDebugger.StepInto(); } void StepOverExecuted(object sender, ExecutedRoutedEventArgs e) { - if (DebuggerService.CurrentDebugger.IsDebugging) + if (DebuggerService.CurrentDebugger.IsDebugging && !DebuggerService.CurrentDebugger.IsProcessRunning) DebuggerService.CurrentDebugger.StepOver(); } void StepOutExecuted(object sender, ExecutedRoutedEventArgs e) { - if (DebuggerService.CurrentDebugger.IsDebugging) + if (DebuggerService.CurrentDebugger.IsDebugging && !DebuggerService.CurrentDebugger.IsProcessRunning) DebuggerService.CurrentDebugger.StepOut(); }