Browse Source

Refactored the stepping logic to be more friendly with multiple symbol source;

JIT and NGEN optimization are explicit options now
pull/32/merge
David Srbecký 13 years ago
parent
commit
4798dae6fe
  1. 38
      src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptions.cs
  2. 14
      src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml
  3. 3
      src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml.cs
  4. 7
      src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingSymbolsPanel.xaml.cs
  5. 19
      src/AddIns/Debugger/Debugger.AddIn/Pads/CallStackPad.cs
  6. 27
      src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs
  7. 10
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs
  8. 18
      src/AddIns/Debugger/Debugger.Core/Breakpoint.cs
  9. 1
      src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj
  10. 3
      src/AddIns/Debugger/Debugger.Core/Eval.cs
  11. 6
      src/AddIns/Debugger/Debugger.Core/LocalVariable.cs
  12. 6
      src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs
  13. 152
      src/AddIns/Debugger/Debugger.Core/Module.cs
  14. 37
      src/AddIns/Debugger/Debugger.Core/NDebugger.cs
  15. 4
      src/AddIns/Debugger/Debugger.Core/Options.cs
  16. 30
      src/AddIns/Debugger/Debugger.Core/PdbSymbolSource.cs
  17. 61
      src/AddIns/Debugger/Debugger.Core/Process.cs
  18. 82
      src/AddIns/Debugger/Debugger.Core/StackFrame.cs
  19. 101
      src/AddIns/Debugger/Debugger.Core/Util.cs
  20. 2
      src/AddIns/Debugger/Debugger.Tests/DebuggerTestsBase.cs
  21. 6
      src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Callstack.cs
  22. 8
      src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Lifetime.cs
  23. 17
      src/AddIns/DisplayBindings/ILSpyAddIn/ILSpySymbolSource.cs

38
src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptions.cs

@ -29,10 +29,19 @@ namespace ICSharpCode.SharpDevelop.Services
set { PS.Set<bool>("Debugger.EnableJustMyCode", value); } set { PS.Set<bool>("Debugger.EnableJustMyCode", value); }
} }
// This enables decompilation public override bool EnableEditAndContinue {
public override bool StepOverNoSymbols { get { return PS.Get<bool>("Debugger.EnableEditAndContinue", true); }
get { return PS.Get<bool>("Debugger.StepOverNoSymbols", true); } set { PS.Set<bool>("Debugger.EnableEditAndContinue", value); }
set { PS.Set<bool>("Debugger.StepOverNoSymbols", value); } }
public override bool SuppressJITOptimization {
get { return PS.Get<bool>("Debugger.SuppressJITOptimization", true); }
set { PS.Set<bool>("Debugger.SuppressJITOptimization", value); }
}
public override bool SuppressNGENOptimization {
get { return PS.Get<bool>("Debugger.SuppressNGENOptimization", true); }
set { PS.Set<bool>("Debugger.SuppressNGENOptimization", value); }
} }
public override bool StepOverDebuggerAttributes { public override bool StepOverDebuggerAttributes {
@ -99,26 +108,5 @@ namespace ICSharpCode.SharpDevelop.Services
get { return PS.Get<FormWindowState>("Debugger.DebuggeeExceptionWindowState", FormWindowState.Normal); } get { return PS.Get<FormWindowState>("Debugger.DebuggeeExceptionWindowState", FormWindowState.Normal); }
set { PS.Set<FormWindowState>("Debugger.DebuggeeExceptionWindowState", value); } set { PS.Set<FormWindowState>("Debugger.DebuggeeExceptionWindowState", value); }
} }
/// <summary>
/// Used to update status of some debugger properties while debugger is running.
/// </summary>
internal static void ResetStatus(Action<Process> 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();
}
}
} }
} }

14
src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml

@ -8,8 +8,6 @@
<StackPanel> <StackPanel>
<GroupBox Margin="5" Header="{sd:Localize Dialog.Options.IDEOptions.Debugging.Stepping}"> <GroupBox Margin="5" Header="{sd:Localize Dialog.Options.IDEOptions.Debugging.Stepping}">
<widgets:StackPanelWithSpacing SpaceBetweenItems="5"> <widgets:StackPanelWithSpacing SpaceBetweenItems="5">
<CheckBox Content="{sd:Localize Dialog.Options.IDEOptions.Debugging.Stepping.StepOverNoSymbols}"
IsChecked="{sd:OptionBinding debugger:DebuggingOptions.StepOverNoSymbols}" />
<CheckBox Content="{sd:Localize Dialog.Options.IDEOptions.Debugging.Stepping.StepOverDebuggerAttributes}" <CheckBox Content="{sd:Localize Dialog.Options.IDEOptions.Debugging.Stepping.StepOverDebuggerAttributes}"
IsChecked="{sd:OptionBinding debugger:DebuggingOptions.StepOverDebuggerAttributes}" /> IsChecked="{sd:OptionBinding debugger:DebuggingOptions.StepOverDebuggerAttributes}" />
<CheckBox Content="{sd:Localize Dialog.Options.IDEOptions.Debugging.Stepping.StepOverAllProperties}" <CheckBox Content="{sd:Localize Dialog.Options.IDEOptions.Debugging.Stepping.StepOverAllProperties}"
@ -24,5 +22,17 @@
IsChecked="{sd:OptionBinding debugger:DebuggingOptions.PauseOnHandledExceptions}" /> IsChecked="{sd:OptionBinding debugger:DebuggingOptions.PauseOnHandledExceptions}" />
</widgets:StackPanelWithSpacing> </widgets:StackPanelWithSpacing>
</GroupBox> </GroupBox>
<GroupBox Margin="5" Header="{sd:Localize Gloabl.Advanced}">
<widgets:StackPanelWithSpacing SpaceBetweenItems="5">
<!--
<CheckBox Content="{sd:Localize Dialog.Options.IDEOptions.Debugging.EnableEditAndContinue}"
IsChecked="{sd:OptionBinding debugger:DebuggingOptions.EnableEditAndContinue}" />
-->
<CheckBox Content="{sd:Localize Dialog.Options.IDEOptions.Debugging.SuppressJITOptimization}"
IsChecked="{sd:OptionBinding debugger:DebuggingOptions.SuppressJITOptimization}" />
<CheckBox Content="{sd:Localize Dialog.Options.IDEOptions.Debugging.SuppressNGENOptimization}"
IsChecked="{sd:OptionBinding debugger:DebuggingOptions.SuppressNGENOptimization}" />
</widgets:StackPanelWithSpacing>
</GroupBox>
</StackPanel> </StackPanel>
</gui:OptionPanel> </gui:OptionPanel>

