From 91da244c02434e56e5fe51a24c6a5ca963535a3f Mon Sep 17 00:00:00 2001 From: GreemDev Date: Wed, 22 Oct 2025 15:04:03 -0500 Subject: [PATCH 1/2] gdb: some more cleanups --- .../Debugger/Debugger.MainThread.cs | 2 -- .../Debugger/Debugger.MessageHandler.cs | 4 +-- src/Ryujinx.HLE/Debugger/Debugger.cs | 3 +- src/Ryujinx.HLE/Debugger/Gdb/Commands.cs | 32 +++++-------------- src/Ryujinx.HLE/Debugger/Gdb/Registers.cs | 3 ++ 5 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs b/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs index a76221da8..144fb65bb 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs @@ -60,7 +60,6 @@ namespace Ryujinx.HLE.Debugger _readStream = new NetworkStream(_clientSocket, System.IO.FileAccess.Read); _writeStream = new NetworkStream(_clientSocket, System.IO.FileAccess.Write); _commands = new GdbCommands(_listenerSocket, _clientSocket, _readStream, _writeStream, this); - _commandProcessor = _commands.CreateProcessor(); Logger.Notice.Print(LogClass.GdbStub, "GDB client connected"); @@ -119,7 +118,6 @@ namespace Ryujinx.HLE.Debugger _writeStream = null; _clientSocket.Close(); _clientSocket = null; - _commandProcessor = null; _commands = null; BreakpointManager.ClearAll(); diff --git a/src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs b/src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs index 69d0b15fe..5ed8ba528 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs @@ -29,14 +29,14 @@ namespace Ryujinx.HLE.Debugger case CommandMessage { Command: { } cmd }: Logger.Debug?.Print(LogClass.GdbStub, $"Received Command: {cmd}"); _writeStream.WriteByte((byte)'+'); - _commandProcessor.Process(cmd); + _commands.Processor.Process(cmd); break; case ThreadBreakMessage { Context: { } ctx }: DebugProcess.DebugStop(); GThread = CThread = ctx.ThreadUid; _breakHandlerEvent.Set(); - _commandProcessor.Reply($"T05thread:{ctx.ThreadUid:x};"); + _commands.Processor.Reply($"T05thread:{ctx.ThreadUid:x};"); break; } } diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index ca6f39ec2..cab9c8eb6 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -26,8 +26,7 @@ namespace Ryujinx.HLE.Debugger private Socket _clientSocket; private NetworkStream _readStream; private NetworkStream _writeStream; - - private GdbCommandProcessor _commandProcessor; + private GdbCommands _commands; private bool _shuttingDown; diff --git a/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs b/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs index a7cefce2e..f7c6cab37 100644 --- a/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs +++ b/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs @@ -11,19 +11,18 @@ namespace Ryujinx.HLE.Debugger.Gdb { class GdbCommands { - const int GdbRegisterCount64 = 68; - const int GdbRegisterCount32 = 66; - public readonly Debugger Debugger; - public GdbCommandProcessor Processor { get; private set; } + private GdbCommandProcessor _processor; + + public GdbCommandProcessor Processor + => _processor ??= new GdbCommandProcessor(this); internal readonly TcpListener ListenerSocket; internal readonly Socket ClientSocket; internal readonly NetworkStream ReadStream; internal readonly NetworkStream WriteStream; - public GdbCommands(TcpListener listenerSocket, Socket clientSocket, NetworkStream readStream, NetworkStream writeStream, Debugger debugger) { @@ -34,21 +33,6 @@ namespace Ryujinx.HLE.Debugger.Gdb Debugger = debugger; } - public void SetProcessor(GdbCommandProcessor commandProcessor) - { - if (Processor != null) return; - - Processor = commandProcessor; - } - - public GdbCommandProcessor CreateProcessor() - { - if (Processor != null) - return Processor; - - return Processor = new GdbCommandProcessor(this); - } - internal void Query() { // GDB is performing initial contact. Stop everything. @@ -104,14 +88,14 @@ namespace Ryujinx.HLE.Debugger.Gdb string registers = string.Empty; if (Debugger.IsProcess32Bit) { - for (int i = 0; i < GdbRegisterCount32; i++) + for (int i = 0; i < GdbRegisters.Count32; i++) { registers += ctx.ReadRegister32(i); } } else { - for (int i = 0; i < GdbRegisterCount64; i++) + for (int i = 0; i < GdbRegisters.Count64; i++) { registers += ctx.ReadRegister64(i); } @@ -131,7 +115,7 @@ namespace Ryujinx.HLE.Debugger.Gdb IExecutionContext ctx = Debugger.DebugProcess.GetThread(Debugger.GThread.Value).Context; if (Debugger.IsProcess32Bit) { - for (int i = 0; i < GdbRegisterCount32; i++) + for (int i = 0; i < GdbRegisters.Count32; i++) { if (!ctx.WriteRegister32(i, ss)) { @@ -142,7 +126,7 @@ namespace Ryujinx.HLE.Debugger.Gdb } else { - for (int i = 0; i < GdbRegisterCount64; i++) + for (int i = 0; i < GdbRegisters.Count64; i++) { if (!ctx.WriteRegister64(i, ss)) { diff --git a/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs b/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs index 7d3083b31..40a095a07 100644 --- a/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs +++ b/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs @@ -6,6 +6,9 @@ namespace Ryujinx.HLE.Debugger.Gdb { static class GdbRegisters { + public const int Count64 = 68; + public const int Count32 = 66; + /* FPCR = FPSR & ~FpcrMask All of FPCR's bits are reserved in FPCR and vice versa, From 0c165c3f627d06fd6908444935204776fe07dd8c Mon Sep 17 00:00:00 2001 From: Coxxs <58-coxxs@users.noreply.git.ryujinx.app> Date: Wed, 22 Oct 2025 16:20:13 -0500 Subject: [PATCH 2/2] Move ProcessInfo and Minidump to HleProcessDebugger (ryubing/ryujinx!187) See merge request ryubing/ryujinx!187 --- src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs | 64 ++-------- .../HOS/Kernel/Process/HleProcessDebugger.cs | 110 ++++++++++++++++++ 2 files changed, 120 insertions(+), 54 deletions(-) diff --git a/src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs b/src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs index f63817ef3..1cf86b153 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs @@ -62,38 +62,16 @@ namespace Ryujinx.HLE.Debugger public string GetMinidump() { - StringBuilder response = new(); - response.AppendLine("=== Begin Minidump ===\n"); - response.AppendLine(GetProcessInfo()); + if (Process is not { } kProcess) + return "No application process found\n"; - foreach (KThread thread in GetThreads()) - { - response.AppendLine($"=== Thread {thread.ThreadUid} ==="); - try - { - string stackTrace = Process.Debugger.GetGuestStackTrace(thread); - response.AppendLine(stackTrace); - } - catch (Exception e) - { - response.AppendLine($"[Error getting stack trace: {e.Message}]"); - } + if (kProcess.Debugger is not { } debugger) + return $"Error getting minidump: debugger is null\n"; - try - { - string registers = Process.Debugger.GetCpuRegisterPrintout(thread); - response.AppendLine(registers); - } - catch (Exception e) - { - response.AppendLine($"[Error getting registers: {e.Message}]"); - } - } + var response = debugger.GetMinidump(); - response.AppendLine("=== End Minidump ==="); - - Logger.Info?.Print(LogClass.GdbStub, response.ToString()); - return response.ToString(); + Logger.Info?.Print(LogClass.GdbStub, response); + return response; } public string GetProcessInfo() @@ -103,32 +81,10 @@ namespace Ryujinx.HLE.Debugger if (Process is not { } kProcess) return "No application process found\n"; - StringBuilder sb = new(); + if (kProcess.Debugger is not { } debugger) + return $"Error getting process info: debugger is null\n"; - sb.AppendLine($"Program Id: 0x{kProcess.TitleId:x16}"); - sb.AppendLine($"Application: {(kProcess.IsApplication ? 1 : 0)}"); - sb.AppendLine("Layout:"); - sb.AppendLine( - $" Alias: 0x{kProcess.MemoryManager.AliasRegionStart:x10} - 0x{kProcess.MemoryManager.AliasRegionEnd - 1:x10}"); - sb.AppendLine( - $" Heap: 0x{kProcess.MemoryManager.HeapRegionStart:x10} - 0x{kProcess.MemoryManager.HeapRegionEnd - 1:x10}"); - sb.AppendLine( - $" Aslr: 0x{kProcess.MemoryManager.AslrRegionStart:x10} - 0x{kProcess.MemoryManager.AslrRegionEnd - 1:x10}"); - sb.AppendLine( - $" Stack: 0x{kProcess.MemoryManager.StackRegionStart:x10} - 0x{kProcess.MemoryManager.StackRegionEnd - 1:x10}"); - - sb.AppendLine("Modules:"); - HleProcessDebugger debugger = kProcess.Debugger; - if (debugger != null) - { - foreach (HleProcessDebugger.Image image in debugger.GetLoadedImages()) - { - ulong endAddress = image.BaseAddress + image.Size - 1; - sb.AppendLine($" 0x{image.BaseAddress:x10} - 0x{endAddress:x10} {image.Name}"); - } - } - - return sb.ToString(); + return debugger.GetProcessInfoPrintout(); } catch (Exception e) { diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs index 2e14f2a40..71ee7a086 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs @@ -161,6 +161,116 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return sb.ToString(); } + public string GetProcessInfoPrintout() + { + StringBuilder sb = new(); + + sb.AppendLine($"Process: {_owner.Name}, PID: {_owner.Pid}"); + sb.AppendLine($"Program Id: 0x{_owner.TitleId:x16}"); + sb.AppendLine($"Application: {(_owner.IsApplication ? 1 : 0)}"); + + sb.AppendLine("Layout:"); + sb.AppendLine( + $" Alias: 0x{_owner.MemoryManager.AliasRegionStart:x10} - 0x{_owner.MemoryManager.AliasRegionEnd - 1:x10}"); + sb.AppendLine( + $" Heap: 0x{_owner.MemoryManager.HeapRegionStart:x10} - 0x{_owner.MemoryManager.HeapRegionEnd - 1:x10}"); + sb.AppendLine( + $" Aslr: 0x{_owner.MemoryManager.AslrRegionStart:x10} - 0x{_owner.MemoryManager.AslrRegionEnd - 1:x10}"); + sb.AppendLine( + $" Stack: 0x{_owner.MemoryManager.StackRegionStart:x10} - 0x{_owner.MemoryManager.StackRegionEnd - 1:x10}"); + + sb.AppendLine("Modules:"); + + foreach (Image image in GetLoadedImages()) + { + ulong endAddress = image.BaseAddress + image.Size - 1; + sb.AppendLine($" 0x{image.BaseAddress:x10} - 0x{endAddress:x10} {image.Name}"); + } + + return sb.ToString(); + } + + public string GetMinidump() + { + var result = new StringBuilder(); + + result.AppendLine("=== Begin Minidump ===\n"); + try + { + result.AppendLine(GetProcessInfoPrintout()); + } + catch (Exception e) + { + result.AppendLine($"[Error getting process info: {e.Message}]"); + } + + var debugInterface = _owner?.DebugInterface; + + if (debugInterface != null) + { + ulong[] threadUids; + + try + { + threadUids = debugInterface.ThreadUids ?? []; + } + catch (Exception e) + { + result.AppendLine($"[Error getting thread uids: {e.Message}]"); + threadUids = []; + } + + foreach (ulong threadUid in threadUids) + { + result.AppendLine($"=== Thread {threadUid} ==="); + + KThread thread; + + try + { + thread = debugInterface.GetThread(threadUid); + } + catch (Exception e) + { + result.AppendLine($"[Error getting thread: {e.Message}]"); + continue; + } + + if (thread == null) + { + result.AppendLine("[Thread not found]"); + continue; + } + + try + { + result.AppendLine(GetGuestStackTrace(thread)); + } + catch (Exception e) + { + result.AppendLine($"[Error getting stack trace: {e.Message}]"); + } + + try + { + result.AppendLine(GetCpuRegisterPrintout(thread)); + } + catch (Exception e) + { + result.AppendLine($"[Error getting registers: {e.Message}]"); + } + } + } + else + { + result.AppendLine("[Error generating minidump: debugInterface is null]"); + } + + result.AppendLine("=== End Minidump ==="); + + return result.ToString(); + } + private static bool TryGetSubName(Image image, ulong address, out ElfSymbol symbol) { address -= image.BaseAddress;