mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-04-27 09:02:54 +00:00
gdb: Code cleanup pass #2
Moved the reply functionality into the command processor, move the main debugger thread into a dedicated class part, and more
This commit is contained in:
@@ -6,37 +6,39 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using IExecutionContext = Ryujinx.Cpu.IExecutionContext;
|
||||
using static Ryujinx.HLE.Debugger.Helpers;
|
||||
|
||||
|
||||
namespace Ryujinx.HLE.Debugger
|
||||
{
|
||||
public class Debugger : IDisposable
|
||||
public partial class Debugger : IDisposable
|
||||
{
|
||||
internal Switch Device { get; private set; }
|
||||
|
||||
public ushort GdbStubPort { get; private set; }
|
||||
|
||||
private TcpListener ListenerSocket;
|
||||
private Socket ClientSocket = null;
|
||||
private NetworkStream ReadStream = null;
|
||||
private NetworkStream WriteStream = null;
|
||||
private BlockingCollection<IMessage> Messages = new(1);
|
||||
private Thread DebuggerThread;
|
||||
private Thread MessageHandlerThread;
|
||||
private bool _shuttingDown = false;
|
||||
private ManualResetEventSlim _breakHandlerEvent = new(false);
|
||||
private readonly BlockingCollection<IMessage> _messages = new(1);
|
||||
private readonly Thread _debuggerThread;
|
||||
private readonly Thread _messageHandlerThread;
|
||||
|
||||
private GdbCommandProcessor CommandProcessor = null;
|
||||
private TcpListener _listenerSocket;
|
||||
private Socket _clientSocket;
|
||||
private NetworkStream _readStream;
|
||||
private NetworkStream _writeStream;
|
||||
|
||||
private GdbCommandProcessor _commandProcessor;
|
||||
private GdbCommands _commands;
|
||||
|
||||
private bool _shuttingDown;
|
||||
private readonly ManualResetEventSlim _breakHandlerEvent = new(false);
|
||||
|
||||
internal ulong? CThread;
|
||||
internal ulong? GThread;
|
||||
|
||||
internal BreakpointManager BreakpointManager;
|
||||
public readonly BreakpointManager BreakpointManager;
|
||||
|
||||
public Debugger(Switch device, ushort port)
|
||||
{
|
||||
@@ -45,10 +47,10 @@ namespace Ryujinx.HLE.Debugger
|
||||
|
||||
ARMeilleure.Optimizations.EnableDebugging = true;
|
||||
|
||||
DebuggerThread = new Thread(DebuggerThreadMain);
|
||||
DebuggerThread.Start();
|
||||
MessageHandlerThread = new Thread(MessageHandlerMain);
|
||||
MessageHandlerThread.Start();
|
||||
_debuggerThread = new Thread(DebuggerThreadMain);
|
||||
_debuggerThread.Start();
|
||||
_messageHandlerThread = new Thread(MessageHandlerMain);
|
||||
_messageHandlerThread.Start();
|
||||
BreakpointManager = new BreakpointManager(this);
|
||||
}
|
||||
|
||||
@@ -58,37 +60,37 @@ namespace Ryujinx.HLE.Debugger
|
||||
internal KThread[] GetThreads() =>
|
||||
DebugProcess.GetThreadUids().Select(x => DebugProcess.GetThread(x)).ToArray();
|
||||
|
||||
internal bool IsProcessAarch32 => DebugProcess.GetThread(GThread.Value).Context.IsAarch32;
|
||||
internal bool IsProcess32Bit => DebugProcess.GetThread(GThread.Value).Context.IsAarch32;
|
||||
|
||||
private void MessageHandlerMain()
|
||||
{
|
||||
while (!_shuttingDown)
|
||||
{
|
||||
IMessage msg = Messages.Take();
|
||||
IMessage msg = _messages.Take();
|
||||
try
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case BreakInMessage:
|
||||
Logger.Notice.Print(LogClass.GdbStub, "Break-in requested");
|
||||
CommandProcessor.Commands.CommandInterrupt();
|
||||
_commandProcessor.Commands.Interrupt();
|
||||
break;
|
||||
|
||||
case SendNackMessage:
|
||||
WriteStream.WriteByte((byte)'-');
|
||||
_writeStream.WriteByte((byte)'-');
|
||||
break;
|
||||
|
||||
case CommandMessage { Command: var cmd }:
|
||||
Logger.Debug?.Print(LogClass.GdbStub, $"Received Command: {cmd}");
|
||||
WriteStream.WriteByte((byte)'+');
|
||||
CommandProcessor.Process(cmd);
|
||||
_writeStream.WriteByte((byte)'+');
|
||||
_commandProcessor.Process(cmd);
|
||||
break;
|
||||
|
||||
case ThreadBreakMessage { Context: var ctx }:
|
||||
DebugProcess.DebugStop();
|
||||
GThread = CThread = ctx.ThreadUid;
|
||||
_breakHandlerEvent.Set();
|
||||
CommandProcessor.Commands.Reply($"T05thread:{ctx.ThreadUid:x};");
|
||||
_commandProcessor.Reply($"T05thread:{ctx.ThreadUid:x};");
|
||||
break;
|
||||
|
||||
case KillMessage:
|
||||
@@ -214,106 +216,6 @@ namespace Ryujinx.HLE.Debugger
|
||||
}
|
||||
}
|
||||
|
||||
private void DebuggerThreadMain()
|
||||
{
|
||||
var endpoint = new IPEndPoint(IPAddress.Any, GdbStubPort);
|
||||
ListenerSocket = new TcpListener(endpoint);
|
||||
ListenerSocket.Start();
|
||||
Logger.Notice.Print(LogClass.GdbStub, $"Currently waiting on {endpoint} for GDB client");
|
||||
|
||||
while (!_shuttingDown)
|
||||
{
|
||||
try
|
||||
{
|
||||
ClientSocket = ListenerSocket.AcceptSocket();
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user connects before the application is running, wait for the application to start.
|
||||
int retries = 10;
|
||||
while ((DebugProcess == null || GetThreads().Length == 0) && retries-- > 0)
|
||||
{
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
|
||||
if (DebugProcess == null || GetThreads().Length == 0)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.GdbStub,
|
||||
"Application is not running, cannot accept GDB client connection");
|
||||
ClientSocket.Close();
|
||||
continue;
|
||||
}
|
||||
|
||||
ClientSocket.NoDelay = true;
|
||||
ReadStream = new NetworkStream(ClientSocket, System.IO.FileAccess.Read);
|
||||
WriteStream = new NetworkStream(ClientSocket, System.IO.FileAccess.Write);
|
||||
CommandProcessor = new GdbCommandProcessor(ListenerSocket, ClientSocket, ReadStream, WriteStream, this);
|
||||
Logger.Notice.Print(LogClass.GdbStub, "GDB client connected");
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (ReadStream.ReadByte())
|
||||
{
|
||||
case -1:
|
||||
goto EndOfLoop;
|
||||
case '+':
|
||||
continue;
|
||||
case '-':
|
||||
Logger.Notice.Print(LogClass.GdbStub, "NACK received!");
|
||||
continue;
|
||||
case '\x03':
|
||||
Messages.Add(new BreakInMessage());
|
||||
break;
|
||||
case '$':
|
||||
string cmd = "";
|
||||
while (true)
|
||||
{
|
||||
int x = ReadStream.ReadByte();
|
||||
if (x == -1)
|
||||
goto EndOfLoop;
|
||||
if (x == '#')
|
||||
break;
|
||||
cmd += (char)x;
|
||||
}
|
||||
|
||||
string checksum = $"{(char)ReadStream.ReadByte()}{(char)ReadStream.ReadByte()}";
|
||||
if (checksum == $"{CalculateChecksum(cmd):x2}")
|
||||
{
|
||||
Messages.Add(new CommandMessage(cmd));
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Add(new SendNackMessage());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
goto EndOfLoop;
|
||||
}
|
||||
}
|
||||
|
||||
EndOfLoop:
|
||||
Logger.Notice.Print(LogClass.GdbStub, "GDB client lost connection");
|
||||
ReadStream.Close();
|
||||
ReadStream = null;
|
||||
WriteStream.Close();
|
||||
WriteStream = null;
|
||||
ClientSocket.Close();
|
||||
ClientSocket = null;
|
||||
CommandProcessor = null;
|
||||
|
||||
BreakpointManager.ClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
@@ -325,15 +227,15 @@ namespace Ryujinx.HLE.Debugger
|
||||
{
|
||||
_shuttingDown = true;
|
||||
|
||||
ListenerSocket.Stop();
|
||||
ClientSocket?.Shutdown(SocketShutdown.Both);
|
||||
ClientSocket?.Close();
|
||||
ReadStream?.Close();
|
||||
WriteStream?.Close();
|
||||
DebuggerThread.Join();
|
||||
Messages.Add(new KillMessage());
|
||||
MessageHandlerThread.Join();
|
||||
Messages.Dispose();
|
||||
_listenerSocket.Stop();
|
||||
_clientSocket?.Shutdown(SocketShutdown.Both);
|
||||
_clientSocket?.Close();
|
||||
_readStream?.Close();
|
||||
_writeStream?.Close();
|
||||
_debuggerThread.Join();
|
||||
_messages.Add(new KillMessage());
|
||||
_messageHandlerThread.Join();
|
||||
_messages.Dispose();
|
||||
_breakHandlerEvent.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -343,7 +245,7 @@ namespace Ryujinx.HLE.Debugger
|
||||
DebugProcess.DebugInterruptHandler(ctx);
|
||||
|
||||
_breakHandlerEvent.Reset();
|
||||
Messages.Add(new ThreadBreakMessage(ctx, address, imm));
|
||||
_messages.Add(new ThreadBreakMessage(ctx, address, imm));
|
||||
// Messages.Add can block, so we log it after adding the message to make sure user can see the log at the same time GDB receives the break message
|
||||
Logger.Notice.Print(LogClass.GdbStub, $"Break hit on thread {ctx.ThreadUid} at pc {address:x016}");
|
||||
// Wait for the process to stop before returning to avoid BreakHandler being called multiple times from the same breakpoint
|
||||
|
||||
Reference in New Issue
Block a user