Refactor stepping code, fix stepping over a breakpoint

This commit is contained in:
svc64 2023-12-13 19:11:57 +02:00
parent cc32ac251b
commit 6b74bcec7c
10 changed files with 74 additions and 62 deletions

View file

@ -98,18 +98,19 @@ namespace ARMeilleure.State
private readonly ExceptionCallbackNoArgs _interruptCallback; private readonly ExceptionCallbackNoArgs _interruptCallback;
private readonly ExceptionCallback _breakCallback; private readonly ExceptionCallback _breakCallback;
private readonly ExceptionCallbackNoArgs _stepCallback;
private readonly ExceptionCallback _supervisorCallback; private readonly ExceptionCallback _supervisorCallback;
private readonly ExceptionCallback _undefinedCallback; private readonly ExceptionCallback _undefinedCallback;
internal int ShouldStep; internal int ShouldStep;
public ulong DebugPc { get; set; } public ulong DebugPc { get; set; }
public Barrier StepBarrier { get; }
public ExecutionContext( public ExecutionContext(
IJitMemoryAllocator allocator, IJitMemoryAllocator allocator,
ICounter counter, ICounter counter,
ExceptionCallbackNoArgs interruptCallback = null, ExceptionCallbackNoArgs interruptCallback = null,
ExceptionCallback breakCallback = null, ExceptionCallback breakCallback = null,
ExceptionCallbackNoArgs stepCallback = null,
ExceptionCallback supervisorCallback = null, ExceptionCallback supervisorCallback = null,
ExceptionCallback undefinedCallback = null) ExceptionCallback undefinedCallback = null)
{ {
@ -117,11 +118,11 @@ namespace ARMeilleure.State
_counter = counter; _counter = counter;
_interruptCallback = interruptCallback; _interruptCallback = interruptCallback;
_breakCallback = breakCallback; _breakCallback = breakCallback;
_stepCallback = stepCallback;
_supervisorCallback = supervisorCallback; _supervisorCallback = supervisorCallback;
_undefinedCallback = undefinedCallback; _undefinedCallback = undefinedCallback;
Running = true; Running = true;
StepBarrier = new Barrier(2);
_nativeContext.SetCounter(MinCountForCheck); _nativeContext.SetCounter(MinCountForCheck);
} }
@ -155,6 +156,11 @@ namespace ARMeilleure.State
Interrupted = true; Interrupted = true;
} }
public void StepHandler()
{
_stepCallback.Invoke(this);
}
public void RequestDebugStep() public void RequestDebugStep()
{ {
Interlocked.Exchange(ref ShouldStep, 1); Interlocked.Exchange(ref ShouldStep, 1);

View file

@ -148,8 +148,7 @@ namespace ARMeilleure.Translation
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.DebugPc = Step(context, context.DebugPc);
context.StepBarrier.SignalAndWait(); context.StepHandler();
context.StepBarrier.SignalAndWait();
} }
else else
{ {

View file

@ -93,7 +93,6 @@ namespace Ryujinx.Cpu.AppleHv
_shadowContext = new HvExecutionContextShadow(); _shadowContext = new HvExecutionContextShadow();
_impl = _shadowContext; _impl = _shadowContext;
_exceptionCallbacks = exceptionCallbacks; _exceptionCallbacks = exceptionCallbacks;
StepBarrier = new(2);
Running = true; Running = true;
} }
@ -119,6 +118,11 @@ namespace Ryujinx.Cpu.AppleHv
_exceptionCallbacks.BreakCallback?.Invoke(this, address, imm); _exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
} }
private void StepHandler()
{
_exceptionCallbacks.StepCallback?.Invoke(this);
}
private void SupervisorCallHandler(ulong address, int imm) private void SupervisorCallHandler(ulong address, int imm)
{ {
_exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm); _exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
@ -167,8 +171,6 @@ namespace Ryujinx.Cpu.AppleHv
} }
} }
public Barrier StepBarrier { get; }
/// <inheritdoc/> /// <inheritdoc/>
public void StopRunning() public void StopRunning()
{ {
@ -272,9 +274,7 @@ namespace Ryujinx.Cpu.AppleHv
HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.SPSR_EL1, spsr).ThrowOnError(); HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.SPSR_EL1, spsr).ThrowOnError();
HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.MDSCR_EL1, 0); HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.MDSCR_EL1, 0);
ReturnToPool(vcpu); ReturnToPool(vcpu);
StepBarrier.SignalAndWait(); StepHandler();
StepBarrier.SignalAndWait();
InterruptHandler();
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu); vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
break; break;
case ExceptionClass.BrkAarch64: case ExceptionClass.BrkAarch64:

