Support vCont with specific threads to resume

(gdb) set scheduler-locking on
(gdb) continue
This commit is contained in:
Duncan Ogilvie 2026-04-09 20:05:30 +02:00
parent 6c21530e43
commit 0a5b92e821
4 changed files with 71 additions and 20 deletions

View file

@ -470,27 +470,57 @@ void GDBStub::HandleVCont(std::string_view sv, std::vector<DebuggerAction>& acti
// Continuing and stepping are supported (signal is ignored, but required for GDB to use vCont)
if (sv == "?") {
SendReply("vCont;c;C;s;S");
} else {
Kernel::KThread* stepped_thread = nullptr;
bool lock_execution = true;
std::vector<std::string> entries;
boost::split(entries, sv.substr(1), boost::is_any_of(";"));
for (auto const& thread_action : entries) {
std::vector<std::string> parts;
boost::split(parts, thread_action, boost::is_any_of(":"));
if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C")))
lock_execution = false;
if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S")))
stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
}
return;
}
if (stepped_thread) {
backend.SetActiveThread(stepped_thread);
actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked : DebuggerAction::StepThreadUnlocked);
} else {
actions.push_back(DebuggerAction::Continue);
Kernel::KThread* stepped_thread = nullptr;
bool has_default_continue = false;
std::vector<Kernel::KThread*> continue_threads;
std::vector<std::string> entries;
boost::split(entries, sv.substr(1), boost::is_any_of(";"));
for (auto const& thread_action : entries) {
std::vector<std::string> parts;
boost::split(parts, thread_action, boost::is_any_of(":"));
const bool is_step = parts[0] == "s" || parts[0].starts_with("S");
const bool is_continue = parts[0] == "c" || parts[0].starts_with("C");
if (parts.size() == 1) {
// Bare action (no thread ID) = default for all unmentioned threads.
if (is_continue)
has_default_continue = true;
} else if (parts.size() == 2) {
auto* thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
if (is_step && thread)
stepped_thread = thread;
else if (is_continue && thread)
continue_threads.push_back(thread);
}
}
if (stepped_thread) {
backend.SetActiveThread(stepped_thread);
if (has_default_continue) {
// Step one thread, continue all others (e.g. vCont;s:4c;c).
actions.push_back(DebuggerAction::StepThreadUnlocked);
} else if (!continue_threads.empty()) {
// Step one thread, continue specific others.
// For now, treat as step-unlocked since we resume the listed threads.
vcont_threads = std::move(continue_threads);
actions.push_back(DebuggerAction::StepThreadUnlocked);
} else {
// Step one thread, keep all others stopped (e.g. vCont;s:4c).
actions.push_back(DebuggerAction::StepThreadLocked);
}
} else if (has_default_continue) {
// Continue all threads.
actions.push_back(DebuggerAction::Continue);
} else if (!continue_threads.empty()) {
// Continue only specific threads.
vcont_threads = std::move(continue_threads);
actions.push_back(DebuggerAction::ContinueThreads);
}
}
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {