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) {