mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-11-20 08:34:16 +00:00
Debugger: Initial implementation
# Conflicts: # src/Ryujinx.HLE/Debugger/Debugger.cs # src/Ryujinx.HLE/Debugger/GdbSignal.cs # src/Ryujinx.HLE/Debugger/Message/AbortMessage.cs # src/Ryujinx.HLE/Debugger/Message/BreakInMessage.cs # src/Ryujinx.HLE/Debugger/Message/CommandMessage.cs # src/Ryujinx.HLE/Debugger/Message/IMessage.cs # src/Ryujinx.HLE/Debugger/Message/SendNackMessage.cs # src/Ryujinx.HLE/Debugger/StringStream.cs # src/Ryujinx.HLE/Switch.cs
This commit is contained in:
parent
2a17f1314d
commit
6edc00ec9c
19 changed files with 569 additions and 3 deletions
|
@ -136,6 +136,13 @@ namespace Ryujinx.Cpu.AppleHv
|
|||
/// <inheritdoc/>
|
||||
public void DebugContinue() => _impl.DebugContinue();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ulong DebugPc
|
||||
{
|
||||
get => _impl.DebugPc;
|
||||
set => _impl.DebugPc = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void StopRunning()
|
||||
{
|
||||
|
|
|
@ -21,6 +21,8 @@ namespace Ryujinx.Cpu.AppleHv
|
|||
private readonly ulong[] _x;
|
||||
private readonly V128[] _v;
|
||||
|
||||
public ulong DebugPc { get; set; }
|
||||
|
||||
public HvExecutionContextShadow()
|
||||
{
|
||||
_x = new ulong[32];
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||
internal Barrier _stepBarrier = new Barrier(2);
|
||||
|
||||
// This is only valid while debugging is enabled.
|
||||
public ulong DebugPc;
|
||||
public ulong DebugPc { get; set; }
|
||||
|
||||
static HvExecutionContextVcpu()
|
||||
{
|
||||
|
|
|
@ -47,5 +47,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||
void DebugStop();
|
||||
bool DebugStep();
|
||||
void DebugContinue();
|
||||
|
||||
ulong DebugPc { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,5 +113,7 @@ namespace Ryujinx.Cpu
|
|||
void DebugStop();
|
||||
bool DebugStep();
|
||||
void DebugContinue();
|
||||
|
||||
ulong DebugPc { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,6 +118,13 @@ namespace Ryujinx.Cpu.Jit
|
|||
/// <inheritdoc/>
|
||||
public void DebugContinue() => _impl.DebugContinue();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ulong DebugPc
|
||||
{
|
||||
get => _impl.DebugPc;
|
||||
set => _impl.DebugPc = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void StopRunning()
|
||||
{
|
||||
|
|
410
src/Ryujinx.HLE/Debugger/Debugger.cs
Normal file
410
src/Ryujinx.HLE/Debugger/Debugger.cs
Normal file
|
@ -0,0 +1,410 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.Debugger
|
||||
{
|
||||
public 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 BlockingCollection<IMessage>(1);
|
||||
private Thread SocketThread;
|
||||
private Thread HandlerThread;
|
||||
|
||||
private ulong cThread;
|
||||
private ulong gThread;
|
||||
|
||||
public Debugger(Switch device, ushort port)
|
||||
{
|
||||
Device = device;
|
||||
GdbStubPort = port;
|
||||
|
||||
ARMeilleure.Optimizations.EnableDebugging = true;
|
||||
|
||||
SocketThread = new Thread(SocketReaderThreadMain);
|
||||
HandlerThread = new Thread(HandlerThreadMain);
|
||||
SocketThread.Start();
|
||||
HandlerThread.Start();
|
||||
}
|
||||
|
||||
private void HaltApplication() => Device.System.DebugGetApplicationProcess().DebugStopAllThreads();
|
||||
private ulong[] GetThreadIds() => Device.System.DebugGetApplicationProcess().DebugGetThreadUids();
|
||||
private Ryujinx.Cpu.IExecutionContext GetThread(ulong threadUid) => Device.System.DebugGetApplicationProcess().DebugGetThreadContext(threadUid);
|
||||
private Ryujinx.Cpu.IExecutionContext[] GetThreads() => GetThreadIds().Select(x => GetThread(x)).ToArray();
|
||||
private IVirtualMemoryManager GetMemory() => Device.System.DebugGetApplicationProcess().CpuMemory;
|
||||
|
||||
const int GdbRegisterCount = 34;
|
||||
|
||||
private int GdbRegisterHexSize(int gdbRegId)
|
||||
{
|
||||
switch (gdbRegId)
|
||||
{
|
||||
case >= 0 and <= 31:
|
||||
return 16;
|
||||
case 32:
|
||||
return 16;
|
||||
case 33:
|
||||
return 8;
|
||||
default:
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
private string GdbReadRegister(Ryujinx.Cpu.IExecutionContext state, int gdbRegId)
|
||||
{
|
||||
switch (gdbRegId)
|
||||
{
|
||||
case >= 0 and <= 31:
|
||||
return $"{state.GetX(gdbRegId):x16}";
|
||||
case 32:
|
||||
return $"{state.DebugPc:x16}";
|
||||
case 33:
|
||||
return $"{state.Pstate:x8}";
|
||||
default:
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
private void GdbWriteRegister(Ryujinx.Cpu.IExecutionContext state, int gdbRegId, ulong value)
|
||||
{
|
||||
switch (gdbRegId)
|
||||
{
|
||||
case >= 0 and <= 31:
|
||||
state.SetX(gdbRegId, value);
|
||||
return;
|
||||
case 32:
|
||||
state.DebugPc = value;
|
||||
return;
|
||||
case 33:
|
||||
state.Pstate = (uint)value;
|
||||
return;
|
||||
default:
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlerThreadMain()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
switch (Messages.Take())
|
||||
{
|
||||
case AbortMessage _:
|
||||
return;
|
||||
|
||||
case BreakInMessage _:
|
||||
Logger.Notice.Print(LogClass.GdbStub, "Break-in requested");
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case SendNackMessage _:
|
||||
WriteStream.WriteByte((byte)'-');
|
||||
break;
|
||||
|
||||
case CommandMessage { Command: var cmd }:
|
||||
Logger.Debug?.Print(LogClass.GdbStub, $"Received Command: {cmd}");
|
||||
WriteStream.WriteByte((byte)'+');
|
||||
ProcessCommand(cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessCommand(string cmd)
|
||||
{
|
||||
StringStream ss = new StringStream(cmd);
|
||||
|
||||
switch (ss.ReadChar())
|
||||
{
|
||||
case '!':
|
||||
if (!ss.IsEmpty())
|
||||
{
|
||||
goto default;
|
||||
}
|
||||
// Enable extended mode
|
||||
Reply("OK");
|
||||
break;
|
||||
case '?':
|
||||
if (!ss.IsEmpty())
|
||||
{
|
||||
goto default;
|
||||
}
|
||||
CommandQuery();
|
||||
break;
|
||||
case 'c':
|
||||
CommandContinue(ss.IsEmpty() ? null : ss.ReadRemainingAsHex());
|
||||
break;
|
||||
case 'D':
|
||||
if (!ss.IsEmpty())
|
||||
{
|
||||
goto default;
|
||||
}
|
||||
CommandDetach();
|
||||
break;
|
||||
case 'g':
|
||||
if (!ss.IsEmpty())
|
||||
{
|
||||
goto default;
|
||||
}
|
||||
CommandReadGeneralRegisters();
|
||||
break;
|
||||
case 'G':
|
||||
CommandWriteGeneralRegisters(ss);
|
||||
break;
|
||||
case 'H':
|
||||
{
|
||||
char op = ss.ReadChar();
|
||||
ulong threadId = ss.ReadRemainingAsHex();
|
||||
CommandSetThread(op, threadId);
|
||||
break;
|
||||
}
|
||||
case 'k':
|
||||
Logger.Notice.Print(LogClass.GdbStub, "Kill request received");
|
||||
Reply("");
|
||||
break;
|
||||
case 'm':
|
||||
{
|
||||
ulong addr = ss.ReadUntilAsHex(',');
|
||||
ulong len = ss.ReadRemainingAsHex();
|
||||
CommandReadMemory(addr, len);
|
||||
break;
|
||||
}
|
||||
case 'M':
|
||||
{
|
||||
ulong addr = ss.ReadUntilAsHex(',');
|
||||
ulong len = ss.ReadUntilAsHex(':');
|
||||
CommandWriteMemory(addr, len, ss);
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
ulong gdbRegId = ss.ReadRemainingAsHex();
|
||||
CommandReadGeneralRegister((int)gdbRegId);
|
||||
break;
|
||||
}
|
||||
case 'P':
|
||||
{
|
||||
ulong gdbRegId = ss.ReadUntilAsHex('=');
|
||||
ulong value = ss.ReadRemainingAsHex();
|
||||
CommandWriteGeneralRegister((int)gdbRegId, value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Logger.Notice.Print(LogClass.GdbStub, $"Unknown command: {cmd}");
|
||||
Reply("");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandQuery()
|
||||
{
|
||||
// GDB is performing initial contact. Stop everything.
|
||||
HaltApplication();
|
||||
gThread = cThread = GetThreadIds().First();
|
||||
Reply($"T05thread:{cThread:x}");
|
||||
}
|
||||
|
||||
void CommandContinue(ulong? newPc)
|
||||
{
|
||||
if (newPc.HasValue)
|
||||
{
|
||||
GetThread(cThread).DebugPc = newPc.Value;
|
||||
}
|
||||
|
||||
foreach (var thread in GetThreads())
|
||||
{
|
||||
thread.DebugContinue();
|
||||
}
|
||||
}
|
||||
|
||||
void CommandDetach()
|
||||
{
|
||||
// TODO: Remove all breakpoints
|
||||
CommandContinue(null);
|
||||
}
|
||||
|
||||
void CommandReadGeneralRegisters()
|
||||
{
|
||||
var ctx = GetThread(gThread);
|
||||
string registers = "";
|
||||
for (int i = 0; i < GdbRegisterCount; i++)
|
||||
{
|
||||
registers += GdbReadRegister(ctx, i);
|
||||
}
|
||||
Reply(registers);
|
||||
}
|
||||
|
||||
void CommandWriteGeneralRegisters(StringStream ss)
|
||||
{
|
||||
var ctx = GetThread(gThread);
|
||||
for (int i = 0; i < GdbRegisterCount; i++)
|
||||
{
|
||||
GdbWriteRegister(ctx, i, ss.ReadLengthAsHex(GdbRegisterHexSize(i)));
|
||||
}
|
||||
Reply(ss.IsEmpty() ? "OK" : "E99");
|
||||
}
|
||||
|
||||
void CommandSetThread(char op, ulong threadId)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case 'c':
|
||||
cThread = threadId;
|
||||
Reply("OK");
|
||||
return;
|
||||
case 'g':
|
||||
gThread = threadId;
|
||||
Reply("OK");
|
||||
return;
|
||||
default:
|
||||
Reply("E99");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandReadMemory(ulong addr, ulong len)
|
||||
{
|
||||
var data = new byte[len];
|
||||
GetMemory().Read(addr, data);
|
||||
Reply(string.Join("", data.Select(x => $"{x:x2}")));
|
||||
}
|
||||
|
||||
void CommandWriteMemory(ulong addr, ulong len, StringStream ss)
|
||||
{
|
||||
var data = new byte[len];
|
||||
for (ulong i = 0; i < len; i++)
|
||||
{
|
||||
data[i] = (byte)ss.ReadLengthAsHex(2);
|
||||
}
|
||||
GetMemory().Write(addr, data);
|
||||
}
|
||||
|
||||
void CommandReadGeneralRegister(int gdbRegId)
|
||||
{
|
||||
var ctx = GetThread(gThread);
|
||||
Reply(GdbReadRegister(ctx, gdbRegId));
|
||||
}
|
||||
|
||||
void CommandWriteGeneralRegister(int gdbRegId, ulong value)
|
||||
{
|
||||
var ctx = GetThread(gThread);
|
||||
GdbWriteRegister(ctx, gdbRegId, value);
|
||||
Reply("OK");
|
||||
}
|
||||
|
||||
private void Reply(string cmd)
|
||||
{
|
||||
WriteStream.Write(Encoding.ASCII.GetBytes($"${cmd}#{CalculateChecksum(cmd):x2}"));
|
||||
}
|
||||
|
||||
private void SocketReaderThreadMain()
|
||||
{
|
||||
restartListen:
|
||||
try
|
||||
{
|
||||
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");
|
||||
|
||||
ClientSocket = ListenerSocket.AcceptSocket();
|
||||
ReadStream = new NetworkStream(ClientSocket, System.IO.FileAccess.Read);
|
||||
WriteStream = new NetworkStream(ClientSocket, System.IO.FileAccess.Write);
|
||||
Logger.Notice.Print(LogClass.GdbStub, "GDB client connected");
|
||||
|
||||
while (true)
|
||||
{
|
||||
switch (ReadStream.ReadByte())
|
||||
{
|
||||
case -1:
|
||||
goto eof;
|
||||
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 eof;
|
||||
if (x == '#')
|
||||
break;
|
||||
cmd += (char)x;
|
||||
}
|
||||
|
||||
string checksum = $"{(char)ReadStream.ReadByte()}{(char)ReadStream.ReadByte()}";
|
||||
// Debug.Assert(checksum == $"{CalculateChecksum(cmd):x2}");
|
||||
|
||||
Messages.Add(new CommandMessage(cmd));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
eof:
|
||||
Logger.Notice.Print(LogClass.GdbStub, "GDB client lost connection");
|
||||
goto restartListen;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logger.Notice.Print(LogClass.GdbStub, "GDB stub socket closed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private byte CalculateChecksum(string cmd)
|
||||
{
|
||||
byte checksum = 0;
|
||||
foreach (char x in cmd)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
checksum += (byte)x;
|
||||
}
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (HandlerThread.IsAlive)
|
||||
{
|
||||
Messages.Add(new AbortMessage());
|
||||
}
|
||||
ListenerSocket.Stop();
|
||||
ClientSocket?.Shutdown(SocketShutdown.Both);
|
||||
ClientSocket?.Close();
|
||||
ReadStream?.Close();
|
||||
WriteStream?.Close();
|
||||
SocketThread.Join();
|
||||
HandlerThread.Join();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
src/Ryujinx.HLE/Debugger/GdbSignal.cs
Normal file
15
src/Ryujinx.HLE/Debugger/GdbSignal.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace Ryujinx.HLE.Debugger
|
||||
{
|
||||
enum GdbSignal
|
||||
{
|
||||
Zero = 0,
|
||||
Int = 2,
|
||||
Quit = 3,
|
||||
Trap = 5,
|
||||
Abort = 6,
|
||||
Alarm = 14,
|
||||
IO = 23,
|
||||
XCPU = 24,
|
||||
Unknown = 143
|
||||
}
|
||||
}
|
6
src/Ryujinx.HLE/Debugger/Message/AbortMessage.cs
Normal file
6
src/Ryujinx.HLE/Debugger/Message/AbortMessage.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Ryujinx.HLE.Debugger
|
||||
{
|
||||
struct AbortMessage : IMessage
|
||||
{
|
||||
}
|
||||
}
|
6
src/Ryujinx.HLE/Debugger/Message/BreakInMessage.cs
Normal file
6
src/Ryujinx.HLE/Debugger/Message/BreakInMessage.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Ryujinx.HLE.Debugger
|
||||
{
|
||||
struct BreakInMessage : IMessage
|
||||
{
|
||||
}
|
||||
}
|
12
src/Ryujinx.HLE/Debugger/Message/CommandMessage.cs
Normal file
12
src/Ryujinx.HLE/Debugger/Message/CommandMessage.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.HLE.Debugger
|
||||
{
|
||||
struct CommandMessage : IMessage
|
||||
{
|
||||
public string Command;
|
||||
|
||||
public CommandMessage(string cmd)
|
||||
{
|
||||
Command = cmd;
|
||||
}
|
||||
}
|
||||
}
|
6
src/Ryujinx.HLE/Debugger/Message/IMessage.cs
Normal file
6
src/Ryujinx.HLE/Debugger/Message/IMessage.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Ryujinx.HLE.Debugger
|
||||
{
|
||||
interface IMessage
|
||||
{
|
||||
}
|
||||
}
|
6
src/Ryujinx.HLE/Debugger/Message/SendNackMessage.cs
Normal file
6
src/Ryujinx.HLE/Debugger/Message/SendNackMessage.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Ryujinx.HLE.Debugger
|
||||
{
|
||||
struct SendNackMessage : IMessage
|
||||
{
|
||||
}
|
||||
}
|
68
src/Ryujinx.HLE/Debugger/StringStream.cs
Normal file
68
src/Ryujinx.HLE/Debugger/StringStream.cs
Normal file
|
@ -0,0 +1,68 @@
|
|||
using System.Globalization;
|
||||
|
||||
namespace Ryujinx.HLE.Debugger
|
||||
{
|
||||
class StringStream
|
||||
{
|
||||
private readonly string Data;
|
||||
private int Position;
|
||||
|
||||
public StringStream(string s)
|
||||
{
|
||||
Data = s;
|
||||
}
|
||||
|
||||
public char ReadChar()
|
||||
{
|
||||
return Data[Position++];
|
||||
}
|
||||
|
||||
public string ReadUntil(char needle)
|
||||
{
|
||||
int needlePos = Data.IndexOf(needle, Position);
|
||||
|
||||
if (needlePos == -1)
|
||||
{
|
||||
needlePos = Data.Length;
|
||||
}
|
||||
|
||||
string result = Data.Substring(Position, needlePos - Position);
|
||||
Position = needlePos + 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
public string ReadLength(int len)
|
||||
{
|
||||
string result = Data.Substring(Position, len);
|
||||
Position += len;
|
||||
return result;
|
||||
}
|
||||
|
||||
public string ReadRemaining()
|
||||
{
|
||||
string result = Data.Substring(Position);
|
||||
Position = Data.Length;
|
||||
return result;
|
||||
}
|
||||
|
||||
public ulong ReadRemainingAsHex()
|
||||
{
|
||||
return ulong.Parse(ReadRemaining(), NumberStyles.HexNumber);
|
||||
}
|
||||
|
||||
public ulong ReadUntilAsHex(char needle)
|
||||
{
|
||||
return ulong.Parse(ReadUntil(needle), NumberStyles.HexNumber);
|
||||
}
|
||||
|
||||
public ulong ReadLengthAsHex(int len)
|
||||
{
|
||||
return ulong.Parse(ReadLength(len), NumberStyles.HexNumber);
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return Position >= Data.Length;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
private readonly ulong[] _x = new ulong[32];
|
||||
|
||||
public ulong DebugPc { get; set; }
|
||||
|
||||
public ulong GetX(int index) => _x[index];
|
||||
public void SetX(int index, ulong value) => _x[index] = value;
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace Ryujinx.HLE
|
|||
public Hid Hid { get; }
|
||||
public TamperMachine TamperMachine { get; }
|
||||
public IHostUIHandler UIHandler { get; }
|
||||
public Debugger.Debugger Debugger { get; }
|
||||
|
||||
public bool EnableDeviceVsync { get; set; } = true;
|
||||
|
||||
|
@ -53,6 +54,7 @@ namespace Ryujinx.HLE
|
|||
Statistics = new PerformanceStatistics();
|
||||
Hid = new Hid(this, System.HidStorage);
|
||||
Processes = new ProcessLoader(this);
|
||||
Debugger = Configuration.EnableGdbStub ? new Debugger.Debugger(this, configuration.GdbStubPort) : null;
|
||||
TamperMachine = new TamperMachine();
|
||||
|
||||
System.InitializeServices();
|
||||
|
@ -154,6 +156,7 @@ namespace Ryujinx.HLE
|
|||
AudioDeviceDriver.Dispose();
|
||||
FileSystem.Dispose();
|
||||
Memory.Dispose();
|
||||
Debugger.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -229,5 +229,13 @@ namespace Ryujinx.Headless.SDL2
|
|||
|
||||
[Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)]
|
||||
public string InputPath { get; set; }
|
||||
|
||||
// Debugging
|
||||
|
||||
[Option("enable-gdb-stub", Required = false, Default = false, HelpText = "Enable the GDB stub.")]
|
||||
public bool EnableGdbStub { get; set; }
|
||||
|
||||
[Option("gdb-stub-port", Required = false, Default = 55555, HelpText = "GDB stub port.")]
|
||||
public ushort GdbStubPort { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -571,7 +571,9 @@ namespace Ryujinx.Headless.SDL2
|
|||
options.AudioVolume,
|
||||
options.UseHypervisor ?? true,
|
||||
options.MultiplayerLanInterfaceId,
|
||||
Common.Configuration.Multiplayer.MultiplayerMode.Disabled);
|
||||
Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
|
||||
options.EnableGdbStub,
|
||||
options.GdbStubPort);
|
||||
|
||||
return new Switch(configuration);
|
||||
}
|
||||
|
|
|
@ -872,7 +872,9 @@ namespace Ryujinx.Ava
|
|||
ConfigurationState.Instance.System.AudioVolume,
|
||||
ConfigurationState.Instance.System.UseHypervisor,
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value,
|
||||
ConfigurationState.Instance.Multiplayer.Mode);
|
||||
ConfigurationState.Instance.Multiplayer.Mode,
|
||||
ConfigurationState.Instance.Debug.EnableGdbStub.Value,
|
||||
ConfigurationState.Instance.Debug.GdbStubPort.Value);
|
||||
|
||||
Device = new Switch(configuration);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue