Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding variable tracking throughout assembly printout #2067

Merged
merged 8 commits into from
Aug 8, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

<ItemGroup>
<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>

<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" />
Expand Down
153 changes: 146 additions & 7 deletions ILSpy.ReadyToRun/ReadyToRunLanguage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,82 @@ public override void WriteCommentLine(ITextOutput output, string comment)
output.WriteLine("; " + comment);
}

private Dictionary<VarLocType, HashSet<Tuple<DebugInfo, NativeVarInfo>>> WriteDebugInfo(ReadyToRunMethod readyToRunMethod, ITextOutput output)
siegfriedpammer marked this conversation as resolved.
Show resolved Hide resolved
{
Dictionary<VarLocType, HashSet<Tuple<DebugInfo, NativeVarInfo>>> debugInfoDict = new Dictionary<VarLocType, HashSet<Tuple<DebugInfo, NativeVarInfo>>>();
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 {
HashSet<Tuple<DebugInfo, NativeVarInfo>> typeSet = new HashSet<Tuple<DebugInfo, NativeVarInfo>>();
bool found = debugInfoDict.TryGetValue(varLoc.VariableLocation.VarLocType, out typeSet);
if (found) {
typeSet.Add(new Tuple<DebugInfo, NativeVarInfo>(debugInfo, varLoc));
} else {
typeSet = new HashSet<Tuple<DebugInfo, NativeVarInfo>>();
debugInfoDict.Add(varLoc.VariableLocation.VarLocType, typeSet);
typeSet.Add(new Tuple<DebugInfo, NativeVarInfo>(debugInfo, varLoc));
}
} 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:
throw new BadImageFormatException("Unexpected var loc type");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we print a user-friendly message instead?

}
output.WriteLine("");
}
}
}
}
return debugInfoDict;
}

private Dictionary<ulong, UnwindCode> WriteUnwindInfo(RuntimeFunction runtimeFunction, ITextOutput output)

