Browse Source

Refactored handling of debug symbols

newNRvisualizers
David Srbecký 13 years ago
parent
commit
c2855d5a65
  1. 13
      src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs
  2. 28
      src/AddIns/Debugger/Debugger.Core/Breakpoint.cs
  3. 2
      src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj
  4. 39
      src/AddIns/Debugger/Debugger.Core/Interop/CorSymExtensionMethods.cs
  5. 125
      src/AddIns/Debugger/Debugger.Core/LocalVariable.cs
  6. 2
      src/AddIns/Debugger/Debugger.Core/NDebugger.cs
  7. 256
      src/AddIns/Debugger/Debugger.Core/PdbSymbolSource.cs
  8. 52
      src/AddIns/Debugger/Debugger.Core/Process.cs
  9. 339
      src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs
  10. 105
      src/AddIns/Debugger/Debugger.Core/StackFrame.cs
  11. 2
      src/AddIns/Debugger/Debugger.Core/Thread.cs
  12. 2
      src/AddIns/Debugger/Debugger.Core/TypeSystemExtensions.cs
  13. 24
      src/AddIns/Debugger/Debugger.Tests/Tests/Breakpoint_Tests.cs
  14. 2
      src/AddIns/Debugger/Debugger.Tests/Tests/ControlFlow_Stepping.cs
  15. 8
      src/AddIns/Debugger/Debugger.Tests/Tests/StackFrame_SetIP.cs

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

