From 26e5b5acffa28c7fc9b6d13612ef5077fa35c971 Mon Sep 17 00:00:00 2001
From: Somebody Whoisbored <13044396+shadowninja108@users.noreply.github.com>
Date: Thu, 20 May 2021 16:27:16 -0700
Subject: Extend info printed when guest crashes/breaks execution (#1845)

* Add CPU register printout when guest crashes/breaks execution

* Print out registers when undefined instruction is hit

* Apply suggestions from code review

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Fixes after rebase

* Address gdkchan's comments

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: Mary <me@thog.eu>
---
 .../HOS/Kernel/Process/HleProcessDebugger.cs       | 190 +++++++++++++++++----
 1 file changed, 156 insertions(+), 34 deletions(-)

(limited to 'Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs')

diff --git a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
index 5ad33154..627049bc 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
@@ -1,5 +1,6 @@
 using Ryujinx.HLE.HOS.Diagnostics.Demangler;
 using Ryujinx.HLE.HOS.Kernel.Memory;
+using Ryujinx.HLE.HOS.Kernel.Threading;
 using Ryujinx.HLE.Loaders.Elf;
 using Ryujinx.Memory;
 using System.Collections.Generic;
@@ -18,12 +19,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
         private class Image
         {
             public ulong BaseAddress { get; }
+            public ulong Size { get; }
+            public ulong EndAddress => BaseAddress + Size;
 
             public ElfSymbol[] Symbols { get; }
 
-            public Image(ulong baseAddress, ElfSymbol[] symbols)
+            public Image(ulong baseAddress, ulong size, ElfSymbol[] symbols)
             {
                 BaseAddress = baseAddress;
+                Size        = size;
                 Symbols     = symbols;
             }
         }
@@ -39,41 +43,28 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             _images = new List<Image>();
         }
 
-        public string GetGuestStackTrace(ARMeilleure.State.ExecutionContext context)
+        public string GetGuestStackTrace(KThread thread)
         {
             EnsureLoaded();
 
+            var context = thread.Context;
+
             StringBuilder trace = new StringBuilder();
 
+            trace.AppendLine($"Process: {_owner.Name}, PID: {_owner.Pid}");
+
             void AppendTrace(ulong address)
             {
-                Image image = GetImage(address, out int imageIndex);
-
-                if (image == null || !TryGetSubName(image, address, out string subName))
-                {
-                    subName = $"Sub{address:x16}";
-                }
-                else if (subName.StartsWith("_Z"))
+                if(AnalyzePointer(out PointerInfo info, address, thread))
                 {
-                    subName = Demangler.Parse(subName);
-                }
-
-                if (image != null)
-                {
-                    ulong offset = address - image.BaseAddress;
-
-                    string imageName = GetGuessedNsoNameFromIndex(imageIndex);
-
-                    trace.AppendLine($"   {imageName}:0x{offset:x8} {subName}");
+                    trace.AppendLine($"   0x{address:x16}\t{info.ImageDisplay}\t{info.SubDisplay}");
                 }
                 else
                 {
-                    trace.AppendLine($"   ??? {subName}");
+                    trace.AppendLine($"   0x{address:x16}");
                 }
             }
 
-            trace.AppendLine($"Process: {_owner.Name}, PID: {_owner.Pid}");
-
             if (context.IsAarch32)
             {
                 ulong framePointer = context.GetX(11);
@@ -114,7 +105,47 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             return trace.ToString();
         }
 
-        private bool TryGetSubName(Image image, ulong address, out string name)
+        public string GetCpuRegisterPrintout(KThread thread)
+        {
+            EnsureLoaded();
+
+            var context = thread.Context;
+
+            StringBuilder sb = new StringBuilder();
+
+            string GetReg(int x)
+            {
+                var v = x == 32 ? (ulong)thread.LastPc : context.GetX(x);
+                if (!AnalyzePointer(out PointerInfo info, v, thread))
+                {
+                    return $"0x{v:x16}";
+                }
+                else
+                {
+                    if (!string.IsNullOrEmpty(info.ImageName))
+                    {
+                        return $"0x{v:x16} ({info.ImageDisplay})\t=> {info.SubDisplay}";
+                    }
+                    else
+                    {
+                        return $"0x{v:x16} ({info.SpDisplay})";
+                    }
+                }
+            }
+
+            for (int i = 0; i <= 28; i++)
+            {
+                sb.AppendLine($"\tX[{i:d2}]:\t{GetReg(i)}");
+            }
+            sb.AppendLine($"\tFP:\t{GetReg(29)}");
+            sb.AppendLine($"\tLR:\t{GetReg(30)}");
+            sb.AppendLine($"\tSP:\t{GetReg(31)}");
+            sb.AppendLine($"\tPC:\t{GetReg(32)}");
+
+            return sb.ToString();
+        }
+
+        private bool TryGetSubName(Image image, ulong address, out ElfSymbol symbol)
         {
             address -= image.BaseAddress;
 
@@ -127,18 +158,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
                 int middle = left + (size >> 1);
 
-                ElfSymbol symbol = image.Symbols[middle];
+                symbol = image.Symbols[middle];
 
                 ulong endAddr = symbol.Value + symbol.Size;
 
-                if ((ulong)address >= symbol.Value && (ulong)address < endAddr)
+                if (address >= symbol.Value && address < endAddr)
                 {
-                    name = symbol.Name;
-
                     return true;
                 }
 
-                if ((ulong)address < (ulong)symbol.Value)
+                if (middle + 1 < image.Symbols.Length)
+                {
+                    ElfSymbol next = image.Symbols[middle + 1];
+                    
+                    // If our symbol points inbetween two symbols, we can *guess* that it's referring to the first one
+                    if (address >= symbol.Value && address < next.Value)
+                    {
+                        return true;
+                    }
+                }
+
+                if (address < symbol.Value)
                 {
                     right = middle - 1;
                 }
@@ -148,18 +188,100 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 }
             }
 
-            name = null;
+            symbol = default;
+
+            return false;
+        }
+
+        struct PointerInfo
+        {
+            public string ImageName;
+            public string SubName;
+
+            public ulong Offset;
+            public ulong SubOffset;
+
+            public string ImageDisplay => $"{ImageName}:0x{Offset:x4}";
+            public string SubDisplay => SubOffset == 0 ? SubName : $"{SubName}:0x{SubOffset:x4}";
+            public string SpDisplay => SubOffset == 0 ? "SP" : $"SP:-0x{SubOffset:x4}";
+        }
+
+        private bool AnalyzePointer(out PointerInfo info, ulong address, KThread thread)
+        {
+            if (AnalyzePointerFromImages(out info, address))
+            {
+                return true;
+            }
+
+            if (AnalyzePointerFromStack(out info, address, thread))
+            {
+                return true;
+            }
 
             return false;
         }
 
+        private bool AnalyzePointerFromImages(out PointerInfo info, ulong address)
+        {
+            info = default;
+
+            Image image = GetImage(address, out int imageIndex);
+
+            if (image == null)
+            {
+                // Value isn't a pointer to a known image...
+                return false;
+            }
+
+            info.Offset = address - image.BaseAddress;
+
+            // Try to find what this pointer is referring to
+            if (TryGetSubName(image, address, out ElfSymbol symbol))
+            {
+                info.SubName = symbol.Name;
+
+                // Demangle string if possible
+                if (info.SubName.StartsWith("_Z"))
+                {
+                    info.SubName = Demangler.Parse(info.SubName);
+                }
+                info.SubOffset = info.Offset - symbol.Value;
+            }
+            else
+            {
+                info.SubName = "";
+            }
+
+            info.ImageName = GetGuessedNsoNameFromIndex(imageIndex);
+
+            return true;
+        }
+
+        private bool AnalyzePointerFromStack(out PointerInfo info, ulong address, KThread thread)
+        {
+            info = default;
+
+            ulong sp = thread.Context.GetX(31);
+            var memoryInfo = _owner.MemoryManager.QueryMemory(address);
+            MemoryState memoryState = memoryInfo.State;
+
+            if (!memoryState.HasFlag(MemoryState.Stack)) // Is this pointer within the stack?
+            {
+                return false;
+            }
+
+            info.SubOffset = address - sp;
+
+            return true;
+        }
+
         private Image GetImage(ulong address, out int index)
         {
             lock (_images)
             {
                 for (index = _images.Count - 1; index >= 0; index--)
                 {
-                    if (address >= _images[index].BaseAddress)
+                    if (address >= _images[index].BaseAddress && address < _images[index].EndAddress)
                     {
                         return _images[index];
                     }
@@ -226,7 +348,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
                 if (info.State == MemoryState.CodeStatic && info.Permission == KMemoryPermission.ReadAndExecute)
                 {
-                    LoadMod0Symbols(_owner.CpuMemory, info.Address);
+                    LoadMod0Symbols(_owner.CpuMemory, info.Address, info.Size);
                 }
 
                 oldAddress = address;
@@ -235,7 +357,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             }
         }
 
-        private void LoadMod0Symbols(IVirtualMemoryManager memory, ulong textOffset)
+        private void LoadMod0Symbols(IVirtualMemoryManager memory, ulong textOffset, ulong textSize)
         {
             ulong mod0Offset = textOffset + memory.Read<uint>(textOffset + 4);
 
@@ -315,7 +437,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
             lock (_images)
             {
-                _images.Add(new Image(textOffset, symbols.OrderBy(x => x.Value).ToArray()));
+                _images.Add(new Image(textOffset, textSize, symbols.OrderBy(x => x.Value).ToArray()));
             }
         }
 
@@ -351,4 +473,4 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             return new ElfSymbol(name, sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
         }
     }
-}
\ No newline at end of file
+}
-- 
cgit v1.2.3-70-g09d2