From 7d5f7bc47968095a9b14b7264f36dc64c5cfa89b Mon Sep 17 00:00:00 2001 From: Coxxs <58-coxxs@users.noreply.git.ryujinx.app> Date: Sun, 22 Jun 2025 02:31:04 +0800 Subject: [PATCH] gdb: Implement vCont to support step on AArch32 --- src/Ryujinx.HLE/Debugger/Debugger.cs | 125 ++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index 9b9850e58..d1b41c7f7 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -6,6 +6,7 @@ using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Memory; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; @@ -230,6 +231,7 @@ namespace Ryujinx.HLE.Debugger case ThreadBreakMessage { Context: var ctx }: DebugProcess.DebugStop(); + gThread = cThread = ctx.ThreadUid; Reply($"T05thread:{ctx.ThreadUid:x};"); break; @@ -370,7 +372,7 @@ namespace Ryujinx.HLE.Debugger if (ss.ConsumePrefix("Supported:") || ss.ConsumeRemaining("Supported")) { - Reply("PacketSize=10000;qXfer:features:read+;qXfer:threads:read+"); + Reply("PacketSize=10000;qXfer:features:read+;qXfer:threads:read+;vContSupported+"); break; } @@ -490,6 +492,22 @@ namespace Ryujinx.HLE.Debugger break; } case 'v': + if (ss.ConsumePrefix("Cont")) + { + if (ss.ConsumeRemaining("?")) + { + Reply("vCont;c;C;s;S"); + break; + } + + if (ss.ConsumePrefix(";")) + { + HandleVContCommand(ss); + break; + } + + goto unknownCommand; + } if (ss.ConsumeRemaining("MustReplyEmpty")) { Reply(""); @@ -504,6 +522,109 @@ namespace Ryujinx.HLE.Debugger } } + enum VContAction + { + None, + Continue, + Stop, + Step + } + + record VContPendingAction(VContAction Action, ushort? Signal = null); + + private void HandleVContCommand(StringStream ss) + { + string[] rawActions = ss.ReadRemaining().Split(';', StringSplitOptions.RemoveEmptyEntries); + + var threadActionMap = new Dictionary(); + foreach (var thread in GetThreads()) + { + threadActionMap[thread.ThreadUid] = new VContPendingAction(VContAction.None); + } + + // For each inferior thread, the *leftmost* action with a matching thread-id is applied. + for (int i = rawActions.Length - 1; i >= 0; i--) + { + var rawAction = rawActions[i]; + var stream = new StringStream(rawAction); + + char cmd = stream.ReadChar(); + VContAction action = cmd switch + { + 'c' => VContAction.Continue, + 'C' => VContAction.Continue, + 's' => VContAction.Step, + 'S' => VContAction.Step, + 't' => VContAction.Stop, + _ => VContAction.None + }; + + ushort? signal = null; + if (cmd == 'C' || cmd == 'S') + { + signal = (ushort)stream.ReadLengthAsHex(2); + } + + ulong? threadId = null; + if (stream.ConsumePrefix(":")) + { + threadId = stream.ReadRemainingAsThreadUid(); + } + + if (threadId.HasValue) + { + if (threadActionMap.ContainsKey(threadId.Value)) { + threadActionMap[threadId.Value] = new VContPendingAction(action, signal); + } + } + else + { + foreach (var row in threadActionMap.ToList()) + { + threadActionMap[row.Key] = new VContPendingAction(action, signal); + } + } + } + + bool hasError = false; + + // TODO: We don't support stop or continue yet, and we don't support signals. + foreach (var (threadUid, action) in threadActionMap) + { + if (action.Action == VContAction.Step) + { + var thread = DebugProcess.GetThread(threadUid); + if (!DebugProcess.DebugStep(thread)) { + hasError = true; + } + } + } + + // If all threads are set to continue, continue the process. + if (threadActionMap.Values.All(a => a.Action == VContAction.Continue)) + { + DebugProcess.DebugContinue(); + } + + if (hasError) + { + ReplyError(); + } + else + { + ReplyOK(); + } + + foreach (var (threadUid, action) in threadActionMap) + { + if (action.Action == VContAction.Step) + { + gThread = cThread = threadUid; + Reply($"T05thread:{threadUid:x};"); + } + } + } + private string GetThreadListXml() { var sb = new StringBuilder(); @@ -772,6 +893,7 @@ namespace Ryujinx.HLE.Debugger } else { + gThread = cThread = thread.ThreadUid; Reply($"T05thread:{thread.ThreadUid:x};"); } } @@ -966,7 +1088,6 @@ namespace Ryujinx.HLE.Debugger public void BreakHandler(IExecutionContext ctx, ulong address, int imm) { - gThread = cThread = ctx.ThreadUid; Logger.Notice.Print(LogClass.GdbStub, $"Break hit on thread {ctx.ThreadUid} at pc {address:x016}"); Messages.Add(new ThreadBreakMessage(ctx, address, imm));