diff --git a/src/xenia/apu/audio_system.cc b/src/xenia/apu/audio_system.cc index 45b1f4709..897c7d939 100644 --- a/src/xenia/apu/audio_system.cc +++ b/src/xenia/apu/audio_system.cc @@ -100,6 +100,8 @@ X_STATUS AudioSystem::Setup(kernel::KernelState* kernel_state) { WorkerThreadMain(); return 0; })); + // As we run audio callbacks the debugger must be able to suspend us. + worker_thread_->set_can_debugger_suspend(true); worker_thread_->set_name("Audio Worker"); worker_thread_->Create(); diff --git a/src/xenia/debug/debugger.cc b/src/xenia/debug/debugger.cc index adac2368f..8a773150a 100644 --- a/src/xenia/debug/debugger.cc +++ b/src/xenia/debug/debugger.cc @@ -225,7 +225,7 @@ bool Debugger::SuspendAllThreads() { thread_info->state == ThreadExecutionInfo::State::kExited) { // Thread is dead and cannot be suspended - ignore. continue; - } else if (!thread_info->thread->is_guest_thread()) { + } else if (!thread_info->thread->can_debugger_suspend()) { // Thread is a host thread, and we aren't suspending those (for now). continue; } else if (XThread::IsInThread() && @@ -266,7 +266,7 @@ bool Debugger::ResumeAllThreads() { thread_info->state == ThreadExecutionInfo::State::kExited) { // Thread is dead and cannot be resumed - ignore. continue; - } else if (!thread_info->thread->is_guest_thread()) { + } else if (!thread_info->thread->can_debugger_suspend()) { // Thread is a host thread, and we aren't suspending those (for now). continue; } else if (XThread::IsInThread() && @@ -295,7 +295,7 @@ void Debugger::UpdateThreadExecutionStates(uint32_t override_handle, // Grab PPC context. // Note that this is only up to date if --store_all_context_values is // enabled (or --debug). - if (thread->is_guest_thread()) { + if (thread->can_debugger_suspend()) { std::memcpy(&thread_info->guest_context, thread->thread_state()->context(), sizeof(thread_info->guest_context)); diff --git a/src/xenia/debug/ui/debug_window.cc b/src/xenia/debug/ui/debug_window.cc index 2869ce891..4a3dacdde 100644 --- a/src/xenia/debug/ui/debug_window.cc +++ b/src/xenia/debug/ui/debug_window.cc @@ -1104,7 +1104,7 @@ void DebugWindow::DrawThreadsPane() { ImGui::SetNextTreeNodeOpened(true, ImGuiSetCond_Always); } const char* state_label = "?"; - if (thread->is_guest_thread()) { + if (thread->can_debugger_suspend()) { if (thread->is_running()) { if (thread->suspend_count() > 1) { state_label = "SUSPEND"; diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 1ce91ddd4..d47c483fe 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -305,7 +305,7 @@ bool Emulator::ExceptionCallback(Exception* ex) { kernel::XObject::kTypeThread); auto current_thread = kernel::XThread::GetCurrentThread(); for (auto thread : threads) { - if (!thread->is_guest_thread()) { + if (!thread->can_debugger_suspend()) { // Don't pause host threads. continue; } @@ -325,7 +325,7 @@ bool Emulator::ExceptionCallback(Exception* ex) { }); // Now suspend ourself (we should be a guest thread). - assert_true(current_thread->is_guest_thread()); + assert_true(current_thread->can_debugger_suspend()); current_thread->Suspend(nullptr); // We should not arrive here! diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.cc b/src/xenia/gpu/gl4/gl4_graphics_system.cc index 056af726e..dfc95e8dd 100644 --- a/src/xenia/gpu/gl4/gl4_graphics_system.cc +++ b/src/xenia/gpu/gl4/gl4_graphics_system.cc @@ -130,6 +130,8 @@ X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor, } return 0; })); + // As we run vblank interrupts the debugger must be able to suspend us. + worker_thread_->set_can_debugger_suspend(true); worker_thread_->set_name("GL4 Vsync"); worker_thread_->Create(); diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index c0be947ba..efc0dd67f 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -263,6 +263,8 @@ void KernelState::SetExecutableModule(object_ref module) { } return 0; })); + // As we run guest callbacks the debugger must be able to suspend us. + dispatch_thread_->set_can_debugger_suspend(true); dispatch_thread_->set_name("Kernel Dispatch Thread"); dispatch_thread_->Create(); } diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index 2437952bd..5b28865c6 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -702,7 +702,11 @@ X_STATUS XThread::Delay(uint32_t processor_mode, uint32_t alertable, XHostThread::XHostThread(KernelState* kernel_state, uint32_t stack_size, uint32_t creation_flags, std::function host_fn) : XThread(kernel_state, stack_size, 0, 0, 0, creation_flags, false), - host_fn_(host_fn) {} + host_fn_(host_fn) { + // By default host threads are not debugger suspendable. If the thread runs + // any guest code this must be overridden. + can_debugger_suspend_ = false; +} void XHostThread::Execute() { XELOGKERNEL( diff --git a/src/xenia/kernel/xthread.h b/src/xenia/kernel/xthread.h index f340f8867..d6e6af0d9 100644 --- a/src/xenia/kernel/xthread.h +++ b/src/xenia/kernel/xthread.h @@ -125,7 +125,13 @@ class XThread : public XObject { const CreationParams* creation_params() const { return &creation_params_; } uint32_t tls_ptr() const { return tls_address_; } uint32_t pcr_ptr() const { return pcr_address_; } + // True if the thread is created by the guest app. bool is_guest_thread() const { return guest_thread_; } + // True if the thread should be paused by the debugger. + // All threads that can run guest code must be stopped for the debugger to + // work properly. + bool can_debugger_suspend() const { return can_debugger_suspend_; } + void set_can_debugger_suspend(bool value) { can_debugger_suspend_ = value; } bool is_running() const { return running_; } cpu::ThreadState* thread_state() const { return thread_state_; } @@ -182,7 +188,8 @@ class XThread : public XObject { uint32_t tls_address_ = 0; uint32_t pcr_address_ = 0; cpu::ThreadState* thread_state_ = nullptr; - bool guest_thread_ = false; // Launched into guest code? + bool guest_thread_ = false; + bool can_debugger_suspend_ = true; bool running_ = false; std::string name_;