diff --git a/ILSpy.ReadyToRun/Properties/Resources.Designer.cs b/ILSpy.ReadyToRun/Properties/Resources.Designer.cs
index f28df710f..2b8162693 100644
--- a/ILSpy.ReadyToRun/Properties/Resources.Designer.cs
+++ b/ILSpy.ReadyToRun/Properties/Resources.Designer.cs
@@ -90,10 +90,19 @@ namespace ILSpy.ReadyToRun.Properties {
///
/// Looks up a localized string similar to Show Unwind Info.
///
- public static string ShowUnwindInfo {
+ public static string ShowStackUnwindInfo {
get {
- return ResourceManager.GetString("ShowUnwindInfo", resourceCulture);
+ return ResourceManager.GetString("ShowStackUnwindInfo", resourceCulture);
}
}
- }
+
+ ///
+ /// Looks up a localized string similar to Show GC Info.
+ ///
+ public static string ShowGCInfo {
+ get {
+ return ResourceManager.GetString("ShowGCInfo", resourceCulture);
+ }
+ }
+ }
}
diff --git a/ILSpy.ReadyToRun/Properties/Resources.resx b/ILSpy.ReadyToRun/Properties/Resources.resx
index 8c6d1ce8c..bd77f2bf6 100644
--- a/ILSpy.ReadyToRun/Properties/Resources.resx
+++ b/ILSpy.ReadyToRun/Properties/Resources.resx
@@ -126,7 +126,10 @@
Show Debug Info
-
- Show Unwind Info
+
+ Show GC Info
+
+
+ Show Stack Unwind Info
\ No newline at end of file
diff --git a/ILSpy.ReadyToRun/Properties/Resources.zh-Hans.resx b/ILSpy.ReadyToRun/Properties/Resources.zh-Hans.resx
index 9430c8a3d..57523b138 100644
--- a/ILSpy.ReadyToRun/Properties/Resources.zh-Hans.resx
+++ b/ILSpy.ReadyToRun/Properties/Resources.zh-Hans.resx
@@ -126,7 +126,10 @@
显示调试信息
-
- 显示展开信息
+
+ 显示垃圾回收信息
+
+
+ 显示堆栈展开信息
\ No newline at end of file
diff --git a/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs b/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs
index 6996f211d..a5780db4e 100644
--- a/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs
+++ b/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs
@@ -47,10 +47,26 @@ namespace ICSharpCode.ILSpy.ReadyToRun
public void Disassemble(PEFile currentFile, int bitness, ulong address, bool showMetadataTokens, bool showMetadataTokensInBase10)
{
- // TODO: Decorate the disassembly with GCInfo
ReadyToRunMethod readyToRunMethod = runtimeFunction.Method;
WriteCommentLine(readyToRunMethod.SignatureString);
+ if (ReadyToRunOptions.GetIsShowGCInfo(null))
+ {
+ if (readyToRunMethod.GcInfo != null)
+ {
+ string[] lines = readyToRunMethod.GcInfo.ToString().Split(Environment.NewLine);
+ WriteCommentLine("GC info:");
+ foreach (string line in lines)
+ {
+ WriteCommentLine(line);
+ }
+ }
+ else
+ {
+ WriteCommentLine("GC Info is not available for this method");
+ }
+ }
+
Dictionary unwindInfo = null;
if (ReadyToRunOptions.GetIsShowUnwindInfo(null) && bitness == 64)
{
@@ -96,30 +112,36 @@ namespace ICSharpCode.ILSpy.ReadyToRun
formatter.Options.FirstOperandCharIndex = 10;
var tempOutput = new StringOutput();
ulong baseInstrIP = instructions[0].IP;
+
+ var boundsMap = new Dictionary();
+ foreach (var bound in runtimeFunction.DebugInfo.BoundsList)
+ {
+ // ignoring the return value assuming the same key is always mapped to the same value in runtimeFunction.DebugInfo.BoundsList
+ boundsMap.TryAdd(bound.NativeOffset, bound.ILOffset);
+ }
+
foreach (var instr in instructions)
{
int byteBaseIndex = (int)(instr.IP - address);
if (isShowDebugInfo && runtimeFunction.DebugInfo != null)
{
- foreach (var bound in runtimeFunction.DebugInfo.BoundsList)
+ if (byteBaseIndex >= 0 && boundsMap.TryGetValue((uint)byteBaseIndex, out uint boundILOffset))
{
- if (bound.NativeOffset == byteBaseIndex)
+ if (boundILOffset == (uint)DebugInfoBoundsType.Prolog)
{
- if (bound.ILOffset == (uint)DebugInfoBoundsType.Prolog)
- {
- WriteCommentLine("Prolog");
- }
- else if (bound.ILOffset == (uint)DebugInfoBoundsType.Epilog)
- {
- WriteCommentLine("Epilog");
- }
- else
- {
- WriteCommentLine($"IL_{bound.ILOffset:x4}");
- }
+ WriteCommentLine("Prolog");
+ }
+ else if (boundILOffset == (uint)DebugInfoBoundsType.Epilog)
+ {
+ WriteCommentLine("Epilog");
+ }
+ else
+ {
+ WriteCommentLine($"IL_{boundILOffset:x4}");
}
}
}
+ DecorateGCInfo(instr, baseInstrIP, readyToRunMethod.GcInfo);
formatter.Format(instr, tempOutput);
output.Write(instr.IP.ToString("X16"));
output.Write(" ");
@@ -143,6 +165,20 @@ namespace ICSharpCode.ILSpy.ReadyToRun
output.WriteLine();
}
+ private void DecorateGCInfo(Instruction instr, ulong baseInstrIP, BaseGcInfo gcInfo)
+ {
+ ulong codeOffset = instr.IP - baseInstrIP;
+ if (gcInfo != null && gcInfo.Transitions != null && gcInfo.Transitions.TryGetValue((int)codeOffset, out List transitionsForOffset))
+ {
+ // this value comes from a manual count of the spaces used for each instruction in Disassemble()
+ string indent = new string(' ', 36);
+ foreach (var transition in transitionsForOffset)
+ {
+ WriteCommentLine(indent + transition.ToString());
+ }
+ }
+ }
+
private void WriteCommentLine(string comment)
{
output.WriteLine("; " + comment);
diff --git a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml
index c11450ef7..137e0a6a1 100644
--- a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml
+++ b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml
@@ -11,12 +11,15 @@
+
-
+
+
+
\ No newline at end of file
diff --git a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
index a1a9cf689..53da7a71f 100644
--- a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
+++ b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
@@ -39,6 +39,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
s.DisassemblyFormat = ReadyToRunOptions.GetDisassemblyFormat(settings);
s.IsShowUnwindInfo = ReadyToRunOptions.GetIsShowUnwindInfo(settings);
s.IsShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(settings);
+ s.IsShowGCInfo = ReadyToRunOptions.GetIsShowGCInfo(settings);
this.DataContext = s;
}
@@ -51,7 +52,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun
public void Save(XElement root)
{
Options s = (Options)this.DataContext;
- ReadyToRunOptions.SetDisassemblyOptions(root, s.DisassemblyFormat, s.IsShowUnwindInfo, s.IsShowDebugInfo);
+ ReadyToRunOptions.SetDisassemblyOptions(root, s.DisassemblyFormat, s.IsShowUnwindInfo, s.IsShowDebugInfo, s.IsShowGCInfo);
}
}
@@ -86,6 +87,18 @@ namespace ICSharpCode.ILSpy.ReadyToRun
}
}
+ private bool isShowGCInfo;
+
+ public bool IsShowGCInfo {
+ get {
+ return isShowGCInfo;
+ }
+ set {
+ isShowGCInfo = value;
+ OnPropertyChanged(nameof(IsShowGCInfo));
+ }
+ }
+
private string disassemblyFormat;
public string DisassemblyFormat {
diff --git a/ILSpy.ReadyToRun/ReadyToRunOptions.cs b/ILSpy.ReadyToRun/ReadyToRunOptions.cs
index 22fc68639..365d1dd9a 100644
--- a/ILSpy.ReadyToRun/ReadyToRunOptions.cs
+++ b/ILSpy.ReadyToRun/ReadyToRunOptions.cs
@@ -87,12 +87,32 @@ namespace ICSharpCode.ILSpy.ReadyToRun
}
}
- public static void SetDisassemblyOptions(XElement root, string disassemblyFormat, bool isShowUnwindInfo, bool isShowDebugInfo)
+ public static bool GetIsShowGCInfo(ILSpySettings settings)
+ {
+ if (settings == null)
+ {
+ settings = ILSpySettings.Load();
+ }
+ XElement e = settings[ns + "ReadyToRunOptions"];
+ XAttribute a = e.Attribute("IsShowGCInfo");
+
+ if (a == null)
+ {
+ return false;
+ }
+ else
+ {
+ return (bool)a;
+ }
+ }
+
+ public static void SetDisassemblyOptions(XElement root, string disassemblyFormat, bool isShowUnwindInfo, bool isShowDebugInfo, bool isShowGCInfo)
{
XElement section = new XElement(ns + "ReadyToRunOptions");
section.SetAttributeValue("DisassemblyFormat", disassemblyFormat);
section.SetAttributeValue("IsShowUnwindInfo", isShowUnwindInfo);
section.SetAttributeValue("IsShowDebugInfo", isShowDebugInfo);
+ section.SetAttributeValue("IsShowGCInfo", isShowGCInfo);
XElement existingElement = root.Element(ns + "ReadyToRunOptions");
if (existingElement != null)
{