core: Refactor thread unschedule and add debug frontend unschedule (#2145)

This commit is contained in:
PabloMK7 2026-05-23 17:25:14 +02:00 committed by GitHub
parent ab6896a2ca
commit b186b04995
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 99 additions and 30 deletions

View File

@ -879,6 +879,8 @@ void GMainWindow::InitializeHotkeys() {
link_action_shortcut(ui->action_Debug_Pause, QStringLiteral("Debug Pause"));
link_action_shortcut(ui->action_Debug_Resume, QStringLiteral("Debug Resume"));
link_action_shortcut(ui->action_Debug_Step, QStringLiteral("Debug Step"), false, true);
link_action_shortcut(ui->action_Debug_Unschedule_All, QStringLiteral("Debug Unschedule All"));
link_action_shortcut(ui->action_Debug_Schedule_All, QStringLiteral("Debug Schedule All"));
link_action_shortcut(ui->action_Screen_Layout_Swap_Screens, QStringLiteral("Swap Screens"));
link_action_shortcut(ui->action_Screen_Layout_Upright_Screens,
QStringLiteral("Rotate Screens Upright"));
@ -1209,6 +1211,10 @@ void GMainWindow::ConnectMenuEvents() {
emu_thread->ExecStep();
}
});
connect_menu(ui->action_Debug_Unschedule_All,
[this] { system.DebugUnscheduleAllThreadsFromFrontend(true); });
connect_menu(ui->action_Debug_Schedule_All,
[this] { system.DebugUnscheduleAllThreadsFromFrontend(false); });
// Tools
connect_menu(ui->action_Compress_ROM_File, &GMainWindow::OnCompressFile);

View File

