Browse Source

Merge pull request #2067 from edkazcarlson/debugInfo

Adding variable tracking throughout assembly printout
pull/2113/head
Siegfried Pammer 5 years ago committed by GitHub
parent
commit
f014e38614
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj
  2. 156
      ILSpy.ReadyToRun/ReadyToRunLanguage.cs
  3. 2
      ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml
  4. 14
      ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
  5. 24
      ILSpy.ReadyToRun/ReadyToRunOptions.cs

2
ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj

@ -75,7 +75,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Iced" Version="1.6.0" /> <PackageReference Include="Iced" Version="1.6.0" />
<PackageReference Include="ILCompiler.Reflection.ReadyToRun" Version="1.0.8-alpha" /> <PackageReference Include="ILCompiler.Reflection.ReadyToRun" Version="1.0.9-alpha" />
</ItemGroup> </ItemGroup>
<Target Name="RemoveTransitiveProjectReferences" AfterTargets="IncludeTransitiveProjectReferences"> <Target Name="RemoveTransitiveProjectReferences" AfterTargets="IncludeTransitiveProjectReferences">

156
ILSpy.ReadyToRun/ReadyToRunLanguage.cs

@ -103,10 +103,85 @@ namespace ICSharpCode.ILSpy.ReadyToRun
output.WriteLine("; " + comment); output.WriteLine("; " + comment);
} }
private Dictionary<VarLocType, HashSet<(DebugInfo debugInfo, NativeVarInfo varLoc)>> WriteDebugInfo(ReadyToRunMethod readyToRunMethod, ITextOutput output)
{
Dictionary<VarLocType, HashSet<(DebugInfo debugInfo, NativeVarInfo varLoc)>> debugInfoDict = new Dictionary<VarLocType, HashSet<(DebugInfo debugInfo, NativeVarInfo varLoc)>>();
IReadOnlyList<RuntimeFunction> runTimeList = readyToRunMethod.RuntimeFunctions;
foreach (RuntimeFunction runtimeFunction in runTimeList) {
DebugInfo debugInfo = runtimeFunction.DebugInfo;
if (debugInfo != null && debugInfo.BoundsList.Count > 0) {
for (int i = 0; i < debugInfo.VariablesList.Count; ++i) {
var varLoc = debugInfo.VariablesList[i];
try {
var typeSet = new HashSet<ValueTuple<DebugInfo, NativeVarInfo>>();
bool found = debugInfoDict.TryGetValue(varLoc.VariableLocation.VarLocType, out typeSet);
if (found) {
(DebugInfo debugInfo, NativeVarInfo varLoc) newTuple = (debugInfo, varLoc);
typeSet.Add(newTuple);
} else {
typeSet = new HashSet<ValueTuple<DebugInfo, NativeVarInfo>>();
debugInfoDict.Add(varLoc.VariableLocation.VarLocType, typeSet);
(DebugInfo debugInfo, NativeVarInfo varLoc) newTuple = (debugInfo, varLoc);
typeSet.Add(newTuple);
}
} catch (ArgumentNullException) {
output.WriteLine("Failed to find hash set of Debug info type");
}
if (varLoc.VariableLocation.VarLocType != VarLocType.VLT_REG && varLoc.VariableLocation.VarLocType != VarLocType.VLT_STK
&& varLoc.VariableLocation.VarLocType != VarLocType.VLT_STK_BYREF) {
output.WriteLine($" Variable Number: {varLoc.VariableNumber}");
output.WriteLine($" Start Offset: 0x{varLoc.StartOffset:X}");
output.WriteLine($" End Offset: 0x{varLoc.EndOffset:X}");
output.WriteLine($" Loc Type: {varLoc.VariableLocation.VarLocType}");
switch (varLoc.VariableLocation.VarLocType) {
case VarLocType.VLT_REG:
case VarLocType.VLT_REG_FP:
case VarLocType.VLT_REG_BYREF:
output.WriteLine($" Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}");
break;
case VarLocType.VLT_STK:
case VarLocType.VLT_STK_BYREF:
output.WriteLine($" Base Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}");
output.WriteLine($" Stack Offset: {varLoc.VariableLocation.Data2}");
break;
case VarLocType.VLT_REG_REG:
output.WriteLine($" Register 1: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}");
output.WriteLine($" Register 2: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data2)}");
break;
case VarLocType.VLT_REG_STK:
output.WriteLine($" Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}");
output.WriteLine($" Base Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data2)}");
output.WriteLine($" Stack Offset: {varLoc.VariableLocation.Data3}");
break;
case VarLocType.VLT_STK_REG:
output.WriteLine($" Stack Offset: {varLoc.VariableLocation.Data1}");
output.WriteLine($" Base Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data2)}");
output.WriteLine($" Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data3)}");
break;
case VarLocType.VLT_STK2:
output.WriteLine($" Base Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}");
output.WriteLine($" Stack Offset: {varLoc.VariableLocation.Data2}");
break;
case VarLocType.VLT_FPSTK:
output.WriteLine($" Offset: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}");
break;
case VarLocType.VLT_FIXED_VA:
output.WriteLine($" Offset: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}");
break;
default:
output.WriteLine("WRN: Unexpected variable location type");
break;
}
output.WriteLine("");
}
}
}
}
return debugInfoDict;
}
private Dictionary<ulong, UnwindCode> WriteUnwindInfo(RuntimeFunction runtimeFunction, ITextOutput output) private Dictionary<ulong, UnwindCode> WriteUnwindInfo(RuntimeFunction runtimeFunction, ITextOutput output)
{ {
Dictionary<ulong, UnwindCode> unwindCodes = new Dictionary<ulong, UnwindCode>(); Dictionary<ulong, UnwindCode> unwindCodes = new Dictionary<ulong, UnwindCode>();
if (runtimeFunction.UnwindInfo is UnwindInfo amd64UnwindInfo) { if (runtimeFunction.UnwindInfo is UnwindInfo amd64UnwindInfo) {
@ -129,7 +204,6 @@ namespace ICSharpCode.ILSpy.ReadyToRun
WriteCommentLine(output, $"FrameRegister: {((amd64UnwindInfo.FrameRegister == 0) ? "none" : amd64UnwindInfo.FrameRegister.ToString())}"); WriteCommentLine(output, $"FrameRegister: {((amd64UnwindInfo.FrameRegister == 0) ? "none" : amd64UnwindInfo.FrameRegister.ToString())}");
for (int unwindCodeIndex = 0; unwindCodeIndex < amd64UnwindInfo.CountOfUnwindCodes; unwindCodeIndex++) { for (int unwindCodeIndex = 0; unwindCodeIndex < amd64UnwindInfo.CountOfUnwindCodes; unwindCodeIndex++) {
unwindCodes.Add((ulong)(amd64UnwindInfo.UnwindCodeArray[unwindCodeIndex].CodeOffset), amd64UnwindInfo.UnwindCodeArray[unwindCodeIndex]); unwindCodes.Add((ulong)(amd64UnwindInfo.UnwindCodeArray[unwindCodeIndex].CodeOffset), amd64UnwindInfo.UnwindCodeArray[unwindCodeIndex]);
} }
} }
return unwindCodes; return unwindCodes;
@ -137,19 +211,25 @@ namespace ICSharpCode.ILSpy.ReadyToRun
private void Disassemble(PEFile currentFile, ITextOutput output, ReadyToRunReader reader, ReadyToRunMethod readyToRunMethod, RuntimeFunction runtimeFunction, int bitness, ulong address, bool showMetadataTokens, bool showMetadataTokensInBase10) private void Disassemble(PEFile currentFile, ITextOutput output, ReadyToRunReader reader, ReadyToRunMethod readyToRunMethod, RuntimeFunction runtimeFunction, int bitness, ulong address, bool showMetadataTokens, bool showMetadataTokensInBase10)
{ {
// TODO: Decorate the disassembly with GCInfo
WriteCommentLine(output, readyToRunMethod.SignatureString); WriteCommentLine(output, readyToRunMethod.SignatureString);
Dictionary<ulong, UnwindCode> unwindInfo = null; Dictionary<ulong, UnwindCode> unwindInfo = null;
if (ReadyToRunOptions.GetIsShowUnwindInfo(null) && bitness == 64) { if (ReadyToRunOptions.GetIsShowUnwindInfo(null) && bitness == 64) {
unwindInfo = WriteUnwindInfo(runtimeFunction, output); unwindInfo = WriteUnwindInfo(runtimeFunction, output);
} }
bool isShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(null);
Dictionary<VarLocType, HashSet<ValueTuple<DebugInfo, NativeVarInfo>>> debugInfo = null;
if (isShowDebugInfo) {
debugInfo = WriteDebugInfo(readyToRunMethod, output);
}
byte[] codeBytes = new byte[runtimeFunction.Size]; byte[] codeBytes = new byte[runtimeFunction.Size];
for (int i = 0; i < runtimeFunction.Size; i++) { for (int i = 0; i < runtimeFunction.Size; i++) {
codeBytes[i] = reader.Image[reader.GetOffset(runtimeFunction.StartAddress) + i]; codeBytes[i] = reader.Image[reader.GetOffset(runtimeFunction.StartAddress) + i];
} }
// TODO: Decorate the disassembly with GC and debug info
var codeReader = new ByteArrayCodeReader(codeBytes); var codeReader = new ByteArrayCodeReader(codeBytes);
var decoder = Decoder.Create(bitness, codeReader); var decoder = Decoder.Create(bitness, codeReader);
decoder.IP = address; decoder.IP = address;
@ -174,7 +254,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
ulong baseInstrIP = instructions[0].IP; ulong baseInstrIP = instructions[0].IP;
foreach (var instr in instructions) { foreach (var instr in instructions) {
int byteBaseIndex = (int)(instr.IP - address); int byteBaseIndex = (int)(instr.IP - address);
if (runtimeFunction.DebugInfo != null) { if (isShowDebugInfo && runtimeFunction.DebugInfo != null) {
foreach (var bound in runtimeFunction.DebugInfo.BoundsList) { foreach (var bound in runtimeFunction.DebugInfo.BoundsList) {
if (bound.NativeOffset == byteBaseIndex) { if (bound.NativeOffset == byteBaseIndex) {
if (bound.ILOffset == (uint)DebugInfoBoundsType.Prolog) { if (bound.ILOffset == (uint)DebugInfoBoundsType.Prolog) {
@ -191,14 +271,17 @@ namespace ICSharpCode.ILSpy.ReadyToRun
output.Write(instr.IP.ToString("X16")); output.Write(instr.IP.ToString("X16"));
output.Write(" "); output.Write(" ");
int instrLen = instr.Length; int instrLen = instr.Length;
for (int i = 0; i < instrLen; i++) for (int i = 0; i < instrLen; i++) {
output.Write(codeBytes[byteBaseIndex + i].ToString("X2")); output.Write(codeBytes[byteBaseIndex + i].ToString("X2"));
}
int missingBytes = 10 - instrLen; int missingBytes = 10 - instrLen;
for (int i = 0; i < missingBytes; i++) for (int i = 0; i < missingBytes; i++) {
output.Write(" "); output.Write(" ");
}
output.Write(" "); output.Write(" ");
output.Write(tempOutput.ToStringAndReset()); output.Write(tempOutput.ToStringAndReset());
DecorateUnwindInfo(output, unwindInfo, baseInstrIP, instr); DecorateUnwindInfo(output, unwindInfo, baseInstrIP, instr);
DecorateDebugInfo(output, instr, debugInfo, baseInstrIP);
DecorateCallSite(currentFile, output, reader, showMetadataTokens, showMetadataTokensInBase10, instr); DecorateCallSite(currentFile, output, reader, showMetadataTokens, showMetadataTokensInBase10, instr);
} }
output.WriteLine(); output.WriteLine();
@ -213,6 +296,66 @@ namespace ICSharpCode.ILSpy.ReadyToRun
} }
} }
private static void DecorateDebugInfo(ITextOutput output, Instruction instr, Dictionary<VarLocType, HashSet<(DebugInfo debugInfo, NativeVarInfo varLoc)>> debugInfoDict, ulong baseInstrIP)
{
if (debugInfoDict != null) {
InstructionInfoFactory factory = new InstructionInfoFactory();
InstructionInfo info = factory.GetInfo(instr);
HashSet<ValueTuple<DebugInfo, NativeVarInfo>> stkSet = new HashSet<ValueTuple<DebugInfo, NativeVarInfo>>();
if (debugInfoDict.ContainsKey(VarLocType.VLT_STK)) {
stkSet.UnionWith(debugInfoDict[VarLocType.VLT_STK]);
}
if (debugInfoDict.ContainsKey(VarLocType.VLT_STK_BYREF)) {
stkSet.UnionWith(debugInfoDict[VarLocType.VLT_STK_BYREF]);
}
if (stkSet != null) {
foreach (UsedMemory usedMemInfo in info.GetUsedMemory()) { //for each time a [register +- value] is used
foreach ((DebugInfo debugInfo, NativeVarInfo varLoc) tuple in stkSet) { //for each VLT_STK variable
var debugInfo = tuple.debugInfo;
var varInfo = tuple.varLoc;
int stackOffset = varInfo.VariableLocation.Data2;
ulong adjOffset;
bool negativeOffset;
if (stackOffset < 0) {
int absValue = -1 * stackOffset;
adjOffset = ulong.MaxValue - (ulong)absValue + 1;
negativeOffset = true;
} else {
adjOffset = (ulong)stackOffset;
negativeOffset = false;
}
if (varInfo.StartOffset < instr.IP - baseInstrIP && varInfo.EndOffset > instr.IP - baseInstrIP &&
DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varInfo.VariableLocation.Data1) == usedMemInfo.Base.ToString() &&
adjOffset == usedMemInfo.Displacement) {
output.Write($"; [{usedMemInfo.Base.ToString()}{(negativeOffset ? '-' : '+')}{Math.Abs(stackOffset)}] = {varInfo.Variable.Type} {varInfo.Variable.Index}");
}
}
}
}
HashSet<ValueTuple<DebugInfo, NativeVarInfo>> regSet = new HashSet<ValueTuple<DebugInfo, NativeVarInfo>>();
if (debugInfoDict.ContainsKey(VarLocType.VLT_REG)) {
regSet.UnionWith(debugInfoDict[VarLocType.VLT_REG]);
}
if (debugInfoDict.ContainsKey(VarLocType.VLT_REG_BYREF)) {
regSet.UnionWith(debugInfoDict[VarLocType.VLT_REG_BYREF]);
}
if (debugInfoDict.ContainsKey(VarLocType.VLT_REG_FP)) {
regSet.UnionWith(debugInfoDict[VarLocType.VLT_REG_FP]);
}
if (regSet != null) {
foreach (UsedRegister usedMemInfo in info.GetUsedRegisters()) {
foreach ((DebugInfo debugInfo, NativeVarInfo varLoc) tuple in regSet) {
var debugInfo = tuple.debugInfo;
var varInfo = tuple.varLoc;
if (varInfo.StartOffset < instr.IP - baseInstrIP && varInfo.EndOffset > instr.IP - baseInstrIP &&
DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varInfo.VariableLocation.Data1) == usedMemInfo.Register.ToString()) {
output.Write($"; {usedMemInfo.Register.ToString()} = {varInfo.Variable.Type} {varInfo.Variable.Index}");
}
}
}
}
}
}
private static void DecorateCallSite(PEFile currentFile, ITextOutput output, ReadyToRunReader reader, bool showMetadataTokens, bool showMetadataTokensInBase10, Instruction instr) private static void DecorateCallSite(PEFile currentFile, ITextOutput output, ReadyToRunReader reader, bool showMetadataTokens, bool showMetadataTokensInBase10, Instruction instr)
{ {
int importCellAddress = (int)instr.IPRelativeMemoryAddress; int importCellAddress = (int)instr.IPRelativeMemoryAddress;
@ -246,7 +389,6 @@ namespace ICSharpCode.ILSpy.ReadyToRun
output.WriteLine(reader.ImportCellNames[importCellAddress]); output.WriteLine(reader.ImportCellNames[importCellAddress]);
break; break;
} }
output.WriteLine(); output.WriteLine();
} else { } else {
output.WriteLine(); output.WriteLine();

2
ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml

@ -17,6 +17,6 @@
<TextBlock Grid.Row="1" Margin="3" Text="{x:Static properties:Resources.ShowUnwindInfo}"/> <TextBlock Grid.Row="1" Margin="3" Text="{x:Static properties:Resources.ShowUnwindInfo}"/>
<CheckBox Grid.Row="1" Grid.Column="1" Margin="3" IsChecked="{Binding IsShowUnwindInfo}" /> <CheckBox Grid.Row="1" Grid.Column="1" Margin="3" IsChecked="{Binding IsShowUnwindInfo}" />
<TextBlock Grid.Row="2" Margin="3" Text="{x:Static properties:Resources.ShowDebugInfo}"/> <TextBlock Grid.Row="2" Margin="3" Text="{x:Static properties:Resources.ShowDebugInfo}"/>
<CheckBox Grid.Row="2" Grid.Column="1" Margin="3" IsChecked="{Binding DebugIsChecked}" /> <CheckBox Grid.Row="2" Grid.Column="1" Margin="3" IsChecked="{Binding IsShowDebugInfo}" />
</Grid> </Grid>
</UserControl> </UserControl>

14
ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs

@ -36,6 +36,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
Options s = new Options(); Options s = new Options();
s.DisassemblyFormat = ReadyToRunOptions.GetDisassemblyFormat(settings); s.DisassemblyFormat = ReadyToRunOptions.GetDisassemblyFormat(settings);
s.IsShowUnwindInfo = ReadyToRunOptions.GetIsShowUnwindInfo(settings); s.IsShowUnwindInfo = ReadyToRunOptions.GetIsShowUnwindInfo(settings);
s.IsShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(settings);
this.DataContext = s; this.DataContext = s;
} }
@ -48,7 +49,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
public void Save(XElement root) public void Save(XElement root)
{ {
Options s = (Options)this.DataContext; Options s = (Options)this.DataContext;
ReadyToRunOptions.SetDisassemblyOptions(root, s.DisassemblyFormat, s.IsShowUnwindInfo); ReadyToRunOptions.SetDisassemblyOptions(root, s.DisassemblyFormat, s.IsShowUnwindInfo, s.IsShowDebugInfo);
} }
} }
@ -71,6 +72,17 @@ namespace ICSharpCode.ILSpy.ReadyToRun
} }
} }
private bool isShowDebugInfo;
public bool IsShowDebugInfo {
get {
return isShowDebugInfo;
}
set {
isShowDebugInfo = value;
OnPropertyChanged(nameof(IsShowDebugInfo));
}
}
private string disassemblyFormat; private string disassemblyFormat;