View file

@ -29,6 +29,11 @@ namespace Ryujinx.Cpu
/// </summary> /// </summary>
public readonly ExceptionCallback BreakCallback; public readonly ExceptionCallback BreakCallback;
/// <summary>
/// Handler for CPU software interrupts caused by single-stepping.
/// </summary>
public readonly ExceptionCallbackNoArgs StepCallback;
/// <summary> /// <summary>
/// Handler for CPU software interrupts caused by the Arm SVC instruction. /// Handler for CPU software interrupts caused by the Arm SVC instruction.
/// </summary> /// </summary>
@ -47,16 +52,19 @@ namespace Ryujinx.Cpu
/// </remarks> /// </remarks>
/// <param name="interruptCallback">Handler for CPU interrupts triggered using <see cref="IExecutionContext.RequestInterrupt"/></param> /// <param name="interruptCallback">Handler for CPU interrupts triggered using <see cref="IExecutionContext.RequestInterrupt"/></param>
/// <param name="breakCallback">Handler for CPU software interrupts caused by the Arm BRK instruction</param> /// <param name="breakCallback">Handler for CPU software interrupts caused by the Arm BRK instruction</param>
/// <param name="stepCallback">Handler for CPU software interrupts caused by single-stepping</param>
/// <param name="supervisorCallback">Handler for CPU software interrupts caused by the Arm SVC instruction</param> /// <param name="supervisorCallback">Handler for CPU software interrupts caused by the Arm SVC instruction</param>
/// <param name="undefinedCallback">Handler for CPU software interrupts caused by any undefined Arm instruction</param> /// <param name="undefinedCallback">Handler for CPU software interrupts caused by any undefined Arm instruction</param>
public ExceptionCallbacks( public ExceptionCallbacks(
ExceptionCallbackNoArgs interruptCallback = null, ExceptionCallbackNoArgs interruptCallback = null,
ExceptionCallback breakCallback = null, ExceptionCallback breakCallback = null,
ExceptionCallbackNoArgs stepCallback = null,
ExceptionCallback supervisorCallback = null, ExceptionCallback supervisorCallback = null,
ExceptionCallback undefinedCallback = null) ExceptionCallback undefinedCallback = null)
{ {
InterruptCallback = interruptCallback; InterruptCallback = interruptCallback;
BreakCallback = breakCallback; BreakCallback = breakCallback;
StepCallback = stepCallback;
SupervisorCallback = supervisorCallback; SupervisorCallback = supervisorCallback;
UndefinedCallback = undefinedCallback; UndefinedCallback = undefinedCallback;
} }

View file

@ -121,20 +121,10 @@ namespace Ryujinx.Cpu
/// <remarks> /// <remarks>
/// The thread might not pause immediately. /// The thread might not pause immediately.
/// One must not assume that guest code is no longer being executed by the thread after calling this function. /// One must not assume that guest code is no longer being executed by the thread after calling this function.
/// After single stepping, the thread should signal and wait on <see cref="StepBarrier"/> twice to allow /// After single stepping, the thread should call call <see cref="ExceptionCallbacks.StepCallback"/>.
/// changing the thread state after stepping.
/// </remarks> /// </remarks>
void RequestDebugStep(); void RequestDebugStep();
/// <summary>
/// Step barrier
/// </summary>
/// <remarks>
/// The participant count should be 2.
/// Should be signaled and waited on twice after single-stepping.
/// </remarks>
Barrier StepBarrier { get; }
/// <summary> /// <summary>
/// Current Program Counter (for debugging). /// Current Program Counter (for debugging).
/// </summary> /// </summary>

View file

