From 877f81e44a2a60d83a4b57cdddfd82530052ead9 Mon Sep 17 00:00:00 2001 From: merry Date: Sun, 13 Feb 2022 13:59:58 +0000 Subject: [PATCH] Debugger: Fixups from testing with GDB --- Ryujinx.HLE/Debugger/Debugger.cs | 157 +++++++++++++++++++++++---- Ryujinx.HLE/Debugger/StringStream.cs | 9 +- 2 files changed, 141 insertions(+), 25 deletions(-) diff --git a/Ryujinx.HLE/Debugger/Debugger.cs b/Ryujinx.HLE/Debugger/Debugger.cs index b21a93a66..e7856e139 100644 --- a/Ryujinx.HLE/Debugger/Debugger.cs +++ b/Ryujinx.HLE/Debugger/Debugger.cs @@ -25,8 +25,8 @@ namespace Ryujinx.HLE.Debugger private Thread SocketThread; private Thread HandlerThread; - private long cThread; - private long gThread; + private long? cThread; + private long? gThread; public Debugger(Switch device, ushort port) { @@ -43,11 +43,14 @@ namespace Ryujinx.HLE.Debugger private void HaltApplication() => Device.System.DebugGetApplicationProcess().DebugStopAllThreads(); private long[] GetThreadIds() => Device.System.DebugGetApplicationProcess().DebugGetThreadUids(); - private ARMeilleure.State.ExecutionContext GetThread(long threadUid) => Device.System.DebugGetApplicationProcess().DebugGetThreadContext(threadUid); - private ARMeilleure.State.ExecutionContext[] GetThreads() => GetThreadIds().Select(x => GetThread(x)).ToArray(); + + private ARMeilleure.State.ExecutionContext GetThread(long threadUid) => + Device.System.DebugGetApplicationProcess().DebugGetThreadContext(threadUid); + + private ARMeilleure.State.ExecutionContext[] GetThreads() => GetThreadIds().Select(GetThread).ToArray(); private IVirtualMemoryManager GetMemory() => Device.System.DebugGetApplicationProcess().CpuMemory; - const int GdbRegisterCount = 34; + const int GdbRegisterCount = 68; private int GdbRegisterHexSize(int gdbRegId) { @@ -59,6 +62,12 @@ namespace Ryujinx.HLE.Debugger return 16; case 33: return 8; + case >= 34 and <= 65: + return 32; + case 66: + return 8; + case 67: + return 8; default: throw new ArgumentException(); } @@ -74,6 +83,12 @@ namespace Ryujinx.HLE.Debugger return ToHex(BitConverter.GetBytes(state.DebugPc)); case 33: return ToHex(BitConverter.GetBytes(state.GetPstate())); + 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: return null; } @@ -108,15 +123,15 @@ namespace Ryujinx.HLE.Debugger case BreakInMessage _: Logger.Notice.Print(LogClass.GdbStub, "Break-in requested"); - // TODO + CommandQuery(); break; case SendNackMessage _: WriteStream.WriteByte((byte)'-'); break; - case CommandMessage { Command: var cmd }: - Logger.Debug?.Print(LogClass.GdbStub, $"Received Command: {cmd}"); + case CommandMessage {Command: var cmd}: + Logger.Notice.Print(LogClass.GdbStub, $"Received Command: {cmd}"); WriteStream.WriteByte((byte)'+'); ProcessCommand(cmd); break; @@ -135,6 +150,7 @@ namespace Ryujinx.HLE.Debugger { goto unknownCommand; } + // Enable extended mode ReplyOK(); break; @@ -143,6 +159,7 @@ namespace Ryujinx.HLE.Debugger { goto unknownCommand; } + CommandQuery(); break; case 'c': @@ -153,6 +170,7 @@ namespace Ryujinx.HLE.Debugger { goto unknownCommand; } + CommandDetach(); break; case 'g': @@ -160,6 +178,7 @@ namespace Ryujinx.HLE.Debugger { goto unknownCommand; } + CommandReadGeneralRegisters(); break; case 'G': @@ -168,8 +187,8 @@ namespace Ryujinx.HLE.Debugger case 'H': { char op = ss.ReadChar(); - ulong threadId = ss.ReadRemainingAsHex(); - CommandSetThread(op, (long)threadId); + long? threadId = ss.ReadRemainingAsThreadUid(); + CommandSetThread(op, threadId); break; } case 'k': @@ -209,31 +228,39 @@ namespace Ryujinx.HLE.Debugger Reply($"name:Ryujinx;version:{ReleaseInformations.GetVersion()};"); break; } + 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; } + 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; } + if (ss.ConsumePrefix("Supported:") || ss.ConsumeRemaining("Supported")) { - Reply("PacketSize=10000,qXfer:features:read+"); + Reply("PacketSize=10000;qXfer:features:read+"); break; } + if (ss.ConsumeRemaining("fThreadInfo")) { Reply($"m{string.Join(",", GetThreadIds().Select(x => $"{x:x}"))}"); break; } + if (ss.ConsumeRemaining("sThreadInfo")) { Reply("l"); break; } + if (ss.ConsumePrefix("Xfer:features:read:")) { string feature = ss.ReadUntil(':'); @@ -266,11 +293,21 @@ namespace Ryujinx.HLE.Debugger break; } } + goto unknownCommand; case 'Q': goto unknownCommand; + case 's': + CommandStep(ss.IsEmpty() ? null : ss.ReadRemainingAsHex()); + break; + case 'T': + { + long? threadId = ss.ReadRemainingAsThreadUid(); + CommandIsAlive(threadId); + break; + } default: - unknownCommand: + unknownCommand: // Logger.Notice.Print(LogClass.GdbStub, $"Unknown command: {cmd}"); Reply(""); break; @@ -282,14 +319,21 @@ namespace Ryujinx.HLE.Debugger // GDB is performing initial contact. Stop everything. HaltApplication(); gThread = cThread = GetThreadIds().First(); - Reply($"T05thread:{cThread:x}"); + Reply($"T05thread:{cThread:x};"); + //Reply("S05"); } void CommandContinue(ulong? newPc) { if (newPc.HasValue) { - GetThread(cThread).DebugPc = newPc.Value; + if (cThread == null) + { + ReplyError(); + return; + } + + GetThread(cThread.Value).DebugPc = newPc.Value; } foreach (var thread in GetThreads()) @@ -306,22 +350,36 @@ namespace Ryujinx.HLE.Debugger void CommandReadGeneralRegisters() { - var ctx = GetThread(gThread); + if (gThread == null) + { + ReplyError(); + return; + } + + var ctx = GetThread(gThread.Value); string registers = ""; for (int i = 0; i < GdbRegisterCount; i++) { registers += GdbReadRegister(ctx, i); } + Reply(registers); } 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++) { GdbWriteRegister(ctx, i, ss.ReadLengthAsLEHex(GdbRegisterHexSize(i))); } + if (ss.IsEmpty()) { ReplyOK(); @@ -332,8 +390,13 @@ namespace Ryujinx.HLE.Debugger } } - void CommandSetThread(char op, long threadId) + void CommandSetThread(char op, long? threadId) { + if (threadId == 0) + { + threadId = GetThreads().First().ThreadUid; + } + switch (op) { case 'c': @@ -373,6 +436,7 @@ namespace Ryujinx.HLE.Debugger { data[i] = (byte)ss.ReadLengthAsHex(2); } + GetMemory().Write(addr, data); ReplyOK(); } @@ -384,7 +448,13 @@ namespace Ryujinx.HLE.Debugger void CommandReadGeneralRegister(int gdbRegId) { - var ctx = GetThread(gThread); + if (gThread == null) + { + ReplyError(); + return; + } + + var ctx = GetThread(gThread.Value); string result = GdbReadRegister(ctx, gdbRegId); if (result != null) { @@ -398,7 +468,13 @@ namespace Ryujinx.HLE.Debugger 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)) { ReplyOK(); @@ -409,6 +485,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:{ctx.ThreadUid:x};"); + } + + private void CommandIsAlive(long? threadId) + { + if (GetThreads().Any(x => x.ThreadUid == threadId)) + { + ReplyOK(); + } + else + { + Reply("E00"); + } + } + private void Reply(string cmd) { Logger.Notice.Print(LogClass.GdbStub, $"Reply: {cmd}"); @@ -427,7 +534,7 @@ namespace Ryujinx.HLE.Debugger private void SocketReaderThreadMain() { - restartListen: + restartListen: try { var endpoint = new IPEndPoint(IPAddress.Any, GdbStubPort); @@ -475,7 +582,7 @@ namespace Ryujinx.HLE.Debugger } } - eof: + eof: Logger.Notice.Print(LogClass.GdbStub, "GDB client lost connection"); goto restartListen; } @@ -496,6 +603,7 @@ namespace Ryujinx.HLE.Debugger checksum += (byte)x; } } + return checksum; } @@ -522,6 +630,7 @@ namespace Ryujinx.HLE.Debugger { Messages.Add(new AbortMessage()); } + ListenerSocket.Stop(); ClientSocket?.Shutdown(SocketShutdown.Both); ClientSocket?.Close(); @@ -532,4 +641,4 @@ namespace Ryujinx.HLE.Debugger } } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/Debugger/StringStream.cs b/Ryujinx.HLE/Debugger/StringStream.cs index 3197d2e55..4616bf964 100644 --- a/Ryujinx.HLE/Debugger/StringStream.cs +++ b/Ryujinx.HLE/Debugger/StringStream.cs @@ -74,6 +74,13 @@ namespace Ryujinx.HLE.Debugger } return result; } + + public long? ReadRemainingAsThreadUid() + { + string s = ReadRemaining(); + return s == "-1" ? null : long.Parse(s, NumberStyles.HexNumber); + } + public bool ConsumePrefix(string prefix) { if (Data.Substring(Position).StartsWith(prefix)) @@ -99,4 +106,4 @@ namespace Ryujinx.HLE.Debugger return Position >= Data.Length; } } -} +} \ No newline at end of file