diff options
Diffstat (limited to 'src/core/debugger')
| -rw-r--r-- | src/core/debugger/debugger.cpp | 76 | ||||
| -rw-r--r-- | src/core/debugger/debugger.h | 5 | ||||
| -rw-r--r-- | src/core/debugger/debugger_interface.h | 5 | ||||
| -rw-r--r-- | src/core/debugger/gdbstub.cpp | 52 | ||||
| -rw-r--r-- | src/core/debugger/gdbstub.h | 1 | ||||
| -rw-r--r-- | src/core/debugger/gdbstub_arch.h | 1 |
6 files changed, 101 insertions, 39 deletions
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 68ab33e46..edf991d71 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -20,15 +20,16 @@ template <typename Readable, typename Buffer, typename Callback> static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { static_assert(std::is_trivial_v<Buffer>); auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))}; - r.async_read_some(boost_buffer, [&](const boost::system::error_code& error, size_t bytes_read) { - if (!error.failed()) { - const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); - std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; - c(received_data); - } + r.async_read_some( + boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) { + if (!error.failed()) { + const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); + std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; + c(received_data); + } - AsyncReceiveInto(r, buffer, c); - }); + AsyncReceiveInto(r, buffer, c); + }); } template <typename Readable, typename Buffer> @@ -41,6 +42,16 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) { return received_data; } +enum class SignalType { + Stopped, + ShuttingDown, +}; + +struct SignalInfo { + SignalType type; + Kernel::KThread* thread; +}; + namespace Core { class DebuggerImpl : public DebuggerBackend { @@ -55,7 +66,7 @@ public: ShutdownServer(); } - bool NotifyThreadStopped(Kernel::KThread* thread) { + bool SignalDebugger(SignalInfo signal_info) { std::scoped_lock lk{connection_lock}; if (stopped) { @@ -63,9 +74,13 @@ public: // It should be ignored. return false; } + + // Set up the state. stopped = true; + info = signal_info; - signal_pipe.write_some(boost::asio::buffer(&thread, sizeof(thread))); + // Write a single byte into the pipe to wake up the debug interface. + boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); return true; } @@ -74,7 +89,7 @@ public: } void WriteToClient(std::span<const u8> data) override { - client_socket.write_some(boost::asio::buffer(data.data(), data.size_bytes())); + boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes())); } void SetActiveThread(Kernel::KThread* thread) override { @@ -95,7 +110,7 @@ private: 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::endpoint endpoint{boost::asio::ip::address_v4::any(), port}; tcp::acceptor acceptor{io_context, endpoint}; acceptor.async_accept(client_socket, [](const auto&) {}); @@ -123,7 +138,7 @@ private: Common::SetCurrentThreadName("yuzu:Debugger"); // Set up the client signals for new data. - AsyncReceiveInto(signal_pipe, active_thread, [&](auto d) { PipeData(d); }); + AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); // Stop the emulated CPU. @@ -141,9 +156,28 @@ private: } void PipeData(std::span<const u8> data) { - AllCoreStop(); - UpdateActiveThread(); - frontend->Stopped(active_thread); + switch (info.type) { + case SignalType::Stopped: + // Stop emulation. + AllCoreStop(); + + // Notify the client. + active_thread = info.thread; + UpdateActiveThread(); + frontend->Stopped(active_thread); + + break; + case SignalType::ShuttingDown: + frontend->ShuttingDown(); + + // Wait for emulation to shut down gracefully now. + suspend.reset(); + signal_pipe.close(); + client_socket.shutdown(boost::asio::socket_base::shutdown_both); + LOG_INFO(Debug_GDBStub, "Shut down server"); + + break; + } } void ClientData(std::span<const u8> data) { @@ -245,7 +279,9 @@ private: boost::asio::ip::tcp::socket client_socket; std::optional<std::unique_lock<std::mutex>> suspend; + SignalInfo info; Kernel::KThread* active_thread; + bool pipe_data; bool stopped; std::array<u8, 4096> client_data; @@ -262,7 +298,13 @@ Debugger::Debugger(Core::System& system, u16 port) { Debugger::~Debugger() = default; bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) { - return impl && impl->NotifyThreadStopped(thread); + return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread}); +} + +void Debugger::NotifyShutdown() { + if (impl) { + impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr}); + } } } // namespace Core diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h index ea36c6ab2..f9738ca3d 100644 --- a/src/core/debugger/debugger.h +++ b/src/core/debugger/debugger.h @@ -35,6 +35,11 @@ public: */ bool NotifyThreadStopped(Kernel::KThread* thread); + /** + * Notify the debugger that a shutdown is being performed now and disconnect. + */ + void NotifyShutdown(); + private: std::unique_ptr<DebuggerImpl> impl; }; diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h index 35ba0bc61..c0bb4ecaf 100644 --- a/src/core/debugger/debugger_interface.h +++ b/src/core/debugger/debugger_interface.h @@ -67,6 +67,11 @@ public: virtual void Stopped(Kernel::KThread* thread) = 0; /** + * Called when emulation is shutting down. + */ + virtual void ShuttingDown() = 0; + + /** * Called when new data is asynchronously received on the client socket. * A list of actions to perform is returned. */ diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 682651a86..52e76f659 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -106,6 +106,8 @@ GDBStub::~GDBStub() = default; void GDBStub::Connected() {} +void GDBStub::ShuttingDown() {} + void GDBStub::Stopped(Kernel::KThread* thread) { SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)); } @@ -422,6 +424,18 @@ static std::string GetThreadState(const Kernel::KThread* thread) { } } +static std::string PaginateBuffer(std::string_view buffer, std::string_view request) { + const auto amount{request.substr(request.find(',') + 1)}; + const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))}; + const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))}; + + if (offset_val + amount_val > buffer.size()) { + return fmt::format("l{}", buffer.substr(offset_val)); + } else { + return fmt::format("m{}", buffer.substr(offset_val, amount_val)); + } +} + void GDBStub::HandleQuery(std::string_view command) { if (command.starts_with("TStatus")) { // no tracepoint support @@ -430,18 +444,8 @@ void GDBStub::HandleQuery(std::string_view command) { 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)}; - - const auto offset_val{static_cast<u64>(strtoll(offset.data(), nullptr, 16))}; - const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))}; const auto target_xml{arch->GetTargetXML()}; - - if (offset_val + amount_val > target_xml.size()) { - SendReply("l" + target_xml.substr(offset_val)); - } else { - SendReply("m" + target_xml.substr(offset_val, amount_val)); - } + SendReply(PaginateBuffer(target_xml, command.substr(30))); } else if (command.starts_with("Offsets")) { Loader::AppLoader::Modules modules; system.GetAppLoader().ReadNSOModules(modules); @@ -454,6 +458,20 @@ void GDBStub::HandleQuery(std::string_view command) { SendReply(fmt::format("TextSeg={:x}", system.CurrentProcess()->PageTable().GetCodeRegionStart())); } + } else if (command.starts_with("Xfer:libraries:read::")) { + Loader::AppLoader::Modules modules; + system.GetAppLoader().ReadNSOModules(modules); + + std::string buffer; + buffer += R"(<?xml version="1.0"?>)"; + buffer += "<library-list>"; + for (const auto& [base, name] : modules) { + buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)", + EscapeXML(name), base); + } + buffer += "</library-list>"; + + SendReply(PaginateBuffer(buffer, command.substr(21))); } else if (command.starts_with("fThreadInfo")) { // beginning of list const auto& threads = system.GlobalSchedulerContext().GetThreadList(); @@ -484,17 +502,7 @@ void GDBStub::HandleQuery(std::string_view command) { buffer += "</threads>"; - const auto offset{command.substr(19)}; - const auto amount{command.substr(command.find(',') + 1)}; - - const auto offset_val{static_cast<u64>(strtoll(offset.data(), nullptr, 16))}; - const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))}; - - if (offset_val + amount_val > buffer.size()) { - SendReply("l" + buffer.substr(offset_val)); - } else { - SendReply("m" + buffer.substr(offset_val, amount_val)); - } + SendReply(PaginateBuffer(buffer, command.substr(19))); } else if (command.starts_with("Attached")) { SendReply("0"); } else if (command.starts_with("StartNoAckMode")) { diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h index 1bb638187..ec934c77e 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h @@ -23,6 +23,7 @@ public: void Connected() override; void Stopped(Kernel::KThread* thread) override; + void ShuttingDown() override; std::vector<DebuggerAction> ClientData(std::span<const u8> data) override; private: diff --git a/src/core/debugger/gdbstub_arch.h b/src/core/debugger/gdbstub_arch.h index 4d039a9f7..2540d6456 100644 --- a/src/core/debugger/gdbstub_arch.h +++ b/src/core/debugger/gdbstub_arch.h @@ -15,6 +15,7 @@ namespace Core { class GDBStubArch { public: + virtual ~GDBStubArch() = default; virtual std::string GetTargetXML() const = 0; virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0; virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0; |
