Debugger: Fixups from testing with GDB

# Conflicts:
#	src/Ryujinx.HLE/Debugger/Debugger.cs
This commit is contained in:
merry 2022-02-13 13:59:58 +00:00 committed by svc64
parent a1de4f1b5b
commit 1b9753d42a
2 changed files with 136 additions and 22 deletions

View file

@ -25,8 +25,8 @@ namespace Ryujinx.HLE.Debugger
private Thread SocketThread; private Thread SocketThread;
private Thread HandlerThread; private Thread HandlerThread;
private ulong cThread; private ulong? cThread;
private ulong gThread; private ulong? gThread;
public Debugger(Switch device, ushort port) public Debugger(Switch device, ushort port)
{ {
@ -45,9 +45,10 @@ namespace Ryujinx.HLE.Debugger
private ulong[] GetThreadIds() => Device.System.DebugGetApplicationProcess().DebugGetThreadUids(); private ulong[] GetThreadIds() => Device.System.DebugGetApplicationProcess().DebugGetThreadUids();
private Ryujinx.Cpu.IExecutionContext GetThread(ulong threadUid) => Device.System.DebugGetApplicationProcess().DebugGetThreadContext(threadUid); private Ryujinx.Cpu.IExecutionContext GetThread(ulong threadUid) => Device.System.DebugGetApplicationProcess().DebugGetThreadContext(threadUid);
private Ryujinx.Cpu.IExecutionContext[] GetThreads() => GetThreadIds().Select(x => GetThread(x)).ToArray(); private Ryujinx.Cpu.IExecutionContext[] GetThreads() => GetThreadIds().Select(x => GetThread(x)).ToArray();
private ulong? GetThreadUid(Ryujinx.Cpu.IExecutionContext thread) => GetThreadIds().Where(x => GetThread(x) == thread).First();
private IVirtualMemoryManager GetMemory() => Device.System.DebugGetApplicationProcess().CpuMemory; private IVirtualMemoryManager GetMemory() => Device.System.DebugGetApplicationProcess().CpuMemory;
const int GdbRegisterCount = 34; const int GdbRegisterCount = 68;
private int GdbRegisterHexSize(int gdbRegId) private int GdbRegisterHexSize(int gdbRegId)
{ {
@ -59,6 +60,12 @@ namespace Ryujinx.HLE.Debugger
return 16; return 16;
case 33: case 33:
return 8; return 8;
case >= 34 and <= 65:
return 32;
case 66:
return 8;
case 67:
return 8;
default: default:
throw new ArgumentException(); throw new ArgumentException();
} }
@ -74,6 +81,12 @@ namespace Ryujinx.HLE.Debugger
return ToHex(BitConverter.GetBytes(state.DebugPc)); return ToHex(BitConverter.GetBytes(state.DebugPc));
case 33: case 33:
return ToHex(BitConverter.GetBytes(state.Pstate)); return ToHex(BitConverter.GetBytes(state.Pstate));
case >= 34 and <= 65:
return ToHex(state.GetV(gdbRegId - 34).ToArray());
case 66:
return ToHex(BitConverter.GetBytes((uint)state.Fpsr));
case 67:
return ToHex(BitConverter.GetBytes((uint)state.Fpcr));
default: default:
return null; return null;
} }
@ -108,15 +121,15 @@ namespace Ryujinx.HLE.Debugger
case BreakInMessage _: case BreakInMessage _:
Logger.Notice.Print(LogClass.GdbStub, "Break-in requested"); Logger.Notice.Print(LogClass.GdbStub, "Break-in requested");
// TODO CommandQuery();
break; break;
case SendNackMessage _: case SendNackMessage _:
WriteStream.WriteByte((byte)'-'); WriteStream.WriteByte((byte)'-');
break; break;
case CommandMessage { Command: var cmd }: case CommandMessage {Command: var cmd}:
Logger.Debug?.Print(LogClass.GdbStub, $"Received Command: {cmd}"); Logger.Notice.Print(LogClass.GdbStub, $"Received Command: {cmd}");
WriteStream.WriteByte((byte)'+'); WriteStream.WriteByte((byte)'+');
ProcessCommand(cmd); ProcessCommand(cmd);
break; break;
@ -135,6 +148,7 @@ namespace Ryujinx.HLE.Debugger
{ {
goto unknownCommand; goto unknownCommand;
} }
// Enable extended mode // Enable extended mode
ReplyOK(); ReplyOK();
break; break;
@ -143,6 +157,7 @@ namespace Ryujinx.HLE.Debugger
{ {
goto unknownCommand; goto unknownCommand;
} }
CommandQuery(); CommandQuery();
break; break;
case 'c': case 'c':
@ -153,6 +168,7 @@ namespace Ryujinx.HLE.Debugger
{ {
goto unknownCommand; goto unknownCommand;
} }
CommandDetach(); CommandDetach();
break; break;
case 'g': case 'g':
@ -160,6 +176,7 @@ namespace Ryujinx.HLE.Debugger
{ {
goto unknownCommand; goto unknownCommand;
} }
CommandReadGeneralRegisters(); CommandReadGeneralRegisters();
break; break;
case 'G': case 'G':
@ -168,7 +185,7 @@ namespace Ryujinx.HLE.Debugger
case 'H': case 'H':
{ {
char op = ss.ReadChar(); char op = ss.ReadChar();
ulong threadId = ss.ReadRemainingAsHex(); ulong? threadId = ss.ReadRemainingAsThreadUid();
CommandSetThread(op, threadId); CommandSetThread(op, threadId);
break; break;
} }
@ -209,31 +226,39 @@ namespace Ryujinx.HLE.Debugger
Reply($"name:Ryujinx;version:{ReleaseInformation.GetVersion()};"); Reply($"name:Ryujinx;version:{ReleaseInformation.GetVersion()};");
break; break;
} }
if (ss.ConsumeRemaining("HostInfo")) if (ss.ConsumeRemaining("HostInfo"))
{ {
Reply($"triple:{ToHex("aarch64-unknown-linux-android")};endian:little;ptrsize:8;hostname:{ToHex("Ryujinx")};"); Reply(
$"triple:{ToHex("aarch64-unknown-linux-android")};endian:little;ptrsize:8;hostname:{ToHex("Ryujinx")};");
break; break;
} }
if (ss.ConsumeRemaining("ProcessInfo")) if (ss.ConsumeRemaining("ProcessInfo"))
{ {
Reply($"pid:1;cputype:100000c;cpusubtype:0;triple:{ToHex("aarch64-unknown-linux-android")};ostype:unknown;vendor:none;endian:little;ptrsize:8;"); Reply(
$"pid:1;cputype:100000c;cpusubtype:0;triple:{ToHex("aarch64-unknown-linux-android")};ostype:unknown;vendor:none;endian:little;ptrsize:8;");
break; break;
} }
if (ss.ConsumePrefix("Supported:") || ss.ConsumeRemaining("Supported")) if (ss.ConsumePrefix("Supported:") || ss.ConsumeRemaining("Supported"))
{ {
Reply("PacketSize=10000,qXfer:features:read+"); Reply("PacketSize=10000;qXfer:features:read+");
break; break;
} }
if (ss.ConsumeRemaining("fThreadInfo")) if (ss.ConsumeRemaining("fThreadInfo"))
{ {
Reply($"m{string.Join(",", GetThreadIds().Select(x => $"{x:x}"))}"); Reply($"m{string.Join(",", GetThreadIds().Select(x => $"{x:x}"))}");
break; break;
} }
if (ss.ConsumeRemaining("sThreadInfo")) if (ss.ConsumeRemaining("sThreadInfo"))
{ {
Reply("l"); Reply("l");
break; break;
} }
if (ss.ConsumePrefix("Xfer:features:read:")) if (ss.ConsumePrefix("Xfer:features:read:"))
{ {
string feature = ss.ReadUntil(':'); string feature = ss.ReadUntil(':');
@ -266,11 +291,21 @@ namespace Ryujinx.HLE.Debugger
break; break;
} }
} }
goto unknownCommand; goto unknownCommand;
case 'Q': case 'Q':
goto unknownCommand; goto unknownCommand;
case 's':
CommandStep(ss.IsEmpty() ? null : ss.ReadRemainingAsHex());
break;
case 'T':
{
ulong? threadId = ss.ReadRemainingAsThreadUid();
CommandIsAlive(threadId);
break;
}
default: default:
unknownCommand: unknownCommand:
// Logger.Notice.Print(LogClass.GdbStub, $"Unknown command: {cmd}"); // Logger.Notice.Print(LogClass.GdbStub, $"Unknown command: {cmd}");
Reply(""); Reply("");
break; break;
@ -282,14 +317,21 @@ namespace Ryujinx.HLE.Debugger
// GDB is performing initial contact. Stop everything. // GDB is performing initial contact. Stop everything.
HaltApplication(); HaltApplication();
gThread = cThread = GetThreadIds().First(); gThread = cThread = GetThreadIds().First();
Reply($"T05thread:{cThread:x}"); Reply($"T05thread:{cThread:x};");
//Reply("S05");
} }
void CommandContinue(ulong? newPc) void CommandContinue(ulong? newPc)
{ {
if (newPc.HasValue) if (newPc.HasValue)
{ {
GetThread(cThread).DebugPc = newPc.Value; if (cThread == null)
{
ReplyError();
return;
}
GetThread(cThread.Value).DebugPc = newPc.Value;
} }
foreach (var thread in GetThreads()) foreach (var thread in GetThreads())
@ -306,22 +348,36 @@ namespace Ryujinx.HLE.Debugger
void CommandReadGeneralRegisters() void CommandReadGeneralRegisters()
{ {
var ctx = GetThread(gThread); if (gThread == null)
{
ReplyError();
return;
}
var ctx = GetThread(gThread.Value);
string registers = ""; string registers = "";
for (int i = 0; i < GdbRegisterCount; i++) for (int i = 0; i < GdbRegisterCount; i++)
{ {
registers += GdbReadRegister(ctx, i); registers += GdbReadRegister(ctx, i);
} }
Reply(registers); Reply(registers);
} }
void CommandWriteGeneralRegisters(StringStream ss) void CommandWriteGeneralRegisters(StringStream ss)
{ {
var ctx = GetThread(gThread); if (gThread == null)
{
ReplyError();
return;
}
var ctx = GetThread(gThread.Value);
for (int i = 0; i < GdbRegisterCount; i++) for (int i = 0; i < GdbRegisterCount; i++)
{ {
GdbWriteRegister(ctx, i, ss.ReadLengthAsLEHex(GdbRegisterHexSize(i))); GdbWriteRegister(ctx, i, ss.ReadLengthAsLEHex(GdbRegisterHexSize(i)));
} }
if (ss.IsEmpty()) if (ss.IsEmpty())
{ {
ReplyOK(); ReplyOK();
@ -332,8 +388,13 @@ namespace Ryujinx.HLE.Debugger
} }
} }
void CommandSetThread(char op, ulong threadId) void CommandSetThread(char op, ulong? threadId)
{ {
if (threadId == 0)
{
threadId = GetThreadUid(GetThreads().First());
}
switch (op) switch (op)
{ {
case 'c': case 'c':
@ -373,6 +434,7 @@ namespace Ryujinx.HLE.Debugger
{ {
data[i] = (byte)ss.ReadLengthAsHex(2); data[i] = (byte)ss.ReadLengthAsHex(2);
} }
GetMemory().Write(addr, data); GetMemory().Write(addr, data);
ReplyOK(); ReplyOK();
} }
@ -384,7 +446,13 @@ namespace Ryujinx.HLE.Debugger
void CommandReadGeneralRegister(int gdbRegId) void CommandReadGeneralRegister(int gdbRegId)
{ {
var ctx = GetThread(gThread); if (gThread == null)
{
ReplyError();
return;
}
var ctx = GetThread(gThread.Value);
string result = GdbReadRegister(ctx, gdbRegId); string result = GdbReadRegister(ctx, gdbRegId);
if (result != null) if (result != null)
{ {
@ -398,7 +466,13 @@ namespace Ryujinx.HLE.Debugger
void CommandWriteGeneralRegister(int gdbRegId, ulong value) void CommandWriteGeneralRegister(int gdbRegId, ulong value)
{ {
var ctx = GetThread(gThread); if (gThread == null)
{
ReplyError();
return;
}
var ctx = GetThread(gThread.Value);
if (GdbWriteRegister(ctx, gdbRegId, value)) if (GdbWriteRegister(ctx, gdbRegId, value))
{ {
ReplyOK(); ReplyOK();
@ -409,6 +483,37 @@ namespace Ryujinx.HLE.Debugger
} }
} }
private void CommandStep(ulong? newPc)
{
if (cThread == null)
{
ReplyError();
return;
}
var ctx = GetThread(cThread.Value);
if (newPc.HasValue)
{
ctx.DebugPc = newPc.Value;
}
ctx.DebugStep();
Reply($"T00thread:{GetThreadUid(ctx):x};");
}
private void CommandIsAlive(ulong? threadId)
{
if (GetThreads().Any(x => GetThreadUid(x) == threadId))
{
ReplyOK();
}
else
{
Reply("E00");
}
}
private void Reply(string cmd) private void Reply(string cmd)
{ {
Logger.Notice.Print(LogClass.GdbStub, $"Reply: {cmd}"); Logger.Notice.Print(LogClass.GdbStub, $"Reply: {cmd}");
@ -427,7 +532,7 @@ namespace Ryujinx.HLE.Debugger
private void SocketReaderThreadMain() private void SocketReaderThreadMain()
{ {
restartListen: restartListen:
try try
{ {
var endpoint = new IPEndPoint(IPAddress.Any, GdbStubPort); var endpoint = new IPEndPoint(IPAddress.Any, GdbStubPort);
@ -475,7 +580,7 @@ namespace Ryujinx.HLE.Debugger
} }
} }
eof: eof:
Logger.Notice.Print(LogClass.GdbStub, "GDB client lost connection"); Logger.Notice.Print(LogClass.GdbStub, "GDB client lost connection");
goto restartListen; goto restartListen;
} }
@ -496,6 +601,7 @@ namespace Ryujinx.HLE.Debugger
checksum += (byte)x; checksum += (byte)x;
} }
} }
return checksum; return checksum;
} }
@ -522,6 +628,7 @@ namespace Ryujinx.HLE.Debugger
{ {
Messages.Add(new AbortMessage()); Messages.Add(new AbortMessage());
} }
ListenerSocket.Stop(); ListenerSocket.Stop();
ClientSocket?.Shutdown(SocketShutdown.Both); ClientSocket?.Shutdown(SocketShutdown.Both);
ClientSocket?.Close(); ClientSocket?.Close();

View file

@ -74,6 +74,13 @@ namespace Ryujinx.HLE.Debugger
} }
return result; return result;
} }
public ulong? ReadRemainingAsThreadUid()
{
string s = ReadRemaining();
return s == "-1" ? null : ulong.Parse(s, NumberStyles.HexNumber);
}
public bool ConsumePrefix(string prefix) public bool ConsumePrefix(string prefix)
{ {
if (Data.Substring(Position).StartsWith(prefix)) if (Data.Substring(Position).StartsWith(prefix))