diff --git a/src/ARMeilleure/State/DebugState.cs b/src/ARMeilleure/State/DebugState.cs new file mode 100644 index 000000000..dd05ddb61 --- /dev/null +++ b/src/ARMeilleure/State/DebugState.cs @@ -0,0 +1,9 @@ +namespace ARMeilleure.State +{ + enum DebugState + { + Running, + Stopping, + Stopped, + } +} diff --git a/src/ARMeilleure/State/ExecutionContext.cs b/src/ARMeilleure/State/ExecutionContext.cs index ce10a591c..2d57df725 100644 --- a/src/ARMeilleure/State/ExecutionContext.cs +++ b/src/ARMeilleure/State/ExecutionContext.cs @@ -1,5 +1,8 @@ using ARMeilleure.Memory; using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Threading; namespace ARMeilleure.State { @@ -96,6 +99,14 @@ 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); + + // This is only valid while debugging is enabled. + public ulong DebugPc; + public ExecutionContext( IJitMemoryAllocator allocator, ICounter counter, @@ -145,6 +156,33 @@ namespace ARMeilleure.State _interrupted = true; } + public void DebugStop() + { + _debugHalt.Reset(); + Interlocked.CompareExchange(ref _debugState, (int)DebugState.Stopping, (int)DebugState.Running); + } + + 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(); + } + internal void OnBreak(ulong address, int imm) { _breakCallback?.Invoke(this, address, imm); diff --git a/src/Ryujinx.Cpu/AppleHv/DebugState.cs b/src/Ryujinx.Cpu/AppleHv/DebugState.cs new file mode 100644 index 000000000..5a5f2ba03 --- /dev/null +++ b/src/Ryujinx.Cpu/AppleHv/DebugState.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Cpu.AppleHv +{ + enum DebugState + { + Running, + Stopping, + Stopped, + } +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs index fc2b76d15..8ce358898 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs @@ -127,6 +127,15 @@ namespace Ryujinx.Cpu.AppleHv return Interlocked.Exchange(ref _interruptRequested, 0) != 0; } + /// + public void DebugStop() => _impl.DebugStop(); + + /// + public bool DebugStep() => _impl.DebugStep(); + + /// + public void DebugContinue() => _impl.DebugContinue(); + /// public void StopRunning() { diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs index 6ce8e1800..b4e96b561 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs @@ -46,5 +46,27 @@ namespace Ryujinx.Cpu.AppleHv { _v[index] = value; } + + public void RequestInterrupt() + { + } + + public void DebugStop() + { + } + + public bool DebugStep() + { + return false; + } + + public void DebugContinue() + { + } + + public bool GetAndClearInterruptRequested() + { + return false; + } } } diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs index bb232940d..a55a2aba2 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs @@ -2,11 +2,10 @@ using ARMeilleure.State; using Ryujinx.Memory; using System; using System.Runtime.InteropServices; -using System.Runtime.Versioning; +using System.Threading; namespace Ryujinx.Cpu.AppleHv { - [SupportedOSPlatform("macos")] class HvExecutionContextVcpu : IHvExecutionContext { private static readonly MemoryBlock _setSimdFpRegFuncMem; @@ -14,6 +13,14 @@ 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; + static HvExecutionContextVcpu() { // .NET does not support passing vectors by value, so we need to pass a pointer and use a native @@ -136,6 +143,7 @@ namespace Ryujinx.Cpu.AppleHv } private readonly ulong _vcpu; + private int _interruptRequested; public HvExecutionContextVcpu(ulong vcpu) { @@ -181,8 +189,46 @@ namespace Ryujinx.Cpu.AppleHv public void RequestInterrupt() { + if (Interlocked.Exchange(ref _interruptRequested, 1) == 0) + { + ulong vcpu = _vcpu; + HvApi.hv_vcpus_exit(ref vcpu, 1); + } + } + + 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 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 54b73acc6..b385cd78e 100644 --- a/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs +++ b/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs @@ -39,5 +39,13 @@ namespace Ryujinx.Cpu.AppleHv SetV(i, context.GetV(i)); } } + + void RequestInterrupt(); + bool GetAndClearInterruptRequested(); + + // TODO: comments + void DebugStop(); + bool DebugStep(); + void DebugContinue(); } } diff --git a/src/Ryujinx.Cpu/IExecutionContext.cs b/src/Ryujinx.Cpu/IExecutionContext.cs index c38210800..ad07a2766 100644 --- a/src/Ryujinx.Cpu/IExecutionContext.cs +++ b/src/Ryujinx.Cpu/IExecutionContext.cs @@ -108,5 +108,10 @@ namespace Ryujinx.Cpu /// If you only need to pause the thread temporarily, use instead. /// void StopRunning(); + + // TODO: comments + void DebugStop(); + bool DebugStep(); + void DebugContinue(); } } diff --git a/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs b/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs index f15486e68..a3224c366 100644 --- a/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs +++ b/src/Ryujinx.Cpu/Jit/JitExecutionContext.cs @@ -109,6 +109,15 @@ namespace Ryujinx.Cpu.Jit _impl.RequestInterrupt(); } + /// + public void DebugStop() => _impl.DebugStop(); + + /// + public bool DebugStep() => _impl.DebugStep(); + + /// + public void DebugContinue() => _impl.DebugContinue(); + /// public void StopRunning() { diff --git a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs new file mode 100644 index 000000000..c86d4acd5 --- /dev/null +++ b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs @@ -0,0 +1,12 @@ +using Ryujinx.Memory; + +namespace Ryujinx.HLE.Debugger +{ + public interface IDebuggableProcess + { + public void DebugStopAllThreads(); + public ulong[] DebugGetThreadUids(); + public Ryujinx.Cpu.IExecutionContext DebugGetThreadContext(ulong threadUid); + public IVirtualMemoryManager CpuMemory { get; } + } +} diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index 64b08e309..69086c0bc 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -473,5 +473,13 @@ namespace Ryujinx.HLE.HOS } IsPaused = pause; } + + public Debugger.IDebuggableProcess DebugGetApplicationProcess() + { + lock (KernelContext.Processes) + { + return KernelContext.Processes.Values.Where(x => x.IsApplication).First(); + } + } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 422f03c64..0c0004be5 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -14,7 +14,7 @@ using System.Threading; namespace Ryujinx.HLE.HOS.Kernel.Process { - class KProcess : KSynchronizationObject + class KProcess : KSynchronizationObject, Debugger.IDebuggableProcess { public const uint KernelVersionMajor = 10; public const uint KernelVersionMinor = 4; @@ -1175,5 +1175,32 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { return Capabilities.IsSvcPermitted(svcId); } + + public void DebugStopAllThreads() + { + lock (_threadingLock) + { + foreach (KThread thread in _threads) + { + thread.Context.DebugStop(); + } + } + } + + public ulong[] DebugGetThreadUids() + { + lock (_threadingLock) + { + return _threads.Select(x => x.ThreadUid).ToArray(); + } + } + + public Ryujinx.Cpu.IExecutionContext DebugGetThreadContext(ulong threadUid) + { + lock (_threadingLock) + { + return _threads.Where(x => x.ThreadUid == threadUid).FirstOrDefault()?.Context; + } + } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs index b8118fbb4..6050f88f2 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs @@ -31,6 +31,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { } + public void DebugStop() + { + } + + public bool DebugStep() + { + return false; + } + + public void DebugContinue() + { + } + public void StopRunning() { Running = false;