24
ILSpy.ReadyToRun/ReadyToRunOptions.cs

@ -43,7 +43,6 @@ namespace ICSharpCode.ILSpy.ReadyToRun
} }
public static bool GetIsShowUnwindInfo(ILSpySettings settings) public static bool GetIsShowUnwindInfo(ILSpySettings settings)
{ {
if (settings == null) { if (settings == null) {
settings = ILSpySettings.Load(); settings = ILSpySettings.Load();
@ -58,11 +57,27 @@ namespace ICSharpCode.ILSpy.ReadyToRun
} }
} }
public static void SetDisassemblyOptions(XElement root, string disassemblyFormat, bool IsShowUnwindInfo) public static bool GetIsShowDebugInfo(ILSpySettings settings)
{
if (settings == null) {
settings = ILSpySettings.Load();
}
XElement e = settings[ns + "ReadyToRunOptions"];
XAttribute a = e.Attribute("IsShowDebugInfo");
if (a == null) {
return true;
} else {
return (bool)a;
}
}
public static void SetDisassemblyOptions(XElement root, string disassemblyFormat, bool isShowUnwindInfo, bool isShowDebugInfo)
{ {
XElement section = new XElement(ns + "ReadyToRunOptions"); XElement section = new XElement(ns + "ReadyToRunOptions");
section.SetAttributeValue("DisassemblyFormat", disassemblyFormat); section.SetAttributeValue("DisassemblyFormat", disassemblyFormat);
section.SetAttributeValue("IsShowUnwindInfo", IsShowUnwindInfo); section.SetAttributeValue("IsShowUnwindInfo", isShowUnwindInfo);
section.SetAttributeValue("IsShowDebugInfo", isShowDebugInfo);
XElement existingElement = root.Element(ns + "ReadyToRunOptions"); XElement existingElement = root.Element(ns + "ReadyToRunOptions");
if (existingElement != null) { if (existingElement != null) {
existingElement.ReplaceWith(section); existingElement.ReplaceWith(section);
@ -70,8 +85,5 @@ namespace ICSharpCode.ILSpy.ReadyToRun
root.Add(section); root.Add(section);
} }
} }
} }
} }

Loading…
Cancel
Save