diff --git a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj
index 878beb5d62..9e27fa5b56 100644
--- a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj
+++ b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj
@@ -75,7 +75,7 @@
-
+
diff --git a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs
index 2bfb3bccfc..2d6271028a 100644
--- a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs
+++ b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs
@@ -103,10 +103,85 @@ public override void WriteCommentLine(ITextOutput output, string comment)
output.WriteLine("; " + comment);
}
+ private Dictionary> WriteDebugInfo(ReadyToRunMethod readyToRunMethod, ITextOutput output)
+ {
+ Dictionary> debugInfoDict = new Dictionary>();
+ IReadOnlyList 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>();
+ 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>();
+ 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 WriteUnwindInfo(RuntimeFunction runtimeFunction, ITextOutput output)
-
{
Dictionary unwindCodes = new Dictionary();
if (runtimeFunction.UnwindInfo is UnwindInfo amd64UnwindInfo) {
@@ -129,7 +204,6 @@ private Dictionary 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;
@@ -137,19 +211,25 @@ private Dictionary WriteUnwindInfo(RuntimeFunction runtimeFun
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);
+
Dictionary unwindInfo = null;
if (ReadyToRunOptions.GetIsShowUnwindInfo(null) && bitness == 64) {
unwindInfo = WriteUnwindInfo(runtimeFunction, output);
}
+ bool isShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(null);
+ Dictionary>> 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
var codeReader = new ByteArrayCodeReader(codeBytes);
var decoder = Decoder.Create(bitness, codeReader);
decoder.IP = address;
@@ -174,7 +254,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) {
@@ -191,14 +271,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();
@@ -213,6 +296,66 @@ private static void DecorateUnwindInfo(ITextOutput output, Dictionary> debugInfoDict, ulong baseInstrIP)
+ {
+ if (debugInfoDict != null) {
+ InstructionInfoFactory factory = new InstructionInfoFactory();
+ InstructionInfo info = factory.GetInfo(instr);
+ HashSet> stkSet = new HashSet>();
+ 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> regSet = new HashSet>();
+ 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)
{
int importCellAddress = (int)instr.IPRelativeMemoryAddress;
@@ -246,7 +389,6 @@ private static void DecorateCallSite(PEFile currentFile, ITextOutput output, Rea
output.WriteLine(reader.ImportCellNames[importCellAddress]);
break;
}
-
output.WriteLine();
} else {
output.WriteLine();
diff --git a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml
index c3e28eaf0d..c11450ef70 100644
--- a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml
+++ b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml
@@ -17,6 +17,6 @@
-
+
\ No newline at end of file
diff --git a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
index 59ee6d7346..7abcb69a1c 100644
--- a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
+++ b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
@@ -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;
}
@@ -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);
}
}
@@ -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;
diff --git a/ILSpy.ReadyToRun/ReadyToRunOptions.cs b/ILSpy.ReadyToRun/ReadyToRunOptions.cs
index 5eddd67891..f82535e630 100644
--- a/ILSpy.ReadyToRun/ReadyToRunOptions.cs
+++ b/ILSpy.ReadyToRun/ReadyToRunOptions.cs
@@ -43,7 +43,6 @@ public static string GetDisassemblyFormat(ILSpySettings settings)
}
public static bool GetIsShowUnwindInfo(ILSpySettings settings)
-
{
if (settings == null) {
settings = ILSpySettings.Load();
@@ -58,11 +57,27 @@ 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);
@@ -70,8 +85,5 @@ public static void SetDisassemblyOptions(XElement root, string disassemblyFormat
root.Add(section);
}
}
-
-
}
-
}