diff options
author | Liam <byteslice@airmail.cc> | 2022-05-31 20:37:37 +0200 |
---|---|---|
committer | Liam <byteslice@airmail.cc> | 2022-06-01 08:15:15 +0200 |
commit | 989d4a7a41f449af0ea09e34bee331a3a3ac8170 (patch) | |
tree | df24bd9d7e6942b939e3ea42d08c0d65006e539f | |
parent | core/debugger: Implement new GDB stub debugger (diff) | |
download | yuzu-989d4a7a41f449af0ea09e34bee331a3a3ac8170.tar yuzu-989d4a7a41f449af0ea09e34bee331a3a3ac8170.tar.gz yuzu-989d4a7a41f449af0ea09e34bee331a3a3ac8170.tar.bz2 yuzu-989d4a7a41f449af0ea09e34bee331a3a3ac8170.tar.lz yuzu-989d4a7a41f449af0ea09e34bee331a3a3ac8170.tar.xz yuzu-989d4a7a41f449af0ea09e34bee331a3a3ac8170.tar.zst yuzu-989d4a7a41f449af0ea09e34bee331a3a3ac8170.zip |
Diffstat (limited to '')
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/core/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/core/arm/arm_interface.cpp | 45 | ||||
-rw-r--r-- | src/core/arm/arm_interface.h | 14 | ||||
-rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_32.cpp | 45 | ||||
-rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_32.h | 9 | ||||
-rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.cpp | 45 | ||||
-rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.h | 9 | ||||
-rw-r--r-- | src/core/debugger/debugger.cpp | 61 | ||||
-rw-r--r-- | src/core/debugger/debugger.h | 5 | ||||
-rw-r--r-- | src/core/debugger/debugger_interface.h | 9 | ||||
-rw-r--r-- | src/core/debugger/gdbstub.cpp | 99 | ||||
-rw-r--r-- | src/core/debugger/gdbstub.h | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/k_thread.h | 15 | ||||
-rw-r--r-- | src/video_core/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/yuzu/CMakeLists.txt | 4 |
16 files changed, 252 insertions, 122 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e3de90ff..be70c04ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,6 +222,11 @@ else() list(APPEND CONAN_REQUIRED_LIBS "boost/1.79.0") endif() +# boost:asio has functions that require AcceptEx et al +if (MINGW) + find_library(MSWSOCK_LIBRARY mswsock REQUIRED) +endif() + # Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS yuzu_find_packages() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 948cc318a..2bd720f08 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -768,6 +768,9 @@ create_target_directory_groups(core) target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus) +if (MINGW) + target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) +endif() if (ENABLE_WEB_SERVICE) target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 1310f72bf..9b5a5ca57 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -11,6 +11,7 @@ #include "core/core.h" #include "core/debugger/debugger.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" #include "core/loader/loader.h" #include "core/memory.h" @@ -89,8 +90,48 @@ void ARM_Interface::LogBacktrace() const { } } -bool ARM_Interface::ShouldStep() const { - return system.DebuggerEnabled() && system.GetDebugger().IsStepping(); +void ARM_Interface::Run() { + using Kernel::StepState; + using Kernel::SuspendType; + + while (true) { + Kernel::KThread* current_thread{system.Kernel().CurrentScheduler()->GetCurrentThread()}; + Dynarmic::HaltReason hr{}; + + // Notify the debugger and go to sleep if a step was performed + // and this thread has been scheduled again. + if (current_thread->GetStepState() == StepState::StepPerformed) { + system.GetDebugger().NotifyThreadStopped(current_thread); + current_thread->RequestSuspend(SuspendType::Debug); + break; + } + + // Otherwise, run the thread. + if (current_thread->GetStepState() == StepState::StepPending) { + hr = StepJit(); + + if (Has(hr, step_thread)) { + current_thread->SetStepState(StepState::StepPerformed); + } + } else { + hr = RunJit(); + } + + // Notify the debugger and go to sleep if a breakpoint was hit. + if (Has(hr, breakpoint)) { + system.GetDebugger().NotifyThreadStopped(current_thread); + current_thread->RequestSuspend(Kernel::SuspendType::Debug); + break; + } + + // Handle syscalls and scheduling (this may change the current thread) + if (Has(hr, svc_call)) { + Kernel::Svc::Call(system, GetSvcNumber()); + } + if (Has(hr, break_loop) || !uses_wall_clock) { + break; + } + } } } // namespace Core diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 7842c626b..66f6107e9 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -6,6 +6,9 @@ #include <array> #include <vector> + +#include <dynarmic/interface/halt_reason.h> + #include "common/common_funcs.h" #include "common/common_types.h" #include "core/hardware_properties.h" @@ -64,7 +67,7 @@ public: static_assert(sizeof(ThreadContext64) == 0x320); /// Runs the CPU until an event happens - virtual void Run() = 0; + void Run(); /// Clear all instruction cache virtual void ClearInstructionCache() = 0; @@ -191,7 +194,10 @@ public: void LogBacktrace() const; - bool ShouldStep() const; + static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step; + static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2; + static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; + static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; protected: /// System context that this ARM interface is running under. @@ -200,6 +206,10 @@ protected: bool uses_wall_clock; static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out); + + virtual Dynarmic::HaltReason RunJit() = 0; + virtual Dynarmic::HaltReason StepJit() = 0; + virtual u32 GetSvcNumber() const = 0; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 894c1c527..7c82d0b96 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -26,10 +26,6 @@ namespace Core { using namespace Common::Literals; -constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2; -constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; -constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; - class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { public: explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) @@ -82,8 +78,8 @@ public: void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { if (parent.system.DebuggerEnabled()) { - parent.breakpoint_pc = pc; - parent.jit.load()->HaltExecution(breakpoint); + parent.jit.load()->Regs()[15] = pc; + parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); return; } @@ -95,7 +91,7 @@ public: void CallSVC(u32 swi) override { parent.svc_swi = swi; - parent.jit.load()->HaltExecution(svc_call); + parent.jit.load()->HaltExecution(ARM_Interface::svc_call); } void AddTicks(u64 ticks) override { @@ -240,35 +236,16 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* return std::make_unique<Dynarmic::A32::Jit>(config); } -void ARM_Dynarmic_32::Run() { - while (true) { - const auto hr = ShouldStep() ? jit.load()->Step() : jit.load()->Run(); - if (Has(hr, svc_call)) { - Kernel::Svc::Call(system, svc_swi); - } - - // Check to see if breakpoint is triggered. - // Recheck step condition in case stop is no longer desired. - Kernel::KThread* current_thread = system.Kernel().GetCurrentEmuThread(); - if (Has(hr, breakpoint)) { - jit.load()->Regs()[15] = breakpoint_pc; +Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() { + return jit.load()->Run(); +} - if (system.GetDebugger().NotifyThreadStopped(current_thread)) { - current_thread->RequestSuspend(Kernel::SuspendType::Debug); - } - break; - } - if (ShouldStep()) { - // When stepping, this should be the only thread running. - ASSERT(system.GetDebugger().NotifyThreadStopped(current_thread)); - current_thread->RequestSuspend(Kernel::SuspendType::Debug); - break; - } +Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() { + return jit.load()->Step(); +} - if (Has(hr, break_loop) || !uses_wall_clock) { - break; - } - } +u32 ARM_Dynarmic_32::GetSvcNumber() const { + return svc_swi; } ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 0557d5940..5b1d60005 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -41,7 +41,6 @@ public: void SetVectorReg(int index, u128 value) override; u32 GetPSTATE() const override; void SetPSTATE(u32 pstate) override; - void Run() override; VAddr GetTlsAddress() const override; void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; @@ -69,6 +68,11 @@ public: std::vector<BacktraceEntry> GetBacktrace() const override; +protected: + Dynarmic::HaltReason RunJit() override; + Dynarmic::HaltReason StepJit() override; + u32 GetSvcNumber() const override; + private: std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; @@ -94,9 +98,6 @@ private: // SVC callback u32 svc_swi{}; - - // Debug restart address - u32 breakpoint_pc{}; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 1f596cfef..d4c67eafd 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -26,10 +26,6 @@ namespace Core { using Vector = Dynarmic::A64::Vector; using namespace Common::Literals; -constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2; -constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; -constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; - class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { public: explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) @@ -123,8 +119,8 @@ public: return; default: if (parent.system.DebuggerEnabled()) { - parent.breakpoint_pc = pc; - parent.jit.load()->HaltExecution(breakpoint); + parent.jit.load()->SetPC(pc); + parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); return; } @@ -136,7 +132,7 @@ public: void CallSVC(u32 swi) override { parent.svc_swi = swi; - parent.jit.load()->HaltExecution(svc_call); + parent.jit.load()->HaltExecution(ARM_Interface::svc_call); } void AddTicks(u64 ticks) override { @@ -300,35 +296,16 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* return std::make_shared<Dynarmic::A64::Jit>(config); } -void ARM_Dynarmic_64::Run() { - while (true) { - const auto hr = jit.load()->Run(); - if (Has(hr, svc_call)) { - Kernel::Svc::Call(system, svc_swi); - } - - // Check to see if breakpoint is triggered. - // Recheck step condition in case stop is no longer desired. - Kernel::KThread* current_thread = system.Kernel().GetCurrentEmuThread(); - if (Has(hr, breakpoint)) { - jit.load()->SetPC(breakpoint_pc); +Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() { + return jit.load()->Run(); +} - if (system.GetDebugger().NotifyThreadStopped(current_thread)) { - current_thread->RequestSuspend(Kernel::SuspendType::Debug); - } - break; - } - if (ShouldStep()) { - // When stepping, this should be the only thread running. - ASSERT(system.GetDebugger().NotifyThreadStopped(current_thread)); - current_thread->RequestSuspend(Kernel::SuspendType::Debug); - break; - } +Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() { + return jit.load()->Step(); +} - if (Has(hr, break_loop) || !uses_wall_clock) { - break; - } - } +u32 ARM_Dynarmic_64::GetSvcNumber() const { + return svc_swi; } ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index aa7054e0c..abfbc3c3f 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -39,7 +39,6 @@ public: void SetVectorReg(int index, u128 value) override; u32 GetPSTATE() const override; void SetPSTATE(u32 pstate) override; - void Run() override; VAddr GetTlsAddress() const override; void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; @@ -63,6 +62,11 @@ public: std::vector<BacktraceEntry> GetBacktrace() const override; +protected: + Dynarmic::HaltReason RunJit() override; + Dynarmic::HaltReason StepJit() override; + u32 GetSvcNumber() const override; + private: std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, std::size_t address_space_bits) const; @@ -87,9 +91,6 @@ private: // SVC callback u32 svc_swi{}; - - // Debug restart address - u64 breakpoint_pc{}; }; } // namespace Core diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 7a2012d3c..a73f2279d 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <algorithm> #include <mutex> #include <thread> @@ -84,31 +85,31 @@ public: return active_thread; } - bool IsStepping() const { - return stepping; - } - private: void InitializeServer(u16 port) { using boost::asio::ip::tcp; LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port); - // Initialize the listening socket and accept a new client. - tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port}; - tcp::acceptor acceptor{io_context, endpoint}; - client_socket = acceptor.accept(); - // Run the connection thread. - connection_thread = std::jthread([&](std::stop_token stop_token) { + connection_thread = std::jthread([&, port](std::stop_token stop_token) { try { + // Initialize the listening socket and accept a new client. + tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port}; + tcp::acceptor acceptor{io_context, endpoint}; + + acceptor.async_accept(client_socket, [](const auto&) {}); + io_context.run_one(); + io_context.restart(); + + if (stop_token.stop_requested()) { + return; + } + ThreadLoop(stop_token); } catch (const std::exception& ex) { LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what()); } - - client_socket.shutdown(client_socket.shutdown_both); - client_socket.close(); }); } @@ -129,8 +130,7 @@ private: AllCoreStop(); // Set the active thread. - active_thread = ThreadList()[0]; - active_thread->Resume(Kernel::SuspendType::Debug); + UpdateActiveThread(); // Set up the frontend. frontend->Connected(); @@ -142,7 +142,7 @@ private: void PipeData(std::span<const u8> data) { AllCoreStop(); - active_thread->Resume(Kernel::SuspendType::Debug); + UpdateActiveThread(); frontend->Stopped(active_thread); } @@ -156,18 +156,22 @@ private: stopped = true; } AllCoreStop(); - active_thread = ThreadList()[0]; - active_thread->Resume(Kernel::SuspendType::Debug); + UpdateActiveThread(); frontend->Stopped(active_thread); break; } case DebuggerAction::Continue: - stepping = false; + active_thread->SetStepState(Kernel::StepState::NotStepping); ResumeInactiveThreads(); AllCoreResume(); break; - case DebuggerAction::StepThread: - stepping = true; + case DebuggerAction::StepThreadUnlocked: + active_thread->SetStepState(Kernel::StepState::StepPending); + ResumeInactiveThreads(); + AllCoreResume(); + break; + case DebuggerAction::StepThreadLocked: + active_thread->SetStepState(Kernel::StepState::StepPending); SuspendInactiveThreads(); AllCoreResume(); break; @@ -212,10 +216,20 @@ private: for (auto* thread : ThreadList()) { if (thread != active_thread) { thread->Resume(Kernel::SuspendType::Debug); + thread->SetStepState(Kernel::StepState::NotStepping); } } } + void UpdateActiveThread() { + const auto& threads{ThreadList()}; + if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) { + active_thread = threads[0]; + } + active_thread->Resume(Kernel::SuspendType::Debug); + active_thread->SetStepState(Kernel::StepState::NotStepping); + } + const std::vector<Kernel::KThread*>& ThreadList() { return system.GlobalSchedulerContext().GetThreadList(); } @@ -233,7 +247,6 @@ private: Kernel::KThread* active_thread; bool stopped; - bool stepping; std::array<u8, 4096> client_data; }; @@ -252,8 +265,4 @@ bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) { return impl && impl->NotifyThreadStopped(thread); } -bool Debugger::IsStepping() const { - return impl && impl->IsStepping(); -} - } // namespace Core diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h index 7acd11815..ea36c6ab2 100644 --- a/src/core/debugger/debugger.h +++ b/src/core/debugger/debugger.h @@ -35,11 +35,6 @@ public: */ bool NotifyThreadStopped(Kernel::KThread* thread); - /** - * Returns whether a step is in progress. - */ - bool IsStepping() const; - private: std::unique_ptr<DebuggerImpl> impl; }; diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h index 0b357fcb5..6ae3dc091 100644 --- a/src/core/debugger/debugger_interface.h +++ b/src/core/debugger/debugger_interface.h @@ -16,10 +16,11 @@ class KThread; namespace Core { enum class DebuggerAction { - Interrupt, // Stop emulation as soon as possible. - Continue, // Resume emulation. - StepThread, // Step the currently-active thread. - ShutdownEmulation, // Shut down the emulator. + Interrupt, ///< Stop emulation as soon as possible. + Continue, ///< Resume emulation. + StepThreadLocked, ///< Step the currently-active thread without resuming others. + StepThreadUnlocked, ///< Step the currently-active thread and resume others. + ShutdownEmulation, ///< Shut down the emulator. }; class DebuggerBackend { diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 718c45952..44ebbe3e0 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -6,8 +6,7 @@ #include <optional> #include <thread> -#include <boost/asio.hpp> -#include <boost/process/async_pipe.hpp> +#include <boost/algorithm/string.hpp> #include "common/hex_util.h" #include "common/logging/log.h" @@ -114,6 +113,11 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction return; } + if (packet.starts_with("vCont")) { + HandleVCont(packet.substr(5), actions); + return; + } + std::string_view command{packet.substr(1, packet.size())}; switch (packet[0]) { @@ -122,6 +126,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction s64 thread_id{strtoll(command.data() + 1, nullptr, 16)}; if (thread_id >= 1) { thread = GetThreadByID(thread_id); + } else { + thread = backend.GetActiveThread(); } if (thread) { @@ -141,6 +147,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction } break; } + case 'Q': case 'q': HandleQuery(command); break; @@ -204,7 +211,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction break; } case 's': - actions.push_back(DebuggerAction::StepThread); + actions.push_back(DebuggerAction::StepThreadLocked); break; case 'C': case 'c': @@ -248,12 +255,47 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction } } +static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { + switch (thread->GetWaitReasonForDebugging()) { + case Kernel::ThreadWaitReasonForDebugging::Sleep: + return "Sleep"; + case Kernel::ThreadWaitReasonForDebugging::IPC: + return "IPC"; + case Kernel::ThreadWaitReasonForDebugging::Synchronization: + return "Synchronization"; + case Kernel::ThreadWaitReasonForDebugging::ConditionVar: + return "ConditionVar"; + case Kernel::ThreadWaitReasonForDebugging::Arbitration: + return "Arbitration"; + case Kernel::ThreadWaitReasonForDebugging::Suspended: + return "Suspended"; + default: + return "Unknown"; + } +} + +static std::string GetThreadState(const Kernel::KThread* thread) { + switch (thread->GetState()) { + case Kernel::ThreadState::Initialized: + return "Initialized"; + case Kernel::ThreadState::Waiting: + return fmt::format("Waiting ({})", GetThreadWaitReason(thread)); + case Kernel::ThreadState::Runnable: + return "Runnable"; + case Kernel::ThreadState::Terminated: + return "Terminated"; + default: + return "Unknown"; + } +} + void GDBStub::HandleQuery(std::string_view command) { if (command.starts_with("TStatus")) { // no tracepoint support SendReply("T0"); } else if (command.starts_with("Supported")) { - SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+"); + SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;" + "vContSupported+;QStartNoAckMode+"); } else if (command.starts_with("Xfer:features:read:target.xml:")) { const auto offset{command.substr(30)}; const auto amount{command.substr(command.find(',') + 1)}; @@ -297,18 +339,57 @@ void GDBStub::HandleQuery(std::string_view command) { const auto& threads = system.GlobalSchedulerContext().GetThreadList(); for (const auto& thread : threads) { - buffer += - fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}"/>)", - thread->GetThreadID(), thread->GetActiveCore(), thread->GetThreadID()); + buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}">{}</thread>)", + thread->GetThreadID(), thread->GetActiveCore(), + thread->GetThreadID(), GetThreadState(thread)); } buffer += "</threads>"; SendReply(buffer); + } else if (command.starts_with("Attached")) { + SendReply("0"); + } else if (command.starts_with("StartNoAckMode")) { + no_ack = true; + SendReply(GDB_STUB_REPLY_OK); } else { SendReply(GDB_STUB_REPLY_EMPTY); } } +void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) { + if (command == "?") { + // Continuing and stepping are supported + // (signal is ignored, but required for GDB to use vCont) + SendReply("vCont;c;C;s;S"); + return; + } + + Kernel::KThread* stepped_thread{nullptr}; + bool lock_execution{true}; + + std::vector<std::string> entries; + boost::split(entries, command.substr(1), boost::is_any_of(";")); + for (const auto& 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)); + } + } + + if (stepped_thread) { + backend.SetActiveThread(stepped_thread); + actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked + : DebuggerAction::StepThreadUnlocked); + } else { + actions.push_back(DebuggerAction::Continue); + } +} + Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; for (auto* thread : threads) { @@ -374,6 +455,10 @@ void GDBStub::SendReply(std::string_view data) { } void GDBStub::SendStatus(char status) { + if (no_ack) { + return; + } + std::array<u8, 1> buf = {static_cast<u8>(status)}; LOG_TRACE(Debug_GDBStub, "Writing status: {}", status); backend.WriteToClient(buf); diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h index b93a3a511..6f8ac2263 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h @@ -28,6 +28,7 @@ public: private: void ProcessData(std::vector<DebuggerAction>& actions); void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions); + void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions); void HandleQuery(std::string_view command); std::vector<char>::const_iterator CommandEnd() const; std::optional<std::string> DetachCommand(); @@ -42,6 +43,7 @@ private: std::unique_ptr<GDBStubArch> arch; std::vector<char> current_command; std::map<VAddr, u32> replaced_instructions; + bool no_ack{}; }; } // namespace Core diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index b55a922ab..60ae0da78 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -100,6 +100,12 @@ enum class ThreadWaitReasonForDebugging : u32 { Suspended, ///< Thread is waiting due to process suspension }; +enum class StepState : u32 { + NotStepping, ///< Thread is not currently stepping + StepPending, ///< Thread will step when next scheduled + StepPerformed, ///< Thread has stepped, waiting to be scheduled again +}; + [[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel); [[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel); [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); @@ -267,6 +273,14 @@ public: void SetState(ThreadState state); + [[nodiscard]] StepState GetStepState() const { + return step_state; + } + + void SetStepState(StepState state) { + step_state = state; + } + [[nodiscard]] s64 GetLastScheduledTick() const { return last_scheduled_tick; } @@ -769,6 +783,7 @@ private: std::shared_ptr<Common::Fiber> host_context{}; bool is_single_core{}; ThreadType thread_type{}; + StepState step_state{}; std::mutex dummy_wait_lock; std::condition_variable dummy_wait_cv; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 6a6325e38..256695804 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -277,3 +277,7 @@ else() $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> ) endif() + +if (ARCHITECTURE_x86_64) + target_link_libraries(video_core PRIVATE dynarmic) +endif() diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 404acdd05..07df9675d 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -319,3 +319,7 @@ endif() if (NOT APPLE) target_compile_definitions(yuzu PRIVATE HAS_OPENGL) endif() + +if (ARCHITECTURE_x86_64) + target_link_libraries(yuzu PRIVATE dynarmic) +endif() |