Refactor the debugger interface
Also support stepping through blocking syscalls
This commit is contained in:
parent
0f50273d4f
commit
e3b8060417
5 changed files with 109 additions and 98 deletions
|
@ -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 &&
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<KThread>();
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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<ushort>(_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue