diff --git a/src/xenia/debug/gdb/gdbstub.cc b/src/xenia/debug/gdb/gdbstub.cc index 55fc849aec..930a779987 100644 --- a/src/xenia/debug/gdb/gdbstub.cc +++ b/src/xenia/debug/gdb/gdbstub.cc @@ -193,24 +193,6 @@ uint8_t from_hexchar(char c) { return 0; } -std::string GDBStub::DebuggerDetached() { - // Delete all breakpoints - auto& state = cache_.breakpoints; - - for (auto& breakpoint : state.all_breakpoints) { - processor_->RemoveBreakpoint(breakpoint.get()); - } - - state.code_breakpoints_by_guest_address.clear(); - state.all_breakpoints.clear(); - - if (processor_->execution_state() == cpu::ExecutionState::kPaused) { - ExecutionContinue(); - } - - return kGdbReplyOK; -} - std::string GDBStub::ReadRegister(xe::cpu::ThreadDebugInfo* thread, uint32_t rid) { // Send registers as 32-bit, otherwise some debuggers will switch to 64-bit @@ -546,12 +528,18 @@ void GDBStub::UpdateCache() { object_table->GetObjectsByType(XObject::Type::Module); cache_.thread_debug_infos = processor_->QueryThreadDebugInfos(); - cache_.cur_thread_id = cache_.thread_debug_infos[0]->thread_id; + if (cache_.cur_thread_id == -1) { + cache_.cur_thread_id = emulator_->main_thread_id(); + } } std::string GDBStub::ReadRegister(const std::string& data) { + auto* thread = cache_.cur_thread_info(); + if (!thread) { + return kGdbReplyError; + } uint32_t rid = hex_to_u32(data); - std::string result = ReadRegister(cache_.cur_thread_info(), rid); + std::string result = ReadRegister(thread, rid); if (result.empty()) { return kGdbReplyError; // TODO: is this error correct? } @@ -559,10 +547,14 @@ std::string GDBStub::ReadRegister(const std::string& data) { } std::string GDBStub::ReadRegisters() { + auto* thread = cache_.cur_thread_info(); + if (!thread) { + return kGdbReplyError; + } std::string result; result.reserve(68 * 16 + 3 * 8); for (int i = 0; i < 71; ++i) { - result += ReadRegister(cache_.cur_thread_info(), i); + result += ReadRegister(thread, i); } return result; } @@ -758,9 +750,18 @@ void GDBStub::OnFocus() {} void GDBStub::OnDetached() { UpdateCache(); - // Remove all breakpoints. - while (!cache_.breakpoints.all_breakpoints.empty()) { - DeleteCodeBreakpoint(cache_.breakpoints.all_breakpoints.front().get()); + // Delete all breakpoints + auto& state = cache_.breakpoints; + + for (auto& breakpoint : state.all_breakpoints) { + processor_->RemoveBreakpoint(breakpoint.get()); + } + + state.code_breakpoints_by_guest_address.clear(); + state.all_breakpoints.clear(); + + if (processor_->execution_state() == cpu::ExecutionState::kPaused) { + ExecutionContinue(); } } @@ -821,10 +822,18 @@ std::string GDBStub::HandleGDBCommand(const GDBCommand& command) { }}, // Detach - {"D", [&](const GDBCommand& cmd) { return DebuggerDetached(); }}, + {"D", + [&](const GDBCommand& cmd) { + OnDetached(); + return kGdbReplyOK; + }}, // Kill request (just treat as detach for now) - {"k", [&](const GDBCommand& cmd) { return DebuggerDetached(); }}, + {"k", + [&](const GDBCommand& cmd) { + OnDetached(); + return kGdbReplyOK; + }}, // Enable extended mode {"!", [&](const GDBCommand& cmd) { return kGdbReplyOK; }}, @@ -857,24 +866,32 @@ std::string GDBStub::HandleGDBCommand(const GDBCommand& command) { // Get current debugger thread ID {"qC", [&](const GDBCommand& cmd) { - return "QC" + std::to_string(cache_.cur_thread_info()->thread_id); + auto* thread = cache_.cur_thread_info(); + if (!thread) { + return std::string(kGdbReplyError); + } + return "QC" + std::to_string(thread->thread_id); }}, // Set current debugger thread ID {"H", [&](const GDBCommand& cmd) { - // Reset to known good ID - cache_.cur_thread_id = - cache_.thread_debug_infos.size() - ? cache_.thread_debug_infos[0]->thread_id - : -1; - - // Check if the desired thread ID exists int threadId = std::stol(cmd.data.substr(1), 0, 16); - for (auto& thread : cache_.thread_debug_infos) { - if (thread->thread_id == threadId) { - cache_.cur_thread_id = threadId; - break; + + if (!threadId) { + // Treat Thread 0 as main thread, seems to work for IDA + cache_.cur_thread_id = emulator_->main_thread_id(); + } else { + uint32_t thread_id = -1; + + // Check if the desired thread ID exists + for (auto& thread : cache_.thread_debug_infos) { + if (thread->thread_id == threadId) { + thread_id = threadId; + break; + } } + + cache_.cur_thread_id = thread_id; } return kGdbReplyOK; diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 376088f8fb..043e121d00 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -174,6 +174,10 @@ Emulator::~Emulator() { ExceptionHandler::Uninstall(Emulator::ExceptionCallbackThunk, this); } +uint32_t Emulator::main_thread_id() { + return main_thread_ ? main_thread_->thread_id() : 0; +} + X_STATUS Emulator::Setup( ui::Window* display_window, ui::ImGuiDrawer* imgui_drawer, bool require_cpu_backend, diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index ccf37d7f9d..5d2db462f5 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -124,6 +124,8 @@ class Emulator { // Are we currently running a title? bool is_title_open() const { return title_id_.has_value(); } + uint32_t main_thread_id(); + // Window used for displaying graphical output. Can be null. ui::Window* display_window() const { return display_window_; }