diff --git a/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptions.cs b/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptions.cs index 333ccbe98a..8192c68cbf 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptions.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptions.cs @@ -29,10 +29,19 @@ namespace ICSharpCode.SharpDevelop.Services set { PS.Set("Debugger.EnableJustMyCode", value); } } - // This enables decompilation - public override bool StepOverNoSymbols { - get { return PS.Get("Debugger.StepOverNoSymbols", true); } - set { PS.Set("Debugger.StepOverNoSymbols", value); } + public override bool EnableEditAndContinue { + get { return PS.Get("Debugger.EnableEditAndContinue", true); } + set { PS.Set("Debugger.EnableEditAndContinue", value); } + } + + public override bool SuppressJITOptimization { + get { return PS.Get("Debugger.SuppressJITOptimization", true); } + set { PS.Set("Debugger.SuppressJITOptimization", value); } + } + + public override bool SuppressNGENOptimization { + get { return PS.Get("Debugger.SuppressNGENOptimization", true); } + set { PS.Set("Debugger.SuppressNGENOptimization", value); } } public override bool StepOverDebuggerAttributes { @@ -99,26 +108,5 @@ namespace ICSharpCode.SharpDevelop.Services get { return PS.Get("Debugger.DebuggeeExceptionWindowState", FormWindowState.Normal); } set { PS.Set("Debugger.DebuggeeExceptionWindowState", value); } } - - /// - /// Used to update status of some debugger properties while debugger is running. - /// - internal static void ResetStatus(Action resetStatus) - { - Process proc = WindowsDebugger.CurrentProcess; - // debug session is running - if (proc != null) { - bool wasPausedNow = false; - // if it is not paused, break execution - if (!proc.IsPaused) { - proc.Break(); - wasPausedNow = true; - } - resetStatus(proc); - // continue if it was not paused before - if (wasPausedNow) - proc.AsyncContinue(); - } - } } } diff --git a/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml b/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml index c7bbf833c7..5cc4caef34 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml +++ b/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml @@ -8,8 +8,6 @@ - + + + + + + + \ No newline at end of file diff --git a/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml.cs index 9b6c00485b..a09c630ad3 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml.cs @@ -30,7 +30,8 @@ namespace Debugger.AddIn.Options public override bool SaveOptions() { bool result = base.SaveOptions(); - DebuggingOptions.ResetStatus(proc => proc.Debugger.ResetJustMyCodeStatus()); + if (WindowsDebugger.CurrentDebugger != null) + WindowsDebugger.CurrentDebugger.ReloadOptions(); return result; } } diff --git a/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingSymbolsPanel.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingSymbolsPanel.xaml.cs index 0c6f0edeb5..16e0704b6e 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingSymbolsPanel.xaml.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingSymbolsPanel.xaml.cs @@ -45,11 +45,8 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels public override bool SaveOptions() { DebuggingOptions.Instance.SymbolsSearchPaths = editor.GetList(); - DebuggingOptions.ResetStatus( - proc => { - proc.Debugger.ReloadModuleSymbols(); - proc.Debugger.ResetJustMyCodeStatus(); - }); + if (WindowsDebugger.CurrentDebugger != null) + WindowsDebugger.CurrentDebugger.ReloadOptions(); return base.SaveOptions(); } } diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/CallStackPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/CallStackPad.cs index 81e14df6cd..244d34415f 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/CallStackPad.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/CallStackPad.cs @@ -103,12 +103,6 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads if (item.Frame.Process.IsPaused) { if (item.Frame != null) { - // check for options - if these options are enabled, selecting the frame should not continue - if (!item.Frame.HasSymbols && !item.Frame.Process.Options.StepOverNoSymbols) { - MessageService.ShowMessage("${res:MainWindow.Windows.Debug.CallStack.CannotSwitchWithoutSymbolsOrDecompiledCodeOptions}", - "${res:MainWindow.Windows.Debug.CallStack.FunctionSwitch}"); - return; - } WindowsDebugger.CurrentStackFrame = item.Frame; WindowsDebugger.Instance.JumpToCurrentLine(); WindowsDebugger.RefreshPads(); @@ -138,13 +132,16 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads CallStackItem CreateItem(StackFrame frame, ref bool previousItemIsExternalMethod) { bool showExternalMethods = DebuggingOptions.Instance.ShowExternalMethods; - if (frame.HasSymbols || showExternalMethods) { + var symSource = WindowsDebugger.PdbSymbolSource; + bool hasSymbols = symSource.Handles(frame.MethodInfo) && !symSource.IsCompilerGenerated(frame.MethodInfo); + if (showExternalMethods || hasSymbols) { // Show the method in the list previousItemIsExternalMethod = false; return new CallStackItem() { Frame = frame, ImageSource = SD.ResourceService.GetImageSource("Icons.16x16.Method"), - Name = GetFullName(frame) + Name = GetFullName(frame), + HasSymbols = hasSymbols, }; } else { // Show [External methods] in the list @@ -152,7 +149,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads return null; previousItemIsExternalMethod = true; return new CallStackItem() { - Name = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ExternalMethods") + Name = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ExternalMethods"), + HasSymbols = false }; } } @@ -204,9 +202,10 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads public StackFrame Frame { get; set; } public ImageSource ImageSource { get; set; } public string Name { get; set; } + public bool HasSymbols { get; set; } public Brush FontColor { - get { return this.Frame == null || this.Frame.HasSymbols ? Brushes.Black : Brushes.Gray; } + get { return this.HasSymbols ? Brushes.Black : Brushes.Gray; } } } } diff --git a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs b/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs index 4eaf8b20d2..e62001b7ba 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs @@ -36,6 +36,8 @@ namespace ICSharpCode.SharpDevelop.Services public static Thread CurrentThread { get; set; } public static StackFrame CurrentStackFrame { get; set; } + public static PdbSymbolSource PdbSymbolSource = new PdbSymbolSource(); + public static Action RefreshingPads; public static void RefreshPads() @@ -78,8 +80,6 @@ namespace ICSharpCode.SharpDevelop.Services ICorPublish corPublish; - List symbolSources = new List(); - /// public bool BreakAtBeginning { get; set; } @@ -152,8 +152,6 @@ namespace ICSharpCode.SharpDevelop.Services UpdateBreakpointLines(); try { - // set the JIT flag for evaluating optimized code - Process.DebugMode = DebugModeFlag.Debug; CurrentProcess = CurrentDebugger.Start(processStartInfo.FileName, processStartInfo.WorkingDirectory, processStartInfo.Arguments, this.BreakAtBeginning); debugger_ProcessStarted(); } catch (System.Exception e) { @@ -213,8 +211,6 @@ namespace ICSharpCode.SharpDevelop.Services UpdateBreakpointLines(); try { - // set the JIT flag for evaluating optimized code - Process.DebugMode = DebugModeFlag.Debug; CurrentProcess = CurrentDebugger.Attach(existingProcess); debugger_ProcessStarted(); attached = true; @@ -348,12 +344,14 @@ namespace ICSharpCode.SharpDevelop.Services public void InitializeService() { - symbolSources.Add(new PdbSymbolSource()); + List symbolSources = new List(); + symbolSources.Add(PdbSymbolSource); symbolSources.AddRange(AddInTree.BuildItems("/SharpDevelop/Services/DebuggerService/SymbolSource", null, false)); // init NDebugger CurrentDebugger = new NDebugger(); CurrentDebugger.Options = DebuggingOptions.Instance; + CurrentDebugger.SymbolSources = symbolSources; foreach (BreakpointBookmark b in SD.BookmarkManager.Bookmarks.OfType()) { AddBreakpoint(b); @@ -466,10 +464,6 @@ namespace ICSharpCode.SharpDevelop.Services CurrentThread = e.Thread; CurrentStackFrame = CurrentThread != null ? CurrentThread.MostRecentUserStackFrame : null; - // Select the symbol source - var symbolSource = symbolSources.FirstOrDefault(s => CurrentStackFrame == null || s.HasSymbols(CurrentStackFrame.MethodInfo)); - CurrentProcess.SymbolSource = symbolSource ?? symbolSources.First(); - // We can have several events happening at the same time bool breakProcess = e.Break; @@ -599,13 +593,10 @@ namespace ICSharpCode.SharpDevelop.Services return; SD.Workbench.MainWindow.Activate(); - - if (CurrentStackFrame.HasSymbols) { - SequencePoint nextStatement = CurrentStackFrame.NextStatement; - if (nextStatement != null) { - DebuggerService.RemoveCurrentLineMarker(); - DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn); - } + DebuggerService.RemoveCurrentLineMarker(); + SequencePoint nextStatement = CurrentStackFrame.NextStatement; + if (nextStatement != null) { + DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn); } } diff --git a/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs b/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs index e3688abf78..d0e378af01 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs @@ -249,13 +249,9 @@ namespace Debugger.AddIn.TreeModel var parCopy = par; yield return new ValueNode(ClassBrowserIconService.Parameter, par.Param.Name, () => GetCurrentStackFrame().GetArgumentValue(par.Index)); } - if (stackFrame.HasSymbols) { - foreach(LocalVariable locVar in stackFrame.GetLocalVariables(stackFrame.IP)) { - var locVarCopy = locVar; - yield return new ValueNode(ClassBrowserIconService.LocalVariable, locVar.Name, () => locVarCopy.GetValue(GetCurrentStackFrame())); - } - } else { - WindowsDebugger debugger = (WindowsDebugger)DebuggerService.CurrentDebugger; + foreach(LocalVariable locVar in stackFrame.GetLocalVariables(stackFrame.IP)) { + var locVarCopy = locVar; + yield return new ValueNode(ClassBrowserIconService.LocalVariable, locVar.Name, () => locVarCopy.GetValue(GetCurrentStackFrame())); } } diff --git a/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs b/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs index a7c819fc91..d36f9f3f18 100644 --- a/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs +++ b/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs @@ -67,17 +67,17 @@ namespace Debugger corBreakpoints.Clear(); } - public virtual bool SetBreakpoint(Module module) + public void SetBreakpoint(Module module) { - var seq = module.SymbolSource.GetSequencePoint(module, this.FileName, this.Line, this.Column); - if (seq != null) { - ICorDebugFunction corFuction = module.CorModule.GetFunctionFromToken(seq.MethodDefToken); - ICorDebugFunctionBreakpoint corBreakpoint = corFuction.GetILCode().CreateBreakpoint((uint)seq.ILOffset); - corBreakpoint.Activate(enabled ? 1 : 0); - corBreakpoints.Add(corBreakpoint); - return true; + foreach(var symbolSource in module.Process.Debugger.SymbolSources) { + var seq = symbolSource.GetSequencePoint(module, this.FileName, this.Line, this.Column); + if (seq != null) { + ICorDebugFunction corFuction = module.CorModule.GetFunctionFromToken(seq.MethodDefToken); + ICorDebugFunctionBreakpoint corBreakpoint = corFuction.GetILCode().CreateBreakpoint((uint)seq.ILOffset); + corBreakpoint.Activate(enabled ? 1 : 0); + corBreakpoints.Add(corBreakpoint); + } } - return false; } } } diff --git a/src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj b/src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj index ef2a7119d3..9803299e21 100644 --- a/src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj +++ b/src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj @@ -92,6 +92,7 @@ + diff --git a/src/AddIns/Debugger/Debugger.Core/Eval.cs b/src/AddIns/Debugger/Debugger.Core/Eval.cs index d605a26d45..c429adf549 100644 --- a/src/AddIns/Debugger/Debugger.Core/Eval.cs +++ b/src/AddIns/Debugger/Debugger.Core/Eval.cs @@ -187,8 +187,7 @@ namespace Debugger /// Synchronously calls a function and returns its return value public static Value InvokeMethod(Thread evalThread, IMethod method, Value thisValue, Value[] args) { - Module module = method.DeclaringTypeDefinition.ParentAssembly.GetModule(); - uint fieldToken = module.GetBackingFieldToken(method.ToCorFunction()); + uint fieldToken = method.GetBackingFieldToken(); if (fieldToken != 0) { var field = method.DeclaringType.ImportField(fieldToken); if (field != null) { diff --git a/src/AddIns/Debugger/Debugger.Core/LocalVariable.cs b/src/AddIns/Debugger/Debugger.Core/LocalVariable.cs index 1eca86ba14..3e9aeef644 100644 --- a/src/AddIns/Debugger/Debugger.Core/LocalVariable.cs +++ b/src/AddIns/Debugger/Debugger.Core/LocalVariable.cs @@ -49,15 +49,13 @@ namespace Debugger.MetaData return this.Type.Name + " " + this.Name + (this.IsCaptured ? " (captured)" : ""); } - public static List GetLocalVariables(IMethod method) + public static List GetLocalVariables(ISymbolSource symbolSource, IMethod method) { var module = method.ParentAssembly.GetModule(); - if (!module.SymbolSource.HasSymbols(method)) - return null; List localVariables = new List(); - foreach (ILLocalVariable ilvar in module.SymbolSource.GetLocalVariables(method)) { + foreach (ILLocalVariable ilvar in symbolSource.GetLocalVariables(method)) { int index = ilvar.Index; // NB: Display class does not have the compiler-generated flag if (ilvar.IsCompilerGenerated || ilvar.Name.StartsWith("CS$")) { diff --git a/src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs b/src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs index 3d20dc0113..0151f5e46f 100644 --- a/src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs +++ b/src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs @@ -332,10 +332,10 @@ namespace Debugger EnterCallback("CreateProcess", pProcess); // Process is added in NDebugger.Start - // disable NGen if we want to do decompilation - if (!this.process.Options.StepOverNoSymbols) { + + if (this.process.Options.SuppressNGENOptimization) { ICorDebugProcess2 pProcess2 = pProcess as ICorDebugProcess2; - if (pProcess2 != null && Process.DebugMode == DebugModeFlag.Debug) { + if (pProcess2 != null) { try { pProcess2.SetDesiredNGENCompilerFlags((uint)CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION); } catch (COMException) { diff --git a/src/AddIns/Debugger/Debugger.Core/Module.cs b/src/AddIns/Debugger/Debugger.Core/Module.cs index d305de112a..5234c880ca 100644 --- a/src/AddIns/Debugger/Debugger.Core/Module.cs +++ b/src/AddIns/Debugger/Debugger.Core/Module.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading.Tasks; +using System.Linq; using Debugger.Interop; using Debugger.Interop.CorDebug; using Debugger.Interop.CorSym; @@ -46,10 +47,6 @@ namespace Debugger get { return process; } } - public ISymbolSource SymbolSource { - get { return this.Process.SymbolSource; } - } - NDebugger Debugger { get { return this.AppDomain.Process.Debugger; } } @@ -188,7 +185,7 @@ namespace Debugger SetJITCompilerFlags(); LoadSymbolsFromDisk(process.Options.SymbolsSearchPaths); - ResetJustMyCodeStatus(); + ResetJustMyCode(); LoadSymbolsDynamic(); } @@ -269,48 +266,50 @@ namespace Debugger foreach (Breakpoint b in this.Debugger.Breakpoints) { b.SetBreakpoint(this); } - ResetJustMyCodeStatus(); + ResetJustMyCode(); } void SetJITCompilerFlags() { - if (Process.DebugMode != DebugModeFlag.Default) { - // translate DebugModeFlags to JITCompilerFlags - CorDebugJITCompilerFlags jcf = MapDebugModeToJITCompilerFlags(Process.DebugMode); - - try - { - this.JITCompilerFlags = jcf; + CorDebugJITCompilerFlags flags; + if (this.Process.Options.EnableEditAndContinue) { + flags = CorDebugJITCompilerFlags.CORDEBUG_JIT_ENABLE_ENC; + } else if (this.Process.Options.SuppressJITOptimization) { + flags = CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION; + } else { + flags = CorDebugJITCompilerFlags.CORDEBUG_JIT_DEFAULT; + } - // Flags may succeed but not set all bits, so requery. - CorDebugJITCompilerFlags jcfActual = this.JITCompilerFlags; - - #if DEBUG - if (jcf != jcfActual) - Console.WriteLine("Couldn't set all flags. Actual flags:" + jcfActual.ToString()); - else - Console.WriteLine("Actual flags:" + jcfActual.ToString()); - #endif - } - catch (COMException ex) - { - // we'll ignore the error if we cannot set the jit flags - Console.WriteLine(string.Format("Failed to set flags with hr=0x{0:x}", ex.ErrorCode)); - } + try + { + this.JITCompilerFlags = flags; + } + catch (COMException ex) + { + Console.WriteLine(string.Format("Failed to set flags with hr=0x{0:x}", ex.ErrorCode)); } + + CorDebugJITCompilerFlags actual = this.JITCompilerFlags; + if (flags != actual) + this.Process.TraceMessage("Couldn't set JIT flags to {0}. Actual flags: {1}", flags, actual); + else + this.Process.TraceMessage("JIT flags: {0}", actual); } - /// Sets all code as being 'my code'. The code will be gradually - /// set to not-user-code as encountered according to stepping options - public void ResetJustMyCodeStatus() + /// + /// Sets all code as being 'my code'. The code will be gradually switch + /// to not-user-code as encountered according to stepping options. + /// + public void ResetJustMyCode() { uint unused = 0; - if (process.Options.StepOverNoSymbols && !this.HasSymbols) { + if (this.Process.Debugger.SymbolSources.All(s => s is PdbSymbolSource) && this.SymReader == null) { // Optimization - set the code as non-user right away this.CorModule2.SetJMCStatus(0, 0, ref unused); return; } try { + // Reqires the process to be synchronized! this.CorModule2.SetJMCStatus(process.Options.EnableJustMyCode ? 1 : 0, 0, ref unused); } catch (COMException e) { // Cannot use JMC on this code (likely wrong JIT settings). @@ -337,94 +336,5 @@ namespace Debugger { return string.Format("{0}", this.Name); } - - public static CorDebugJITCompilerFlags MapDebugModeToJITCompilerFlags(DebugModeFlag debugMode) - { - CorDebugJITCompilerFlags jcf; - switch (debugMode) - { - case DebugModeFlag.Optimized: - jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DEFAULT; // DEFAULT really means force optimized. - break; - case DebugModeFlag.Debug: - jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION; - break; - case DebugModeFlag.Enc: - jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_ENABLE_ENC; - break; - default: - // we don't have mapping from default to "default", - // therefore we'll use DISABLE_OPTIMIZATION. - jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION; - break; - } - return jcf; - } - - Dictionary backingFieldCache = new Dictionary(); - - /// Is this method in form 'return this.field;'? - internal uint GetBackingFieldToken(ICorDebugFunction corFunction) - { - uint token; - if (backingFieldCache.TryGetValue(corFunction, out token)) { - return token; - } - - ICorDebugCode corCode; - try { - corCode = corFunction.GetILCode(); - } catch (COMException) { - backingFieldCache[corFunction] = 0; - return 0; - } - - if (corCode == null || corCode.IsIL() == 0 || corCode.GetSize() > 12) { - backingFieldCache[corFunction] = 0; - return 0; - } - - List code = new List(corCode.GetCode()); - - bool success = - (Read(code, 0x00) || true) && // nop || nothing - (Read(code, 0x02, 0x7B) || Read(code, 0x7E)) && // ldarg.0; ldfld || ldsfld - ReadToken(code, ref token) && // - (Read(code, 0x0A, 0x2B, 0x00, 0x06) || true) && // stloc.0; br.s; offset+00; ldloc.0 || nothing - Read(code, 0x2A); // ret - - if (!success) { - backingFieldCache[corFunction] = 0; - return 0; - } - - backingFieldCache[corFunction] = token; - return token; - } - - // Read expected sequence of bytes - static bool Read(List code, params byte[] expected) - { - if (code.Count < expected.Length) - return false; - for(int i = 0; i < expected.Length; i++) { - if (code[i] != expected[i]) - return false; - } - code.RemoveRange(0, expected.Length); - return true; - } - - // Read field token - static bool ReadToken(List code, ref uint token) - { - if (code.Count < 4) - return false; - if (code[3] != 0x04) // field token - return false; - token = ((uint)code[0]) + ((uint)code[1] << 8) + ((uint)code[2] << 16) + ((uint)code[3] << 24); - code.RemoveRange(0, 4); - return true; - } } } diff --git a/src/AddIns/Debugger/Debugger.Core/NDebugger.cs b/src/AddIns/Debugger/Debugger.Core/NDebugger.cs index b99431f703..c7ba570052 100644 --- a/src/AddIns/Debugger/Debugger.Core/NDebugger.cs +++ b/src/AddIns/Debugger/Debugger.Core/NDebugger.cs @@ -66,6 +66,12 @@ namespace Debugger get { return this.processes; } } + /// + /// The debugger might use one or more symbol sources. + /// The first symbol source willing to handle a given method will be used. + /// + public IEnumerable SymbolSources { get; set; } + public NDebugger() { if (ApartmentState.STA == System.Threading.Thread.CurrentThread.GetApartmentState()) { @@ -73,6 +79,7 @@ namespace Debugger } else { mta2sta.CallMethod = CallMethod.DirectCall; } + SymbolSources = new ISymbolSource[] { new PdbSymbolSource() }; } internal Process GetProcess(ICorDebugProcess corProcess) { @@ -270,26 +277,26 @@ namespace Debugger } } - /// Try to load module symbols using the search path defined in the options - public void ReloadModuleSymbols() - { - foreach(Process process in this.Processes) { - foreach(Module module in process.Modules) { - module.LoadSymbolsFromDisk(process.Options.SymbolsSearchPaths); - } - } - TraceMessage("Reloaded symbols"); - } - - /// Reset the just my code status of modules. Use this after changing any stepping options. - public void ResetJustMyCodeStatus() + /// + /// Notify the the debugger that the options have changed + /// + public void ReloadOptions() { foreach(Process process in this.Processes) { + bool isPaused = process.IsPaused; + if (!isPaused) + process.Break(); + + // We need to be paused for this foreach(Module module in process.Modules) { - module.ResetJustMyCodeStatus(); + module.LoadSymbolsFromDisk(this.Options.SymbolsSearchPaths); + module.ResetJustMyCode(); } + + if (!isPaused) + process.AsyncContinue(); } - TraceMessage("Just my code reseted"); + TraceMessage("Reloaded options"); } } diff --git a/src/AddIns/Debugger/Debugger.Core/Options.cs b/src/AddIns/Debugger/Debugger.Core/Options.cs index 7c943cb7fd..95077f8fb3 100644 --- a/src/AddIns/Debugger/Debugger.Core/Options.cs +++ b/src/AddIns/Debugger/Debugger.Core/Options.cs @@ -7,7 +7,9 @@ namespace Debugger public class Options { public virtual bool EnableJustMyCode { get; set; } - public virtual bool StepOverNoSymbols { get; set; } + public virtual bool EnableEditAndContinue { get; set; } + public virtual bool SuppressJITOptimization { get; set; } + public virtual bool SuppressNGENOptimization { get; set; } public virtual bool StepOverDebuggerAttributes { get; set; } public virtual bool StepOverAllProperties { get; set; } public virtual bool StepOverFieldAccessProperties { get; set; } diff --git a/src/AddIns/Debugger/Debugger.Core/PdbSymbolSource.cs b/src/AddIns/Debugger/Debugger.Core/PdbSymbolSource.cs index 578fdc4fe6..77722bf759 100644 --- a/src/AddIns/Debugger/Debugger.Core/PdbSymbolSource.cs +++ b/src/AddIns/Debugger/Debugger.Core/PdbSymbolSource.cs @@ -63,16 +63,21 @@ namespace Debugger public interface ISymbolSource { + /// Use this symbol store handle the method + bool Handles(IMethod method); + + /// Does this method correspond to any source code? + bool IsCompilerGenerated(IMethod method); + /// Find sequence point by IL offset SequencePoint GetSequencePoint(IMethod method, int iloffset); /// Find sequence point by source code location + /// Only source files corresponding to the given module are searched SequencePoint GetSequencePoint(Module module, string filename, int line, int column); - /// Determine whether the method is debugable - bool HasSymbols(IMethod method); - /// Get IL ranges that should be always stepped over by the debugger + /// This is used for compiler generated code IEnumerable GetIgnoredILRanges(IMethod method); /// Get local variable metadata @@ -81,6 +86,16 @@ namespace Debugger public class PdbSymbolSource : ISymbolSource { + public bool Handles(IMethod method) + { + return method.ParentAssembly.GetModule().SymReader != null; + } + + public bool IsCompilerGenerated(IMethod method) + { + return method.GetSymMethod() == null; + } + /// /// Get absolute source code path for the specified document. /// If the file is not found, some effort will be made to locate it. @@ -209,11 +224,6 @@ namespace Debugger return seqPoint; } - public bool HasSymbols(IMethod method) - { - return method.GetSymMethod() != null; - } - public IEnumerable GetIgnoredILRanges(IMethod method) { var symMethod = method.GetSymMethod(); @@ -226,11 +236,11 @@ namespace Debugger public IEnumerable GetLocalVariables(IMethod method) { + List vars = new List(); var symMethod = method.GetSymMethod(); if (symMethod == null) - return null; + return vars; - List vars = new List(); Stack scopes = new Stack(); scopes.Push(symMethod.GetRootScope()); while(scopes.Count > 0) { diff --git a/src/AddIns/Debugger/Debugger.Core/Process.cs b/src/AddIns/Debugger/Debugger.Core/Process.cs index b4cd2f0612..1d3645c597 100644 --- a/src/AddIns/Debugger/Debugger.Core/Process.cs +++ b/src/AddIns/Debugger/Debugger.Core/Process.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.TypeSystem; using Debugger.Interop.CorDebug; using Debugger.Interop.CorSym; @@ -11,29 +13,6 @@ namespace Debugger { internal enum DebuggeeStateAction { Keep, Clear } - /// - /// Debug Mode Flags. - /// - public enum DebugModeFlag - { - /// - /// Run in the same mode as without debugger. - /// - Default, - /// - /// Run in forced optimized mode. - /// - Optimized, - /// - /// Run in debug mode (easy inspection) but slower. - /// - Debug, - /// - /// Run in ENC mode (ENC possible) but even slower than debug - /// - Enc - } - public class Process: DebuggerObject { NDebugger debugger; @@ -106,18 +85,13 @@ namespace Debugger } public string Filename { get; private set; } - - public ISymbolSource SymbolSource { get; set; } - - public static DebugModeFlag DebugMode { get; set; } - + internal Process(NDebugger debugger, ICorDebugProcess corProcess, string filename, string workingDirectory) { this.debugger = debugger; this.corProcess = corProcess; this.workingDirectory = workingDirectory; this.Filename = System.IO.Path.GetFullPath(filename); // normalize path - this.SymbolSource = new PdbSymbolSource(); this.callbackInterface = new ManagedCallback(this); } @@ -231,6 +205,11 @@ namespace Debugger return newThread; } + public ISymbolSource GetSymbolSource(IMethod method) + { + return this.Debugger.SymbolSources.FirstOrDefault(s => s.Handles(method)) ?? this.Debugger.SymbolSources.First(); + } + /// Read the specified amount of memory at the given memory address /// The content of the memory. The amount of the read memory may be less then requested. public unsafe byte[] ReadMemory(ulong address, int size) @@ -454,18 +433,22 @@ namespace Debugger public void RunTo(string fileName, int line, int column) { - foreach(Module module in this.Modules) { - SequencePoint seq = this.SymbolSource.GetSequencePoint(module, fileName, line, column); - if (seq != null) { - ICorDebugFunction corFunction = module.CorModule.GetFunctionFromToken(seq.MethodDefToken); - ICorDebugFunctionBreakpoint corBreakpoint = corFunction.GetILCode().CreateBreakpoint((uint)seq.ILOffset); - corBreakpoint.Activate(1); - this.tempBreakpoints.Add(corBreakpoint); + foreach(var symbolSource in this.Debugger.SymbolSources) { + foreach(Module module in this.Modules) { + // Note the we might get multiple matches + SequencePoint seq = symbolSource.GetSequencePoint(module, fileName, line, column); + if (seq != null) { + ICorDebugFunction corFunction = module.CorModule.GetFunctionFromToken(seq.MethodDefToken); + ICorDebugFunctionBreakpoint corBreakpoint = corFunction.GetILCode().CreateBreakpoint((uint)seq.ILOffset); + corBreakpoint.Activate(1); + this.tempBreakpoints.Add(corBreakpoint); + + if (this.IsPaused) { + AsyncContinue(); + } + } } } - if (this.IsPaused) { - AsyncContinue(); - } } /// diff --git a/src/AddIns/Debugger/Debugger.Core/StackFrame.cs b/src/AddIns/Debugger/Debugger.Core/StackFrame.cs index 4780446a78..ebe49ec296 100644 --- a/src/AddIns/Debugger/Debugger.Core/StackFrame.cs +++ b/src/AddIns/Debugger/Debugger.Core/StackFrame.cs @@ -20,8 +20,6 @@ namespace Debugger ICorDebugILFrame corILFrame; long corILFramePauseSession; - List localVariables; - /// The process in which this stack frame is executed public AppDomain AppDomain { get; private set; } @@ -43,13 +41,6 @@ namespace Debugger internal ICorDebugFunction CorFunction { get; private set; } - /// True if the stack frame has symbols defined. - public bool HasSymbols { - get { - return this.Module.SymbolSource.HasSymbols(this.MethodInfo); - } - } - /// Returns true if this instance can not be used any more. public bool IsInvalid { get { @@ -154,11 +145,12 @@ namespace Debugger void AsyncStep(bool stepIn) { List stepRanges = new List(); - var seq = this.Module.SymbolSource.GetSequencePoint(this.MethodInfo, this.IP); + var symbolSource = this.Process.GetSymbolSource(this.MethodInfo); + var seq = symbolSource.GetSequencePoint(this.MethodInfo, this.IP); if (seq != null) { Process.TraceMessage("Step over: {0} IL:{1}", seq, string.Join(" ", seq.ILRanges)); stepRanges.AddRange(seq.ILRanges); - stepRanges.AddRange(this.Module.SymbolSource.GetIgnoredILRanges(this.MethodInfo)); + stepRanges.AddRange(symbolSource.GetIgnoredILRanges(this.MethodInfo)); } // Remove overlapping and connected ranges @@ -196,7 +188,8 @@ namespace Debugger /// public SequencePoint NextStatement { get { - return this.Module.SymbolSource.GetSequencePoint(this.MethodInfo, this.IP); + var symbolSource = this.Process.GetSymbolSource(this.MethodInfo); + return symbolSource.GetSequencePoint(this.MethodInfo, this.IP); } } @@ -204,21 +197,23 @@ namespace Debugger { this.Process.AssertPaused(); - var seq = this.Module.SymbolSource.GetSequencePoint(this.Module, filename, line, column); - if (seq != null && seq.MethodDefToken == this.MethodInfo.GetMetadataToken()) { - try { - if (dryRun) { - CorILFrame.CanSetIP((uint)seq.ILOffset); - } else { - CorILFrame.SetIP((uint)seq.ILOffset); - // Invalidates all frames and chains for the current thread - this.Process.NotifyResumed(DebuggeeStateAction.Keep); - this.Process.NotifyPaused(); + foreach(var symbolSource in this.Process.Debugger.SymbolSources) { + var seq = symbolSource.GetSequencePoint(this.Module, filename, line, column); + if (seq != null && seq.MethodDefToken == this.MethodInfo.GetMetadataToken()) { + try { + if (dryRun) { + CorILFrame.CanSetIP((uint)seq.ILOffset); + } else { + CorILFrame.SetIP((uint)seq.ILOffset); + // Invalidates all frames and chains for the current thread + this.Process.NotifyResumed(DebuggeeStateAction.Keep); + this.Process.NotifyPaused(); + } + } catch { + return false; } - } catch { - return false; + return true; } - return true; } return false; } @@ -320,11 +315,9 @@ namespace Debugger /// Get all local variables public IEnumerable GetLocalVariables() { - // Note that the user might load symbols later - if (localVariables == null) { - localVariables = LocalVariable.GetLocalVariables(this.MethodInfo); - } - return localVariables ?? new List(); + // TODO: This needs caching, however, note that the symbols might be chaning + var symbolSource = this.Process.GetSymbolSource(this.MethodInfo); + return LocalVariable.GetLocalVariables(symbolSource, this.MethodInfo); } /// Get local variables valid at the given IL offset @@ -347,25 +340,18 @@ namespace Debugger public bool IsNonUserCode { get { Options opt = this.Process.Options; + var symbolSource = this.Process.GetSymbolSource(this.MethodInfo); - if (opt.StepOverNoSymbols) { - if (!this.Module.SymbolSource.HasSymbols(this.MethodInfo)) return true; - } - if (opt.StepOverDebuggerAttributes) { - string[] debuggerAttributes = { - typeof(System.Diagnostics.DebuggerStepThroughAttribute).FullName, - typeof(System.Diagnostics.DebuggerNonUserCodeAttribute).FullName, - typeof(System.Diagnostics.DebuggerHiddenAttribute).FullName - }; - if (this.MethodInfo.Attributes.Any(a => debuggerAttributes.Contains(a.AttributeType.FullName))) return true; - if (this.MethodInfo.DeclaringType.GetDefinition().Attributes.Any(a => debuggerAttributes.Contains(a.AttributeType.FullName))) return true; - } - if (opt.StepOverAllProperties) { - if (this.MethodInfo.IsAccessor) return true; - } - if (opt.StepOverFieldAccessProperties) { - if (this.MethodInfo.IsAccessor && this.Module.GetBackingFieldToken(this.CorFunction) != 0) return true; - } + if (!symbolSource.Handles(this.MethodInfo)) + return true; + if (symbolSource.IsCompilerGenerated(this.MethodInfo)) + return true; + if (opt.StepOverDebuggerAttributes && this.MethodInfo.HasStepOverAttribute()) + return true; + if (opt.StepOverAllProperties && this.MethodInfo.IsAccessor) + return true; + if (opt.StepOverFieldAccessProperties && this.MethodInfo.IsAccessor && this.MethodInfo.GetBackingFieldToken() != 0) + return true; return false; } } diff --git a/src/AddIns/Debugger/Debugger.Core/Util.cs b/src/AddIns/Debugger/Debugger.Core/Util.cs new file mode 100644 index 0000000000..1a0b3568ff --- /dev/null +++ b/src/AddIns/Debugger/Debugger.Core/Util.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using ICSharpCode.NRefactory.TypeSystem; +using Debugger.Interop.CorDebug; + +namespace Debugger +{ + public static class Util + { + // Type-safe box because ConditionalWeakTable requires reference type + class Box + { + public T Value; + + public static implicit operator Box(T value) + { + return new Box { Value = value }; + } + } + + static ConditionalWeakTable> hasStepOverAttribute = new ConditionalWeakTable>(); + + public static bool HasStepOverAttribute(this IMethod method) + { + return hasStepOverAttribute.GetValue(method.UnresolvedMember, delegate { + string[] debuggerAttributes = { + typeof(System.Diagnostics.DebuggerStepThroughAttribute).FullName, + typeof(System.Diagnostics.DebuggerNonUserCodeAttribute).FullName, + typeof(System.Diagnostics.DebuggerHiddenAttribute).FullName + }; + if (method.Attributes.Any(a => debuggerAttributes.Contains(a.AttributeType.FullName))) + return true; + if (method.DeclaringType.GetDefinition().Attributes.Any(a => debuggerAttributes.Contains(a.AttributeType.FullName))) + return true; + return false; + }).Value; + } + + static ConditionalWeakTable> backingFieldToken = new ConditionalWeakTable>(); + + /// Is this method in form 'return this.field;'? + public static uint GetBackingFieldToken(this IMethod method) + { + return backingFieldToken.GetValue(method.UnresolvedMember, delegate { + ICorDebugCode corCode; + try { + corCode = method.ToCorFunction().GetILCode(); + } catch (System.Runtime.InteropServices.COMException) { + return 0; + } + + if (corCode == null || corCode.IsIL() == 0 || corCode.GetSize() > 12) { + return 0; + } + + List code = new List(corCode.GetCode()); + + uint token = 0; + bool success = + (Read(code, 0x00) || true) && // nop || nothing + (Read(code, 0x02, 0x7B) || Read(code, 0x7E)) && // ldarg.0; ldfld || ldsfld + ReadToken(code, ref token) && // + (Read(code, 0x0A, 0x2B, 0x00, 0x06) || true) && // stloc.0; br.s; offset+00; ldloc.0 || nothing + Read(code, 0x2A); // ret + + if (!success) { + return 0; + } + + return token; + }).Value; + } + + // Read expected sequence of bytes + static bool Read(List code, params byte[] expected) + { + if (code.Count < expected.Length) + return false; + for(int i = 0; i < expected.Length; i++) { + if (code[i] != expected[i]) + return false; + } + code.RemoveRange(0, expected.Length); + return true; + } + + // Read field token + static bool ReadToken(List code, ref uint token) + { + if (code.Count < 4) + return false; + if (code[3] != 0x04) // field token + return false; + token = ((uint)code[0]) + ((uint)code[1] << 8) + ((uint)code[2] << 16) + ((uint)code[3] << 24); + code.RemoveRange(0, 4); + return true; + } + } +} diff --git a/src/AddIns/Debugger/Debugger.Tests/DebuggerTestsBase.cs b/src/AddIns/Debugger/Debugger.Tests/DebuggerTestsBase.cs index 308478de5a..4076cb6bd4 100644 --- a/src/AddIns/Debugger/Debugger.Tests/DebuggerTestsBase.cs +++ b/src/AddIns/Debugger/Debugger.Tests/DebuggerTestsBase.cs @@ -202,7 +202,7 @@ namespace Debugger.Tests debugger.Options = new Options(); debugger.Options.EnableJustMyCode = true; - debugger.Options.StepOverNoSymbols = true; + debugger.Options.SuppressJITOptimization = true; debugger.Options.StepOverDebuggerAttributes = true; debugger.Options.StepOverAllProperties = false; debugger.Options.StepOverFieldAccessProperties = true; diff --git a/src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Callstack.cs b/src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Callstack.cs index b929394a39..096052fbb1 100644 --- a/src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Callstack.cs +++ b/src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Callstack.cs @@ -59,7 +59,6 @@ namespace Debugger.Tests { @@ -68,7 +67,6 @@ namespace Debugger.Tests { @@ -76,7 +74,6 @@ namespace Debugger.Tests { @@ -88,7 +85,6 @@ namespace Debugger.Tests { @@ -96,7 +92,6 @@ namespace Debugger.Tests { @@ -107,7 +102,6 @@ namespace Debugger.Tests { diff --git a/src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Lifetime.cs b/src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Lifetime.cs index 0e2fee9370..108496613c 100644 --- a/src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Lifetime.cs +++ b/src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Lifetime.cs @@ -72,7 +72,6 @@ namespace Debugger.Tests { ArgumentCount="1" ChainIndex="1" FrameIndex="1" - HasSymbols="True" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]" NextStatement="StackFrame_Lifetime.cs:18,4-18,40" Thread="Thread Name = Suspended = False" /> @@ -83,7 +82,6 @@ namespace Debugger.Tests { ArgumentCount="1" ChainIndex="1" FrameIndex="1" - HasSymbols="True" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]" NextStatement="StackFrame_Lifetime.cs:19,4-19,18" Thread="Thread Name = Suspended = False" /> @@ -92,7 +90,6 @@ namespace Debugger.Tests { @@ -103,7 +100,6 @@ namespace Debugger.Tests { ArgumentCount="1" ChainIndex="1" FrameIndex="1" - HasSymbols="True" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]" NextStatement="StackFrame_Lifetime.cs:20,4-20,40" Thread="Thread Name = Suspended = False" /> @@ -113,7 +109,6 @@ namespace Debugger.Tests { ArgumentCount="1" ChainIndex="1" FrameIndex="1" - HasSymbols="True" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]" NextStatement="StackFrame_Lifetime.cs:20,4-20,40" Thread="Thread Name = Suspended = False" /> @@ -122,7 +117,6 @@ namespace Debugger.Tests {
@@ -132,7 +126,6 @@ namespace Debugger.Tests { ArgumentCount="{Exception: The requested frame index is too big}" ChainIndex="1" FrameIndex="1" - HasSymbols="True" IsInvalid="True" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]" NextStatement="{Exception: The requested frame index is too big}" @@ -141,7 +134,6 @@ namespace Debugger.Tests { diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpySymbolSource.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpySymbolSource.cs index 23bba2f4ad..738666b64d 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpySymbolSource.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpySymbolSource.cs @@ -13,6 +13,17 @@ namespace ICSharpCode.ILSpyAddIn { public class ILSpySymbolSource : ISymbolSource { + public bool Handles(IMethod method) + { + return true; + } + + public bool IsCompilerGenerated(IMethod method) + { + var symbols = GetSymbols(method); + return symbols == null || symbols.SequencePoints.Count == 0; + } + public static MethodDebugSymbols GetSymbols(IMethod method) { // Use the non-specialised method definition to look up decompiled symbols @@ -67,12 +78,6 @@ namespace ICSharpCode.ILSpyAddIn return null; } - public bool HasSymbols(IMethod method) - { - var symbols = GetSymbols(method); - return symbols != null && symbols.SequencePoints.Any(); - } - public IEnumerable GetIgnoredILRanges(IMethod method) { var symbols = GetSymbols(method);