Add GDB Stub

Author: merry, svc64
This commit is contained in:
Coxxs 2025-06-20 16:14:12 +08:00
parent 5d136980a3
commit 4a8463c2f7
53 changed files with 2428 additions and 21 deletions

View file

@ -5,6 +5,7 @@ using LibHac.Fs.Shim;
using LibHac.FsSystem;
using LibHac.Tools.FsSystem;
using Ryujinx.Cpu;
using Ryujinx.HLE.Debugger;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Memory;
@ -500,5 +501,13 @@ namespace Ryujinx.HLE.HOS
IsPaused = pause;
}
internal IDebuggableProcess DebugGetApplicationProcess()
{
lock (KernelContext.Processes)
{
return KernelContext.Processes.Values.FirstOrDefault(x => x.IsApplication)?.DebugInterface;
}
}
}
}

View file

@ -1,6 +1,7 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.Debugger;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
@ -11,6 +12,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using ExceptionCallback = Ryujinx.Cpu.ExceptionCallback;
using ExceptionCallbackNoArgs = Ryujinx.Cpu.ExceptionCallbackNoArgs;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
@ -89,6 +92,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public IVirtualMemoryManager CpuMemory => Context.AddressSpace;
public HleProcessDebugger Debugger { get; private set; }
public IDebuggableProcess DebugInterface { get; private set; }
protected int debugState = (int)DebugState.Running;
public KProcess(KernelContext context, bool allowCodeMemoryForJit = false) : base(context)
{
@ -110,6 +115,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
_threads = [];
Debugger = new HleProcessDebugger(this);
DebugInterface = new DebuggerInterface(this);
}
public Result InitializeKip(
@ -679,6 +685,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
SetState(newState);
if (KernelContext.Device.Configuration.DebuggerSuspendOnStart && IsApplication)
{
mainThread.Suspend(ThreadSchedState.ThreadPauseFlag);
debugState = (int)DebugState.Stopped;
}
result = mainThread.Start();
if (result != Result.Success)
@ -727,9 +739,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public IExecutionContext CreateExecutionContext()
{
ExceptionCallback breakCallback = null;
ExceptionCallbackNoArgs stepCallback = null;
if (KernelContext.Device.Configuration.EnableGdbStub)
{
breakCallback = KernelContext.Device.Debugger.BreakHandler;
stepCallback = KernelContext.Device.Debugger.StepHandler;
}
return Context?.CreateExecutionContext(new ExceptionCallbacks(
InterruptHandler,
null,
breakCallback,
stepCallback,
KernelContext.SyscallHandler.SvcCall,
UndefinedInstructionHandler));
}
@ -1174,5 +1196,157 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
return Capabilities.IsSvcPermitted(svcId);
}
private class DebuggerInterface : IDebuggableProcess
{
private Barrier StepBarrier;
private readonly KProcess _parent;
private readonly KernelContext _kernelContext;
private KThread steppingThread;
public DebuggerInterface(KProcess p)
{
_parent = p;
_kernelContext = p.KernelContext;
StepBarrier = new(2);
}
public void DebugStop()
{
if (Interlocked.CompareExchange(ref _parent.debugState, (int)DebugState.Stopping,
(int)DebugState.Running) != (int)DebugState.Running)
{
return;
}
_kernelContext.CriticalSection.Enter();
lock (_parent._threadingLock)
{
foreach (KThread thread in _parent._threads)
{
thread.Suspend(ThreadSchedState.ThreadPauseFlag);
thread.Context.RequestInterrupt();
if (!thread.DebugHalt.WaitOne(TimeSpan.FromMilliseconds(50)))
{
Logger.Warning?.Print(LogClass.Kernel, $"Failed to suspend thread {thread.ThreadUid} in time.");
}
}
}
_parent.debugState = (int)DebugState.Stopped;
_kernelContext.CriticalSection.Leave();
}
public void DebugContinue()
{
if (Interlocked.CompareExchange(ref _parent.debugState, (int)DebugState.Running,
(int)DebugState.Stopped) != (int)DebugState.Stopped)
{
return;
}
_kernelContext.CriticalSection.Enter();
lock (_parent._threadingLock)
{
foreach (KThread thread in _parent._threads)
{
thread.Resume(ThreadSchedState.ThreadPauseFlag);
}
}
_kernelContext.CriticalSection.Leave();
}
public bool DebugStep(KThread target)
{
if (_parent.debugState != (int)DebugState.Stopped)
{
return false;
}
_kernelContext.CriticalSection.Enter();
steppingThread = target;
bool waiting = target.MutexOwner != null || target.WaitingSync || target.WaitingInArbitration;
target.Context.RequestDebugStep();
if (waiting)
{
lock (_parent._threadingLock)
{
foreach (KThread thread in _parent._threads)
{
thread.Resume(ThreadSchedState.ThreadPauseFlag);
}
}
}
else
{
target.Resume(ThreadSchedState.ThreadPauseFlag);
}
_kernelContext.CriticalSection.Leave();
StepBarrier.SignalAndWait();
_kernelContext.CriticalSection.Enter();
steppingThread = null;
if (waiting)
{
lock (_parent._threadingLock)
{
foreach (KThread thread in _parent._threads)
{
thread.Suspend(ThreadSchedState.ThreadPauseFlag);
}
}
}
else
{
target.Suspend(ThreadSchedState.ThreadPauseFlag);
}
_kernelContext.CriticalSection.Leave();
StepBarrier.SignalAndWait();
return true;
}
public DebugState GetDebugState()
{
return (DebugState)_parent.debugState;
}
public ulong[] GetThreadUids()
{
lock (_parent._threadingLock)
{
var threads = _parent._threads.Where(x => !x.TerminationRequested).ToArray();
return threads.Select(x => x.ThreadUid).ToArray();
}
}
public KThread GetThread(ulong threadUid)
{
lock (_parent._threadingLock)
{
var threads = _parent._threads.Where(x => !x.TerminationRequested).ToArray();
return threads.FirstOrDefault(x => x.ThreadUid == threadUid);
}
}
public void DebugInterruptHandler(IExecutionContext ctx)
{
_kernelContext.CriticalSection.Enter();
bool stepping = steppingThread != null;
_kernelContext.CriticalSection.Leave();
if (stepping)
{
StepBarrier.SignalAndWait();
StepBarrier.SignalAndWait();
}
_parent.InterruptHandler(ctx);
}
public IVirtualMemoryManager CpuMemory { get { return _parent.CpuMemory; } }
public void InvalidateCacheRegion(ulong address, ulong size)
{
_parent.Context.InvalidateCacheRegion(address, size);
}
}
}
}

