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: [PATCH] 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 * Fixes after rebase * Address gdkchan's comments Co-authored-by: Ac_K Co-authored-by: Mary --- .../HOS/Kernel/Process/HleProcessDebugger.cs | 190 ++++++++++++++---- Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 2 + .../HOS/Kernel/SupervisorCall/Syscall.cs | 1 + Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs | 12 +- 4 files changed, 170 insertions(+), 35 deletions(-) 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(); } - 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)) + if(AnalyzePointer(out PointerInfo info, address, thread)) { - subName = $"Sub{address:x16}"; - } - else if (subName.StartsWith("_Z")) - { - 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(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 +} diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 15ab82b8..516b53f4 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -1073,6 +1073,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process private bool InvalidAccessHandler(ulong va) { KernelStatic.GetCurrentThread()?.PrintGuestStackTrace(); + KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout(); Logger.Error?.Print(LogClass.Cpu, $"Invalid memory access at virtual address 0x{va:X16}."); @@ -1082,6 +1083,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e) { KernelStatic.GetCurrentThread().PrintGuestStackTrace(); + KernelStatic.GetCurrentThread()?.PrintGuestRegisterPrintout(); throw new UndefinedInstructionException(e.Address, e.OpCode); } diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index 238f1226..39161f22 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -1409,6 +1409,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall if ((reason & (1UL << 31)) == 0) { currentThread.PrintGuestStackTrace(); + currentThread.PrintGuestRegisterPrintout(); // As the process is exiting, this is probably caused by emulation termination. if (currentThread.Owner.State == ProcessState.Exiting) diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 7ba9e43a..3ea03f16 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -991,7 +991,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public string GetGuestStackTrace() { - return Owner.Debugger.GetGuestStackTrace(Context); + return Owner.Debugger.GetGuestStackTrace(this); + } + + public string GetGuestRegisterPrintout() + { + return Owner.Debugger.GetCpuRegisterPrintout(this); } public void PrintGuestStackTrace() @@ -999,6 +1004,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading Logger.Info?.Print(LogClass.Cpu, $"Guest stack trace:\n{GetGuestStackTrace()}\n"); } + public void PrintGuestRegisterPrintout() + { + Logger.Info?.Print(LogClass.Cpu, $"Guest CPU registers:\n{GetGuestRegisterPrintout()}\n"); + } + public void AddCpuTime(long ticks) { Interlocked.Add(ref _totalTimeRunning, ticks);