|
|
|
@ -185,110 +185,158 @@ namespace DebuggerLibrary
@@ -185,110 +185,158 @@ namespace DebuggerLibrary
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get the information about the next statement to be executed.
|
|
|
|
|
///
|
|
|
|
|
/// Throws NextStatementNotAviableException on error.
|
|
|
|
|
///
|
|
|
|
|
/// 'ILStart <= ILOffset <= ILEnd' and this range includes at least
|
|
|
|
|
/// the returned area of source code. (May incude some extra compiler generated IL too)
|
|
|
|
|
/// Returns null on error.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public SourcecodeSegment NextStatement { |
|
|
|
|
get { |
|
|
|
|
ISymbolMethod symMethod; |
|
|
|
|
|
|
|
|
|
symMethod = this.symMethod; |
|
|
|
|
if (symMethod == null) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
return GetSegmentForOffet(corInstructionPtr); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int sequencePointCount = symMethod.SequencePointCount; |
|
|
|
|
|
|
|
|
|
int[] offsets = new int[sequencePointCount]; |
|
|
|
|
int[] startLine = new int[sequencePointCount]; |
|
|
|
|
int[] startColumn = new int[sequencePointCount]; |
|
|
|
|
int[] endLine = new int[sequencePointCount]; |
|
|
|
|
int[] endColumn = new int[sequencePointCount]; |
|
|
|
|
|
|
|
|
|
ISymbolDocument[] Doc = new ISymbolDocument[sequencePointCount]; |
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns null on error.
|
|
|
|
|
///
|
|
|
|
|
/// 'ILStart <= ILOffset <= ILEnd' and this range includes at least
|
|
|
|
|
/// the returned area of source code. (May incude some extra compiler generated IL too)
|
|
|
|
|
/// </summary>
|
|
|
|
|
SourcecodeSegment GetSegmentForOffet(uint offset) |
|
|
|
|
{ |
|
|
|
|
ISymbolMethod symMethod; |
|
|
|
|
|
|
|
|
|
symMethod.GetSequencePoints( |
|
|
|
|
offsets, |
|
|
|
|
Doc, |
|
|
|
|
startLine, |
|
|
|
|
startColumn, |
|
|
|
|
endLine, |
|
|
|
|
endColumn |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
uint corInstructionPtr = this.corInstructionPtr; // cache
|
|
|
|
|
SourcecodeSegment retVal = new SourcecodeSegment(); |
|
|
|
|
|
|
|
|
|
// Get i for which: offsets[i] <= corInstructionPtr < offsets[i + 1]
|
|
|
|
|
for (int i = sequencePointCount - 1; i >= 0; i--) // backwards
|
|
|
|
|
if (offsets[i] <= corInstructionPtr) |
|
|
|
|
{ |
|
|
|
|
// Set inforamtion about current IL range
|
|
|
|
|
ICorDebugCode code; |
|
|
|
|
corFunction.GetILCode(out code); |
|
|
|
|
uint codeSize; |
|
|
|
|
code.GetSize(out codeSize); |
|
|
|
|
|
|
|
|
|
retVal.ILOffset = (int)corInstructionPtr; |
|
|
|
|
retVal.ILStart = offsets[i]; |
|
|
|
|
symMethod = this.symMethod; |
|
|
|
|
if (symMethod == null) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int sequencePointCount = symMethod.SequencePointCount; |
|
|
|
|
|
|
|
|
|
int[] offsets = new int[sequencePointCount]; |
|
|
|
|
int[] startLine = new int[sequencePointCount]; |
|
|
|
|
int[] startColumn = new int[sequencePointCount]; |
|
|
|
|
int[] endLine = new int[sequencePointCount]; |
|
|
|
|
int[] endColumn = new int[sequencePointCount]; |
|
|
|
|
|
|
|
|
|
ISymbolDocument[] Doc = new ISymbolDocument[sequencePointCount]; |
|
|
|
|
|
|
|
|
|
symMethod.GetSequencePoints( |
|
|
|
|
offsets, |
|
|
|
|
Doc, |
|
|
|
|
startLine, |
|
|
|
|
startColumn, |
|
|
|
|
endLine, |
|
|
|
|
endColumn |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
SourcecodeSegment retVal = new SourcecodeSegment(); |
|
|
|
|
|
|
|
|
|
// Get i for which: offsets[i] <= corInstructionPtr < offsets[i + 1]
|
|
|
|
|
for (int i = sequencePointCount - 1; i >= 0; i--) // backwards
|
|
|
|
|
if (offsets[i] <= offset) |
|
|
|
|
{ |
|
|
|
|
// Set inforamtion about current IL range
|
|
|
|
|
ICorDebugCode code; |
|
|
|
|
corFunction.GetILCode(out code); |
|
|
|
|
uint codeSize; |
|
|
|
|
code.GetSize(out codeSize); |
|
|
|
|
|
|
|
|
|
retVal.ILOffset = (int)offset; |
|
|
|
|
retVal.ILStart = offsets[i]; |
|
|
|
|
retVal.ILEnd = (i + 1 < sequencePointCount) ? offsets[i + 1] : (int)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 && startLine[i] == 0xFeeFee) { |
|
|
|
|
i++; |
|
|
|
|
retVal.ILEnd = (i + 1 < sequencePointCount) ? offsets[i + 1] : (int)codeSize; |
|
|
|
|
} |
|
|
|
|
// Look back for 'real' sequence
|
|
|
|
|
while (i - 1 >= 0 && startLine[i] == 0xFeeFee) { |
|
|
|
|
i--; |
|
|
|
|
retVal.ILStart = offsets[i]; |
|
|
|
|
} |
|
|
|
|
// Wow, there are no 'real' sequences
|
|
|
|
|
if (startLine[i] == 0xFeeFee) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
retVal.ModuleFilename = module.FullPath; |
|
|
|
|
|
|
|
|
|
// Look ahead for 'real' sequence
|
|
|
|
|
while (i + 1 < sequencePointCount && startLine[i] == 0xFeeFee) { |
|
|
|
|
i++; |
|
|
|
|
retVal.ILEnd = (i + 1 < sequencePointCount) ? offsets[i + 1] : (int)codeSize; |
|
|
|
|
} |
|
|
|
|
// Look back for 'real' sequence
|
|
|
|
|
while (i - 1 >= 0 && startLine[i] == 0xFeeFee) { |
|
|
|
|
i--; |
|
|
|
|
retVal.ILStart = offsets[i]; |
|
|
|
|
} |
|
|
|
|
// Wow, there are no 'real' sequences
|
|
|
|
|
if (startLine[i] == 0xFeeFee) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
retVal.SourceFullFilename = Doc[i].URL; |
|
|
|
|
|
|
|
|
|
retVal.StartLine = startLine[i]; |
|
|
|
|
retVal.StartColumn = startColumn[i]; |
|
|
|
|
retVal.EndLine = endLine[i]; |
|
|
|
|
retVal.EndColumn = endColumn[i]; |
|
|
|
|
|
|
|
|
|
retVal.ModuleFilename = module.FullPath; |
|
|
|
|
|
|
|
|
|
retVal.SourceFullFilename = Doc[i].URL; |
|
|
|
|
|
|
|
|
|
retVal.StartLine = startLine[i]; |
|
|
|
|
retVal.StartColumn = startColumn[i]; |
|
|
|
|
retVal.EndLine = endLine[i]; |
|
|
|
|
retVal.EndColumn = endColumn[i]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 (startLine[j] == 0xFeeFee || j == i) { |
|
|
|
|
// Add start offset or remove last end (to connect two ranges into one)
|
|
|
|
|
if (stepRanges.Count > 0 && stepRanges[stepRanges.Count - 1] == offsets[j]) { |
|
|
|
|
stepRanges.RemoveAt(stepRanges.Count - 1); |
|
|
|
|
} else { |
|
|
|
|
stepRanges.Add(offsets[j]); |
|
|
|
|
} |
|
|
|
|
// Add end offset | handle last sequence point
|
|
|
|
|
if (j + 1 < sequencePointCount) { |
|
|
|
|
stepRanges.Add(offsets[j + 1]); |
|
|
|
|
} else { |
|
|
|
|
stepRanges.Add((int)codeSize); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 (startLine[j] == 0xFeeFee || j == i) { |
|
|
|
|
// Add start offset or remove last end (to connect two ranges into one)
|
|
|
|
|
if (stepRanges.Count > 0 && stepRanges[stepRanges.Count - 1] == offsets[j]) { |
|
|
|
|
stepRanges.RemoveAt(stepRanges.Count - 1); |
|
|
|
|
} else { |
|
|
|
|
stepRanges.Add(offsets[j]); |
|
|
|
|
} |
|
|
|
|
// Add end offset | handle last sequence point
|
|
|
|
|
if (j + 1 < sequencePointCount) { |
|
|
|
|
stepRanges.Add(offsets[j + 1]); |
|
|
|
|
} else { |
|
|
|
|
stepRanges.Add((int)codeSize); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
retVal.StepRanges = stepRanges.ToArray(); |
|
|
|
|
retVal.StepRanges = stepRanges.ToArray(); |
|
|
|
|
|
|
|
|
|
return retVal; |
|
|
|
|
} |
|
|
|
|
return retVal; |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public SourcecodeSegment CanSetIP(string filename, int line, int column) |
|
|
|
|
{ |
|
|
|
|
return SetIP(true, filename, line, column); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public SourcecodeSegment SetIP(string filename, int line, int column) |
|
|
|
|
{ |
|
|
|
|
return SetIP(false, filename, line, column); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
SourcecodeSegment SetIP(bool simulate, string filename, int line, int column) |
|
|
|
|
{ |
|
|
|
|
SourcecodeSegment suggestion = new SourcecodeSegment(filename, line, column, column); |
|
|
|
|
ICorDebugFunction corFunction; |
|
|
|
|
int ilOffset; |
|
|
|
|
if (!suggestion.GetFunctionAndOffset(debugger, false, out corFunction, out ilOffset)) { |
|
|
|
|
return null; |
|
|
|
|
} else { |
|
|
|
|
uint token; |
|
|
|
|
corFunction.GetToken(out token); |
|
|
|
|
if (token != methodProps.Token) { |
|
|
|
|
return null; |
|
|
|
|
} else { |
|
|
|
|
try { |
|
|
|
|
if (simulate) { |
|
|
|
|
corILFrame.CanSetIP((uint)ilOffset); |
|
|
|
|
} else { |
|
|
|
|
corILFrame.SetIP((uint)ilOffset); |
|
|
|
|
Thread thread = debugger.CurrentThread; |
|
|
|
|
debugger.OnDebuggingResumed(); |
|
|
|
|
thread.CurrentFunction = thread.LastFunctionWithLoadedSymbols; |
|
|
|
|
debugger.OnDebuggingPaused(PausedReason.SetIP); |
|
|
|
|
} |
|
|
|
|
} catch { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
return GetSegmentForOffet((uint)ilOffset); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|