aboutsummaryrefslogtreecommitdiff
path: root/src/core/debugger
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/debugger')
-rw-r--r--src/core/debugger/debugger.cpp76
-rw-r--r--src/core/debugger/debugger.h5
-rw-r--r--src/core/debugger/debugger_interface.h5
-rw-r--r--src/core/debugger/gdbstub.cpp52
-rw-r--r--src/core/debugger/gdbstub.h1
-rw-r--r--src/core/debugger/gdbstub_arch.h1
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;