@ -74,6 +74,7 @@ namespace Ryujinx.Cpu.Jit
counter, counter,
InterruptHandler, InterruptHandler,
BreakHandler, BreakHandler,
StepHandler,
SupervisorCallHandler, SupervisorCallHandler,
UndefinedHandler); UndefinedHandler);
@ -102,6 +103,11 @@ namespace Ryujinx.Cpu.Jit
_exceptionCallbacks.BreakCallback?.Invoke(this, address, imm); _exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
} }
private void StepHandler(ExecutionContext context)
{
_exceptionCallbacks.StepCallback?.Invoke(this);
}
private void SupervisorCallHandler(ExecutionContext context, ulong address, int imm) private void SupervisorCallHandler(ExecutionContext context, ulong address, int imm)
{ {
_exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm); _exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
@ -128,9 +134,6 @@ namespace Ryujinx.Cpu.Jit
set => _impl.DebugPc = value; set => _impl.DebugPc = value;
} }
/// <inheritdoc/>
public Barrier StepBarrier => _impl.StepBarrier;
/// <inheritdoc/> /// <inheritdoc/>
public void StopRunning() public void StopRunning()
{ {

View file

@ -737,34 +737,17 @@ namespace Ryujinx.HLE.Debugger
} }
} }
public void ThreadBreak(IExecutionContext ctx, ulong address, int imm) public void BreakHandler(IExecutionContext ctx, ulong address, int imm)
{ {
Logger.Notice.Print(LogClass.GdbStub, $"Break hit on thread {ctx.ThreadUid} at pc {address:x016}"); Logger.Notice.Print(LogClass.GdbStub, $"Break hit on thread {ctx.ThreadUid} at pc {address:x016}");
Messages.Add(new ThreadBreakMessage(ctx, address, imm)); Messages.Add(new ThreadBreakMessage(ctx, address, imm));
DebugProcess.DebugInterruptHandler(ctx);
KThread currentThread = DebugProcess.GetThread(ctx.ThreadUid);
if (currentThread.Context.Running &&
currentThread.Owner != null &&
currentThread.GetUserDisableCount() != 0 &&
currentThread.Owner.PinnedThreads[currentThread.CurrentCore] == null)
{
KernelContext.CriticalSection.Enter();
currentThread.Owner.PinThread(currentThread);
currentThread.SetUserInterruptFlag();
KernelContext.CriticalSection.Leave();
} }
if (currentThread.IsSchedulable) public void StepHandler(IExecutionContext ctx)
{ {
KernelContext.Schedulers[currentThread.CurrentCore].Schedule(); DebugProcess.DebugInterruptHandler(ctx);
}
currentThread.HandlePostSyscall();
} }
} }
} }

View file

@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Memory; using Ryujinx.Memory;
namespace Ryujinx.HLE.Debugger namespace Ryujinx.HLE.Debugger
@ -11,6 +12,7 @@ namespace Ryujinx.HLE.Debugger
KThread GetThread(ulong threadUid); KThread GetThread(ulong threadUid);
DebugState GetDebugState(); DebugState GetDebugState();
ulong[] GetThreadUids(); ulong[] GetThreadUids();
public void DebugInterruptHandler(IExecutionContext ctx);
IVirtualMemoryManager CpuMemory { get; } IVirtualMemoryManager CpuMemory { get; }
void InvalidateCacheRegion(ulong address, ulong size); void InvalidateCacheRegion(ulong address, ulong size);
} }

View file