@ -57,15 +57,17 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> QtConfi
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<UISettings::Shortcut, 41> QtConfig::default_hotkeys {{
const std::array<UISettings::Shortcut, 43> QtConfig::default_hotkeys {{
{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
{QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Debug Pause"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F4"),Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Debug Resume"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"),Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Debug Step"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"),Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Debug Pause"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Debug Resume"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Debug Step"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Debug Unschedule All"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Debug Schedule All"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
{QStringLiteral("Decrease 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+-"), Qt::ApplicationShortcut}},
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},

View File

@ -26,7 +26,7 @@ public:
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
static const std::array<UISettings::Shortcut, 41> default_hotkeys;
static const std::array<UISettings::Shortcut, 43> default_hotkeys;
private:
void Initialize(const std::string& config_name);

View File

@ -213,6 +213,9 @@
<addaction name="action_Debug_Pause"/>
<addaction name="action_Debug_Resume"/>
<addaction name="action_Debug_Step"/>
<addaction name="separator"/>
<addaction name="action_Debug_Unschedule_All"/>
<addaction name="action_Debug_Schedule_All"/>
</widget>
<addaction name="menu_Debug"/>
<addaction name="separator"/>
@ -487,6 +490,22 @@
<string>Debug Step</string>
</property>
</action>
<action name="action_Debug_Unschedule_All">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Debug Unschedule All</string>
</property>
</action>
<action name="action_Debug_Schedule_All">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Debug Schedule All</string>
</property>
</action>
<action name="action_Dump_Video">
<property name="checkable">
<bool>true</bool>

View File

@ -833,6 +833,19 @@ bool System::IsInitialSetup() {
return app_loader && app_loader->DoingInitialSetup();
}
void System::DebugUnscheduleAllThreadsFromFrontend(bool unschedule) {
if (!is_powered_on)
return;
for (auto proc : kernel->GetProcessList()) {
if (unschedule) {
proc->SetUnscheduleMode(Kernel::UnscheduleMode::FRONTEND);
} else {
proc->ClearUnscheduleMode(Kernel::UnscheduleMode::FRONTEND);
}
}
}
template <class Archive>
void System::serialize(Archive& ar, const unsigned int file_version) {

View File

@ -419,6 +419,8 @@ public:
debug_next_process = false;
}
void DebugUnscheduleAllThreadsFromFrontend(bool unschedule);
private:
/**
* Initialize the emulated system.

View File

@ -877,7 +877,7 @@ static void HandleGetStopReason() {
for (const auto& process : process_list) {
if (process->codeset->program_id == program_id) {
current_process = process.get();
current_process->SetDebugBreak(true);
current_process->SetUnscheduleMode(Kernel::UnscheduleMode::GDB);
is_running = false;
if (SetThread(0)) {
SendStopReply(current_thread, 0);
@ -900,7 +900,7 @@ static void BreakImpl(int signal) {
"and memory exceptions. Disable CPU JIT for more accuracy.");
}
current_process->SetDebugBreak(true);
current_process->SetUnscheduleMode(Kernel::UnscheduleMode::GDB);
is_running = false;
latest_signal = signal;
@ -1248,7 +1248,7 @@ static void Continue() {
continue_list.push_back(thread_id);
}
current_process->SetDebugBreak(false, continue_list);
current_process->ClearUnscheduleMode(Kernel::UnscheduleMode::GDB, continue_list);
is_running = true;
ClearAllInstructionCache();
@ -1414,7 +1414,7 @@ void HandleVCommand() {
SendReply("E02");
} else {
current_process = process.get();
current_process->SetDebugBreak(true);
current_process->SetUnscheduleMode(Kernel::UnscheduleMode::GDB);
is_running = false;
if (SetThread(0)) {
SendStopReply(current_thread, 0);
@ -1456,7 +1456,7 @@ void HandleVCommand() {
HexToInt(reinterpret_cast<const u8*>(threads[i].c_str()), threads[i].size()));
}
current_process->SetDebugBreak(false, thread_ids);
current_process->ClearUnscheduleMode(Kernel::UnscheduleMode::GDB, thread_ids);
is_running = true;
}
} else {

View File

@ -270,7 +270,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
#ifdef ENABLE_GDBSTUB
if (GDBStub::IsServerEnabled()) {
LOG_INFO(Loader, "Pausing process {} at start", process_id);
SetDebugBreak(true);
SetUnscheduleMode(Kernel::UnscheduleMode::GDB);
}
#endif
Core::System::GetInstance().ClearDebugNextProcessFlag();
@ -624,7 +624,8 @@ std::vector<std::shared_ptr<Kernel::Thread>> Kernel::Process::GetThreadList() {
return ret;
}
void Kernel::Process::SetDebugBreak(bool debug_break, std::vector<u32> thread_ids) {
void Kernel::Process::ChangeUnscheduleMode(UnscheduleMode mode, std::vector<u32> thread_ids,
bool set) {
auto thread_list = GetThreadList();
bool needs_reschedule = false;
for (auto& t : thread_list) {
@ -636,7 +637,7 @@ void Kernel::Process::SetDebugBreak(bool debug_break, std::vector<u32> thread_id
}
}
needs_reschedule |= t->SetDebugBreak(debug_break);
needs_reschedule |= (set ? t->SetUnscheduleMode(mode) : t->ClearUnscheduleMode(mode));
}
if (needs_reschedule) {

View File

@ -13,6 +13,7 @@
#include <boost/container/static_vector.hpp>
#include <boost/serialization/export.hpp>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/object.h"
@ -51,6 +52,13 @@ union ProcessFlags {
BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
};
enum class UnscheduleMode : u32 {
SVC = (1 << 0),
GDB = (1 << 1),
FRONTEND = (1 << 2),
};
DECLARE_ENUM_FLAG_OPERATORS(UnscheduleMode);
enum class ProcessStatus { Created, Running, Exited };
class ResourceLimit;
@ -228,9 +236,15 @@ public:
std::vector<std::shared_ptr<Kernel::Thread>> GetThreadList();
void SetDebugBreak(bool debug_break, std::vector<u32> thread_ids = {});
void SetUnscheduleMode(UnscheduleMode mode, std::vector<u32> thread_ids = {}) {
ChangeUnscheduleMode(mode, thread_ids, true);
}
void ClearUnscheduleMode(UnscheduleMode mode, std::vector<u32> thread_ids = {}) {
ChangeUnscheduleMode(mode, thread_ids, false);
}
private:
void ChangeUnscheduleMode(UnscheduleMode mode, std::vector<u32> thread_ids, bool set);
void FreeAllMemory();
KernelSystem& kernel;

View File

@ -2191,7 +2191,11 @@ Result SVC::ControlProcess(Handle process_handle, u32 process_OP, u32 varg2, u32
kernel.GetCurrentThreadManager().GetCurrentThread()->thread_id) {
continue;
}
thread.get()->can_schedule = !varg2;
if (varg2) {
thread->SetUnscheduleMode(Kernel::UnscheduleMode::SVC);
} else {
thread->ClearUnscheduleMode(Kernel::UnscheduleMode::SVC);
}
}
}
return ResultSuccess;

View File

@ -80,7 +80,7 @@ void Thread::serialize(Archive& ar, const unsigned int file_version) {
}
}
ar & wakeup_callback;
ar & debug_break;
ar & unschedule_mode;
}
SERIALIZE_IMPL(Thread)
@ -545,12 +545,20 @@ VAddr Thread::GetCommandBufferAddress() const {
return GetTLSAddress() + command_header_offset;
}
bool Thread::SetDebugBreak(bool _debug_break) {
if (debug_break == _debug_break) {
return false;
}
debug_break = _debug_break;
return true;
bool Thread::SetUnscheduleMode(UnscheduleMode mode) {
UnscheduleMode old = unschedule_mode;
unschedule_mode |= mode;
return unschedule_mode != old;
}
bool Thread::ClearUnscheduleMode(UnscheduleMode mode) {
UnscheduleMode old = unschedule_mode;
unschedule_mode &= ~mode;
return unschedule_mode != old;
}
CpuLimiter::~CpuLimiter() {}

View File

@ -21,6 +21,7 @@
#include "core/arm/arm_interface.h"
#include "core/core_timing.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
@ -28,7 +29,6 @@
namespace Kernel {
class Mutex;
class Process;
enum ThreadPriority : u32 {
ThreadPrioHighest = 0, ///< Highest thread priority
@ -366,19 +366,16 @@ public:
}
bool CanSchedule() {
// TODO(PabloMK7): This may not be the proper way
// threads are marked as non-schedulable when they
// are in debug break. Figure out and fix.
return can_schedule && !debug_break;
return static_cast<u32>(unschedule_mode) == 0;
}
bool SetDebugBreak(bool debug_break);
bool SetUnscheduleMode(UnscheduleMode mode);
bool ClearUnscheduleMode(UnscheduleMode mode);
Core::ARM_Interface::ThreadContext context{};
u32 thread_id;
bool can_schedule{true};
ThreadStatus status;
VAddr entry_point;
VAddr stack_top;
@ -419,7 +416,10 @@ public:
private:
ThreadManager& thread_manager;
bool debug_break{};
// Does not represent how real HW works, instead it mimics behaviour
// taking into account how our scheduler works.
UnscheduleMode unschedule_mode{};
friend class boost::serialization::access;
template <class Archive>