diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index c94f142cd..015fbf8fb 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -411,13 +411,13 @@ namespace Ryujinx.HLE.Debugger break; } - if (DebugProcess.GetDebugState() == DebugState.Stopped) + if (DebugProcess.IsThreadPaused(DebugProcess.GetThread(threadId.Value))) { - Reply(ToHex("Stopped")); + Reply(ToHex("Paused")); } else { - Reply(ToHex("Not stopped")); + Reply(ToHex("Running")); } break; } @@ -625,6 +625,8 @@ namespace Ryujinx.HLE.Debugger threadActionMap[thread.ThreadUid] = new VContPendingAction(VContAction.None); } + VContAction defaultAction = VContAction.None; + // For each inferior thread, the *leftmost* action with a matching thread-id is applied. for (int i = rawActions.Length - 1; i >= 0; i--) { @@ -642,6 +644,7 @@ namespace Ryujinx.HLE.Debugger _ => VContAction.None }; + // Note: We don't support signals yet. ushort? signal = null; if (cmd == 'C' || cmd == 'S') { @@ -666,12 +669,17 @@ namespace Ryujinx.HLE.Debugger { threadActionMap[row.Key] = new VContPendingAction(action, signal); } + + if (action == VContAction.Continue) { + defaultAction = action; + } else { + Logger.Warning?.Print(LogClass.GdbStub, $"Received vCont command with unsupported default action: {rawAction}"); + } } } bool hasError = false; - // TODO: We don't support stop or continue yet, and we don't support signals. foreach (var (threadUid, action) in threadActionMap) { if (action.Action == VContAction.Step) @@ -683,10 +691,20 @@ namespace Ryujinx.HLE.Debugger } } - // If all threads are set to continue, continue the process. + // If we receive "vCont;c", just continue the process. + // If we receive something like "vCont;c:2e;c:2f" (IDA Pro will send commands like this), continue these threads. + // For "vCont;s:2f;c", we only step thread 2f, and do not continue other threads. (Is this correct?) if (threadActionMap.Values.All(a => a.Action == VContAction.Continue)) { DebugProcess.DebugContinue(); + } else if (defaultAction == VContAction.None) { + foreach (var (threadUid, action) in threadActionMap) + { + if (action.Action == VContAction.Continue) + { + DebugProcess.DebugContinue(DebugProcess.GetThread(threadUid)); + } + } } if (hasError) @@ -716,7 +734,7 @@ namespace Ryujinx.HLE.Debugger foreach (var thread in GetThreads()) { string threadName = System.Security.SecurityElement.Escape(thread.GetThreadName()); - sb.Append($"\n"); + sb.Append($"{(DebugProcess.IsThreadPaused(thread) ? "Paused" : "Running")}\n"); } sb.Append(""); diff --git a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs index 273a1147f..0896f25d2 100644 --- a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs +++ b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs @@ -8,9 +8,11 @@ namespace Ryujinx.HLE.Debugger { void DebugStop(); void DebugContinue(); + void DebugContinue(KThread thread); bool DebugStep(KThread thread); KThread GetThread(ulong threadUid); DebugState GetDebugState(); + bool IsThreadPaused(KThread thread); ulong[] GetThreadUids(); public void DebugInterruptHandler(IExecutionContext ctx); IVirtualMemoryManager CpuMemory { get; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index c24d5c3cc..0a57f5bc6 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -1257,12 +1257,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _kernelContext.CriticalSection.Leave(); } + public void DebugContinue(KThread target) + { + Interlocked.Exchange(ref _parent.debugState, (int)DebugState.Running); + + _kernelContext.CriticalSection.Enter(); + lock (_parent._threadingLock) + { + target.Resume(ThreadSchedState.ThreadPauseFlag); + } + _kernelContext.CriticalSection.Leave(); + } + public bool DebugStep(KThread target) { - if (_parent.debugState != (int)DebugState.Stopped) + if (!IsThreadPaused(target)) { return false; } + _kernelContext.CriticalSection.Enter(); steppingThread = target; bool waiting = target.MutexOwner != null || target.WaitingSync || target.WaitingInArbitration; @@ -1322,6 +1335,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return (DebugState)_parent.debugState; } + public bool IsThreadPaused(KThread target) + { + return (target.SchedFlags & ThreadSchedState.ThreadPauseFlag) != 0; + } + public ulong[] GetThreadUids() { lock (_parent._threadingLock)