@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using ExceptionCallback = Ryujinx.Cpu.ExceptionCallback; using ExceptionCallback = Ryujinx.Cpu.ExceptionCallback;
using ExceptionCallbackNoArgs = Ryujinx.Cpu.ExceptionCallbackNoArgs;
namespace Ryujinx.HLE.HOS.Kernel.Process namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
@ -733,15 +734,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public IExecutionContext CreateExecutionContext() public IExecutionContext CreateExecutionContext()
{ {
ExceptionCallback breakCallback = null; ExceptionCallback breakCallback = null;
ExceptionCallbackNoArgs stepCallback = null;
if (KernelContext.Device.Configuration.EnableGdbStub) if (KernelContext.Device.Configuration.EnableGdbStub)
{ {
breakCallback = KernelContext.Device.Debugger.ThreadBreak; breakCallback = KernelContext.Device.Debugger.BreakHandler;
stepCallback = KernelContext.Device.Debugger.StepHandler;
} }
return Context?.CreateExecutionContext(new ExceptionCallbacks( return Context?.CreateExecutionContext(new ExceptionCallbacks(
InterruptHandler, InterruptHandler,
breakCallback, breakCallback,
stepCallback,
KernelContext.SyscallHandler.SvcCall, KernelContext.SyscallHandler.SvcCall,
UndefinedInstructionHandler)); UndefinedInstructionHandler));
} }
@ -1189,14 +1193,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private class DebuggerInterface : IDebuggableProcess private class DebuggerInterface : IDebuggableProcess
{ {
private Barrier StepBarrier;
private readonly KProcess _parent; private readonly KProcess _parent;
private readonly KernelContext KernelContext; private readonly KernelContext _kernelContext;
private KThread steppingThread;
private int _debugState = (int)DebugState.Running; private int _debugState = (int)DebugState.Running;
public DebuggerInterface(KProcess p) public DebuggerInterface(KProcess p)
{ {
_parent = p; _parent = p;
KernelContext = p.KernelContext; _kernelContext = p.KernelContext;
StepBarrier = new(2);
} }
public void DebugStop() public void DebugStop()
@ -1207,7 +1214,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return; return;
} }
KernelContext.CriticalSection.Enter(); _kernelContext.CriticalSection.Enter();
lock (_parent._threadingLock) lock (_parent._threadingLock)
{ {
foreach (KThread thread in _parent._threads) foreach (KThread thread in _parent._threads)
@ -1219,7 +1226,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
} }
_debugState = (int)DebugState.Stopped; _debugState = (int)DebugState.Stopped;
KernelContext.CriticalSection.Leave(); _kernelContext.CriticalSection.Leave();
} }
public void DebugContinue() public void DebugContinue()
@ -1230,7 +1237,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return; return;
} }
KernelContext.CriticalSection.Enter(); _kernelContext.CriticalSection.Enter();
lock (_parent._threadingLock) lock (_parent._threadingLock)
{ {
foreach (KThread thread in _parent._threads) foreach (KThread thread in _parent._threads)
@ -1238,7 +1245,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
thread.Resume(ThreadSchedState.ThreadPauseFlag); thread.Resume(ThreadSchedState.ThreadPauseFlag);
} }
} }
KernelContext.CriticalSection.Leave(); _kernelContext.CriticalSection.Leave();
} }
public bool DebugStep(KThread target) public bool DebugStep(KThread target)
@ -1247,7 +1254,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
return false; return false;
} }
KernelContext.CriticalSection.Enter(); _kernelContext.CriticalSection.Enter();
steppingThread = target;
bool waiting = target.MutexOwner != null || target.WaitingSync || target.WaitingInArbitration; bool waiting = target.MutexOwner != null || target.WaitingSync || target.WaitingInArbitration;
target.Context.RequestDebugStep(); target.Context.RequestDebugStep();
if (waiting) if (waiting)
@ -1264,12 +1272,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
target.Resume(ThreadSchedState.ThreadPauseFlag); target.Resume(ThreadSchedState.ThreadPauseFlag);
} }
KernelContext.CriticalSection.Leave(); _kernelContext.CriticalSection.Leave();
target.Context.StepBarrier.SignalAndWait(); StepBarrier.SignalAndWait();
target.Context.StepBarrier.SignalAndWait();
KernelContext.CriticalSection.Enter(); _kernelContext.CriticalSection.Enter();
steppingThread = null;
if (waiting) if (waiting)
{ {
lock (_parent._threadingLock) lock (_parent._threadingLock)
@ -1284,7 +1292,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
target.Suspend(ThreadSchedState.ThreadPauseFlag); target.Suspend(ThreadSchedState.ThreadPauseFlag);
} }
KernelContext.CriticalSection.Leave(); _kernelContext.CriticalSection.Leave();
StepBarrier.SignalAndWait();
return true; return true;
} }
@ -1309,6 +1318,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
} }
} }
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 IVirtualMemoryManager CpuMemory { get { return _parent.CpuMemory; } }
public void InvalidateCacheRegion(ulong address, ulong size) public void InvalidateCacheRegion(ulong address, ulong size)

View file

@ -25,7 +25,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private readonly ulong[] _x = new ulong[32]; private readonly ulong[] _x = new ulong[32];
public ulong DebugPc { get; set; } public ulong DebugPc { get; set; }
public Barrier StepBarrier { get; }
public ulong GetX(int index) => _x[index]; public ulong GetX(int index) => _x[index];
public void SetX(int index, ulong value) => _x[index] = value; public void SetX(int index, ulong value) => _x[index] = value;