gdb: dynamic rcmd system & more cleanups

This commit is contained in:
GreemDev
2025-10-20 21:18:06 -05:00
parent a0e5edf8ba
commit 71eb844dd8
9 changed files with 71 additions and 41 deletions

View File

@@ -64,7 +64,7 @@ namespace Ryujinx.HLE.Debugger
Logger.Notice.Print(LogClass.GdbStub, "NACK received!");
continue;
case '\x03':
_messages.Add(StatelessMessage.BreakIn);
_messages.Add(Message.BreakIn);
break;
case '$':
string cmd = string.Empty;
@@ -85,7 +85,7 @@ namespace Ryujinx.HLE.Debugger
}
else
{
_messages.Add(StatelessMessage.SendNack);
_messages.Add(Message.SendNack);
}
break;

View File

@@ -14,16 +14,16 @@ namespace Ryujinx.HLE.Debugger
{
switch (_messages.Take())
{
case StatelessMessage { Type: MessageType.BreakIn }:
case Message { Type: MessageType.BreakIn }:
Logger.Notice.Print(LogClass.GdbStub, "Break-in requested");
_commands.Interrupt();
break;
case StatelessMessage { Type: MessageType.SendNack }:
case Message { Type: MessageType.SendNack }:
_writeStream.WriteByte((byte)'-');
break;
case StatelessMessage { Type: MessageType.Kill }:
case Message { Type: MessageType.Kill }:
return;
case CommandMessage { Command: { } cmd }:

View File

@@ -1,13 +1,50 @@
using Gommon;
using JetBrains.Annotations;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Ryujinx.HLE.Debugger
{
public partial class Debugger
{
static Debugger()
{
_rcmdDelegates.Add(["help"],
_ => _rcmdDelegates.Keys
.Where(x => !x[0].Equals("help"))
.Select(x => x.JoinToString('\n'))
.JoinToString('\n') + '\n'
);
_rcmdDelegates.Add(["get info"], dbgr => dbgr.GetProcessInfo());
_rcmdDelegates.Add(["backtrace", "bt"], dbgr => dbgr.GetStackTrace());
_rcmdDelegates.Add(["registers", "reg"], dbgr => dbgr.GetRegisters());
_rcmdDelegates.Add(["minidump"], dbgr => dbgr.GetMinidump());
}
private static readonly Dictionary<string[], Func<Debugger, string>> _rcmdDelegates = new();
[CanBeNull]
public static Func<Debugger, string> FindRcmdDelegate(string command)
{
Func<Debugger, string> searchResult = null;
foreach ((string[] names, Func<Debugger, string> dlg) in _rcmdDelegates)
{
if (names.ContainsIgnoreCase(command.Trim()))
{
searchResult = dlg;
break;
}
}
return searchResult;
}
public string GetStackTrace()
{
if (GThread == null)

View File

@@ -4,10 +4,8 @@ using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using IExecutionContext = Ryujinx.Cpu.IExecutionContext;
@@ -20,7 +18,7 @@ namespace Ryujinx.HLE.Debugger
public ushort GdbStubPort { get; private set; }
private readonly BlockingCollection<IMessage> _messages = new(1);
private readonly BlockingCollection<Message.IMarker> _messages = new(1);
private readonly Thread _debuggerThread;
private readonly Thread _messageHandlerThread;
@@ -89,7 +87,7 @@ namespace Ryujinx.HLE.Debugger
_readStream?.Close();
_writeStream?.Close();
_debuggerThread.Join();
_messages.Add(StatelessMessage.Kill);
_messages.Add(Message.Kill);
_messageHandlerThread.Join();
_messages.Dispose();
_breakHandlerEvent.Dispose();

View File

@@ -20,6 +20,9 @@ namespace Ryujinx.HLE.Debugger.Gdb
Commands = commands;
}
public void ReplyHex(string data) => Reply(Helpers.ToHex(data));
public void ReplyHex(byte[] data) => Reply(Helpers.ToHex(data));
public void Reply(string cmd)
{
Logger.Debug?.Print(LogClass.GdbStub, $"Reply: {cmd}");
@@ -197,11 +200,10 @@ namespace Ryujinx.HLE.Debugger.Gdb
break;
}
Reply(Helpers.ToHex(
DebugProcess.IsThreadPaused(DebugProcess.GetThread(threadId.Value))
? "Paused"
: "Running"
)
ReplyHex(
DebugProcess.IsThreadPaused(DebugProcess.GetThread(threadId.Value))
? "Paused"
: "Running"
);
break;

View File

@@ -197,7 +197,7 @@ namespace Ryujinx.HLE.Debugger.Gdb
{
byte[] data = new byte[len];
Debugger.DebugProcess.CpuMemory.Read(addr, data);
Processor.Reply(Helpers.ToHex(data));
Processor.ReplyHex(data);
}
catch (InvalidMemoryRegionException)
{
@@ -422,17 +422,9 @@ namespace Ryujinx.HLE.Debugger.Gdb
string command = Helpers.FromHex(hexCommand);
Logger.Debug?.Print(LogClass.GdbStub, $"Received Rcmd: {command}");
string response = command.Trim().ToLowerInvariant() switch
{
"help" => "backtrace\nbt\nregisters\nreg\nget info\nminidump\n",
"get info" => Debugger.GetProcessInfo(),
"backtrace" or "bt" => Debugger.GetStackTrace(),
"registers" or "reg" => Debugger.GetRegisters(),
"minidump" => Debugger.GetMinidump(),
_ => $"Unknown command: {command}\n"
};
var rcmdDelegate = Debugger.FindRcmdDelegate(command);
Processor.Reply(Helpers.ToHex(response));
Processor.ReplyHex(rcmdDelegate?.Invoke(Debugger) ?? $"Unknown command: {command}\n");
}
catch (Exception e)
{

View File

@@ -20,8 +20,8 @@ namespace Ryujinx.HLE.Debugger.Gdb
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)),
66 => Helpers.ToHex(BitConverter.GetBytes(state.Fpsr)),
67 => Helpers.ToHex(BitConverter.GetBytes(state.Fpcr)),
_ => null
};

View File

@@ -6,6 +6,10 @@ namespace Ryujinx.HLE.Debugger
{
internal interface IDebuggableProcess
{
IVirtualMemoryManager CpuMemory { get; }
ulong[] ThreadUids { get; }
DebugState DebugState { get; }
void DebugStop();
void DebugContinue();
void DebugContinue(KThread thread);
@@ -13,9 +17,6 @@ namespace Ryujinx.HLE.Debugger
KThread GetThread(ulong threadUid);
bool IsThreadPaused(KThread thread);
public void DebugInterruptHandler(IExecutionContext ctx);
IVirtualMemoryManager CpuMemory { get; }
ulong[] ThreadUids { get; }
DebugState DebugState { get; }
void InvalidateCacheRegion(ulong address, ulong size);
}
}

View File

@@ -2,11 +2,6 @@ using Ryujinx.Cpu;
namespace Ryujinx.HLE.Debugger
{
/// <summary>
/// Marker interface for debugger messages.
/// </summary>
interface IMessage;
public enum MessageType
{
Kill,
@@ -14,14 +9,19 @@ namespace Ryujinx.HLE.Debugger
SendNack
}
record struct StatelessMessage(MessageType Type) : IMessage
record struct Message(MessageType Type) : Message.IMarker
{
public static StatelessMessage Kill => new(MessageType.Kill);
public static StatelessMessage BreakIn => new(MessageType.BreakIn);
public static StatelessMessage SendNack => new(MessageType.SendNack);
/// <summary>
/// Marker interface for debugger messages.
/// </summary>
internal interface IMarker;
public static Message Kill => new(MessageType.Kill);
public static Message BreakIn => new(MessageType.BreakIn);
public static Message SendNack => new(MessageType.SendNack);
}
struct CommandMessage : IMessage
struct CommandMessage : Message.IMarker
{
public readonly string Command;
@@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Debugger
}
}
public class ThreadBreakMessage : IMessage
public class ThreadBreakMessage : Message.IMarker
{
public IExecutionContext Context { get; }
public ulong Address { get; }