View file

@ -1,5 +1,6 @@
using ARMeilleure.State;
using Ryujinx.Cpu;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
@ -17,10 +18,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public bool IsAarch32 { get => false; set { } }
public ulong ThreadUid { get; set; }
public bool Running { get; private set; } = true;
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;
@ -31,6 +36,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
}
public void RequestDebugStep()
{
}
public void StopRunning()
{
Running = false;

View file

@ -301,6 +301,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
currentThread.SchedulerWaitEvent.Reset();
currentThread.ThreadContext.Unlock();
currentThread.DebugHalt.Set();
// Wake all the threads that might be waiting until this thread context is unlocked.
for (int core = 0; core < CpuCoresCount; core++)

View file

@ -1,5 +1,6 @@
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.Debugger;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
@ -114,6 +115,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private readonly Lock _activityOperationLock = new();
internal readonly ManualResetEvent DebugHalt = new(false);
public KThread(KernelContext context) : base(context)
{
WaitSyncObjects = new KSynchronizationObject[MaxWaitSyncObjects];
@ -202,8 +205,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
}
Context.TpidrroEl0 = (long)_tlsAddress;
Context.DebugPc = _entrypoint;
ThreadUid = KernelContext.NewThreadUid();
Context.ThreadUid = ThreadUid;
HostThread.Name = customThreadStart != null ? $"HLE.OsThread.{ThreadUid}" : $"HLE.GuestThread.{ThreadUid}";
@ -307,7 +312,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
KernelContext.CriticalSection.Enter();
if (Owner != null && Owner.PinnedThreads[KernelStatic.GetCurrentThread().CurrentCore] == this)
KThread currentThread = KernelStatic.GetCurrentThread();
if (Owner != null && currentThread != null && Owner.PinnedThreads[currentThread.CurrentCore] == this)
{
Owner.UnpinThread(this);
}
@ -362,7 +369,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
ThreadSchedState state = PrepareForTermination();
if (state != ThreadSchedState.TerminationPending)
if (KernelStatic.GetCurrentThread() == this && state != ThreadSchedState.TerminationPending)
{
KernelContext.Synchronization.WaitFor(new KSynchronizationObject[] { this }, -1, out _);
}
@ -1248,6 +1255,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private void ThreadStart()
{
_schedulerWaitEvent.WaitOne();
DebugHalt.Reset();
KernelStatic.SetKernelContext(KernelContext, this);
if (_customThreadStart != null)