gdb: dynamic rcmd system & more cleanups
This commit is contained in:
parent
a0e5edf8ba
commit
71eb844dd8
9 changed files with 71 additions and 41 deletions
|
|
@ -64,7 +64,7 @@ namespace Ryujinx.HLE.Debugger
|
||||||
Logger.Notice.Print(LogClass.GdbStub, "NACK received!");
|
Logger.Notice.Print(LogClass.GdbStub, "NACK received!");
|
||||||
continue;
|
continue;
|
||||||
case '\x03':
|
case '\x03':
|
||||||
_messages.Add(StatelessMessage.BreakIn);
|
_messages.Add(Message.BreakIn);
|
||||||
break;
|
break;
|
||||||
case '$':
|
case '$':
|
||||||
string cmd = string.Empty;
|
string cmd = string.Empty;
|
||||||
|
|
@ -85,7 +85,7 @@ namespace Ryujinx.HLE.Debugger
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_messages.Add(StatelessMessage.SendNack);
|
_messages.Add(Message.SendNack);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,16 @@ namespace Ryujinx.HLE.Debugger
|
||||||
{
|
{
|
||||||
switch (_messages.Take())
|
switch (_messages.Take())
|
||||||
{
|
{
|
||||||
case StatelessMessage { Type: MessageType.BreakIn }:
|
case Message { Type: MessageType.BreakIn }:
|
||||||
Logger.Notice.Print(LogClass.GdbStub, "Break-in requested");
|
Logger.Notice.Print(LogClass.GdbStub, "Break-in requested");
|
||||||
_commands.Interrupt();
|
_commands.Interrupt();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StatelessMessage { Type: MessageType.SendNack }:
|
case Message { Type: MessageType.SendNack }:
|
||||||
_writeStream.WriteByte((byte)'-');
|
_writeStream.WriteByte((byte)'-');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StatelessMessage { Type: MessageType.Kill }:
|
case Message { Type: MessageType.Kill }:
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case CommandMessage { Command: { } cmd }:
|
case CommandMessage { Command: { } cmd }:
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,50 @@
|
||||||
|
using Gommon;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Debugger
|
namespace Ryujinx.HLE.Debugger
|
||||||
{
|
{
|
||||||
public partial class 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()
|
public string GetStackTrace()
|
||||||
{
|
{
|
||||||
if (GThread == null)
|
if (GThread == null)
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,8 @@ using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using IExecutionContext = Ryujinx.Cpu.IExecutionContext;
|
using IExecutionContext = Ryujinx.Cpu.IExecutionContext;
|
||||||
|
|
||||||
|
|
@ -20,7 +18,7 @@ namespace Ryujinx.HLE.Debugger
|
||||||
|
|
||||||
public ushort GdbStubPort { get; private set; }
|
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 _debuggerThread;
|
||||||
private readonly Thread _messageHandlerThread;
|
private readonly Thread _messageHandlerThread;
|
||||||
|
|
||||||
|
|
@ -89,7 +87,7 @@ namespace Ryujinx.HLE.Debugger
|
||||||
_readStream?.Close();
|
_readStream?.Close();
|
||||||
_writeStream?.Close();
|
_writeStream?.Close();
|
||||||
_debuggerThread.Join();
|
_debuggerThread.Join();
|
||||||
_messages.Add(StatelessMessage.Kill);
|
_messages.Add(Message.Kill);
|
||||||
_messageHandlerThread.Join();
|
_messageHandlerThread.Join();
|
||||||
_messages.Dispose();
|
_messages.Dispose();
|
||||||
_breakHandlerEvent.Dispose();
|
_breakHandlerEvent.Dispose();
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,9 @@ namespace Ryujinx.HLE.Debugger.Gdb
|
||||||
Commands = commands;
|
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)
|
public void Reply(string cmd)
|
||||||
{
|
{
|
||||||
Logger.Debug?.Print(LogClass.GdbStub, $"Reply: {cmd}");
|
Logger.Debug?.Print(LogClass.GdbStub, $"Reply: {cmd}");
|
||||||
|
|
@ -197,11 +200,10 @@ namespace Ryujinx.HLE.Debugger.Gdb
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Reply(Helpers.ToHex(
|
ReplyHex(
|
||||||
DebugProcess.IsThreadPaused(DebugProcess.GetThread(threadId.Value))
|
DebugProcess.IsThreadPaused(DebugProcess.GetThread(threadId.Value))
|
||||||
? "Paused"
|
? "Paused"
|
||||||
: "Running"
|
: "Running"
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ namespace Ryujinx.HLE.Debugger.Gdb
|
||||||
{
|
{
|
||||||
byte[] data = new byte[len];
|
byte[] data = new byte[len];
|
||||||
Debugger.DebugProcess.CpuMemory.Read(addr, data);
|
Debugger.DebugProcess.CpuMemory.Read(addr, data);
|
||||||
Processor.Reply(Helpers.ToHex(data));
|
Processor.ReplyHex(data);
|
||||||
}
|
}
|
||||||
catch (InvalidMemoryRegionException)
|
catch (InvalidMemoryRegionException)
|
||||||
{
|
{
|
||||||
|
|
@ -422,17 +422,9 @@ namespace Ryujinx.HLE.Debugger.Gdb
|
||||||
string command = Helpers.FromHex(hexCommand);
|
string command = Helpers.FromHex(hexCommand);
|
||||||
Logger.Debug?.Print(LogClass.GdbStub, $"Received Rcmd: {command}");
|
Logger.Debug?.Print(LogClass.GdbStub, $"Received Rcmd: {command}");
|
||||||
|
|
||||||
string response = command.Trim().ToLowerInvariant() switch
|
var rcmdDelegate = Debugger.FindRcmdDelegate(command);
|
||||||
{
|
|
||||||
"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"
|
|
||||||
};
|
|
||||||
|
|
||||||
Processor.Reply(Helpers.ToHex(response));
|
Processor.ReplyHex(rcmdDelegate?.Invoke(Debugger) ?? $"Unknown command: {command}\n");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ namespace Ryujinx.HLE.Debugger.Gdb
|
||||||
32 => Helpers.ToHex(BitConverter.GetBytes(state.DebugPc)),
|
32 => Helpers.ToHex(BitConverter.GetBytes(state.DebugPc)),
|
||||||
33 => Helpers.ToHex(BitConverter.GetBytes(state.Pstate)),
|
33 => Helpers.ToHex(BitConverter.GetBytes(state.Pstate)),
|
||||||
>= 34 and <= 65 => Helpers.ToHex(state.GetV(registerId - 34).ToArray()),
|
>= 34 and <= 65 => Helpers.ToHex(state.GetV(registerId - 34).ToArray()),
|
||||||
66 => Helpers.ToHex(BitConverter.GetBytes((uint)state.Fpsr)),
|
66 => Helpers.ToHex(BitConverter.GetBytes(state.Fpsr)),
|
||||||
67 => Helpers.ToHex(BitConverter.GetBytes((uint)state.Fpcr)),
|
67 => Helpers.ToHex(BitConverter.GetBytes(state.Fpcr)),
|
||||||
_ => null
|
_ => null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,10 @@ namespace Ryujinx.HLE.Debugger
|
||||||
{
|
{
|
||||||
internal interface IDebuggableProcess
|
internal interface IDebuggableProcess
|
||||||
{
|
{
|
||||||
|
IVirtualMemoryManager CpuMemory { get; }
|
||||||
|
ulong[] ThreadUids { get; }
|
||||||
|
DebugState DebugState { get; }
|
||||||
|
|
||||||
void DebugStop();
|
void DebugStop();
|
||||||
void DebugContinue();
|
void DebugContinue();
|
||||||
void DebugContinue(KThread thread);
|
void DebugContinue(KThread thread);
|
||||||
|
|
@ -13,9 +17,6 @@ namespace Ryujinx.HLE.Debugger
|
||||||
KThread GetThread(ulong threadUid);
|
KThread GetThread(ulong threadUid);
|
||||||
bool IsThreadPaused(KThread thread);
|
bool IsThreadPaused(KThread thread);
|
||||||
public void DebugInterruptHandler(IExecutionContext ctx);
|
public void DebugInterruptHandler(IExecutionContext ctx);
|
||||||
IVirtualMemoryManager CpuMemory { get; }
|
|
||||||
ulong[] ThreadUids { get; }
|
|
||||||
DebugState DebugState { get; }
|
|
||||||
void InvalidateCacheRegion(ulong address, ulong size);
|
void InvalidateCacheRegion(ulong address, ulong size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,6 @@ using Ryujinx.Cpu;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Debugger
|
namespace Ryujinx.HLE.Debugger
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Marker interface for debugger messages.
|
|
||||||
/// </summary>
|
|
||||||
interface IMessage;
|
|
||||||
|
|
||||||
public enum MessageType
|
public enum MessageType
|
||||||
{
|
{
|
||||||
Kill,
|
Kill,
|
||||||
|
|
@ -14,14 +9,19 @@ namespace Ryujinx.HLE.Debugger
|
||||||
SendNack
|
SendNack
|
||||||
}
|
}
|
||||||
|
|
||||||
record struct StatelessMessage(MessageType Type) : IMessage
|
record struct Message(MessageType Type) : Message.IMarker
|
||||||
{
|
{
|
||||||
public static StatelessMessage Kill => new(MessageType.Kill);
|
/// <summary>
|
||||||
public static StatelessMessage BreakIn => new(MessageType.BreakIn);
|
/// Marker interface for debugger messages.
|
||||||
public static StatelessMessage SendNack => new(MessageType.SendNack);
|
/// </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;
|
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 IExecutionContext Context { get; }
|
||||||
public ulong Address { get; }
|
public ulong Address { get; }
|
||||||
Loading…
Add table
Add a link
Reference in a new issue