3
src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingOptionsPanel.xaml.cs

@ -30,7 +30,8 @@ namespace Debugger.AddIn.Options
public override bool SaveOptions() public override bool SaveOptions()
{ {
bool result = base.SaveOptions(); bool result = base.SaveOptions();
DebuggingOptions.ResetStatus(proc => proc.Debugger.ResetJustMyCodeStatus()); if (WindowsDebugger.CurrentDebugger != null)
WindowsDebugger.CurrentDebugger.ReloadOptions();
return result; return result;
} }
} }

7
src/AddIns/Debugger/Debugger.AddIn/Options/DebuggingSymbolsPanel.xaml.cs

@ -45,11 +45,8 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels
public override bool SaveOptions() public override bool SaveOptions()
{ {
DebuggingOptions.Instance.SymbolsSearchPaths = editor.GetList(); DebuggingOptions.Instance.SymbolsSearchPaths = editor.GetList();
DebuggingOptions.ResetStatus( if (WindowsDebugger.CurrentDebugger != null)
proc => { WindowsDebugger.CurrentDebugger.ReloadOptions();
proc.Debugger.ReloadModuleSymbols();
proc.Debugger.ResetJustMyCodeStatus();
});
return base.SaveOptions(); return base.SaveOptions();
} }
} }