{
Dictionary<ulong, UnwindCode> unwindCodes = new Dictionary<ulong, UnwindCode>();
if (runtimeFunction.UnwindInfo is UnwindInfo amd64UnwindInfo) {
Expand All @@ -129,7 +201,6 @@ private Dictionary<ulong, UnwindCode> WriteUnwindInfo(RuntimeFunction runtimeFun
WriteCommentLine(output, $"FrameRegister: {((amd64UnwindInfo.FrameRegister == 0) ? "none" : amd64UnwindInfo.FrameRegister.ToString())}");
for (int unwindCodeIndex = 0; unwindCodeIndex < amd64UnwindInfo.CountOfUnwindCodes; unwindCodeIndex++) {
unwindCodes.Add((ulong)(amd64UnwindInfo.UnwindCodeArray[unwindCodeIndex].CodeOffset), amd64UnwindInfo.UnwindCodeArray[unwindCodeIndex]);

}
}
return unwindCodes;
Expand All @@ -138,18 +209,24 @@ private Dictionary<ulong, UnwindCode> WriteUnwindInfo(RuntimeFunction runtimeFun
private void Disassemble(PEFile currentFile, ITextOutput output, ReadyToRunReader reader, ReadyToRunMethod readyToRunMethod, RuntimeFunction runtimeFunction, int bitness, ulong address, bool showMetadataTokens, bool showMetadataTokensInBase10)
{
WriteCommentLine(output, readyToRunMethod.SignatureString);

Dictionary<ulong, UnwindCode> unwindInfo = null;
if (ReadyToRunOptions.GetIsShowUnwindInfo(null) && bitness == 64) {
unwindInfo = WriteUnwindInfo(runtimeFunction, output);
}

bool isShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(null);
Dictionary<VarLocType, HashSet<Tuple<DebugInfo, NativeVarInfo>>> debugInfo = null;
if (isShowDebugInfo) {
debugInfo = WriteDebugInfo(readyToRunMethod, output);
}

byte[] codeBytes = new byte[runtimeFunction.Size];
for (int i = 0; i < runtimeFunction.Size; i++) {
codeBytes[i] = reader.Image[reader.GetOffset(runtimeFunction.StartAddress) + i];
}

// TODO: Decorate the disassembly with GC and debug info
// TODO: Decorate the disassembly with GCInfo
var codeReader = new ByteArrayCodeReader(codeBytes);
var decoder = Decoder.Create(bitness, codeReader);
decoder.IP = address;
Expand All @@ -174,7 +251,7 @@ private void Disassemble(PEFile currentFile, ITextOutput output, ReadyToRunReade
ulong baseInstrIP = instructions[0].IP;
foreach (var instr in instructions) {
int byteBaseIndex = (int)(instr.IP - address);
if (runtimeFunction.DebugInfo != null) {
if (isShowDebugInfo && runtimeFunction.DebugInfo != null) {
foreach (var bound in runtimeFunction.DebugInfo.BoundsList) {
if (bound.NativeOffset == byteBaseIndex) {
if (bound.ILOffset == (uint)DebugInfoBoundsType.Prolog) {
Expand All @@ -191,14 +268,17 @@ private void Disassemble(PEFile currentFile, ITextOutput output, ReadyToRunReade
output.Write(instr.IP.ToString("X16"));
output.Write(" ");
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"));
}
int missingBytes = 10 - instrLen;
for (int i = 0; i < missingBytes; i++)
for (int i = 0; i < missingBytes; i++) {
output.Write(" ");
}
output.Write(" ");
output.Write(tempOutput.ToStringAndReset());
DecorateUnwindInfo(output, unwindInfo, baseInstrIP, instr);
DecorateDebugInfo(output, instr, debugInfo, baseInstrIP);
DecorateCallSite(currentFile, output, reader, showMetadataTokens, showMetadataTokensInBase10, instr);
}
output.WriteLine();
Expand All @@ -213,6 +293,66 @@ private static void DecorateUnwindInfo(ITextOutput output, Dictionary<ulong, Unw
}
}

private static void DecorateDebugInfo(ITextOutput output, Instruction instr, Dictionary<VarLocType, HashSet<Tuple<DebugInfo, NativeVarInfo>>> debugInfoDict, ulong baseInstrIP)
{
if (debugInfoDict != null) {
InstructionInfoFactory factory = new InstructionInfoFactory();
InstructionInfo info = factory.GetInfo(instr);
HashSet<Tuple<DebugInfo, NativeVarInfo>> stkSet = new HashSet<Tuple<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 (Tuple<DebugInfo, NativeVarInfo> tuple in stkSet) { //for each VLT_STK variable
var debugInfo = tuple.Item1;
var varInfo = tuple.Item2;
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}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to show hex for stackOffset to be consistent with the disassembly.

}
}
}
}
HashSet<Tuple<DebugInfo, NativeVarInfo>> regSet = new HashSet<Tuple<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 (Tuple<DebugInfo, NativeVarInfo> tuple in regSet) {
var debugInfo = tuple.Item1;
var varInfo = tuple.Item2;
if (varInfo.StartOffset < instr.IP - baseInstrIP && varInfo.EndOffset > instr.IP - baseInstrIP &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to double-check with this, I think the StartOffset should be inclusive, the EndOffset I am not sure.

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)
{
int importCellAddress = (int)instr.IPRelativeMemoryAddress;
Expand Down Expand Up @@ -246,7 +386,6 @@ private static void DecorateCallSite(PEFile currentFile, ITextOutput output, Rea
output.WriteLine(reader.ImportCellNames[importCellAddress]);
break;
}

output.WriteLine();
} else {
output.WriteLine();
Expand Down
2 changes: 1 addition & 1 deletion ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
<TextBlock Grid.Row="1" Margin="3">Show Unwind Info</TextBlock>
<CheckBox Grid.Row="1" Grid.Column="1" Margin="3" IsChecked="{Binding IsShowUnwindInfo}" />
<TextBlock Grid.Row="2" Margin="3">Show Debug Info</TextBlock>
<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>
</UserControl>
14 changes: 13 additions & 1 deletion ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public void Load(ILSpySettings settings)
Options s = new Options();
s.DisassemblyFormat = ReadyToRunOptions.GetDisassemblyFormat(settings);
s.IsShowUnwindInfo = ReadyToRunOptions.GetIsShowUnwindInfo(settings);
s.IsShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(settings);

this.DataContext = s;
}
Expand All @@ -48,7 +49,7 @@ public void LoadDefaults()
public void Save(XElement root)
{
Options s = (Options)this.DataContext;
ReadyToRunOptions.SetDisassemblyOptions(root, s.DisassemblyFormat, s.IsShowUnwindInfo);
ReadyToRunOptions.SetDisassemblyOptions(root, s.DisassemblyFormat, s.IsShowUnwindInfo, s.IsShowDebugInfo);
}
}

Expand All @@ -71,6 +72,17 @@ public bool IsShowUnwindInfo {
}
}

private bool isShowDebugInfo;

public bool IsShowDebugInfo {
get {
return isShowDebugInfo;
}
set {
isShowDebugInfo = value;
OnPropertyChanged(nameof(IsShowDebugInfo));
}
}

private string disassemblyFormat;

Expand Down
24 changes: 18 additions & 6 deletions ILSpy.ReadyToRun/ReadyToRunOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public static string GetDisassemblyFormat(ILSpySettings settings)
}

public static bool GetIsShowUnwindInfo(ILSpySettings settings)

{
if (settings == null) {
settings = ILSpySettings.Load();
Expand All @@ -58,20 +57,33 @@ public static bool GetIsShowUnwindInfo(ILSpySettings settings)
}
}

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");
section.SetAttributeValue("DisassemblyFormat", disassemblyFormat);
section.SetAttributeValue("IsShowUnwindInfo", IsShowUnwindInfo);
section.SetAttributeValue("IsShowUnwindInfo", isShowUnwindInfo);
section.SetAttributeValue("IsShowDebugInfo", isShowDebugInfo);
XElement existingElement = root.Element(ns + "ReadyToRunOptions");
if (existingElement != null) {
existingElement.ReplaceWith(section);
} else {
root.Add(section);
}
}


}

}