gdb: Implement vCont to support step on AArch32

This commit is contained in:
Coxxs 2025-06-22 02:31:04 +08:00
parent 44f4e9af51
commit 7d5f7bc479

View file

@ -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<ulong, VContPendingAction>();
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));