19
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.Process.IsPaused) {
if (item.Frame != null) { 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.CurrentStackFrame = item.Frame;
WindowsDebugger.Instance.JumpToCurrentLine(); WindowsDebugger.Instance.JumpToCurrentLine();
WindowsDebugger.RefreshPads(); WindowsDebugger.RefreshPads();
@ -138,13 +132,16 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
CallStackItem CreateItem(StackFrame frame, ref bool previousItemIsExternalMethod) CallStackItem CreateItem(StackFrame frame, ref bool previousItemIsExternalMethod)
{ {
bool showExternalMethods = DebuggingOptions.Instance.ShowExternalMethods; 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 // Show the method in the list
previousItemIsExternalMethod = false; previousItemIsExternalMethod = false;
return new CallStackItem() { return new CallStackItem() {
Frame = frame, Frame = frame,
ImageSource = SD.ResourceService.GetImageSource("Icons.16x16.Method"), ImageSource = SD.ResourceService.GetImageSource("Icons.16x16.Method"),
Name = GetFullName(frame) Name = GetFullName(frame),
HasSymbols = hasSymbols,
}; };
} else { } else {
// Show [External methods] in the list // Show [External methods] in the list
@ -152,7 +149,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
return null; return null;
previousItemIsExternalMethod = true; previousItemIsExternalMethod = true;
return new CallStackItem() { 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 StackFrame Frame { get; set; }
public ImageSource ImageSource { get; set; } public ImageSource ImageSource { get; set; }
public string Name { get; set; } public string Name { get; set; }
public bool HasSymbols { get; set; }
public Brush FontColor { public Brush FontColor {
get { return this.Frame == null || this.Frame.HasSymbols ? Brushes.Black : Brushes.Gray; } get { return this.HasSymbols ? Brushes.Black : Brushes.Gray; }
} }
} }
} }

27
src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs

@ -36,6 +36,8 @@ namespace ICSharpCode.SharpDevelop.Services
public static Thread CurrentThread { get; set; } public static Thread CurrentThread { get; set; }
public static StackFrame CurrentStackFrame { get; set; } public static StackFrame CurrentStackFrame { get; set; }
public static PdbSymbolSource PdbSymbolSource = new PdbSymbolSource();
public static Action RefreshingPads; public static Action RefreshingPads;
public static void RefreshPads() public static void RefreshPads()
@ -78,8 +80,6 @@ namespace ICSharpCode.SharpDevelop.Services
ICorPublish corPublish; ICorPublish corPublish;
List<ISymbolSource> symbolSources = new List<ISymbolSource>();
/// <inheritdoc/> /// <inheritdoc/>
public bool BreakAtBeginning { get; set; } public bool BreakAtBeginning { get; set; }
@ -152,8 +152,6 @@ namespace ICSharpCode.SharpDevelop.Services
UpdateBreakpointLines(); UpdateBreakpointLines();
try { try {
// set the JIT flag for evaluating optimized code
Process.DebugMode = DebugModeFlag.Debug;
CurrentProcess = CurrentDebugger.Start(processStartInfo.FileName, processStartInfo.WorkingDirectory, processStartInfo.Arguments, this.BreakAtBeginning); CurrentProcess = CurrentDebugger.Start(processStartInfo.FileName, processStartInfo.WorkingDirectory, processStartInfo.Arguments, this.BreakAtBeginning);
debugger_ProcessStarted(); debugger_ProcessStarted();
} catch (System.Exception e) { } catch (System.Exception e) {
@ -213,8 +211,6 @@ namespace ICSharpCode.SharpDevelop.Services
UpdateBreakpointLines(); UpdateBreakpointLines();
try { try {
// set the JIT flag for evaluating optimized code
Process.DebugMode = DebugModeFlag.Debug;
CurrentProcess = CurrentDebugger.Attach(existingProcess); CurrentProcess = CurrentDebugger.Attach(existingProcess);
debugger_ProcessStarted(); debugger_ProcessStarted();
attached = true; attached = true;
@ -348,12 +344,14 @@ namespace ICSharpCode.SharpDevelop.Services
public void InitializeService() public void InitializeService()
{ {
symbolSources.Add(new PdbSymbolSource()); List<ISymbolSource> symbolSources = new List<ISymbolSource>();
symbolSources.Add(PdbSymbolSource);
symbolSources.AddRange(AddInTree.BuildItems<ISymbolSource>("/SharpDevelop/Services/DebuggerService/SymbolSource", null, false)); symbolSources.AddRange(AddInTree.BuildItems<ISymbolSource>("/SharpDevelop/Services/DebuggerService/SymbolSource", null, false));
// init NDebugger // init NDebugger
CurrentDebugger = new NDebugger(); CurrentDebugger = new NDebugger();
CurrentDebugger.Options = DebuggingOptions.Instance; CurrentDebugger.Options = DebuggingOptions.Instance;
CurrentDebugger.SymbolSources = symbolSources;
foreach (BreakpointBookmark b in SD.BookmarkManager.Bookmarks.OfType<BreakpointBookmark>()) { foreach (BreakpointBookmark b in SD.BookmarkManager.Bookmarks.OfType<BreakpointBookmark>()) {
AddBreakpoint(b); AddBreakpoint(b);
@ -466,10 +464,6 @@ namespace ICSharpCode.SharpDevelop.Services
CurrentThread = e.Thread; CurrentThread = e.Thread;
CurrentStackFrame = CurrentThread != null ? CurrentThread.MostRecentUserStackFrame : null; 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 // We can have several events happening at the same time
bool breakProcess = e.Break; bool breakProcess = e.Break;
@ -599,13 +593,10 @@ namespace ICSharpCode.SharpDevelop.Services
return; return;
SD.Workbench.MainWindow.Activate(); SD.Workbench.MainWindow.Activate();
DebuggerService.RemoveCurrentLineMarker();
if (CurrentStackFrame.HasSymbols) { SequencePoint nextStatement = CurrentStackFrame.NextStatement;
SequencePoint nextStatement = CurrentStackFrame.NextStatement; if (nextStatement != null) {
if (nextStatement != null) { DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn);
DebuggerService.RemoveCurrentLineMarker();
DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn);
}
} }
} }

10
src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs

@ -249,13 +249,9 @@ namespace Debugger.AddIn.TreeModel
var parCopy = par; var parCopy = par;
yield return new ValueNode(ClassBrowserIconService.Parameter, par.Param.Name, () => GetCurrentStackFrame().GetArgumentValue(par.Index)); yield return new ValueNode(ClassBrowserIconService.Parameter, par.Param.Name, () => GetCurrentStackFrame().GetArgumentValue(par.Index));
} }
if (stackFrame.HasSymbols) { foreach(LocalVariable locVar in stackFrame.GetLocalVariables(stackFrame.IP)) {
foreach(LocalVariable locVar in stackFrame.GetLocalVariables(stackFrame.IP)) { var locVarCopy = locVar;
var locVarCopy = locVar; yield return new ValueNode(ClassBrowserIconService.LocalVariable, locVar.Name, () => locVarCopy.GetValue(GetCurrentStackFrame()));
yield return new ValueNode(ClassBrowserIconService.LocalVariable, locVar.Name, () => locVarCopy.GetValue(GetCurrentStackFrame()));
}
} else {
WindowsDebugger debugger = (WindowsDebugger)DebuggerService.CurrentDebugger;
} }
} }

18
src/AddIns/Debugger/Debugger.Core/Breakpoint.cs

@ -67,17 +67,17 @@ namespace Debugger
corBreakpoints.Clear(); 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); foreach(var symbolSource in module.Process.Debugger.SymbolSources) {
if (seq != null) { var seq = symbolSource.GetSequencePoint(module, this.FileName, this.Line, this.Column);
ICorDebugFunction corFuction = module.CorModule.GetFunctionFromToken(seq.MethodDefToken); if (seq != null) {
ICorDebugFunctionBreakpoint corBreakpoint = corFuction.GetILCode().CreateBreakpoint((uint)seq.ILOffset); ICorDebugFunction corFuction = module.CorModule.GetFunctionFromToken(seq.MethodDefToken);
corBreakpoint.Activate(enabled ? 1 : 0); ICorDebugFunctionBreakpoint corBreakpoint = corFuction.GetILCode().CreateBreakpoint((uint)seq.ILOffset);
corBreakpoints.Add(corBreakpoint); corBreakpoint.Activate(enabled ? 1 : 0);
return true; corBreakpoints.Add(corBreakpoint);
}
} }
return false;
} }
} }
} }

1
src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj

@ -92,6 +92,7 @@
<Compile Include="StackFrame.cs" /> <Compile Include="StackFrame.cs" />
<Compile Include="Stepper.cs" /> <Compile Include="Stepper.cs" />
<Compile Include="Thread.cs" /> <Compile Include="Thread.cs" />
<Compile Include="Util.cs" />
<Compile Include="Value.cs" /> <Compile Include="Value.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

3
src/AddIns/Debugger/Debugger.Core/Eval.cs