@ -338,13 +338,12 @@ namespace ICSharpCode.SharpDevelop.Services @@ -338,13 +338,12 @@ namespace ICSharpCode.SharpDevelop.Services
public bool SetInstructionPointer(string filename, int line, int column, bool dryRun)
{
if (CurrentStackFrame != null) {
SourcecodeSegment seg = CurrentStackFrame.SetIP(filename, line, column, dryRun);
WindowsDebugger.RefreshPads();
JumpToCurrentLine();
return seg != null;
} else {
return false;
if (CurrentStackFrame.SetIP(filename, line, column, dryRun)) {
WindowsDebugger.RefreshPads();
JumpToCurrentLine();
}
}
return false;
}
public void Dispose()
@ -645,7 +644,7 @@ namespace ICSharpCode.SharpDevelop.Services @@ -645,7 +644,7 @@ namespace ICSharpCode.SharpDevelop.Services
// if (debuggedProcess.IsSelectedFrameForced()) {
if (CurrentStackFrame.HasSymbols) {
SourcecodeSegment nextStatement = CurrentStackFrame.NextStatement;
SequencePoint nextStatement = CurrentStackFrame.NextStatement;
if (nextStatement != null) {
DebuggerService.RemoveCurrentLineMarker();
DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn);

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

@ -12,12 +12,13 @@ namespace Debugger @@ -12,12 +12,13 @@ namespace Debugger
public class Breakpoint: DebuggerObject
{
bool enabled;
protected List<ICorDebugFunctionBreakpoint> corBreakpoints = new List<ICorDebugFunctionBreakpoint>();
List<ICorDebugFunctionBreakpoint> corBreakpoints = new List<ICorDebugFunctionBreakpoint>();
[Tests.Ignore]
public string FileName { get; private set; }
public int Line { get; set; }
public int Column { get; set; }
public SourcecodeSegment OriginalLocation { get; private set; }
public byte[] Checksum { get; private set; }
public bool IsEnabled {
get { return enabled; }
@ -41,8 +42,6 @@ namespace Debugger @@ -41,8 +42,6 @@ namespace Debugger
get { return corBreakpoints.Count > 0; }
}
public string TypeName { get; protected set; }
internal Breakpoint()
{
}
@ -70,18 +69,15 @@ namespace Debugger @@ -70,18 +69,15 @@ namespace Debugger
public virtual bool SetBreakpoint(Module module)
{
SourcecodeSegment segment = SourcecodeSegment.Resolve(module, FileName, Line, Column);
if (segment == null)
return false;
OriginalLocation = segment;
ICorDebugFunctionBreakpoint corBreakpoint = segment.CorFunction.GetILCode().CreateBreakpoint((uint)segment.ILStart);
corBreakpoint.Activate(enabled ? 1 : 0);
corBreakpoints.Add(corBreakpoint);
return true;
var seq = PDBSymbolSource.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;
}
return false;
}
}
/*

2
src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj

@ -88,7 +88,7 @@ @@ -88,7 +88,7 @@
<Compile Include="NDebugger.cs" />
<Compile Include="Options.cs" />
<Compile Include="Process.cs" />
<Compile Include="SourcecodeSegment.cs" />
<Compile Include="PDBSymbolSource.cs" />
<Compile Include="StackFrame.cs" />
<Compile Include="Stepper.cs" />
<Compile Include="Thread.cs" />

39
src/AddIns/Debugger/Debugger.Core/Interop/CorSymExtensionMethods.cs

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Linq;
using System.Runtime.InteropServices;
namespace Debugger.Interop.CorSym
@ -57,7 +58,7 @@ namespace Debugger.Interop.CorSym @@ -57,7 +58,7 @@ namespace Debugger.Interop.CorSym
// ISymUnmanagedMethod
public static SequencePoint[] GetSequencePoints(this ISymUnmanagedMethod symMethod)
public static SequencePoint[] GetSequencePoints(this ISymUnmanagedMethod symMethod, int codesize)
{
uint count = symMethod.GetSequencePointCount();
@ -79,16 +80,21 @@ namespace Debugger.Interop.CorSym @@ -79,16 +80,21 @@ namespace Debugger.Interop.CorSym
endColumns
);
SequencePoint[] sequencePoints = new SequencePoint[count];
var sequencePoints = new SequencePoint[count];
var urls = documents.Distinct().ToDictionary(d => d, d => d.GetURL());
var sums = documents.Distinct().ToDictionary(d => d, d => d.GetCheckSum());
uint token = symMethod.GetToken();
for(int i = 0; i < count; i++) {
sequencePoints[i] = new SequencePoint() {
Document = documents[i],
Offset = offsets[i],
Line = lines[i],
Column = columns[i],
EndLine = endLines[i],
EndColumn = endColumns[i]
MethodDefToken = token,
ILRanges = new [] { new ILRange((int)offsets[i], i + 1 < count ? (int)offsets[i + 1] : codesize) },
Filename = urls[documents[i]],
FileCheckSum = sums[documents[i]],
StartLine = (int)lines[i],
StartColumn = (int)columns[i],
EndLine = (int)endLines[i],
EndColumn = (int)endColumns[i]
};
}
@ -174,21 +180,4 @@ namespace Debugger.Interop.CorSym @@ -174,21 +180,4 @@ namespace Debugger.Interop.CorSym
return namespaces;
}
}
public class SequencePoint: IComparable<SequencePoint>
{
public ISymUnmanagedDocument Document { get; internal set; }
public uint Offset { get; internal set; }
public uint Line { get; internal set; }
public uint Column { get; internal set; }
public uint EndLine { get; internal set; }
public uint EndColumn { get; internal set; }
public int CompareTo(SequencePoint other)
{
if (this.Line != other.Line) return this.Line.CompareTo(other.Line);
if (this.Column != other.Column) return this.Column.CompareTo(other.Column);
return this.Offset.CompareTo(other.Offset);
}
}
}

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

@ -3,41 +3,34 @@ @@ -3,41 +3,34 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using Debugger.Interop.CorDebug;
using Debugger.Interop.CorSym;
using Debugger.Interop.MetaData;
using ICSharpCode.NRefactory.TypeSystem;
using Debugger.Interop.CorDebug;
namespace Debugger.MetaData
{
public class LocalVariable
{
delegate Value Getter(StackFrame context);
public IMethod Method { get; private set; }
/// <summary> Index of the local variable in method. -1 for captured variables </summary>
public int Index { get; private set; }
public IType Type { get; private set; }
public string Name { get; internal set; }
/// <summary> IL offset of the start of the variable scope (inclusive) </summary>
public int StartOffset { get; private set; }
/// <summary> IL offset of the end of the variable scope (exclusive) </summary>
public int EndOffset { get; private set; }
public bool IsThis { get; internal set; }
public bool IsCaptured { get; internal set; }
public string Name { get; private set; }
public ILRange[] ILRanges { get; private set; }
public bool IsThis { get; private set; }
public bool IsCaptured { get; private set; }
ValueGetter getter { get; set; }
Getter getter { get; set; }
public LocalVariable(IMethod method, int index, IType type, string name, int startOffset, int endOffset, ValueGetter getter)
LocalVariable(IMethod method, int index, IType type, string name, ILRange[] ilranges, Getter getter)
{
this.Method = method;
this.Index = index;
this.Type = type;
this.Name = name;
this.StartOffset = startOffset;
this.EndOffset = endOffset;
this.ILRanges = ilranges;
this.getter = getter;
}
@ -53,29 +46,47 @@ namespace Debugger.MetaData @@ -53,29 +46,47 @@ namespace Debugger.MetaData
public override string ToString()
{
string msg = this.Type.Name + " " + this.Name;
if (IsCaptured)
msg += " (captured)";
return msg;
return this.Type.Name + " " + this.Name + (this.IsCaptured ? " (captured)" : "");
}
public static List<LocalVariable> GetLocalVariables(IMethod method, ISymUnmanagedMethod symMethod)
public static List<LocalVariable> GetLocalVariables(IMethod method)
{
List<LocalVariable> localVariables;
if (!PDBSymbolSource.HasSymbols(method))
return null;
// Generated constructor may not have any symbols
if (symMethod == null)
return new List<LocalVariable>();
List<LocalVariable> localVariables = new List<LocalVariable>();
localVariables = GetLocalVariablesInScope(method, symMethod.GetRootScope());
foreach (ILLocalVariable ilvar in PDBSymbolSource.GetLocalVariables(method)) {
int index = ilvar.Index;
// NB: Display class does not have the compiler-generated flag
if (ilvar.IsCompilerGenerated || ilvar.Name.StartsWith("CS$")) {
// Get display class from local variable
AddCapturedLocalVariables(
localVariables,
method,
ilvar.ILRanges,
context => GetLocalVariableValue(context, index),
ilvar.Type
);
} else {
LocalVariable locVar = new LocalVariable(
method,
ilvar.Index,
ilvar.Type,
ilvar.Name,
ilvar.ILRanges,
context => GetLocalVariableValue(context, index)
);
localVariables.Add(locVar);
}
}
if (method.DeclaringType.IsDisplayClass()) {
// Get display class from self
// Get display class from 'this'
AddCapturedLocalVariables(
localVariables,
method,
0,
int.MaxValue,
new [] { new ILRange(0, int.MaxValue) },
context => context.GetThisValue(false),
method.DeclaringType
);
@ -85,8 +96,7 @@ namespace Debugger.MetaData @@ -85,8 +96,7 @@ namespace Debugger.MetaData
AddCapturedLocalVariables(
localVariables,
method,
0,
int.MaxValue,
new [] { new ILRange(0, int.MaxValue) },
// TODO: Use eval thread
context => context.GetThisValue(false).GetFieldValue(context.Thread, fieldInfoCopy),
fieldInfo.Type
@ -100,18 +110,18 @@ namespace Debugger.MetaData @@ -100,18 +110,18 @@ namespace Debugger.MetaData
-1,
method.DeclaringType,
"this",
0,
int.MaxValue,
new [] { new ILRange(0, int.MaxValue) },
context => context.GetThisValue(false)
);
thisVar.IsThis = true;
localVariables.Add(thisVar);
}
}
return localVariables;
}
static void AddCapturedLocalVariables(List<LocalVariable> vars, IMethod method, int scopeStartOffset, int scopeEndOffset, ValueGetter getCaptureClass, IType captureClassType)
static void AddCapturedLocalVariables(List<LocalVariable> vars, IMethod method, ILRange[] ilranges, ValueGetter getCaptureClass, IType captureClassType)
{
if (captureClassType.IsDisplayClass()) {
foreach(IField fieldInfo in captureClassType.GetFields()) {
@ -122,8 +132,7 @@ namespace Debugger.MetaData @@ -122,8 +132,7 @@ namespace Debugger.MetaData
-1,
fieldInfo.Type,
fieldInfo.Name,
scopeStartOffset,
scopeEndOffset,
ilranges,
// TODO: Use eval thread
context => getCaptureClass(context).GetFieldValue(context.Thread, fieldInfoCopy)
);
@ -146,49 +155,11 @@ namespace Debugger.MetaData @@ -146,49 +155,11 @@ namespace Debugger.MetaData
}
}
static List<LocalVariable> GetLocalVariablesInScope(IMethod method, ISymUnmanagedScope symScope)
{
List<LocalVariable> vars = new List<LocalVariable>();
foreach (ISymUnmanagedVariable symVar in symScope.GetLocals()) {
uint address = (uint)symVar.GetAddressField1();
IType type = method.GetLocalVariableType((int)address);
// Compiler generated?
// NB: Display class does not have the compiler-generated flag
if ((symVar.GetAttributes() & 1) == 1 || symVar.GetName().StartsWith("CS$")) {
// Get display class from local variable
AddCapturedLocalVariables(
vars,
method,
(int)symScope.GetStartOffset(),
(int)symScope.GetEndOffset(),
context => GetLocalVariableValue(context, address),
type
);
} else {
LocalVariable locVar = new LocalVariable(
method,
(int)address,
type,
symVar.GetName(),
// symVar also has Get*Offset methods, but the are not implemented
(int)symScope.GetStartOffset(),
(int)symScope.GetEndOffset(),
context => GetLocalVariableValue(context, address)
);
vars.Add(locVar);
}
}
foreach(ISymUnmanagedScope childScope in symScope.GetChildren()) {
vars.AddRange(GetLocalVariablesInScope(method, childScope));
}
return vars;
}
public static Value GetLocalVariableValue(StackFrame context, uint address)
public static Value GetLocalVariableValue(StackFrame context, int index)
{
context.Process.AssertPaused();
try {
return new Value(context.AppDomain, context.CorILFrame.GetLocalVariable(address));
return new Value(context.AppDomain, context.CorILFrame.GetLocalVariable((uint)index));
} catch (COMException e) {
if ((uint)e.ErrorCode == 0x80131304) throw new GetValueException("Unavailable in optimized code");
throw;

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

@ -241,7 +241,7 @@ namespace Debugger @@ -241,7 +241,7 @@ namespace Debugger
lock(ProcessIsBeingCreatedLock) {
ICorDebugProcess corDebugProcess = corDebug.DebugActiveProcess((uint)existingProcess.Id, 0);
// TODO: Can we get the acutal working directory?
Process process = new Process(this, corDebugProcess, Path.GetDirectoryName(mainModule));
Process process = new Process(this, corDebugProcess, mainModule, Path.GetDirectoryName(mainModule));
this.processes.Add(process);
return process;
}

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

@ -0,0 +1,256 @@ @@ -0,0 +1,256 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Debugger;
using Debugger.Interop.CorDebug;
using Debugger.Interop.CorSym;
using ICSharpCode.NRefactory.TypeSystem;
namespace Debugger
{
public class SequencePoint
{
public uint MethodDefToken { get; set; }
public ILRange[] ILRanges { get; set; }
public int ILOffset { get { return ILRanges.Length > 0 ? ILRanges[0].From : 0; } }
public string Filename { get; set; }
public byte[] FileCheckSum { get; set; }
public int StartLine { get; set; }
public int StartColumn { get; set; }
public int EndLine { get; set; }
public int EndColumn { get; set; }
public override string ToString()
{
return string.Format("{0}:{1},{2}-{3},{4}",
Path.GetFileName(this.Filename ?? string.Empty),
this.StartLine, this.StartColumn,
this.EndLine, this.EndColumn);
}
}
public struct ILRange
{
public int From;
public int To;
public ILRange(int from, int to)
{
this.From = from;
this.To = to;
}
}
public class ILLocalVariable
{
public int Index { get; set; }
public IType Type { get; set; }
public string Name { get; set; }
public bool IsCompilerGenerated { get; set; }
public ILRange[] ILRanges { get; set; }
}
public class PDBSymbolSource
{
/// <summary>
/// Get absolute source code path for the specified document.
/// If the file is not found, some effort will be made to locate it.
/// (i.e. to handle case when the directory is moved after compilation)
/// One document maps exactly to one disk file.
/// </summary>
/// <remarks>
/// Be careful to use this in the same way in both mapping directinons so that
/// setting breakpoints (src->IL) and stepping (IL->src) work consistently.
/// </remarks>
static string GetSourceCodePath(Process process, string origPath)
{
var paths = RelocatePath(process.Filename, origPath).Distinct();
var path = paths.FirstOrDefault(f => File.Exists(f));
return path;
}
/// <summary>
/// Suggest posible new locations for original path.
/// </summary>
static IEnumerable<string> RelocatePath(string basePath, string origPath)
{
if (!string.IsNullOrEmpty(origPath)) {
if (Path.IsPathRooted(origPath)) {
// Try without relocating
yield return origPath;
string[] baseParts = basePath.Split('\\');
string[] origParts = origPath.Split('\\');
// Join the paths at some point (joining directry must match)
for (int i = 0; i < baseParts.Length; i++) {
for (int j = 0; j < origParts.Length; j++) {
if (!string.IsNullOrEmpty(baseParts[i]) && string.Equals(baseParts[i], origParts[j], StringComparison.OrdinalIgnoreCase)) {
// Join the paths
string[] joinedDirs = new string[i + (origParts.Length - j)];
Array.Copy(baseParts, joinedDirs, i);
Array.Copy(origParts, j, joinedDirs, i, origParts.Length - j);
string joined = string.Join(@"\", joinedDirs);
yield return joined;
}
}
}
} else {
if (origPath.StartsWith(@".\"))
origPath = origPath.Substring(2);
if (origPath.StartsWith(@"\"))
origPath = origPath.Substring(1);
// Try 0, 1 and 2 levels above the base path
string dir = basePath;
for(int i = 0; i <= 2; i++) {
dir = Path.GetDirectoryName(dir);
if (!string.IsNullOrEmpty(dir))
yield return Path.Combine(dir, origPath);
}
}
}
}
public static SequencePoint GetSequencePoint(IMethod method, int iloffset)
{
var symMethod = method.GetSymMethod();
if (symMethod == null)
return null;
// 0xFEEFEE means "code generated by compiler"
int codeSize = (int)method.ToCorFunction().GetILCode().GetSize();
var sequencePoints = symMethod.GetSequencePoints(codeSize);
var realSeqPoints = sequencePoints.Where(p => p.StartLine != 0xFEEFEE);
// Find point for which (ilstart <= iloffset < ilend) or fallback to the next valid sequence point
var sequencePoint = realSeqPoints.FirstOrDefault(p => p.ILRanges.Any(r => r.From <= iloffset && iloffset < r.To)) ??
realSeqPoints.FirstOrDefault(p => iloffset <= p.ILOffset);
// VB.NET sometimes produces temporary files which it then deletes
// (eg 17d14f5c-a337-4978-8281-53493378c1071.vb)
string name = Path.GetFileName(sequencePoint.Filename);
if (name.Length == 40 && name.EndsWith(".vb")) {
if (name.Substring(0, name.Length - 3).All(c => ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F') || (c == '-'))) {
return null;
}
}
if (sequencePoint != null) {
sequencePoint.Filename = GetSourceCodePath(method.ParentAssembly.GetModule().Process, sequencePoint.Filename);
}
return sequencePoint;
}
public static SequencePoint GetSequencePoint(Module module, string filename, int line, int column)
{
// Do not use ISymUnmanagedReader.GetDocument! It is broken if two files have the same name
// Do not use ISymUnmanagedMethod.GetOffset! It sometimes returns negative offset
if (filename == null || !Path.IsPathRooted(filename))
throw new DebuggerException("Invalid filename. Absolute path is required.");
ISymUnmanagedReader symReader = module.SymReader;
if (symReader == null)
return null; // No symbols
// Find ISymUnmanagedDocument which excactly matches the filename.
var symDoc = module.SymDocuments.FirstOrDefault(d => string.Equals(filename, d.GetURL(), StringComparison.OrdinalIgnoreCase));
// Find the file even if the symbol is relative or if the file was moved
var symDocs = module.SymDocuments.Where(d => string.Equals(Path.GetFileName(filename), Path.GetFileName(d.GetURL()), StringComparison.OrdinalIgnoreCase));
symDoc = symDoc ?? symDocs.FirstOrDefault(d => string.Equals(GetSourceCodePath(module.Process, d.GetURL()), filename, StringComparison.OrdinalIgnoreCase));
if (symDoc == null) return null; // Document not found
ISymUnmanagedMethod symMethod;
try {
uint validLine = symDoc.FindClosestLine((uint)line);
symMethod = symReader.GetMethodFromDocumentPosition(symDoc, (uint)validLine, (uint)column);
} catch {
return null; //Not found
}
var corFunction = module.CorModule.GetFunctionFromToken(symMethod.GetToken());
int codesize = (int)corFunction.GetILCode().GetSize();
var seqPoints = symMethod.GetSequencePoints(codesize).Where(s => s.StartLine != 0xFEEFEE);
SequencePoint seqPoint = null;
if (column == 0) {
seqPoint = seqPoints.FirstOrDefault(s => s.StartLine <= line && line <= s.EndLine);
} else {
seqPoint = seqPoints.FirstOrDefault(s => (s.StartLine < line || (s.StartLine == line && s.StartColumn <= column)) &&
(line < s.EndLine || (line == s.EndLine && column <= s.EndColumn)));
}
seqPoint = seqPoint ?? seqPoints.FirstOrDefault(s => line <= s.StartLine);
return seqPoint;
}
public static bool HasSymbols(IMethod method)
{
return method.GetSymMethod() != null;
}
public static IEnumerable<ILRange> GetIgnoredILRanges(IMethod method)
{
var symMethod = method.GetSymMethod();
if (symMethod == null)
return null;
int codeSize = (int)method.ToCorFunction().GetILCode().GetSize();
return symMethod.GetSequencePoints(codeSize).Where(p => p.StartLine == 0xFEEFEE).SelectMany(p => p.ILRanges).ToList();
}
public static IEnumerable<ILLocalVariable> GetLocalVariables(IMethod method)
{
var symMethod = method.GetSymMethod();
if (symMethod == null)
return null;
List<ILLocalVariable> vars = new List<ILLocalVariable>();
Stack<ISymUnmanagedScope> scopes = new Stack<ISymUnmanagedScope>();
scopes.Push(symMethod.GetRootScope());
while(scopes.Count > 0) {
ISymUnmanagedScope scope = scopes.Pop();
foreach (ISymUnmanagedVariable symVar in scope.GetLocals()) {
int index = (int)symVar.GetAddressField1();
vars.Add(new ILLocalVariable() {
Index = index,
Type = method.GetLocalVariableType(index),
Name = symVar.GetName(),
IsCompilerGenerated = (symVar.GetAttributes() & 1) == 1,
// symVar also has Get*Offset methods, but the are not implemented
ILRanges = new [] { new ILRange() { From = (int)scope.GetStartOffset(), To = (int)scope.GetEndOffset() } }
});
}
foreach(ISymUnmanagedScope childScope in scope.GetChildren()) {
scopes.Push(childScope);
}
}
return vars;
}
}
static class PDBSymbolSourceExtensions
{
public static ISymUnmanagedMethod GetSymMethod(this IMethod method)
{
var module = method.ParentAssembly.GetModule();
if (module.SymReader == null) {
return null;
}
try {
return module.SymReader.GetMethod(method.GetMetadataToken());
} catch (System.Runtime.InteropServices.COMException) {
// Can not find the method
// eg. Compiler generated constructors are not in symbol store
return null;
}
}
}
}

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

@ -3,7 +3,6 @@ @@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Debugger.Interop.CorDebug;
using Debugger.Interop.CorSym;
@ -106,13 +105,16 @@ namespace Debugger @@ -106,13 +105,16 @@ namespace Debugger
get { return workingDirectory; }
}
public string Filename { get; private set; }
public static DebugModeFlag DebugMode { get; set; }
internal Process(NDebugger debugger, ICorDebugProcess corProcess, string workingDirectory)
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.callbackInterface = new ManagedCallback(this);
}
@ -127,7 +129,7 @@ namespace Debugger @@ -127,7 +129,7 @@ namespace Debugger
ICorDebugProcess outProcess;
if (workingDirectory == null || workingDirectory == "") {
if (string.IsNullOrEmpty(workingDirectory)) {
workingDirectory = System.IO.Path.GetDirectoryName(filename);
}
@ -136,25 +138,26 @@ namespace Debugger @@ -136,25 +138,26 @@ namespace Debugger
secAttr.lpSecurityDescriptor = IntPtr.Zero;
secAttr.nLength = (uint)sizeof(_SECURITY_ATTRIBUTES);
fixed (uint* pprocessStartupInfo = processStartupInfo)
fixed (uint* pprocessInfo = processInfo)
outProcess =
debugger.CorDebug.CreateProcess(
filename, // lpApplicationName
// If we do not prepend " ", the first argument migh just get lost
" " + arguments, // lpCommandLine
ref secAttr, // lpProcessAttributes
ref secAttr, // lpThreadAttributes
1,//TRUE // bInheritHandles
0x00000010 /*CREATE_NEW_CONSOLE*/, // dwCreationFlags
IntPtr.Zero, // lpEnvironment
workingDirectory, // lpCurrentDirectory
(uint)pprocessStartupInfo, // lpStartupInfo
(uint)pprocessInfo, // lpProcessInformation,
CorDebugCreateProcessFlags.DEBUG_NO_SPECIAL_OPTIONS // debuggingFlags
);
fixed (uint* pprocessStartupInfo = processStartupInfo) {
fixed (uint* pprocessInfo = processInfo) {
outProcess = debugger.CorDebug.CreateProcess(
filename, // lpApplicationName
// If we do not prepend " ", the first argument migh just get lost
" " + arguments, // lpCommandLine
ref secAttr, // lpProcessAttributes
ref secAttr, // lpThreadAttributes
1,//TRUE // bInheritHandles
0x00000010 /*CREATE_NEW_CONSOLE*/, // dwCreationFlags
IntPtr.Zero, // lpEnvironment
workingDirectory, // lpCurrentDirectory
(uint)pprocessStartupInfo, // lpStartupInfo
(uint)pprocessInfo, // lpProcessInformation,
CorDebugCreateProcessFlags.DEBUG_NO_SPECIAL_OPTIONS // debuggingFlags
);
}
}
return new Process(debugger, outProcess, workingDirectory);
return new Process(debugger, outProcess, filename, workingDirectory);
}
internal void OnLogMessage(MessageEventArgs arg)
@ -449,9 +452,10 @@ namespace Debugger @@ -449,9 +452,10 @@ namespace Debugger
public void RunTo(string fileName, int line, int column)
{
foreach(Module module in this.Modules) {
SourcecodeSegment segment = SourcecodeSegment.Resolve(module, fileName, line, column);
if (segment != null) {
ICorDebugFunctionBreakpoint corBreakpoint = segment.CorFunction.GetILCode().CreateBreakpoint((uint)segment.ILStart);
SequencePoint seq = PDBSymbolSource.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);
}

339
src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs

@ -1,339 +0,0 @@ @@ -1,339 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Debugger.Interop.CorDebug;
using Debugger.Interop.CorSym;
namespace Debugger
{
public class SourcecodeSegment: DebuggerObject
{
public Module Module { get; private set; }
public string Filename { get; private set; }
public string Typename { get; private set; }
public byte[] CheckSum { get; private set; }
public int StartLine { get; private set; }
public int StartColumn { get; private set; }
public int EndLine { get; private set; }
public int EndColumn { get; private set; }
internal ICorDebugFunction CorFunction { get; private set; }
public int ILStart { get; private set; }
public int ILEnd { get; private set; }
public int[] StepRanges { get; private set; }
private SourcecodeSegment()
{
}
/// <summary>
/// Use the module path to figure out where to look for the source file
/// </summary>
static IEnumerable<string> RelocateSymURL(Module module, string symUrl)
{
string modulePath = module.Process.WorkingDirectory;
if (module.IsInMemory || module.IsDynamic) {
// Just use any module with symboles
foreach(Module m in module.Process.Modules) {
if (m.HasSymbols) {
if (!string.IsNullOrEmpty(m.FullPath)) {
modulePath = Path.GetDirectoryName(m.FullPath);
break;
}
}
}
} else {
if (!string.IsNullOrEmpty(module.FullPath))
modulePath = Path.GetDirectoryName(module.FullPath);
}
if (string.IsNullOrEmpty(modulePath)) {
yield return symUrl;
yield break;
}
if (Path.IsPathRooted(symUrl)) {
Dictionary<string, object> returned = new Dictionary<string, object>();
// Try without relocating
returned.Add(symUrl, null);
yield return symUrl;
// The two paths to combine
string[] moduleDirs = modulePath.Split('\\');
string[] urlDirs = symUrl.Split('\\');
// Join the paths at some point (joining directry must match)
for (int i = 0; i < moduleDirs.Length; i++) {
for (int j = 0; j < urlDirs.Length; j++) {
if (!string.IsNullOrEmpty(moduleDirs[i]) &&
!string.IsNullOrEmpty(urlDirs[j]) &&
string.Equals(moduleDirs[i], urlDirs[j], StringComparison.OrdinalIgnoreCase))
{
// Join the paths
string[] joinedDirs = new string[i + (urlDirs.Length - j)];
Array.Copy(moduleDirs, joinedDirs, i);
Array.Copy(urlDirs, j, joinedDirs, i, urlDirs.Length - j);
string joined = string.Join(@"\", joinedDirs);
// Return joined path
if (!returned.ContainsKey(joined)) {
returned.Add(joined, null);
yield return joined;
}
}
}
}
} else {
if (symUrl.StartsWith(@".\")) symUrl = symUrl.Substring(2);
if (symUrl.StartsWith(@"\")) symUrl = symUrl.Substring(1);
// Try 0, 1 and 2 levels above the module directory
string dir = modulePath;
if (!string.IsNullOrEmpty(dir)) yield return Path.Combine(dir, symUrl);
dir = Path.GetDirectoryName(dir);
if (!string.IsNullOrEmpty(dir)) yield return Path.Combine(dir, symUrl);
dir = Path.GetDirectoryName(dir);
if (!string.IsNullOrEmpty(dir)) yield return Path.Combine(dir, symUrl);
}
}
static ISymUnmanagedDocument GetSymDocumentFromFilename(Module module, string filename)
{
if (filename == null) throw new ArgumentNullException("filename");
if (Path.IsPathRooted(filename)) {
foreach(ISymUnmanagedDocument symDoc in module.SymDocuments) {
foreach (string url in RelocateSymURL(module, symDoc.GetURL())) {
if (string.Equals(url, filename, StringComparison.OrdinalIgnoreCase))
return symDoc;
}
}
} else {
foreach(ISymUnmanagedDocument symDoc in module.SymDocuments) {
if (filename.StartsWith(@".\")) filename = filename.Substring(2);
if (filename.StartsWith(@"\")) filename = filename.Substring(1);
if (symDoc.GetURL().ToLowerInvariant().EndsWith(@"\" + filename.ToLowerInvariant())) {
return symDoc;
}
}
}
return null;
}
public static SourcecodeSegment Resolve(Module module, string fileName, int line, int column)
{
// Do not use ISymUnmanagedReader.GetDocument! It is broken if two files have the same name
// Do not use ISymUnmanagedMethod.GetOffset! It sometimes returns negative offset
if (fileName == null) return null;
ISymUnmanagedReader symReader = module.SymReader;
if (symReader == null) return null; // No symbols
ISymUnmanagedDocument symDoc = GetSymDocumentFromFilename(module, fileName);
if (symDoc == null) return null; // Document not found
ISymUnmanagedMethod symMethod;
try {
uint validLine = symDoc.FindClosestLine((uint)line);
symMethod = symReader.GetMethodFromDocumentPosition(symDoc, (uint)validLine, (uint)column);
} catch {
return null; //Not found
}
SequencePoint[] seqPoints = symMethod.GetSequencePoints();
Array.Sort(seqPoints);
if (seqPoints.Length == 0) return null;
if (line < seqPoints[0].Line) return null;
foreach(SequencePoint sqPoint in seqPoints) {
if (sqPoint.Line == 0xFEEFEE) continue;
// If the desired breakpoint position is before the end of the sequence point
if (line < sqPoint.EndLine || (line == sqPoint.EndLine && column < sqPoint.EndColumn)) {
SourcecodeSegment segment = new SourcecodeSegment();
segment.Module = module;
segment.Filename = symDoc.GetURL();
segment.CheckSum = symDoc.GetCheckSum();
segment.StartLine = (int)sqPoint.Line;
segment.StartColumn = (int)sqPoint.Column;
segment.EndLine = (int)sqPoint.EndLine;
segment.EndColumn = (int)sqPoint.EndColumn;
segment.CorFunction = module.CorModule.GetFunctionFromToken(symMethod.GetToken());
segment.ILStart = (int)sqPoint.Offset;
segment.ILEnd = (int)sqPoint.Offset;
segment.StepRanges = null;
return segment;
}
}
return null;
}
static string GetFilenameFromSymDocument(Module module, ISymUnmanagedDocument symDoc)
{
foreach (string filename in RelocateSymURL(module, symDoc.GetURL())) {
if (File.Exists(filename))
return filename;
}
return symDoc.GetURL();
}
/// <summary>
/// 'ILStart &lt;= ILOffset &lt;= ILEnd' and this range includes at least
/// the returned area of source code. (May incude some extra compiler generated IL too)
/// </summary>
internal static SourcecodeSegment Resolve(Module module, ISymUnmanagedMethod symMethod, ICorDebugFunction corFunction, int offset)
{
if (symMethod == null)
return null;
uint sequencePointCount = symMethod.GetSequencePointCount();
SequencePoint[] sequencePoints = symMethod.GetSequencePoints();
// Get i for which: offsets[i] <= offset < offsets[i + 1]
// or fallback to first element if offset < offsets[0]
for (int i = (int)sequencePointCount - 1; i >= 0; i--) { // backwards
if ((int)sequencePoints[i].Offset <= offset || i == 0) {
// Set inforamtion about current IL range
int codeSize = (int)corFunction.GetILCode().GetSize();
int ilStart = (int)sequencePoints[i].Offset;
int ilEnd = (i + 1 < sequencePointCount) ? (int)sequencePoints[i+1].Offset : codeSize;
// 0xFeeFee means "code generated by compiler"
// If we are in generated sequence use to closest real one instead,
// extend the ILStart and ILEnd to include the 'real' sequence
// Look ahead for 'real' sequence
while (i + 1 < sequencePointCount && sequencePoints[i].Line == 0xFeeFee) {
i++;
ilEnd = (i + 1 < sequencePointCount) ? (int)sequencePoints[i+1].Offset : codeSize;
}
// Look back for 'real' sequence
while (i - 1 >= 0 && sequencePoints[i].Line == 0xFeeFee) {
i--;
ilStart = (int)sequencePoints[i].Offset;
}
// Wow, there are no 'real' sequences
if (sequencePoints[i].Line == 0xFeeFee) {
return null;
}
List<int> stepRanges = new List<int>();
for (int j = 0; j < sequencePointCount; j++) {
// Step over compiler generated sequences and current statement
// 0xFeeFee means "code generated by compiler"
if (sequencePoints[j].Line == 0xFeeFee || j == i) {
// Add start offset or remove last end (to connect two ranges into one)
if (stepRanges.Count > 0 && stepRanges[stepRanges.Count - 1] == sequencePoints[j].Offset) {
stepRanges.RemoveAt(stepRanges.Count - 1);
} else {
stepRanges.Add((int)sequencePoints[j].Offset);
}
// Add end offset | handle last sequence point
if (j + 1 < sequencePointCount) {
stepRanges.Add((int)sequencePoints[j+1].Offset);
} else {
stepRanges.Add(codeSize);
}
}
}
SourcecodeSegment segment = new SourcecodeSegment();
segment.Module = module;
segment.Filename = GetFilenameFromSymDocument(module, sequencePoints[i].Document);
segment.CheckSum = sequencePoints[i].Document.GetCheckSum();
segment.StartLine = (int)sequencePoints[i].Line;
segment.StartColumn = (int)sequencePoints[i].Column;
segment.EndLine = (int)sequencePoints[i].EndLine;
segment.EndColumn = (int)sequencePoints[i].EndColumn;
segment.CorFunction = corFunction;
segment.ILStart = ilStart;
segment.ILEnd = ilEnd;
segment.StepRanges = stepRanges.ToArray();
// VB.NET sometimes produces temporary files which it then deletes
// (eg 17d14f5c-a337-4978-8281-53493378c1071.vb)
string filename = Path.GetFileName(segment.Filename);
if (filename.Length == 40 && filename.EndsWith(".vb")) {
bool guidName = true;
foreach(char c in filename.Substring(0, filename.Length - 3)) {
if (('0' <= c && c <= '9') ||
('a' <= c && c <= 'f') ||
('A' <= c && c <= 'F') ||
(c == '-'))
{
guidName = true;
} else {
guidName = false;
break;
}
}
if (guidName)
return null;
}
return segment;
}
}
return null;
}
public override string ToString()
{
return string.Format("{0}:{1},{2}-{3},{4}",
Path.GetFileName(this.Filename ?? string.Empty),
this.StartLine, this.StartColumn, this.EndLine, this.EndColumn);
}
#region Decompiled breakpoint
public static SourcecodeSegment CreateForIL(Module module, int line, int metadataToken, int iLOffset)
{
try {
SourcecodeSegment segment = new SourcecodeSegment();
segment.Module = module;
segment.Typename = null;
segment.CheckSum = null;
segment.StartLine = line;
segment.StartColumn = 0;
segment.EndLine = line;
segment.EndColumn = 0;
segment.CorFunction = module.CorModule.GetFunctionFromToken((uint)metadataToken);
segment.ILStart = iLOffset;
segment.ILEnd = iLOffset;
segment.StepRanges = null;
return segment;
} catch {
return null;
}
}
public static SourcecodeSegment ResolveForIL(Module module, ICorDebugFunction corFunction, int line, int offset, int[] ranges)
{
try {
SourcecodeSegment segment = new SourcecodeSegment();
segment.Module = module;
segment.Typename = null;
segment.CheckSum = null;
segment.StartLine = line;
segment.StartColumn = 0;
segment.EndLine = line;
segment.EndColumn = 0;
segment.CorFunction = corFunction;
segment.ILStart = offset;
segment.ILEnd = ranges[1];
segment.StepRanges = ranges;
return segment;
} catch {
return null;
}
}
#endregion
}
}

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

@ -5,11 +5,9 @@ using System; @@ -5,11 +5,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Debugger.Interop.CorSym;
using Debugger.MetaData;
using Debugger.Interop.CorDebug;
using Debugger.Interop.MetaData;
using ICSharpCode.NRefactory.TypeSystem;
using Debugger.Interop.CorDebug;
using Debugger.MetaData;
namespace Debugger
{
@ -45,30 +43,14 @@ namespace Debugger @@ -45,30 +43,14 @@ namespace Debugger
internal ICorDebugFunction CorFunction { get; private set; }
/// <summary> True if the stack frame has symbols defined.
/// (That is has accesss to the .pdb file) </summary>
/// <summary> True if the stack frame has symbols defined. </summary>
public bool HasSymbols {
get {
return GetSegmentForOffset(0) != null;
return PDBSymbolSource.HasSymbols(this.MethodInfo);
}
}
internal ISymUnmanagedMethod SymMethod {
get {
if (this.Module.SymReader == null) {
return null;
}
try {
return this.Module.SymReader.GetMethod(this.CorFunction.GetToken());
} catch (COMException) {
// Can not find the method
// eg. Compiler generated constructors are not in symbol store
return null;
}
}
}
/// <summary> Returns true is this incance can not be used any more. </summary>
/// <summary> Returns true if this instance can not be used any more. </summary>
public bool IsInvalid {
get {
try {
@ -128,17 +110,6 @@ namespace Debugger @@ -128,17 +110,6 @@ namespace Debugger
}
}
public int[] ILRanges { get; set; }
public int SourceCodeLine { get; set; }
SourcecodeSegment GetSegmentForOffset(int offset)
{
if (SourceCodeLine != 0)
return SourcecodeSegment.ResolveForIL(this.Module, this.CorFunction, SourceCodeLine, offset, ILRanges);
return SourcecodeSegment.Resolve(this.Module, this.SymMethod, this.CorFunction, offset);
}
/// <summary> Step into next instruction </summary>
public void StepInto()
{
@ -182,19 +153,26 @@ namespace Debugger @@ -182,19 +153,26 @@ namespace Debugger
void AsyncStep(bool stepIn)
{
int[] stepRanges;
if (ILRanges == null) {
SourcecodeSegment nextSt = NextStatement;
if (nextSt == null) {
throw new DebuggerException("Unable to step. Next statement not aviable");
}
stepRanges = nextSt.StepRanges;
} else {
stepRanges = ILRanges;
List<ILRange> stepRanges = new List<ILRange>();
var seq = PDBSymbolSource.GetSequencePoint(this.MethodInfo, this.IP);
if (seq != null) {
stepRanges.AddRange(seq.ILRanges);
stepRanges.AddRange(PDBSymbolSource.GetIgnoredILRanges(this.MethodInfo));
}
// Remove overlapping and connected ranges
List<int> fromToList = new List<int>();
foreach(var range in stepRanges.OrderBy(r => r.From)) {
if (fromToList.Count > 0 && range.From <= fromToList[fromToList.Count - 1]) {
fromToList[fromToList.Count - 1] = Math.Max(range.To, fromToList[fromToList.Count - 1]);
} else {
fromToList.Add(range.From);
fromToList.Add(range.To);
}
}
if (stepIn) {
Stepper stepInStepper = Stepper.StepIn(this, stepRanges, "normal");
Stepper stepInStepper = Stepper.StepIn(this, fromToList.ToArray(), "normal");
this.Thread.CurrentStepIn = stepInStepper;
Stepper clearCurrentStepIn = Stepper.StepOut(this, "clear current step in");
clearCurrentStepIn.StepComplete += delegate {
@ -204,7 +182,7 @@ namespace Debugger @@ -204,7 +182,7 @@ namespace Debugger
};
clearCurrentStepIn.Ignore = true;
} else {
Stepper.StepOver(this, stepRanges, "normal");
Stepper.StepOver(this, fromToList.ToArray(), "normal");
}
this.Process.AsyncContinue(DebuggeeStateAction.Clear);
@ -215,34 +193,33 @@ namespace Debugger @@ -215,34 +193,33 @@ namespace Debugger
///
/// Returns null on error.
/// </summary>
public SourcecodeSegment NextStatement {
public SequencePoint NextStatement {
get {
return GetSegmentForOffset(IP);
return PDBSymbolSource.GetSequencePoint(this.MethodInfo, this.IP);
}
}
public SourcecodeSegment SetIP(string filename, int line, int column, bool dryRun)
public bool SetIP(string filename, int line, int column, bool dryRun)
{
this.Process.AssertPaused();
SourcecodeSegment segment = SourcecodeSegment.Resolve(this.Module, filename, line, column);
if (segment != null && segment.CorFunction.GetToken() == this.MethodInfo.GetMetadataToken()) {
var seq = PDBSymbolSource.GetSequencePoint(this.Module, filename, line, column);
if (seq != null && seq.MethodDefToken == this.MethodInfo.GetMetadataToken()) {
try {
if (dryRun) {
CorILFrame.CanSetIP((uint)segment.ILStart);
CorILFrame.CanSetIP((uint)seq.ILOffset);
} else {
CorILFrame.SetIP((uint)segment.ILStart);
CorILFrame.SetIP((uint)seq.ILOffset);
// Invalidates all frames and chains for the current thread
this.Process.NotifyResumed(DebuggeeStateAction.Keep);
this.Process.NotifyPaused();
}
} catch {
return null;
return false;
}
return segment;
return true;
}
return null;
return false;
}
/// <summary> Get instance of 'this'. </summary>
@ -341,28 +318,24 @@ namespace Debugger @@ -341,28 +318,24 @@ namespace Debugger
/// <summary> Get all local variables </summary>
public IEnumerable<LocalVariable> GetLocalVariables()
{
// Note that the user might load symbols later
if (localVariables == null) {
var symMethod = this.SymMethod;
// Note that the user might load symbols later
if (symMethod == null)
return new List<LocalVariable>();
localVariables = LocalVariable.GetLocalVariables(this.MethodInfo, symMethod);
localVariables = LocalVariable.GetLocalVariables(this.MethodInfo);
}
return localVariables;
return localVariables ?? new List<LocalVariable>();
}
/// <summary> Get local variables valid at the given IL offset </summary>
public IEnumerable<LocalVariable> GetLocalVariables(int offset)
{
return GetLocalVariables().Where(v => v.StartOffset <= offset && offset < v.EndOffset);
return GetLocalVariables().Where(v => v.ILRanges.Any(r => r.From <= offset && offset < r.To));
}
/// <summary> Get local variable with given name which is valid at the current IP </summary>
/// <returns> Null if not found </returns>
public Value GetLocalVariableValue(string name)
{
var loc = GetLocalVariables(this.IP).Where(v => v.Name == name).FirstOrDefault();
var loc = GetLocalVariables(this.IP).FirstOrDefault(v => v.Name == name);
if (loc == null)
throw new GetValueException("Local variable \"{0}\" not found", name);
return loc.GetValue(this);
@ -374,7 +347,7 @@ namespace Debugger @@ -374,7 +347,7 @@ namespace Debugger
Options opt = this.Process.Options;
if (opt.StepOverNoSymbols) {
if (this.SymMethod == null) return true;
if (!PDBSymbolSource.HasSymbols(this.MethodInfo)) return true;
}
if (opt.StepOverDebuggerAttributes) {
string[] debuggerAttributes = {

2
src/AddIns/Debugger/Debugger.Core/Thread.cs

@ -300,7 +300,7 @@ namespace Debugger @@ -300,7 +300,7 @@ namespace Debugger
{
StringBuilder stackTrace = new StringBuilder();
foreach(StackFrame stackFrame in this.GetCallstack(100)) {
SourcecodeSegment loc = stackFrame.NextStatement;
SequencePoint loc = stackFrame.NextStatement;
stackTrace.Append(" ");
if (loc != null) {
stackTrace.AppendFormat(formatSymbols, stackFrame.MethodInfo.FullName, loc.Filename, loc.StartLine);

2
src/AddIns/Debugger/Debugger.Core/TypeSystemExtensions.cs

@ -461,7 +461,7 @@ namespace Debugger @@ -461,7 +461,7 @@ namespace Debugger
public static ICorDebugFunction ToCorFunction(this IMethod method)
{
Module module = method.DeclaringType.GetDefinition().ParentAssembly.GetModule();
Module module = method.ParentAssembly.GetModule();
return module.CorModule.GetFunctionFromToken(method.GetMetadataToken());
}

24
src/AddIns/Debugger/Debugger.Tests/Tests/Breakpoint_Tests.cs

@ -28,11 +28,13 @@ namespace Debugger.Tests { @@ -28,11 +28,13 @@ namespace Debugger.Tests {
[NUnit.Framework.Test]
public void Breakpoint_Tests()
{
Breakpoint breakpoint1 = debugger.AddBreakpoint(@"Breakpoint_Tests.cs", 14);
Breakpoint breakpoint2 = debugger.AddBreakpoint(@"Breakpoint_Tests.cs", 15);
StartTest();
string filename = CurrentStackFrame.NextStatement.Filename;
Breakpoint breakpoint1 = debugger.AddBreakpoint(filename, 14);
Breakpoint breakpoint2 = debugger.AddBreakpoint(filename, 15);
Assert.IsTrue(breakpoint1.IsSet);
Assert.IsTrue(breakpoint2.IsSet);
ObjectDump("Breakpoint1", breakpoint1);
@ -64,19 +66,15 @@ namespace Debugger.Tests { @@ -64,19 +66,15 @@ namespace Debugger.Tests {
<Paused>Breakpoint_Tests.cs:12,4-12,40</Paused>
<Breakpoint1>
<Breakpoint
FileName="Breakpoint_Tests.cs"
IsEnabled="True"
IsSet="True"
Line="14"
OriginalLocation="Breakpoint_Tests.cs:14,4-14,49" />
Line="14" />
</Breakpoint1>
<Breakpoint2>
<Breakpoint
FileName="Breakpoint_Tests.cs"
IsEnabled="True"
IsSet="True"
Line="15"
OriginalLocation="Breakpoint_Tests.cs:16,4-16,49" />
Line="15" />
</Breakpoint2>
<ModuleLoaded>System.Configuration.dll (No symbols)</ModuleLoaded>
<ModuleLoaded>System.Xml.dll (No symbols)</ModuleLoaded>
@ -89,17 +87,13 @@ namespace Debugger.Tests { @@ -89,17 +87,13 @@ namespace Debugger.Tests {
<Exited />
<Breakpoint1>
<Breakpoint
FileName="Breakpoint_Tests.cs"
IsEnabled="True"
Line="14"
OriginalLocation="Breakpoint_Tests.cs:14,4-14,49" />
Line="14" />
</Breakpoint1>
<Breakpoint2>
<Breakpoint
FileName="Breakpoint_Tests.cs"
IsEnabled="True"
Line="15"
OriginalLocation="Breakpoint_Tests.cs:16,4-16,49" />
Line="15" />
</Breakpoint2>
</Test>
</DebuggerTests>

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

@ -119,7 +119,7 @@ namespace Debugger.Tests { @@ -119,7 +119,7 @@ namespace Debugger.Tests {
{
StartTest();
SourcecodeSegment start = this.CurrentStackFrame.NextStatement;
SequencePoint start = this.CurrentStackFrame.NextStatement;
foreach (bool jmcEnabled in new bool[] {true, true, false}) {
ObjectDump("Log", "Starting run with JMC=" + jmcEnabled.ToString());

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

@ -26,9 +26,11 @@ namespace Debugger.Tests { @@ -26,9 +26,11 @@ namespace Debugger.Tests {
{
StartTest();
Assert.IsNotNull(this.CurrentStackFrame.SetIP("StackFrame_SetIP.cs", 12, 0, true));
Assert.IsNull(this.CurrentStackFrame.SetIP("StackFrame_SetIP.cs", 100, 0, true));
this.CurrentStackFrame.SetIP("StackFrame_SetIP.cs", 12, 0, false);
string filename = this.CurrentStackFrame.NextStatement.Filename;
Assert.IsNotNull(this.CurrentStackFrame.SetIP(filename, 12, 0, true));
Assert.IsFalse(this.CurrentStackFrame.SetIP(filename, 100, 0, true));
this.CurrentStackFrame.SetIP(filename, 12, 0, false);
process.Continue();
Assert.AreEqual("1\r\n1\r\n", log);

Loading…
Cancel
Save