From ac438d657298a69d1d4c2e6b0775cf40b68e0d09 Mon Sep 17 00:00:00 2001 From: svc64 Date: Mon, 25 Sep 2023 11:00:07 +0300 Subject: [PATCH] KThread based debug This commit starts a big refactor to the original debugger PR by merryhime. The debugger now interfaces with KThreads instead of the ExecutionContext. The corresponding KThread debug functions properly suspend/resume the thread and call the underlying debug related function in the ExecutionContext. I also added debugging support to the AppleHV ExecutionContext. --- .../Instructions/NativeInterface.cs | 5 ++ src/ARMeilleure/State/ExecutionContext.cs | 28 +++----- src/ARMeilleure/Translation/Translator.cs | 17 ++--- src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs | 65 +++++++++++++++---- .../AppleHv/HvExecutionContextShadow.cs | 20 ------ .../AppleHv/HvExecutionContextVcpu.cs | 43 ------------ .../AppleHv/IHvExecutionContext.cs | 8 --- src/Ryujinx.Cpu/IExecutionContext.cs | 1 - src/Ryujinx.Cpu/Jit/JitExecutionContext.cs | 3 - .../Debugger}/DebugState.cs | 2 +- src/Ryujinx.HLE/Debugger/Debugger.cs | 33 ++++++---- .../Debugger/IDebuggableProcess.cs | 5 +- src/Ryujinx.HLE/HOS/Horizon.cs | 2 +- .../HOS/Kernel/Process/KProcess.cs | 15 ++++- .../Kernel/Process/ProcessExecutionContext.cs | 5 -- .../HOS/Kernel/Threading/KScheduler.cs | 1 + .../HOS/Kernel/Threading/KThread.cs | 48 ++++++++++++++ 17 files changed, 157 insertions(+), 144 deletions(-) rename src/{ARMeilleure/State => Ryujinx.HLE/Debugger}/DebugState.cs (73%) diff --git a/src/ARMeilleure/Instructions/NativeInterface.cs b/src/ARMeilleure/Instructions/NativeInterface.cs index 0cd3754f7f..dd53b17c96 100644 --- a/src/ARMeilleure/Instructions/NativeInterface.cs +++ b/src/ARMeilleure/Instructions/NativeInterface.cs @@ -175,6 +175,11 @@ namespace ARMeilleure.Instructions ExecutionContext context = GetContext(); + if (context.DebugStopped == 1) + { + return false; + } + context.CheckInterrupt(); Statistics.ResumeTimer(); diff --git a/src/ARMeilleure/State/ExecutionContext.cs b/src/ARMeilleure/State/ExecutionContext.cs index 47c864bfe7..de4528d11e 100644 --- a/src/ARMeilleure/State/ExecutionContext.cs +++ b/src/ARMeilleure/State/ExecutionContext.cs @@ -101,10 +101,8 @@ namespace ARMeilleure.State private readonly ExceptionCallback _supervisorCallback; private readonly ExceptionCallback _undefinedCallback; - internal int _debugState = (int)DebugState.Running; - internal int _shouldStep = 0; - internal ManualResetEvent _debugHalt = new ManualResetEvent(true); - internal Barrier _stepBarrier = new Barrier(2); + internal int ShouldStep; + internal int DebugStopped; // This is only valid while debugging is enabled. public ulong DebugPc; @@ -160,34 +158,26 @@ namespace ARMeilleure.State public void DebugStop() { - _debugHalt.Reset(); - Interlocked.CompareExchange(ref _debugState, (int)DebugState.Stopping, (int)DebugState.Running); + if (Interlocked.CompareExchange(ref DebugStopped, 1, 0) == 0) + { + RequestInterrupt(); + } } public bool DebugStep() { - if (_debugState != (int)DebugState.Stopped) + if (DebugStopped != 1) { return false; } - _shouldStep = 1; - _debugHalt.Set(); - _stepBarrier.SignalAndWait(); - _debugHalt.Reset(); - _stepBarrier.SignalAndWait(); - + ShouldStep = 1; return true; } public void DebugContinue() { - _debugHalt.Set(); - } - - public DebugState GetDebugState() - { - return (DebugState)_debugState; + Interlocked.CompareExchange(ref DebugStopped, 0, 1); } internal void OnBreak(ulong address, int imm) diff --git a/src/ARMeilleure/Translation/Translator.cs b/src/ARMeilleure/Translation/Translator.cs index 5e25394fae..99db92280d 100644 --- a/src/ARMeilleure/Translation/Translator.cs +++ b/src/ARMeilleure/Translation/Translator.cs @@ -147,21 +147,14 @@ namespace ARMeilleure.Translation { context.DebugPc = ExecuteSingle(context, context.DebugPc); - while (context._debugState != (int)DebugState.Running) + while (context.DebugStopped == 1) { - Interlocked.CompareExchange(ref context._debugState, (int)DebugState.Stopped, (int)DebugState.Stopping); - context._debugHalt.WaitOne(); - if (Interlocked.CompareExchange(ref context._shouldStep, 0, 1) == 1) + if (Interlocked.CompareExchange(ref context.ShouldStep, 0, 1) == 1) { context.DebugPc = Step(context, context.DebugPc); - - context._stepBarrier.SignalAndWait(); - context._stepBarrier.SignalAndWait(); - } - else - { - Interlocked.CompareExchange(ref context._debugState, (int)DebugState.Running, (int)DebugState.Stopped); + context.RequestInterrupt(); } + context.CheckInterrupt(); } } while (context.Running && context.DebugPc != 0); @@ -222,7 +215,7 @@ namespace ARMeilleure.Translation return nextAddr; } - public ulong Step(State.ExecutionContext context, ulong address) + private ulong Step(State.ExecutionContext context, ulong address) { TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true); diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs index e6e33ea6b1..4b6f4d2c09 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs @@ -70,6 +70,8 @@ namespace Ryujinx.Cpu.AppleHv private readonly ICounter _counter; private readonly IHvExecutionContext _shadowContext; private IHvExecutionContext _impl; + private int _shouldStep; + private int _debugStopped; private readonly ExceptionCallbacks _exceptionCallbacks; @@ -131,22 +133,37 @@ namespace Ryujinx.Cpu.AppleHv } /// - public void DebugStop() => _impl.DebugStop(); + public void DebugStop() + { + if (Interlocked.CompareExchange(ref _debugStopped, 1, 0) == 0) + { + RequestInterrupt(); + } + } /// - public bool DebugStep() => _impl.DebugStep(); + public bool DebugStep() + { + if (_debugStopped != 1) + { + return false; + } + + _shouldStep = 1; + return true; + } /// - public void DebugContinue() => _impl.DebugContinue(); - - /// - public DebugState GetDebugState() => _impl.GetDebugState(); + public void DebugContinue() + { + Interlocked.CompareExchange(ref _debugStopped, 0, 1); + } /// public ulong DebugPc { - get => _impl.DebugPc; - set => _impl.DebugPc = value; + get => Pc; + set => Pc = value; } /// @@ -164,6 +181,18 @@ namespace Ryujinx.Cpu.AppleHv while (Running) { + if (Interlocked.CompareExchange(ref _shouldStep, 0, 1) == 1) + { + uint currentEl = Pstate & ~(0xfffffff0); + if (currentEl == 0b0101) + { + HvApi.hv_vcpu_get_sys_reg(vcpu.Handle, HvSysReg.SPSR_EL1, out ulong spsr).ThrowOnError(); + spsr |= (1 << 21); + HvApi.hv_vcpu_set_sys_reg(vcpu.Handle, HvSysReg.SPSR_EL1, spsr); + } + HvApi.hv_vcpu_set_sys_reg(vcpu.Handle, HvSysReg.MDSCR_EL1, 1); + } + HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError(); HvExitReason reason = vcpu.ExitInfo->Reason; @@ -231,6 +260,21 @@ namespace Ryujinx.Cpu.AppleHv SupervisorCallHandler(elr - 4UL, id); vcpu = RentFromPool(memoryManager.AddressSpace, vcpu); break; + case ExceptionClass.SoftwareStepLowerEl: + HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.SPSR_EL1, out ulong spsr).ThrowOnError(); + spsr &= ~((ulong)(1 << 21)); + HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.SPSR_EL1, spsr).ThrowOnError(); + HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.MDSCR_EL1, 0); + ReturnToPool(vcpu); + InterruptHandler(); + vcpu = RentFromPool(memoryManager.AddressSpace, vcpu); + break; + case ExceptionClass.BrkAarch64: + ReturnToPool(vcpu); + BreakHandler(elr, (ushort)esr); + InterruptHandler(); + vcpu = RentFromPool(memoryManager.AddressSpace, vcpu); + break; default: throw new Exception($"Unhandled guest exception {ec}."); } @@ -241,10 +285,7 @@ namespace Ryujinx.Cpu.AppleHv // TODO: Invalidate only the range that was modified? return HvAddressSpace.KernelRegionTlbiEretAddress; } - else - { - return HvAddressSpace.KernelRegionEretAddress; - } + return HvAddressSpace.KernelRegionEretAddress; } private static void DataAbort(MemoryTracking tracking, ulong vcpu, uint esr) diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs index 24eefc42d6..4ea5f276dc 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs @@ -23,8 +23,6 @@ namespace Ryujinx.Cpu.AppleHv private readonly ulong[] _x; private readonly V128[] _v; - public ulong DebugPc { get; set; } - public HvExecutionContextShadow() { _x = new ulong[32]; @@ -55,24 +53,6 @@ namespace Ryujinx.Cpu.AppleHv { } - public void DebugStop() - { - } - - public bool DebugStep() - { - return false; - } - - public void DebugContinue() - { - } - - public DebugState GetDebugState() - { - return DebugState.Stopped; - } - public bool GetAndClearInterruptRequested() { return false; diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs index 03d426331f..4e4b6ef599 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs @@ -13,14 +13,6 @@ namespace Ryujinx.Cpu.AppleHv private static readonly SetSimdFpReg _setSimdFpReg; private static readonly IntPtr _setSimdFpRegNativePtr; - internal int _debugState = (int)DebugState.Running; - internal int _shouldStep = 0; - internal ManualResetEvent _debugHalt = new ManualResetEvent(true); - internal Barrier _stepBarrier = new Barrier(2); - - // This is only valid while debugging is enabled. - public ulong DebugPc { get; set; } - public ulong ThreadUid { get; set; } static HvExecutionContextVcpu() @@ -198,41 +190,6 @@ namespace Ryujinx.Cpu.AppleHv } } - public void DebugStop() - { - _debugHalt.Reset(); - Interlocked.CompareExchange(ref _debugState, (int)DebugState.Stopping, (int)DebugState.Running); - ulong vcpu = _vcpu; - HvApi.hv_vcpus_exit(ref vcpu, 1); - } - - public bool DebugStep() - { - if (_debugState != (int)DebugState.Stopped) - { - return false; - } - - _shouldStep = 1; - _debugHalt.Set(); - _stepBarrier.SignalAndWait(); - _debugHalt.Reset(); - _stepBarrier.SignalAndWait(); - - return true; - } - - public void DebugContinue() - { - _debugHalt.Set(); - HvApi.hv_vcpu_run(_vcpu); - } - - public DebugState GetDebugState() - { - return (DebugState)_debugState; - } - public bool GetAndClearInterruptRequested() { return Interlocked.Exchange(ref _interruptRequested, 0) != 0; diff --git a/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs index f49509db9a..f30030406d 100644 --- a/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs +++ b/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs @@ -42,13 +42,5 @@ namespace Ryujinx.Cpu.AppleHv void RequestInterrupt(); bool GetAndClearInterruptRequested(); - - // TODO: comments - void DebugStop(); - bool DebugStep(); - void DebugContinue(); - DebugState GetDebugState(); - - ulong DebugPc { get; set; } } } diff --git a/src/Ryujinx.Cpu/IExecutionContext.cs b/src/Ryujinx.Cpu/IExecutionContext.cs index 6df28292e0..2f4abdedc9 100644 --- a/src/Ryujinx.Cpu/IExecutionContext.cs +++ b/src/Ryujinx.Cpu/IExecutionContext.cs @@ -118,7 +118,6 @@ namespace Ryujinx.Cpu void DebugStop(); bool DebugStep(); void DebugContinue(); - DebugState GetDebugState(); ulong DebugPc { get; set; } } diff --git a/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs b/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs index 1a8d54c96d..2f3e0d59b2 100644 --- a/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs +++ b/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs @@ -125,9 +125,6 @@ namespace Ryujinx.Cpu.Jit /// public void DebugContinue() => _impl.DebugContinue(); - /// - public DebugState GetDebugState() => _impl.GetDebugState(); - /// public ulong DebugPc { diff --git a/src/ARMeilleure/State/DebugState.cs b/src/Ryujinx.HLE/Debugger/DebugState.cs similarity index 73% rename from src/ARMeilleure/State/DebugState.cs rename to src/Ryujinx.HLE/Debugger/DebugState.cs index 0b00781feb..c2c3878472 100644 --- a/src/ARMeilleure/State/DebugState.cs +++ b/src/Ryujinx.HLE/Debugger/DebugState.cs @@ -1,4 +1,4 @@ -namespace ARMeilleure.State +namespace Ryujinx.HLE.Debugger { public enum DebugState { diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index 2f13e531a2..70877daeaa 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -2,6 +2,8 @@ using ARMeilleure.State; using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Memory; +using Ryujinx.HLE.HOS.Kernel; +using Ryujinx.HLE.HOS.Kernel.Threading; using System; using System.Collections.Concurrent; using System.Linq; @@ -46,8 +48,8 @@ namespace Ryujinx.HLE.Debugger 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 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 void InvalidateCacheRegion(ulong address, ulong size) => Device.System.DebugGetApplicationProcess().InvalidateCacheRegion(address, size); @@ -304,8 +306,8 @@ namespace Ryujinx.HLE.Debugger break; } - IExecutionContext ctx = GetThread(threadId.Value); - if (ctx.GetDebugState() == DebugState.Stopped) + KThread thread = GetThread(threadId.Value); + if (thread.GetDebugState() == DebugState.Stopped) { Reply(ToHex("Stopped")); } @@ -387,7 +389,7 @@ namespace Ryujinx.HLE.Debugger return; } - GetThread(cThread.Value).DebugPc = newPc.Value; + GetThread(cThread.Value).Context.DebugPc = newPc.Value; } foreach (var thread in GetThreads()) @@ -410,7 +412,7 @@ namespace Ryujinx.HLE.Debugger return; } - var ctx = GetThread(gThread.Value); + var ctx = GetThread(gThread.Value).Context; string registers = ""; for (int i = 0; i < GdbRegisterCount; i++) { @@ -428,7 +430,7 @@ namespace Ryujinx.HLE.Debugger return; } - var ctx = GetThread(gThread.Value); + var ctx = GetThread(gThread.Value).Context; for (int i = 0; i < GdbRegisterCount; i++) { if (!GdbWriteRegister(ctx, i, ss)) @@ -513,7 +515,7 @@ namespace Ryujinx.HLE.Debugger return; } - var ctx = GetThread(gThread.Value); + var ctx = GetThread(gThread.Value).Context; string result = GdbReadRegister(ctx, gdbRegId); if (result != null) { @@ -533,7 +535,7 @@ namespace Ryujinx.HLE.Debugger return; } - var ctx = GetThread(gThread.Value); + var ctx = GetThread(gThread.Value).Context; if (GdbWriteRegister(ctx, gdbRegId, ss) && ss.IsEmpty()) { ReplyOK(); @@ -552,15 +554,16 @@ namespace Ryujinx.HLE.Debugger return; } - var ctx = GetThread(cThread.Value); + var thread = GetThread(cThread.Value); if (newPc.HasValue) { - ctx.DebugPc = newPc.Value; + thread.Context.DebugPc = newPc.Value; } - ctx.DebugStep(); - Reply($"T05thread:{ctx.ThreadUid:x};"); + thread.DebugStep(); + + Reply($"T05thread:{thread.ThreadUid:x};"); } private void CommandIsAlive(ulong? threadId) @@ -721,7 +724,9 @@ namespace Ryujinx.HLE.Debugger public void ThreadBreak(IExecutionContext ctx, ulong address, int imm) { - ctx.DebugStop(); + KThread thread = GetThread(ctx.ThreadUid); + + thread.DebugStop(); Logger.Notice.Print(LogClass.GdbStub, $"Break hit on thread {ctx.ThreadUid} at pc {address:x016}"); diff --git a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs index e317471463..5138929605 100644 --- a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs +++ b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs @@ -1,12 +1,13 @@ using Ryujinx.Memory; +using Ryujinx.HLE.HOS.Kernel.Threading; namespace Ryujinx.HLE.Debugger { - public interface IDebuggableProcess + internal interface IDebuggableProcess { void DebugStopAllThreads(); ulong[] DebugGetThreadUids(); - Ryujinx.Cpu.IExecutionContext DebugGetThreadContext(ulong threadUid); + public KThread DebugGetThread(ulong threadUid); 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 a195468116..4f9bff96be 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -475,7 +475,7 @@ namespace Ryujinx.HLE.HOS IsPaused = pause; } - public IDebuggableProcess DebugGetApplicationProcess() + internal IDebuggableProcess DebugGetApplicationProcess() { lock (KernelContext.Processes) { diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 31a8ab9162..261ec649c7 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using ExceptionCallback = Ryujinx.Cpu.ExceptionCallback; namespace Ryujinx.HLE.HOS.Kernel.Process { @@ -748,6 +749,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { KThread currentThread = KernelStatic.GetCurrentThread(); + if (currentThread.GetDebugState() != DebugState.Running) + { + KernelContext.CriticalSection.Enter(); + currentThread.Suspend(ThreadSchedState.ThreadPauseFlag); + currentThread.DebugHalt.Set(); + KernelContext.CriticalSection.Leave(); + } + if (currentThread.Context.Running && currentThread.Owner != null && currentThread.GetUserDisableCount() != 0 && @@ -1200,7 +1209,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { foreach (KThread thread in _parent._threads) { - thread.Context.DebugStop(); + thread.DebugStop(); } } } @@ -1213,11 +1222,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - public Ryujinx.Cpu.IExecutionContext DebugGetThreadContext(ulong threadUid) + public KThread DebugGetThread(ulong threadUid) { lock (_parent._threadingLock) { - return _parent._threads.FirstOrDefault(x => x.ThreadUid == threadUid)?.Context; + return _parent._threads.FirstOrDefault(x => x.ThreadUid == threadUid); } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs index 7dc1178810..2cfdbd6873 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs @@ -48,11 +48,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { } - public DebugState GetDebugState() - { - return DebugState.Stopped; - } - public void StopRunning() { Running = false; diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs index 8ef77902c2..dce99ec882 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs @@ -296,6 +296,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++) diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 4e71faf01a..ed4924d3f1 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -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,9 @@ 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) { WaitSyncObjects = new KSynchronizationObject[MaxWaitSyncObjects]; @@ -1432,5 +1436,49 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { Owner.CpuMemory.Write(_tlsAddress + TlsUserInterruptFlagOffset, 0); } + + public bool DebugStep() + { + if (_debugState != (int)DebugState.Stopped || !Context.DebugStep()) + { + return false; + } + + DebugHalt.Reset(); + SetActivity(false); + DebugHalt.WaitOne(); + return true; + } + + public void DebugStop() + { + if (Interlocked.CompareExchange(ref _debugState, (int)DebugState.Stopping, + (int)DebugState.Running) != (int)DebugState.Running) + { + return; + } + + Context.DebugStop(); + DebugHalt.WaitOne(); + DebugHalt.Reset(); + _debugState = (int)DebugState.Stopped; + } + + public void DebugContinue() + { + if (Interlocked.CompareExchange(ref _debugState, (int)DebugState.Running, + (int)DebugState.Stopped) != (int)DebugState.Stopped) + { + return; + } + + Context.DebugContinue(); + SetActivity(false); + } + + public DebugState GetDebugState() + { + return (DebugState)_debugState; + } } }