@ -187,8 +187,7 @@ namespace Debugger
/// <summary> Synchronously calls a function and returns its return value </summary> /// <summary> Synchronously calls a function and returns its return value </summary>
public static Value InvokeMethod(Thread evalThread, IMethod method, Value thisValue, Value[] args) public static Value InvokeMethod(Thread evalThread, IMethod method, Value thisValue, Value[] args)
{ {
Module module = method.DeclaringTypeDefinition.ParentAssembly.GetModule(); uint fieldToken = method.GetBackingFieldToken();
uint fieldToken = module.GetBackingFieldToken(method.ToCorFunction());
if (fieldToken != 0) { if (fieldToken != 0) {
var field = method.DeclaringType.ImportField(fieldToken); var field = method.DeclaringType.ImportField(fieldToken);
if (field != null) { if (field != null) {

6
src/AddIns/Debugger/Debugger.Core/LocalVariable.cs

@ -49,15 +49,13 @@ namespace Debugger.MetaData
return this.Type.Name + " " + this.Name + (this.IsCaptured ? " (captured)" : ""); return this.Type.Name + " " + this.Name + (this.IsCaptured ? " (captured)" : "");
} }
public static List<LocalVariable> GetLocalVariables(IMethod method) public static List<LocalVariable> GetLocalVariables(ISymbolSource symbolSource, IMethod method)
{ {
var module = method.ParentAssembly.GetModule(); var module = method.ParentAssembly.GetModule();
if (!module.SymbolSource.HasSymbols(method))
return null;
List<LocalVariable> localVariables = new List<LocalVariable>(); List<LocalVariable> localVariables = new List<LocalVariable>();
foreach (ILLocalVariable ilvar in module.SymbolSource.GetLocalVariables(method)) { foreach (ILLocalVariable ilvar in symbolSource.GetLocalVariables(method)) {
int index = ilvar.Index; int index = ilvar.Index;
// NB: Display class does not have the compiler-generated flag // NB: Display class does not have the compiler-generated flag
if (ilvar.IsCompilerGenerated || ilvar.Name.StartsWith("CS$")) { if (ilvar.IsCompilerGenerated || ilvar.Name.StartsWith("CS$")) {

6
src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs

@ -332,10 +332,10 @@ namespace Debugger
EnterCallback("CreateProcess", pProcess); EnterCallback("CreateProcess", pProcess);
// Process is added in NDebugger.Start // 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; ICorDebugProcess2 pProcess2 = pProcess as ICorDebugProcess2;
if (pProcess2 != null && Process.DebugMode == DebugModeFlag.Debug) { if (pProcess2 != null) {
try { try {
pProcess2.SetDesiredNGENCompilerFlags((uint)CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION); pProcess2.SetDesiredNGENCompilerFlags((uint)CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION);
} catch (COMException) { } catch (COMException) {

152
src/AddIns/Debugger/Debugger.Core/Module.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq;
using Debugger.Interop; using Debugger.Interop;
using Debugger.Interop.CorDebug; using Debugger.Interop.CorDebug;
using Debugger.Interop.CorSym; using Debugger.Interop.CorSym;
@ -46,10 +47,6 @@ namespace Debugger
get { return process; } get { return process; }
} }
public ISymbolSource SymbolSource {
get { return this.Process.SymbolSource; }
}
NDebugger Debugger { NDebugger Debugger {
get { return this.AppDomain.Process.Debugger; } get { return this.AppDomain.Process.Debugger; }
} }
@ -188,7 +185,7 @@ namespace Debugger
SetJITCompilerFlags(); SetJITCompilerFlags();
LoadSymbolsFromDisk(process.Options.SymbolsSearchPaths); LoadSymbolsFromDisk(process.Options.SymbolsSearchPaths);
ResetJustMyCodeStatus(); ResetJustMyCode();
LoadSymbolsDynamic(); LoadSymbolsDynamic();
} }
@ -269,48 +266,50 @@ namespace Debugger
foreach (Breakpoint b in this.Debugger.Breakpoints) { foreach (Breakpoint b in this.Debugger.Breakpoints) {
b.SetBreakpoint(this); b.SetBreakpoint(this);
} }
ResetJustMyCodeStatus(); ResetJustMyCode();
} }
void SetJITCompilerFlags() void SetJITCompilerFlags()
{ {
if (Process.DebugMode != DebugModeFlag.Default) { CorDebugJITCompilerFlags flags;
// translate DebugModeFlags to JITCompilerFlags if (this.Process.Options.EnableEditAndContinue) {
CorDebugJITCompilerFlags jcf = MapDebugModeToJITCompilerFlags(Process.DebugMode); flags = CorDebugJITCompilerFlags.CORDEBUG_JIT_ENABLE_ENC;
} else if (this.Process.Options.SuppressJITOptimization) {
try flags = CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION;
{ } else {
this.JITCompilerFlags = jcf; flags = CorDebugJITCompilerFlags.CORDEBUG_JIT_DEFAULT;
}
// Flags may succeed but not set all bits, so requery. try
CorDebugJITCompilerFlags jcfActual = this.JITCompilerFlags; {
this.JITCompilerFlags = flags;
#if DEBUG }
if (jcf != jcfActual) catch (COMException ex)
Console.WriteLine("Couldn't set all flags. Actual flags:" + jcfActual.ToString()); {
else Console.WriteLine(string.Format("Failed to set flags with hr=0x{0:x}", ex.ErrorCode));
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));
}
} }
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);
} }
/// <summary> Sets all code as being 'my code'. The code will be gradually /// <summary>
/// set to not-user-code as encountered according to stepping options </summary> /// Sets all code as being 'my code'. The code will be gradually switch
public void ResetJustMyCodeStatus() /// to not-user-code as encountered according to stepping options.
/// </summary>
public void ResetJustMyCode()
{ {
uint unused = 0; 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 // Optimization - set the code as non-user right away
this.CorModule2.SetJMCStatus(0, 0, ref unused); this.CorModule2.SetJMCStatus(0, 0, ref unused);
return; return;
} }
try { try {
// Reqires the process to be synchronized!
this.CorModule2.SetJMCStatus(process.Options.EnableJustMyCode ? 1 : 0, 0, ref unused); this.CorModule2.SetJMCStatus(process.Options.EnableJustMyCode ? 1 : 0, 0, ref unused);
} catch (COMException e) { } catch (COMException e) {
// Cannot use JMC on this code (likely wrong JIT settings). // Cannot use JMC on this code (likely wrong JIT settings).
@ -337,94 +336,5 @@ namespace Debugger
{ {
return string.Format("{0}", this.Name); 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<ICorDebugFunction, uint> backingFieldCache = new Dictionary<ICorDebugFunction, uint>();
/// <summary> Is this method in form 'return this.field;'? </summary>
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<byte> code = new List<byte>(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) && // <field 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<byte> 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<byte> 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;
}
} }
} }

37
src/AddIns/Debugger/Debugger.Core/NDebugger.cs

@ -66,6 +66,12 @@ namespace Debugger
get { return this.processes; } get { return this.processes; }
} }
/// <summary>
/// The debugger might use one or more symbol sources.
/// The first symbol source willing to handle a given method will be used.
/// </summary>
public IEnumerable<ISymbolSource> SymbolSources { get; set; }
public NDebugger() public NDebugger()
{ {
if (ApartmentState.STA == System.Threading.Thread.CurrentThread.GetApartmentState()) { if (ApartmentState.STA == System.Threading.Thread.CurrentThread.GetApartmentState()) {
@ -73,6 +79,7 @@ namespace Debugger
} else { } else {
mta2sta.CallMethod = CallMethod.DirectCall; mta2sta.CallMethod = CallMethod.DirectCall;
} }
SymbolSources = new ISymbolSource[] { new PdbSymbolSource() };
} }
internal Process GetProcess(ICorDebugProcess corProcess) { internal Process GetProcess(ICorDebugProcess corProcess) {
@ -270,26 +277,26 @@ namespace Debugger
} }
} }
/// <summary> Try to load module symbols using the search path defined in the options </summary> /// <summary>
public void ReloadModuleSymbols() /// Notify the the debugger that the options have changed
{ /// </summary>
foreach(Process process in this.Processes) { public void ReloadOptions()
foreach(Module module in process.Modules) {
module.LoadSymbolsFromDisk(process.Options.SymbolsSearchPaths);
}
}
TraceMessage("Reloaded symbols");
}
/// <summary> Reset the just my code status of modules. Use this after changing any stepping options. </summary>
public void ResetJustMyCodeStatus()
{ {
foreach(Process process in this.Processes) { 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) { 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");
} }
} }

