From e3b806041731c3256e99e89009981bea423bf04e Mon Sep 17 00:00:00 2001 From: svc64 Date: Sat, 11 Nov 2023 16:21:36 +0200 Subject: [PATCH] Refactor the debugger interface Also support stepping through blocking syscalls --- src/Ryujinx.HLE/Debugger/Debugger.cs | 40 ++++----- .../Debugger/IDebuggableProcess.cs | 9 +- src/Ryujinx.HLE/HOS/Horizon.cs | 2 +- .../HOS/Kernel/Process/KProcess.cs | 90 +++++++++++++++++-- .../HOS/Kernel/Threading/KThread.cs | 66 -------------- 5 files changed, 109 insertions(+), 98 deletions(-) diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index e11d12c2cb..10022e1d04 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -47,11 +47,9 @@ namespace Ryujinx.HLE.Debugger HandlerThread.Start(); } - private void HaltApplication() => Device.System.DebugGetApplicationProcess().DebugStopAllThreads(); - private ulong[] GetThreadIds() => Device.System.DebugGetApplicationProcess().DebugGetThreadUids(); - private KThread GetThread(ulong threadUid) => Device.System.DebugGetApplicationProcess().DebugGetThread(threadUid); - private KThread[] GetThreads() => GetThreadIds().Select(x => GetThread(x)).ToArray(); - private IVirtualMemoryManager GetMemory() => Device.System.DebugGetApplicationProcess().CpuMemory; + private IDebuggableProcess DebugProcess => Device.System.DebugGetApplicationProcess(); + private KThread[] GetThreads() => DebugProcess.GetThreadUids().Select(x => DebugProcess.GetThread(x)).ToArray(); + private IVirtualMemoryManager GetMemory() => DebugProcess.CpuMemory; private void InvalidateCacheRegion(ulong address, ulong size) => Device.System.DebugGetApplicationProcess().InvalidateCacheRegion(address, size); private KernelContext KernelContext => Device.System.KernelContext; @@ -171,7 +169,7 @@ namespace Ryujinx.HLE.Debugger break; case ThreadBreakMessage msg: - HaltApplication(); + DebugProcess.DebugStop(); Reply($"T05thread:{msg.Context.ThreadUid:x};"); break; } @@ -289,7 +287,7 @@ namespace Ryujinx.HLE.Debugger if (ss.ConsumeRemaining("fThreadInfo")) { - Reply($"m{string.Join(",", GetThreadIds().Select(x => $"{x:x}"))}"); + Reply($"m{string.Join(",", DebugProcess.GetThreadUids().Select(x => $"{x:x}"))}"); break; } @@ -308,8 +306,7 @@ namespace Ryujinx.HLE.Debugger break; } - KThread thread = GetThread(threadId.Value); - if (thread.GetDebugState() == DebugState.Stopped) + if (DebugProcess.GetDebugState() == DebugState.Stopped) { Reply(ToHex("Stopped")); } @@ -376,8 +373,8 @@ namespace Ryujinx.HLE.Debugger void CommandQuery() { // GDB is performing initial contact. Stop everything. - HaltApplication(); - gThread = cThread = GetThreadIds().First(); + DebugProcess.DebugStop(); + gThread = cThread = DebugProcess.GetThreadUids().First(); Reply($"T05thread:{cThread:x};"); } @@ -391,13 +388,10 @@ namespace Ryujinx.HLE.Debugger return; } - GetThread(cThread.Value).Context.DebugPc = newPc.Value; + DebugProcess.GetThread(cThread.Value).Context.DebugPc = newPc.Value; } - foreach (var thread in GetThreads()) - { - thread.DebugContinue(); - } + DebugProcess.DebugContinue(); } void CommandDetach() @@ -414,7 +408,7 @@ namespace Ryujinx.HLE.Debugger return; } - var ctx = GetThread(gThread.Value).Context; + var ctx = DebugProcess.GetThread(gThread.Value).Context; string registers = ""; for (int i = 0; i < GdbRegisterCount; i++) { @@ -432,7 +426,7 @@ namespace Ryujinx.HLE.Debugger return; } - var ctx = GetThread(gThread.Value).Context; + var ctx = DebugProcess.GetThread(gThread.Value).Context; for (int i = 0; i < GdbRegisterCount; i++) { if (!GdbWriteRegister(ctx, i, ss)) @@ -517,7 +511,7 @@ namespace Ryujinx.HLE.Debugger return; } - var ctx = GetThread(gThread.Value).Context; + var ctx = DebugProcess.GetThread(gThread.Value).Context; string result = GdbReadRegister(ctx, gdbRegId); if (result != null) { @@ -537,7 +531,7 @@ namespace Ryujinx.HLE.Debugger return; } - var ctx = GetThread(gThread.Value).Context; + var ctx = DebugProcess.GetThread(gThread.Value).Context; if (GdbWriteRegister(ctx, gdbRegId, ss) && ss.IsEmpty()) { ReplyOK(); @@ -556,14 +550,14 @@ namespace Ryujinx.HLE.Debugger return; } - var thread = GetThread(cThread.Value); + var thread = DebugProcess.GetThread(cThread.Value); if (newPc.HasValue) { thread.Context.DebugPc = newPc.Value; } - if (!thread.DebugStep()) + if (!DebugProcess.DebugStep(thread)) { ReplyError(); } @@ -745,7 +739,7 @@ namespace Ryujinx.HLE.Debugger Messages.Add(new ThreadBreakMessage(ctx, address, imm)); - KThread currentThread = GetThread(ctx.ThreadUid); + KThread currentThread = DebugProcess.GetThread(ctx.ThreadUid); if (currentThread.Context.Running && currentThread.Owner != null && diff --git a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs index 5138929605..98d2223a6e 100644 --- a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs +++ b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs @@ -5,9 +5,12 @@ namespace Ryujinx.HLE.Debugger { internal interface IDebuggableProcess { - void DebugStopAllThreads(); - ulong[] DebugGetThreadUids(); - public KThread DebugGetThread(ulong threadUid); + void DebugStop(); + void DebugContinue(); + bool DebugStep(KThread thread); + KThread GetThread(ulong threadUid); + DebugState GetDebugState(); + ulong[] GetThreadUids(); IVirtualMemoryManager CpuMemory { get; } void InvalidateCacheRegion(ulong address, ulong size); } diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index 4f9bff96be..4da62d0f07 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -479,7 +479,7 @@ namespace Ryujinx.HLE.HOS { lock (KernelContext.Processes) { - return KernelContext.Processes.Values.FirstOrDefault(x => x.IsApplication)?.GdbStubInterface; + return KernelContext.Processes.Values.FirstOrDefault(x => x.IsApplication)?.DebugInterface; } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 9cdd90de58..80671ccc1b 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public IVirtualMemoryManager CpuMemory => Context.AddressSpace; public HleProcessDebugger Debugger { get; private set; } - public IDebuggableProcess GdbStubInterface { get { return new DebuggerInterface(this); } } + public IDebuggableProcess DebugInterface { get; private set; } public KProcess(KernelContext context, bool allowCodeMemoryForJit = false) : base(context) { @@ -113,6 +113,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _threads = new LinkedList(); Debugger = new HleProcessDebugger(this); + DebugInterface = new DebuggerInterface(this); } public Result InitializeKip( @@ -1189,24 +1190,103 @@ namespace Ryujinx.HLE.HOS.Kernel.Process private class DebuggerInterface : IDebuggableProcess { private readonly KProcess _parent; + private readonly KernelContext KernelContext; + private int _debugState = (int)DebugState.Running; public DebuggerInterface(KProcess p) { _parent = p; } - public void DebugStopAllThreads() + public void DebugStop() { + if (Interlocked.CompareExchange(ref _debugState, (int)DebugState.Stopping, + (int)DebugState.Running) != (int)DebugState.Running) + { + return; + } + + _parent.KernelContext.CriticalSection.Enter(); lock (_parent._threadingLock) { foreach (KThread thread in _parent._threads) { - thread.DebugStop(); + thread.Suspend(ThreadSchedState.ThreadPauseFlag); + thread.Context.RequestInterrupt(); + thread.DebugHalt.WaitOne(); } } + + _debugState = (int)DebugState.Stopped; + _parent.KernelContext.CriticalSection.Leave(); } - public ulong[] DebugGetThreadUids() + public void DebugContinue() + { + if (Interlocked.CompareExchange(ref _debugState, (int)DebugState.Running, + (int)DebugState.Stopped) != (int)DebugState.Stopped) + { + return; + } + + _parent.KernelContext.CriticalSection.Enter(); + lock (_parent._threadingLock) + { + foreach (KThread thread in _parent._threads) + { + thread.Resume(ThreadSchedState.ThreadPauseFlag); + } + } + _parent.KernelContext.CriticalSection.Leave(); + } + + public bool DebugStep(KThread target) + { + if (_debugState != (int)DebugState.Stopped) + { + return false; + } + _parent.KernelContext.CriticalSection.Enter(); + bool wasPaused = (target.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused; + target.Context.RequestDebugStep(); + target.Resume(ThreadSchedState.ThreadPauseFlag); + if (wasPaused) + { + lock (_parent._threadingLock) + { + foreach (KThread thread in _parent._threads) + { + thread.Resume(ThreadSchedState.ThreadPauseFlag); + } + } + } + _parent.KernelContext.CriticalSection.Leave(); + + target.Context.StepBarrier.SignalAndWait(); + target.Context.StepBarrier.SignalAndWait(); + + _parent.KernelContext.CriticalSection.Enter(); + target.Suspend(ThreadSchedState.ThreadPauseFlag); + if (wasPaused) + { + lock (_parent._threadingLock) + { + foreach (KThread thread in _parent._threads) + { + thread.Suspend(ThreadSchedState.ThreadPauseFlag); + } + } + } + _parent.KernelContext.CriticalSection.Leave(); + return true; + } + + public DebugState GetDebugState() + { + return (DebugState)_debugState; + } + + public ulong[] GetThreadUids() { lock (_parent._threadingLock) { @@ -1214,7 +1294,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - public KThread DebugGetThread(ulong threadUid) + public KThread GetThread(ulong threadUid) { lock (_parent._threadingLock) { diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 501344c79a..adef6ed78f 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -115,7 +115,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading private readonly object _activityOperationLock = new(); - private int _debugState = (int)DebugState.Running; internal readonly ManualResetEvent DebugHalt = new(false); public KThread(KernelContext context) : base(context) @@ -1437,70 +1436,5 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { Owner.CpuMemory.Write(_tlsAddress + TlsUserInterruptFlagOffset, 0); } - - public bool DebugStep() - { - lock (_activityOperationLock) - { - KernelContext.CriticalSection.Enter(); - bool blocked = MutexOwner != null || WaitingInArbitration || WaitingSync; - if (_debugState != (int)DebugState.Stopped - || (_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0 - || blocked) - { - KernelContext.CriticalSection.Leave(); - return false; - } - KernelContext.CriticalSection.Leave(); - - Context.RequestDebugStep(); - Resume(ThreadSchedState.ThreadPauseFlag); - Context.StepBarrier.SignalAndWait(); - Suspend(ThreadSchedState.ThreadPauseFlag); - Context.StepBarrier.SignalAndWait(); - - return true; - } - } - - public void DebugStop() - { - lock (_activityOperationLock) - { - if (Interlocked.CompareExchange(ref _debugState, (int)DebugState.Stopping, - (int)DebugState.Running) != (int)DebugState.Running) - { - return; - } - - Suspend(ThreadSchedState.ThreadPauseFlag); - Context.RequestInterrupt(); - DebugHalt.WaitOne(); - - _debugState = (int)DebugState.Stopped; - } - } - - public void DebugContinue() - { - lock (_activityOperationLock) - { - if (Interlocked.CompareExchange(ref _debugState, (int)DebugState.Running, - (int)DebugState.Stopped) != (int)DebugState.Stopped) - { - return; - } - - if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0) - { - Resume(ThreadSchedState.ThreadPauseFlag); - } - } - } - - public DebugState GetDebugState() - { - return (DebugState)_debugState; - } } }