From 247e2e03d60910e01349ae1e93f5f4bb3977d358 Mon Sep 17 00:00:00 2001 From: GreemDev Date: Sun, 19 Oct 2025 04:26:12 -0500 Subject: [PATCH] gdb: More cleanup changes - Move the message handler into its debugger class part, - Move all message types into one file and collapse 3 of the ones with no data into a generic, stateless message with a single property being its type, - Add an Fpscr helper property on IExecutionContext along with a comment about what Fpscr is (similar to the other registers in there) - Moved the Rcmd helpers (such as GetRegisters, GetMinidump, etc) into a dedicated Debugger class part, - Fixed the double-collection (ToArray being called twice) in GetThreadUids & GetThread in KProcess --- src/Ryujinx.Cpu/IExecutionContext.cs | 5 + .../Debugger/Debugger.MainThread.cs | 6 +- .../Debugger/Debugger.MessageHandler.cs | 58 ++++++ src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs | 103 +++++++++++ src/Ryujinx.HLE/Debugger/Debugger.cs | 168 ++---------------- .../Debugger/Gdb/CommandProcessor.cs | 16 +- src/Ryujinx.HLE/Debugger/Gdb/Commands.cs | 8 +- src/Ryujinx.HLE/Debugger/Gdb/Registers.cs | 90 +++++----- .../Debugger/IDebuggableProcess.cs | 4 +- src/Ryujinx.HLE/Debugger/IMessage.cs | 47 +++++ .../Debugger/Message/BreakInMessage.cs | 6 - .../Debugger/Message/CommandMessage.cs | 12 -- src/Ryujinx.HLE/Debugger/Message/IMessage.cs | 6 - .../Debugger/Message/KillMessage.cs | 6 - .../Debugger/Message/SendNackMessage.cs | 6 - .../Debugger/Message/ThreadBreakMessage.cs | 18 -- src/Ryujinx.HLE/Debugger/StringStream.cs | 62 +++---- .../HOS/Kernel/Process/KProcess.cs | 24 +-- .../HOS/Kernel/Threading/KScheduler.cs | 2 +- 19 files changed, 319 insertions(+), 328 deletions(-) create mode 100644 src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs create mode 100644 src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs create mode 100644 src/Ryujinx.HLE/Debugger/IMessage.cs delete mode 100644 src/Ryujinx.HLE/Debugger/Message/BreakInMessage.cs delete mode 100644 src/Ryujinx.HLE/Debugger/Message/CommandMessage.cs delete mode 100644 src/Ryujinx.HLE/Debugger/Message/IMessage.cs delete mode 100644 src/Ryujinx.HLE/Debugger/Message/KillMessage.cs delete mode 100644 src/Ryujinx.HLE/Debugger/Message/SendNackMessage.cs delete mode 100644 src/Ryujinx.HLE/Debugger/Message/ThreadBreakMessage.cs diff --git a/src/Ryujinx.Cpu/IExecutionContext.cs b/src/Ryujinx.Cpu/IExecutionContext.cs index df0c94278..c3ebe5aa5 100644 --- a/src/Ryujinx.Cpu/IExecutionContext.cs +++ b/src/Ryujinx.Cpu/IExecutionContext.cs @@ -42,6 +42,11 @@ namespace Ryujinx.Cpu /// uint Fpsr { get; set; } + /// + /// Floating-point Status and Control Register. + /// + uint Fpscr => Fpsr | Fpcr; + /// /// Indicates whenever the CPU is running 64-bit (AArch64 mode) or 32-bit (AArch32 mode) code. /// diff --git a/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs b/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs index 2e1f40120..c4ec001bf 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs @@ -64,10 +64,10 @@ namespace Ryujinx.HLE.Debugger Logger.Notice.Print(LogClass.GdbStub, "NACK received!"); continue; case '\x03': - _messages.Add(new BreakInMessage()); + _messages.Add(StatelessMessage.BreakIn); break; case '$': - string cmd = ""; + string cmd = string.Empty; while (true) { int x = _readStream.ReadByte(); @@ -85,7 +85,7 @@ namespace Ryujinx.HLE.Debugger } else { - _messages.Add(new SendNackMessage()); + _messages.Add(StatelessMessage.SendNack); } break; diff --git a/src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs b/src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs new file mode 100644 index 000000000..4e2cdc237 --- /dev/null +++ b/src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs @@ -0,0 +1,58 @@ +using Ryujinx.Common.Logging; +using System; +using System.IO; + +namespace Ryujinx.HLE.Debugger +{ + public partial class Debugger + { + private void MessageHandlerMain() + { + while (!_shuttingDown) + { + try + { + switch (_messages.Take()) + { + case StatelessMessage { Type: MessageType.BreakIn }: + Logger.Notice.Print(LogClass.GdbStub, "Break-in requested"); + _commands.Interrupt(); + break; + + case StatelessMessage { Type: MessageType.SendNack }: + _writeStream.WriteByte((byte)'-'); + break; + + case StatelessMessage { Type: MessageType.Kill }: + return; + + case CommandMessage { Command: { } cmd }: + Logger.Debug?.Print(LogClass.GdbStub, $"Received Command: {cmd}"); + _writeStream.WriteByte((byte)'+'); + _commandProcessor.Process(cmd); + break; + + case ThreadBreakMessage { Context: { } ctx }: + DebugProcess.DebugStop(); + GThread = CThread = ctx.ThreadUid; + _breakHandlerEvent.Set(); + _commandProcessor.Reply($"T05thread:{ctx.ThreadUid:x};"); + break; + } + } + catch (IOException e) + { + Logger.Error?.Print(LogClass.GdbStub, "Error while processing GDB messages", e); + } + catch (NullReferenceException e) + { + Logger.Error?.Print(LogClass.GdbStub, "Error while processing GDB messages", e); + } + catch (ObjectDisposedException e) + { + Logger.Error?.Print(LogClass.GdbStub, "Error while processing GDB messages", e); + } + } + } + } +} diff --git a/src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs b/src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs new file mode 100644 index 000000000..302969011 --- /dev/null +++ b/src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs @@ -0,0 +1,103 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Kernel.Process; +using System; +using System.Text; + +namespace Ryujinx.HLE.Debugger +{ + public partial class Debugger + { + public string GetStackTrace() + { + if (GThread == null) + return "No thread selected\n"; + + return Process?.Debugger?.GetGuestStackTrace(DebugProcess.GetThread(GThread.Value)) ?? "No application process found\n"; + } + + public string GetRegisters() + { + if (GThread == null) + return "No thread selected\n"; + + return Process?.Debugger?.GetCpuRegisterPrintout(DebugProcess.GetThread(GThread.Value)) ?? "No application process found\n"; + } + + public string GetMinidump() + { + var response = new StringBuilder(); + response.AppendLine("=== Begin Minidump ===\n"); + response.AppendLine(GetProcessInfo()); + + foreach (var 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}]"); + } + + try + { + string registers = Process.Debugger.GetCpuRegisterPrintout(thread); + response.AppendLine(registers); + } + catch (Exception e) + { + response.AppendLine($"[Error getting registers: {e.Message}]"); + } + } + + response.AppendLine("=== End Minidump ==="); + + Logger.Info?.Print(LogClass.GdbStub, response.ToString()); + return response.ToString(); + } + + public string GetProcessInfo() + { + try + { + if (Process is not { } kProcess) + return "No application process found\n"; + + var sb = new StringBuilder(); + + 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:"); + var 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(); + } + catch (Exception e) + { + Logger.Error?.Print(LogClass.GdbStub, $"Error getting process info: {e.Message}"); + return $"Error getting process info: {e.Message}\n"; + } + } + } +} diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index e8eef26a3..6a5da60ee 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -57,172 +57,24 @@ namespace Ryujinx.HLE.Debugger internal KProcess Process => Device.System?.DebugGetApplicationProcess(); internal IDebuggableProcess DebugProcess => Device.System?.DebugGetApplicationProcessDebugInterface(); - internal KThread[] GetThreads() => - DebugProcess.GetThreadUids().Select(x => DebugProcess.GetThread(x)).ToArray(); + internal KThread[] GetThreads() => DebugProcess.ThreadUids.Select(DebugProcess.GetThread).ToArray(); internal bool IsProcess32Bit => DebugProcess.GetThread(GThread.Value).Context.IsAarch32; - - private void MessageHandlerMain() - { - while (!_shuttingDown) - { - IMessage msg = _messages.Take(); - try - { - switch (msg) - { - case BreakInMessage: - Logger.Notice.Print(LogClass.GdbStub, "Break-in requested"); - _commandProcessor.Commands.Interrupt(); - break; - - case SendNackMessage: - _writeStream.WriteByte((byte)'-'); - break; - - case CommandMessage { Command: var cmd }: - Logger.Debug?.Print(LogClass.GdbStub, $"Received Command: {cmd}"); - _writeStream.WriteByte((byte)'+'); - _commandProcessor.Process(cmd); - break; - - case ThreadBreakMessage { Context: var ctx }: - DebugProcess.DebugStop(); - GThread = CThread = ctx.ThreadUid; - _breakHandlerEvent.Set(); - _commandProcessor.Reply($"T05thread:{ctx.ThreadUid:x};"); - break; - - case KillMessage: - return; - } - } - catch (IOException e) - { - Logger.Error?.Print(LogClass.GdbStub, "Error while processing GDB messages", e); - } - catch (NullReferenceException e) - { - Logger.Error?.Print(LogClass.GdbStub, "Error while processing GDB messages", e); - } - catch (ObjectDisposedException e) - { - Logger.Error?.Print(LogClass.GdbStub, "Error while processing GDB messages", e); - } - } - } - - internal bool WriteRegister(IExecutionContext ctx, int gdbRegId, StringStream ss) => + + internal bool WriteRegister(IExecutionContext ctx, int registerId, StringStream ss) => IsProcess32Bit - ? ctx.WriteRegister32(gdbRegId, ss) - : ctx.WriteRegister64(gdbRegId, ss); + ? ctx.WriteRegister32(registerId, ss) + : ctx.WriteRegister64(registerId, ss); - internal string ReadRegister(IExecutionContext ctx, int gdbRegId) => + internal string ReadRegister(IExecutionContext ctx, int registerId) => IsProcess32Bit - ? ctx.ReadRegister32(gdbRegId) - : ctx.ReadRegister64(gdbRegId); - - public string GetStackTrace() - { - if (GThread == null) - return "No thread selected\n"; - - return Process?.Debugger?.GetGuestStackTrace(DebugProcess.GetThread(GThread.Value)) ?? "No application process found\n"; - } - - public string GetRegisters() - { - if (GThread == null) - return "No thread selected\n"; - - return Process?.Debugger?.GetCpuRegisterPrintout(DebugProcess.GetThread(GThread.Value)) ?? "No application process found\n"; - } - - public string GetMinidump() - { - var response = new StringBuilder(); - response.AppendLine("=== Begin Minidump ===\n"); - response.AppendLine(GetProcessInfo()); - - foreach (var 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}]"); - } - - try - { - string registers = Process.Debugger.GetCpuRegisterPrintout(thread); - response.AppendLine(registers); - } - catch (Exception e) - { - response.AppendLine($"[Error getting registers: {e.Message}]"); - } - } - - response.AppendLine("=== End Minidump ==="); - - Logger.Info?.Print(LogClass.GdbStub, response.ToString()); - return response.ToString(); - } - - public string GetProcessInfo() - { - try - { - if (Process == null) - return "No application process found\n"; - - KProcess kProcess = Process; - - var sb = new StringBuilder(); - - 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:"); - var debugger = kProcess.Debugger; - if (debugger != null) - { - var images = debugger.GetLoadedImages(); - for (int i = 0; i < images.Count; i++) - { - var image = images[i]; - ulong endAddress = image.BaseAddress + image.Size - 1; - string name = image.Name; - sb.AppendLine($" 0x{image.BaseAddress:x10} - 0x{endAddress:x10} {name}"); - } - } - - return sb.ToString(); - } - catch (Exception e) - { - Logger.Error?.Print(LogClass.GdbStub, $"Error getting process info: {e.Message}"); - return $"Error getting process info: {e.Message}\n"; - } - } + ? ctx.ReadRegister32(registerId) + : ctx.ReadRegister64(registerId); public void Dispose() { Dispose(true); + GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) @@ -237,7 +89,7 @@ namespace Ryujinx.HLE.Debugger _readStream?.Close(); _writeStream?.Close(); _debuggerThread.Join(); - _messages.Add(new KillMessage()); + _messages.Add(StatelessMessage.Kill); _messageHandlerThread.Join(); _messages.Dispose(); _breakHandlerEvent.Dispose(); diff --git a/src/Ryujinx.HLE/Debugger/Gdb/CommandProcessor.cs b/src/Ryujinx.HLE/Debugger/Gdb/CommandProcessor.cs index 997b635e4..a07dafd1c 100644 --- a/src/Ryujinx.HLE/Debugger/Gdb/CommandProcessor.cs +++ b/src/Ryujinx.HLE/Debugger/Gdb/CommandProcessor.cs @@ -53,7 +53,7 @@ namespace Ryujinx.HLE.Debugger.Gdb switch (ss.ReadChar()) { case '!': - if (!ss.IsEmpty()) + if (!ss.IsEmpty) { goto unknownCommand; } @@ -62,7 +62,7 @@ namespace Ryujinx.HLE.Debugger.Gdb ReplyOK(); break; case '?': - if (!ss.IsEmpty()) + if (!ss.IsEmpty) { goto unknownCommand; } @@ -70,10 +70,10 @@ namespace Ryujinx.HLE.Debugger.Gdb Commands.Query(); break; case 'c': - Commands.Continue(ss.IsEmpty() ? null : ss.ReadRemainingAsHex()); + Commands.Continue(ss.IsEmpty ? null : ss.ReadRemainingAsHex()); break; case 'D': - if (!ss.IsEmpty()) + if (!ss.IsEmpty) { goto unknownCommand; } @@ -81,7 +81,7 @@ namespace Ryujinx.HLE.Debugger.Gdb Commands.Detach(); break; case 'g': - if (!ss.IsEmpty()) + if (!ss.IsEmpty) { goto unknownCommand; } @@ -172,7 +172,7 @@ namespace Ryujinx.HLE.Debugger.Gdb if (ss.ConsumeRemaining("fThreadInfo")) { Reply( - $"m{Debugger.DebugProcess.GetThreadUids().Select(x => $"{x:x}").JoinToString(",")}"); + $"m{Debugger.DebugProcess.ThreadUids.Select(x => $"{x:x}").JoinToString(",")}"); break; } @@ -225,7 +225,7 @@ namespace Ryujinx.HLE.Debugger.Gdb if (len >= (ulong)data.Length - offset) { - Reply("l" + Helpers.ToBinaryFormat(data.Substring((int)offset))); + Reply("l" + Helpers.ToBinaryFormat(data[(int)offset..])); } else { @@ -274,7 +274,7 @@ namespace Ryujinx.HLE.Debugger.Gdb case 'Q': goto unknownCommand; case 's': - Commands.Step(ss.IsEmpty() ? null : ss.ReadRemainingAsHex()); + Commands.Step(ss.IsEmpty ? null : ss.ReadRemainingAsHex()); break; case 'T': { diff --git a/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs b/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs index 6468e1452..9093440eb 100644 --- a/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs +++ b/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs @@ -53,7 +53,7 @@ namespace Ryujinx.HLE.Debugger.Gdb { // GDB is performing initial contact. Stop everything. Debugger.DebugProcess.DebugStop(); - Debugger.GThread = Debugger.CThread = Debugger.DebugProcess.GetThreadUids().First(); + Debugger.GThread = Debugger.CThread = Debugger.DebugProcess.ThreadUids.First(); Processor.Reply($"T05thread:{Debugger.CThread:x};"); } @@ -63,7 +63,7 @@ namespace Ryujinx.HLE.Debugger.Gdb Debugger.DebugProcess.DebugStop(); if (Debugger.GThread == null || Debugger.GetThreads().All(x => x.ThreadUid != Debugger.GThread.Value)) { - Debugger.GThread = Debugger.CThread = Debugger.DebugProcess.GetThreadUids().First(); + Debugger.GThread = Debugger.CThread = Debugger.DebugProcess.ThreadUids.First(); } Processor.Reply($"T02thread:{Debugger.GThread:x};"); @@ -151,7 +151,7 @@ namespace Ryujinx.HLE.Debugger.Gdb } } - Processor.Reply(ss.IsEmpty()); + Processor.Reply(ss.IsEmpty); } internal void SetThread(char op, ulong? threadId) @@ -251,7 +251,7 @@ namespace Ryujinx.HLE.Debugger.Gdb IExecutionContext ctx = Debugger.DebugProcess.GetThread(Debugger.GThread.Value).Context; - Processor.Reply(Debugger.WriteRegister(ctx, gdbRegId, ss) && ss.IsEmpty()); + Processor.Reply(Debugger.WriteRegister(ctx, gdbRegId, ss) && ss.IsEmpty); } internal void Step(ulong? newPc) diff --git a/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs b/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs index ebd89459f..31203b62b 100644 --- a/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs +++ b/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs @@ -13,65 +13,56 @@ namespace Ryujinx.HLE.Debugger.Gdb */ private const uint FpcrMask = 0xfc1fffff; - public static string ReadRegister64(this IExecutionContext state, int gdbRegId) - { - switch (gdbRegId) + public static string ReadRegister64(this IExecutionContext state, int registerId) => + registerId switch { - case >= 0 and <= 31: - return Helpers.ToHex(BitConverter.GetBytes(state.GetX(gdbRegId))); - case 32: - return Helpers.ToHex(BitConverter.GetBytes(state.DebugPc)); - case 33: - return Helpers.ToHex(BitConverter.GetBytes(state.Pstate)); - case >= 34 and <= 65: - return Helpers.ToHex(state.GetV(gdbRegId - 34).ToArray()); - case 66: - return Helpers.ToHex(BitConverter.GetBytes((uint)state.Fpsr)); - case 67: - return Helpers.ToHex(BitConverter.GetBytes((uint)state.Fpcr)); - default: - return null; - } - } + >= 0 and <= 31 => Helpers.ToHex(BitConverter.GetBytes(state.GetX(registerId))), + 32 => Helpers.ToHex(BitConverter.GetBytes(state.DebugPc)), + 33 => Helpers.ToHex(BitConverter.GetBytes(state.Pstate)), + >= 34 and <= 65 => Helpers.ToHex(state.GetV(registerId - 34).ToArray()), + 66 => Helpers.ToHex(BitConverter.GetBytes((uint)state.Fpsr)), + 67 => Helpers.ToHex(BitConverter.GetBytes((uint)state.Fpcr)), + _ => null + }; - public static bool WriteRegister64(this IExecutionContext state, int gdbRegId, StringStream ss) + public static bool WriteRegister64(this IExecutionContext state, int registerId, StringStream ss) { - switch (gdbRegId) + switch (registerId) { case >= 0 and <= 31: { - ulong value = ss.ReadLengthAsLEHex(16); - state.SetX(gdbRegId, value); + ulong value = ss.ReadLengthAsLittleEndianHex(16); + state.SetX(registerId, value); return true; } case 32: { - ulong value = ss.ReadLengthAsLEHex(16); + ulong value = ss.ReadLengthAsLittleEndianHex(16); state.DebugPc = value; return true; } case 33: { - ulong value = ss.ReadLengthAsLEHex(8); + ulong value = ss.ReadLengthAsLittleEndianHex(8); state.Pstate = (uint)value; return true; } case >= 34 and <= 65: { - ulong value0 = ss.ReadLengthAsLEHex(16); - ulong value1 = ss.ReadLengthAsLEHex(16); - state.SetV(gdbRegId - 34, new V128(value0, value1)); + ulong value0 = ss.ReadLengthAsLittleEndianHex(16); + ulong value1 = ss.ReadLengthAsLittleEndianHex(16); + state.SetV(registerId - 34, new V128(value0, value1)); return true; } case 66: { - ulong value = ss.ReadLengthAsLEHex(8); + ulong value = ss.ReadLengthAsLittleEndianHex(8); state.Fpsr = (uint)value; return true; } case 67: { - ulong value = ss.ReadLengthAsLEHex(8); + ulong value = ss.ReadLengthAsLittleEndianHex(8); state.Fpcr = (uint)value; return true; } @@ -80,65 +71,64 @@ namespace Ryujinx.HLE.Debugger.Gdb } } - public static string ReadRegister32(this IExecutionContext state, int gdbRegId) + public static string ReadRegister32(this IExecutionContext state, int registerId) { - switch (gdbRegId) + switch (registerId) { case >= 0 and <= 14: - return Helpers.ToHex(BitConverter.GetBytes((uint)state.GetX(gdbRegId))); + return Helpers.ToHex(BitConverter.GetBytes((uint)state.GetX(registerId))); case 15: return Helpers.ToHex(BitConverter.GetBytes((uint)state.DebugPc)); case 16: - return Helpers.ToHex(BitConverter.GetBytes((uint)state.Pstate)); + return Helpers.ToHex(BitConverter.GetBytes(state.Pstate)); case >= 17 and <= 32: - return Helpers.ToHex(state.GetV(gdbRegId - 17).ToArray()); + return Helpers.ToHex(state.GetV(registerId - 17).ToArray()); case >= 33 and <= 64: - int reg = (gdbRegId - 33); + int reg = (registerId - 33); int n = reg / 2; int shift = reg % 2; ulong value = state.GetV(n).Extract(shift); return Helpers.ToHex(BitConverter.GetBytes(value)); case 65: - uint fpscr = (uint)state.Fpsr | (uint)state.Fpcr; - return Helpers.ToHex(BitConverter.GetBytes(fpscr)); + return Helpers.ToHex(BitConverter.GetBytes(state.Fpscr)); default: return null; } } - public static bool WriteRegister32(this IExecutionContext state, int gdbRegId, StringStream ss) + public static bool WriteRegister32(this IExecutionContext state, int registerId, StringStream ss) { - switch (gdbRegId) + switch (registerId) { case >= 0 and <= 14: { - ulong value = ss.ReadLengthAsLEHex(8); - state.SetX(gdbRegId, value); + ulong value = ss.ReadLengthAsLittleEndianHex(8); + state.SetX(registerId, value); return true; } case 15: { - ulong value = ss.ReadLengthAsLEHex(8); + ulong value = ss.ReadLengthAsLittleEndianHex(8); state.DebugPc = value; return true; } case 16: { - ulong value = ss.ReadLengthAsLEHex(8); + ulong value = ss.ReadLengthAsLittleEndianHex(8); state.Pstate = (uint)value; return true; } case >= 17 and <= 32: { - ulong value0 = ss.ReadLengthAsLEHex(16); - ulong value1 = ss.ReadLengthAsLEHex(16); - state.SetV(gdbRegId - 17, new V128(value0, value1)); + ulong value0 = ss.ReadLengthAsLittleEndianHex(16); + ulong value1 = ss.ReadLengthAsLittleEndianHex(16); + state.SetV(registerId - 17, new V128(value0, value1)); return true; } case >= 33 and <= 64: { - ulong value = ss.ReadLengthAsLEHex(16); - int regId = (gdbRegId - 33); + ulong value = ss.ReadLengthAsLittleEndianHex(16); + int regId = (registerId - 33); int regNum = regId / 2; int shift = regId % 2; V128 reg = state.GetV(regNum); @@ -147,7 +137,7 @@ namespace Ryujinx.HLE.Debugger.Gdb } case 65: { - ulong value = ss.ReadLengthAsLEHex(8); + ulong value = ss.ReadLengthAsLittleEndianHex(8); state.Fpsr = (uint)value & FpcrMask; state.Fpcr = (uint)value & ~FpcrMask; return true; diff --git a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs index 0896f25d2..a632cea33 100644 --- a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs +++ b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs @@ -11,11 +11,11 @@ namespace Ryujinx.HLE.Debugger void DebugContinue(KThread thread); bool DebugStep(KThread thread); KThread GetThread(ulong threadUid); - DebugState GetDebugState(); bool IsThreadPaused(KThread thread); - ulong[] GetThreadUids(); public void DebugInterruptHandler(IExecutionContext ctx); IVirtualMemoryManager CpuMemory { get; } + ulong[] ThreadUids { get; } + DebugState DebugState { get; } void InvalidateCacheRegion(ulong address, ulong size); } } diff --git a/src/Ryujinx.HLE/Debugger/IMessage.cs b/src/Ryujinx.HLE/Debugger/IMessage.cs new file mode 100644 index 000000000..9877bcd5e --- /dev/null +++ b/src/Ryujinx.HLE/Debugger/IMessage.cs @@ -0,0 +1,47 @@ +using Ryujinx.Cpu; + +namespace Ryujinx.HLE.Debugger +{ + /// + /// Marker interface for debugger messages. + /// + interface IMessage; + + public enum MessageType + { + Kill, + BreakIn, + SendNack + } + + record struct StatelessMessage(MessageType Type) : IMessage + { + public static StatelessMessage Kill => new(MessageType.Kill); + public static StatelessMessage BreakIn => new(MessageType.BreakIn); + public static StatelessMessage SendNack => new(MessageType.SendNack); + } + + struct CommandMessage : IMessage + { + public readonly string Command; + + public CommandMessage(string cmd) + { + Command = cmd; + } + } + + public class ThreadBreakMessage : IMessage + { + public IExecutionContext Context { get; } + public ulong Address { get; } + public int Opcode { get; } + + public ThreadBreakMessage(IExecutionContext context, ulong address, int opcode) + { + Context = context; + Address = address; + Opcode = opcode; + } + } +} diff --git a/src/Ryujinx.HLE/Debugger/Message/BreakInMessage.cs b/src/Ryujinx.HLE/Debugger/Message/BreakInMessage.cs deleted file mode 100644 index 81d8784ae..000000000 --- a/src/Ryujinx.HLE/Debugger/Message/BreakInMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Ryujinx.HLE.Debugger -{ - struct BreakInMessage : IMessage - { - } -} diff --git a/src/Ryujinx.HLE/Debugger/Message/CommandMessage.cs b/src/Ryujinx.HLE/Debugger/Message/CommandMessage.cs deleted file mode 100644 index ad265d432..000000000 --- a/src/Ryujinx.HLE/Debugger/Message/CommandMessage.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.HLE.Debugger -{ - struct CommandMessage : IMessage - { - public string Command; - - public CommandMessage(string cmd) - { - Command = cmd; - } - } -} diff --git a/src/Ryujinx.HLE/Debugger/Message/IMessage.cs b/src/Ryujinx.HLE/Debugger/Message/IMessage.cs deleted file mode 100644 index 4b03183c5..000000000 --- a/src/Ryujinx.HLE/Debugger/Message/IMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Ryujinx.HLE.Debugger -{ - interface IMessage - { - } -} diff --git a/src/Ryujinx.HLE/Debugger/Message/KillMessage.cs b/src/Ryujinx.HLE/Debugger/Message/KillMessage.cs deleted file mode 100644 index 43ae0f21e..000000000 --- a/src/Ryujinx.HLE/Debugger/Message/KillMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Ryujinx.HLE.Debugger -{ - struct KillMessage : IMessage - { - } -} diff --git a/src/Ryujinx.HLE/Debugger/Message/SendNackMessage.cs b/src/Ryujinx.HLE/Debugger/Message/SendNackMessage.cs deleted file mode 100644 index ce804c46e..000000000 --- a/src/Ryujinx.HLE/Debugger/Message/SendNackMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Ryujinx.HLE.Debugger -{ - struct SendNackMessage : IMessage - { - } -} diff --git a/src/Ryujinx.HLE/Debugger/Message/ThreadBreakMessage.cs b/src/Ryujinx.HLE/Debugger/Message/ThreadBreakMessage.cs deleted file mode 100644 index 027096eeb..000000000 --- a/src/Ryujinx.HLE/Debugger/Message/ThreadBreakMessage.cs +++ /dev/null @@ -1,18 +0,0 @@ -using IExecutionContext = Ryujinx.Cpu.IExecutionContext; - -namespace Ryujinx.HLE.Debugger -{ - public class ThreadBreakMessage : IMessage - { - public IExecutionContext Context { get; } - public ulong Address { get; } - public int Opcode { get; } - - public ThreadBreakMessage(IExecutionContext context, ulong address, int opcode) - { - Context = context; - Address = address; - Opcode = opcode; - } - } -} diff --git a/src/Ryujinx.HLE/Debugger/StringStream.cs b/src/Ryujinx.HLE/Debugger/StringStream.cs index bc422f51f..9e5a48704 100644 --- a/src/Ryujinx.HLE/Debugger/StringStream.cs +++ b/src/Ryujinx.HLE/Debugger/StringStream.cs @@ -5,63 +5,56 @@ namespace Ryujinx.HLE.Debugger { internal class StringStream { - private readonly string Data; - private int Position; + private readonly string _data; + private int _position; public StringStream(string s) { - Data = s; + _data = s; } + + public bool IsEmpty => _position >= _data.Length; - public char ReadChar() - { - return Data[Position++]; - } + public char ReadChar() => _data[_position++]; public string ReadUntil(char needle) { - int needlePos = Data.IndexOf(needle, Position); + int needlePos = _data.IndexOf(needle, _position); if (needlePos == -1) { - needlePos = Data.Length; + needlePos = _data.Length; } - string result = Data.Substring(Position, needlePos - Position); - Position = needlePos + 1; + string result = _data.Substring(_position, needlePos - _position); + _position = needlePos + 1; return result; } public string ReadLength(int len) { - string result = Data.Substring(Position, len); - Position += len; + string result = _data.Substring(_position, len); + _position += len; return result; } public string ReadRemaining() { - string result = Data.Substring(Position); - Position = Data.Length; + string result = _data[_position..]; + _position = _data.Length; return result; } - public ulong ReadRemainingAsHex() - { - return ulong.Parse(ReadRemaining(), NumberStyles.HexNumber); - } + public ulong ReadRemainingAsHex() + => ulong.Parse(ReadRemaining(), NumberStyles.HexNumber); - public ulong ReadUntilAsHex(char needle) - { - return ulong.Parse(ReadUntil(needle), NumberStyles.HexNumber); - } + public ulong ReadUntilAsHex(char needle) + => ulong.Parse(ReadUntil(needle), NumberStyles.HexNumber); - public ulong ReadLengthAsHex(int len) - { - return ulong.Parse(ReadLength(len), NumberStyles.HexNumber); - } + public ulong ReadLengthAsHex(int len) + => ulong.Parse(ReadLength(len), NumberStyles.HexNumber); - public ulong ReadLengthAsLEHex(int len) + public ulong ReadLengthAsLittleEndianHex(int len) { Debug.Assert(len % 2 == 0); @@ -83,9 +76,9 @@ namespace Ryujinx.HLE.Debugger public bool ConsumePrefix(string prefix) { - if (Data.Substring(Position).StartsWith(prefix)) + if (_data[_position..].StartsWith(prefix)) { - Position += prefix.Length; + _position += prefix.Length; return true; } return false; @@ -93,17 +86,12 @@ namespace Ryujinx.HLE.Debugger public bool ConsumeRemaining(string match) { - if (Data.Substring(Position) == match) + if (_data[_position..] == match) { - Position += match.Length; + _position += match.Length; return true; } return false; } - - public bool IsEmpty() - { - return Position >= Data.Length; - } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index eb75fb9a1..20c8b116c 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -1338,22 +1338,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return true; } - public DebugState GetDebugState() - { - return (DebugState)_parent.debugState; - } + public DebugState DebugState => (DebugState)_parent.debugState; public bool IsThreadPaused(KThread target) { return (target.SchedFlags & ThreadSchedState.ThreadPauseFlag) != 0; } - public ulong[] GetThreadUids() + public ulong[] ThreadUids { - lock (_parent._threadingLock) + get { - var threads = _parent._threads.Where(x => !x.TerminationRequested).ToArray(); - return threads.Select(x => x.ThreadUid).ToArray(); + lock (_parent._threadingLock) + { + return _parent._threads + .Where(x => !x.TerminationRequested) + .Select(x => x.ThreadUid) + .ToArray(); + } } } @@ -1361,8 +1363,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { lock (_parent._threadingLock) { - var threads = _parent._threads.Where(x => !x.TerminationRequested).ToArray(); - return threads.FirstOrDefault(x => x.ThreadUid == threadUid); + return _parent._threads.Where(x => !x.TerminationRequested) + .FirstOrDefault(x => x.ThreadUid == threadUid); } } @@ -1379,7 +1381,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _parent.InterruptHandler(ctx); } - public IVirtualMemoryManager CpuMemory { get { return _parent.CpuMemory; } } + public IVirtualMemoryManager CpuMemory => _parent.CpuMemory; public void InvalidateCacheRegion(ulong address, ulong size) { diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs index 54440ab5f..fa81ef983 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs @@ -293,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KThread currentThread = KernelStatic.GetCurrentThread(); KThread selectedThread = _state.SelectedThread; - if (!currentThread.IsThreadNamed && currentThread.GetThreadName() != "") + if (!currentThread.IsThreadNamed && string.IsNullOrEmpty(currentThread.GetThreadName())) { currentThread.HostThread.Name = $"<{currentThread.GetThreadName()}>"; currentThread.IsThreadNamed = true;