4
src/AddIns/Debugger/Debugger.Core/Options.cs

@ -7,7 +7,9 @@ namespace Debugger
public class Options public class Options
{ {
public virtual bool EnableJustMyCode { get; set; } 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 StepOverDebuggerAttributes { get; set; }
public virtual bool StepOverAllProperties { get; set; } public virtual bool StepOverAllProperties { get; set; }
public virtual bool StepOverFieldAccessProperties { get; set; } public virtual bool StepOverFieldAccessProperties { get; set; }

30
src/AddIns/Debugger/Debugger.Core/PdbSymbolSource.cs

@ -63,16 +63,21 @@ namespace Debugger
public interface ISymbolSource public interface ISymbolSource
{ {
/// <summary> Use this symbol store handle the method </summary>
bool Handles(IMethod method);
/// <summary> Does this method correspond to any source code? </summary>
bool IsCompilerGenerated(IMethod method);
/// <summary> Find sequence point by IL offset </summary> /// <summary> Find sequence point by IL offset </summary>
SequencePoint GetSequencePoint(IMethod method, int iloffset); SequencePoint GetSequencePoint(IMethod method, int iloffset);
/// <summary> Find sequence point by source code location </summary> /// <summary> Find sequence point by source code location </summary>
/// <remarks> Only source files corresponding to the given module are searched </remarks>
SequencePoint GetSequencePoint(Module module, string filename, int line, int column); SequencePoint GetSequencePoint(Module module, string filename, int line, int column);
/// <summary> Determine whether the method is debugable </summary>
bool HasSymbols(IMethod method);
/// <summary> Get IL ranges that should be always stepped over by the debugger </summary> /// <summary> Get IL ranges that should be always stepped over by the debugger </summary>
/// <remarks> This is used for compiler generated code </remarks>
IEnumerable<ILRange> GetIgnoredILRanges(IMethod method); IEnumerable<ILRange> GetIgnoredILRanges(IMethod method);
/// <summary> Get local variable metadata </summary> /// <summary> Get local variable metadata </summary>
@ -81,6 +86,16 @@ namespace Debugger
public class PdbSymbolSource : ISymbolSource public class PdbSymbolSource : ISymbolSource
{ {
public bool Handles(IMethod method)
{
return method.ParentAssembly.GetModule().SymReader != null;
}
public bool IsCompilerGenerated(IMethod method)
{
return method.GetSymMethod() == null;
}
/// <summary> /// <summary>
/// Get absolute source code path for the specified document. /// Get absolute source code path for the specified document.
/// If the file is not found, some effort will be made to locate it. /// If the file is not found, some effort will be made to locate it.
@ -209,11 +224,6 @@ namespace Debugger
return seqPoint; return seqPoint;
} }
public bool HasSymbols(IMethod method)
{
return method.GetSymMethod() != null;
}
public IEnumerable<ILRange> GetIgnoredILRanges(IMethod method) public IEnumerable<ILRange> GetIgnoredILRanges(IMethod method)
{ {
var symMethod = method.GetSymMethod(); var symMethod = method.GetSymMethod();
@ -226,11 +236,11 @@ namespace Debugger
public IEnumerable<ILLocalVariable> GetLocalVariables(IMethod method) public IEnumerable<ILLocalVariable> GetLocalVariables(IMethod method)
{ {
List<ILLocalVariable> vars = new List<ILLocalVariable>();
var symMethod = method.GetSymMethod(); var symMethod = method.GetSymMethod();
if (symMethod == null) if (symMethod == null)
return null; return vars;
List<ILLocalVariable> vars = new List<ILLocalVariable>();
Stack<ISymUnmanagedScope> scopes = new Stack<ISymUnmanagedScope>(); Stack<ISymUnmanagedScope> scopes = new Stack<ISymUnmanagedScope>();
scopes.Push(symMethod.GetRootScope()); scopes.Push(symMethod.GetRootScope());
while(scopes.Count > 0) { while(scopes.Count > 0) {

61
src/AddIns/Debugger/Debugger.Core/Process.cs

@ -3,7 +3,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using Debugger.Interop.CorDebug; using Debugger.Interop.CorDebug;
using Debugger.Interop.CorSym; using Debugger.Interop.CorSym;
@ -11,29 +13,6 @@ namespace Debugger
{ {
internal enum DebuggeeStateAction { Keep, Clear } internal enum DebuggeeStateAction { Keep, Clear }
/// <summary>
/// Debug Mode Flags.
/// </summary>
public enum DebugModeFlag
{
/// <summary>
/// Run in the same mode as without debugger.
/// </summary>
Default,
/// <summary>
/// Run in forced optimized mode.
/// </summary>
Optimized,
/// <summary>
/// Run in debug mode (easy inspection) but slower.
/// </summary>
Debug,
/// <summary>
/// Run in ENC mode (ENC possible) but even slower than debug
/// </summary>
Enc
}
public class Process: DebuggerObject public class Process: DebuggerObject
{ {
NDebugger debugger; NDebugger debugger;
@ -106,18 +85,13 @@ namespace Debugger
} }
public string Filename { get; private set; } 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) internal Process(NDebugger debugger, ICorDebugProcess corProcess, string filename, string workingDirectory)
{ {
this.debugger = debugger; this.debugger = debugger;
this.corProcess = corProcess; this.corProcess = corProcess;
this.workingDirectory = workingDirectory; this.workingDirectory = workingDirectory;
this.Filename = System.IO.Path.GetFullPath(filename); // normalize path this.Filename = System.IO.Path.GetFullPath(filename); // normalize path
this.SymbolSource = new PdbSymbolSource();
this.callbackInterface = new ManagedCallback(this); this.callbackInterface = new ManagedCallback(this);
} }
@ -231,6 +205,11 @@ namespace Debugger
return newThread; return newThread;
} }
public ISymbolSource GetSymbolSource(IMethod method)
{
return this.Debugger.SymbolSources.FirstOrDefault(s => s.Handles(method)) ?? this.Debugger.SymbolSources.First();
}
/// <summary> Read the specified amount of memory at the given memory address </summary> /// <summary> Read the specified amount of memory at the given memory address </summary>
/// <returns> The content of the memory. The amount of the read memory may be less then requested. </returns> /// <returns> The content of the memory. The amount of the read memory may be less then requested. </returns>
public unsafe byte[] ReadMemory(ulong address, int size) public unsafe byte[] ReadMemory(ulong address, int size)
@ -454,18 +433,22 @@ namespace Debugger
public void RunTo(string fileName, int line, int column) public void RunTo(string fileName, int line, int column)
{ {
foreach(Module module in this.Modules) { foreach(var symbolSource in this.Debugger.SymbolSources) {
SequencePoint seq = this.SymbolSource.GetSequencePoint(module, fileName, line, column); foreach(Module module in this.Modules) {
if (seq != null) { // Note the we might get multiple matches
ICorDebugFunction corFunction = module.CorModule.GetFunctionFromToken(seq.MethodDefToken); SequencePoint seq = symbolSource.GetSequencePoint(module, fileName, line, column);
ICorDebugFunctionBreakpoint corBreakpoint = corFunction.GetILCode().CreateBreakpoint((uint)seq.ILOffset); if (seq != null) {
corBreakpoint.Activate(1); ICorDebugFunction corFunction = module.CorModule.GetFunctionFromToken(seq.MethodDefToken);
this.tempBreakpoints.Add(corBreakpoint); ICorDebugFunctionBreakpoint corBreakpoint = corFunction.GetILCode().CreateBreakpoint((uint)seq.ILOffset);
corBreakpoint.Activate(1);
this.tempBreakpoints.Add(corBreakpoint);
if (this.IsPaused) {
AsyncContinue();
}
}
} }
} }
if (this.IsPaused) {
AsyncContinue();
}
} }
/// <summary> /// <summary>

82
src/AddIns/Debugger/Debugger.Core/StackFrame.cs

@ -20,8 +20,6 @@ namespace Debugger
ICorDebugILFrame corILFrame; ICorDebugILFrame corILFrame;
long corILFramePauseSession; long corILFramePauseSession;
List<LocalVariable> localVariables;
/// <summary> The process in which this stack frame is executed </summary> /// <summary> The process in which this stack frame is executed </summary>
public AppDomain AppDomain { get; private set; } public AppDomain AppDomain { get; private set; }
@ -43,13 +41,6 @@ namespace Debugger
internal ICorDebugFunction CorFunction { get; private set; } internal ICorDebugFunction CorFunction { get; private set; }
/// <summary> True if the stack frame has symbols defined. </summary>
public bool HasSymbols {
get {
return this.Module.SymbolSource.HasSymbols(this.MethodInfo);
}
}
/// <summary> Returns true if this instance can not be used any more. </summary> /// <summary> Returns true if this instance can not be used any more. </summary>
public bool IsInvalid { public bool IsInvalid {
get { get {
@ -154,11 +145,12 @@ namespace Debugger
void AsyncStep(bool stepIn) void AsyncStep(bool stepIn)
{ {
List<ILRange> stepRanges = new List<ILRange>(); List<ILRange> stepRanges = new List<ILRange>();
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) { if (seq != null) {
Process.TraceMessage("Step over: {0} IL:{1}", seq, string.Join(" ", seq.ILRanges)); Process.TraceMessage("Step over: {0} IL:{1}", seq, string.Join(" ", seq.ILRanges));
stepRanges.AddRange(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 // Remove overlapping and connected ranges
@ -196,7 +188,8 @@ namespace Debugger
/// </summary> /// </summary>
public SequencePoint NextStatement { public SequencePoint NextStatement {
get { 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(); this.Process.AssertPaused();
var seq = this.Module.SymbolSource.GetSequencePoint(this.Module, filename, line, column); foreach(var symbolSource in this.Process.Debugger.SymbolSources) {
if (seq != null && seq.MethodDefToken == this.MethodInfo.GetMetadataToken()) { var seq = symbolSource.GetSequencePoint(this.Module, filename, line, column);
try { if (seq != null && seq.MethodDefToken == this.MethodInfo.GetMetadataToken()) {
if (dryRun) { try {
CorILFrame.CanSetIP((uint)seq.ILOffset); if (dryRun) {
} else { CorILFrame.CanSetIP((uint)seq.ILOffset);
CorILFrame.SetIP((uint)seq.ILOffset); } else {
// Invalidates all frames and chains for the current thread CorILFrame.SetIP((uint)seq.ILOffset);
this.Process.NotifyResumed(DebuggeeStateAction.Keep); // Invalidates all frames and chains for the current thread
this.Process.NotifyPaused(); this.Process.NotifyResumed(DebuggeeStateAction.Keep);
this.Process.NotifyPaused();
}
} catch {
return false;
} }
} catch { return true;
return false;
} }
return true;
} }
return false; return false;
} }
@ -320,11 +315,9 @@ namespace Debugger
/// <summary> Get all local variables </summary> /// <summary> Get all local variables </summary>
public IEnumerable<LocalVariable> GetLocalVariables() public IEnumerable<LocalVariable> GetLocalVariables()
{ {
// Note that the user might load symbols later // TODO: This needs caching, however, note that the symbols might be chaning
if (localVariables == null) { var symbolSource = this.Process.GetSymbolSource(this.MethodInfo);
localVariables = LocalVariable.GetLocalVariables(this.MethodInfo); return LocalVariable.GetLocalVariables(symbolSource, this.MethodInfo);
}
return localVariables ?? new List<LocalVariable>();
} }
/// <summary> Get local variables valid at the given IL offset </summary> /// <summary> Get local variables valid at the given IL offset </summary>
@ -347,25 +340,18 @@ namespace Debugger
public bool IsNonUserCode { public bool IsNonUserCode {
get { get {
Options opt = this.Process.Options; Options opt = this.Process.Options;
var symbolSource = this.Process.GetSymbolSource(this.MethodInfo);
if (opt.StepOverNoSymbols) { if (!symbolSource.Handles(this.MethodInfo))
if (!this.Module.SymbolSource.HasSymbols(this.MethodInfo)) return true; return true;
} if (symbolSource.IsCompilerGenerated(this.MethodInfo))
if (opt.StepOverDebuggerAttributes) { return true;
string[] debuggerAttributes = { if (opt.StepOverDebuggerAttributes && this.MethodInfo.HasStepOverAttribute())
typeof(System.Diagnostics.DebuggerStepThroughAttribute).FullName, return true;
typeof(System.Diagnostics.DebuggerNonUserCodeAttribute).FullName, if (opt.StepOverAllProperties && this.MethodInfo.IsAccessor)
typeof(System.Diagnostics.DebuggerHiddenAttribute).FullName return true;
}; if (opt.StepOverFieldAccessProperties && this.MethodInfo.IsAccessor && this.MethodInfo.GetBackingFieldToken() != 0)
if (this.MethodInfo.Attributes.Any(a => debuggerAttributes.Contains(a.AttributeType.FullName))) return true; 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;
}
return false; return false;
} }
} }

101
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<T>
{
public T Value;
public static implicit operator Box<T>(T value)
{
return new Box<T> { Value = value };
}
}
static ConditionalWeakTable<IUnresolvedMember, Box<bool>> hasStepOverAttribute = new ConditionalWeakTable<IUnresolvedMember, Box<bool>>();
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<IUnresolvedMember, Box<uint>> backingFieldToken = new ConditionalWeakTable<IUnresolvedMember, Box<uint>>();
/// <summary> Is this method in form 'return this.field;'? </summary>
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<byte> code = new List<byte>(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) && // <field 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<byte> 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<byte> 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;
}
}
}

2
src/AddIns/Debugger/Debugger.Tests/DebuggerTestsBase.cs

@ -202,7 +202,7 @@ namespace Debugger.Tests
debugger.Options = new Options(); debugger.Options = new Options();
debugger.Options.EnableJustMyCode = true; debugger.Options.EnableJustMyCode = true;
debugger.Options.StepOverNoSymbols = true; debugger.Options.SuppressJITOptimization = true;
debugger.Options.StepOverDebuggerAttributes = true; debugger.Options.StepOverDebuggerAttributes = true;
debugger.Options.StepOverAllProperties = false; debugger.Options.StepOverAllProperties = false;
debugger.Options.StepOverFieldAccessProperties = true; debugger.Options.StepOverFieldAccessProperties = true;

6
src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Callstack.cs

@ -59,7 +59,6 @@ namespace Debugger.Tests {
<StackFrame <StackFrame
ChainIndex="1" ChainIndex="1"
FrameIndex="2" FrameIndex="2"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Sub2():System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Sub2():System.Void]"
NextStatement="StackFrame_Callstack.cs:22,4-22,40" NextStatement="StackFrame_Callstack.cs:22,4-22,40"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />
@ -68,7 +67,6 @@ namespace Debugger.Tests {
<StackFrame <StackFrame
ChainIndex="1" ChainIndex="1"
FrameIndex="1" FrameIndex="1"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Sub1():System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Sub1():System.Void]"
NextStatement="StackFrame_Callstack.cs:17,4-17,11" NextStatement="StackFrame_Callstack.cs:17,4-17,11"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />
@ -76,7 +74,6 @@ namespace Debugger.Tests {
<Item> <Item>
<StackFrame <StackFrame
ChainIndex="1" ChainIndex="1"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Main():System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Main():System.Void]"
NextStatement="StackFrame_Callstack.cs:12,4-12,11" NextStatement="StackFrame_Callstack.cs:12,4-12,11"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />
@ -88,7 +85,6 @@ namespace Debugger.Tests {
<StackFrame <StackFrame
ChainIndex="1" ChainIndex="1"
FrameIndex="1" FrameIndex="1"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Sub1():System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Sub1():System.Void]"
NextStatement="StackFrame_Callstack.cs:17,4-17,11" NextStatement="StackFrame_Callstack.cs:17,4-17,11"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />
@ -96,7 +92,6 @@ namespace Debugger.Tests {
<Item> <Item>
<StackFrame <StackFrame
ChainIndex="1" ChainIndex="1"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Main():System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Main():System.Void]"
NextStatement="StackFrame_Callstack.cs:12,4-12,11" NextStatement="StackFrame_Callstack.cs:12,4-12,11"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />
@ -107,7 +102,6 @@ namespace Debugger.Tests {
<Item> <Item>
<StackFrame <StackFrame
ChainIndex="1" ChainIndex="1"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Main():System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Callstack.Main():System.Void]"
NextStatement="StackFrame_Callstack.cs:12,4-12,11" NextStatement="StackFrame_Callstack.cs:12,4-12,11"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />

8
src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_Lifetime.cs

@ -72,7 +72,6 @@ namespace Debugger.Tests {
ArgumentCount="1" ArgumentCount="1"
ChainIndex="1" ChainIndex="1"
FrameIndex="1" FrameIndex="1"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]"
NextStatement="StackFrame_Lifetime.cs:18,4-18,40" NextStatement="StackFrame_Lifetime.cs:18,4-18,40"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />
@ -83,7 +82,6 @@ namespace Debugger.Tests {
ArgumentCount="1" ArgumentCount="1"
ChainIndex="1" ChainIndex="1"
FrameIndex="1" FrameIndex="1"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]"
NextStatement="StackFrame_Lifetime.cs:19,4-19,18" NextStatement="StackFrame_Lifetime.cs:19,4-19,18"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />
@ -92,7 +90,6 @@ namespace Debugger.Tests {
<StackFrame <StackFrame
ChainIndex="1" ChainIndex="1"
FrameIndex="2" FrameIndex="2"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.SubFunction():System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.SubFunction():System.Void]"
NextStatement="StackFrame_Lifetime.cs:25,4-25,40" NextStatement="StackFrame_Lifetime.cs:25,4-25,40"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />
@ -103,7 +100,6 @@ namespace Debugger.Tests {
ArgumentCount="1" ArgumentCount="1"
ChainIndex="1" ChainIndex="1"
FrameIndex="1" FrameIndex="1"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]"
NextStatement="StackFrame_Lifetime.cs:20,4-20,40" NextStatement="StackFrame_Lifetime.cs:20,4-20,40"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />
@ -113,7 +109,6 @@ namespace Debugger.Tests {
ArgumentCount="1" ArgumentCount="1"
ChainIndex="1" ChainIndex="1"
FrameIndex="1" FrameIndex="1"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]"
NextStatement="StackFrame_Lifetime.cs:20,4-20,40" NextStatement="StackFrame_Lifetime.cs:20,4-20,40"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />
@ -122,7 +117,6 @@ namespace Debugger.Tests {
<Main> <Main>
<StackFrame <StackFrame
ChainIndex="1" ChainIndex="1"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Main():System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Main():System.Void]"
NextStatement="StackFrame_Lifetime.cs:13,4-13,40" NextStatement="StackFrame_Lifetime.cs:13,4-13,40"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />
@ -132,7 +126,6 @@ namespace Debugger.Tests {
ArgumentCount="{Exception: The requested frame index is too big}" ArgumentCount="{Exception: The requested frame index is too big}"
ChainIndex="1" ChainIndex="1"
FrameIndex="1" FrameIndex="1"
HasSymbols="True"
IsInvalid="True" IsInvalid="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Function(i:System.Int32):System.Void]"
NextStatement="{Exception: The requested frame index is too big}" NextStatement="{Exception: The requested frame index is too big}"
@ -141,7 +134,6 @@ namespace Debugger.Tests {
<SelectedStackFrame> <SelectedStackFrame>
<StackFrame <StackFrame
ChainIndex="1" ChainIndex="1"
HasSymbols="True"
MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Main():System.Void]" MethodInfo="[Method Debugger.Tests.StackFrame_Lifetime.Main():System.Void]"
NextStatement="StackFrame_Lifetime.cs:13,4-13,40" NextStatement="StackFrame_Lifetime.cs:13,4-13,40"
Thread="Thread Name = Suspended = False" /> Thread="Thread Name = Suspended = False" />

17
src/AddIns/DisplayBindings/ILSpyAddIn/ILSpySymbolSource.cs

@ -13,6 +13,17 @@ namespace ICSharpCode.ILSpyAddIn
{ {
public class ILSpySymbolSource : ISymbolSource 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) public static MethodDebugSymbols GetSymbols(IMethod method)
{ {
// Use the non-specialised method definition to look up decompiled symbols // Use the non-specialised method definition to look up decompiled symbols
@ -67,12 +78,6 @@ namespace ICSharpCode.ILSpyAddIn
return null; return null;
} }
public bool HasSymbols(IMethod method)
{
var symbols = GetSymbols(method);
return symbols != null && symbols.SequencePoints.Any();
}
public IEnumerable<ILRange> GetIgnoredILRanges(IMethod method) public IEnumerable<ILRange> GetIgnoredILRanges(IMethod method)
{ {
var symbols = GetSymbols(method); var symbols = GetSymbols(method);

Loading…
Cancel
Save