IDebuggableProcess: Create and expose interface to enable implementation of debugging tools

# Conflicts:
#	src/ARMeilleure/State/DebugState.cs
#	src/ARMeilleure/State/ExecutionContext.cs
TODO: Fix comments. I also tried implementing AppleHV primitives here (it's actually broken).
This commit is contained in:
merry 2022-02-08 18:57:32 +00:00 committed by svc64
parent b45a81458a
commit 39a3ba8329
13 changed files with 218 additions and 3 deletions

View file

@ -0,0 +1,9 @@
namespace ARMeilleure.State
{
enum DebugState
{
Running,
Stopping,
Stopped,
}
}

View file

@ -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);

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Cpu.AppleHv
{
enum DebugState
{
Running,
Stopping,
Stopped,
}
}

View file

@ -127,6 +127,15 @@ namespace Ryujinx.Cpu.AppleHv
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
}
/// <inheritdoc/>
public void DebugStop() => _impl.DebugStop();
/// <inheritdoc/>
public bool DebugStep() => _impl.DebugStep();
/// <inheritdoc/>
public void DebugContinue() => _impl.DebugContinue();
/// <inheritdoc/>
public void StopRunning()
{

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}

View file

@ -108,5 +108,10 @@ namespace Ryujinx.Cpu
/// If you only need to pause the thread temporarily, use <see cref="RequestInterrupt"/> instead.
/// </remarks>
void StopRunning();
// TODO: comments
void DebugStop();
bool DebugStep();
void DebugContinue();
}
}

View file

@ -109,6 +109,15 @@ namespace Ryujinx.Cpu.Jit
_impl.RequestInterrupt();
}
/// <inheritdoc/>
public void DebugStop() => _impl.DebugStop();
/// <inheritdoc/>
public bool DebugStep() => _impl.DebugStep();
/// <inheritdoc/>
public void DebugContinue() => _impl.DebugContinue();
/// <inheritdoc/>
public void StopRunning()
{

View file

@ -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; }
}
}

View file

@ -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();
}
}
}
}

View file

@ -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;
}
}
}
}

View file

@ -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;