mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-07-25 21:27:14 +02:00
Add GDB Stub
Author: merry, svc64
This commit is contained in:
parent
5d136980a3
commit
4a8463c2f7
53 changed files with 2428 additions and 21 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue