Merge pull request #8446 from liamwhite/cmd-gdb
core/debugger: support operation in yuzu-cmd
This commit is contained in:
commit
a0407a8e64
12 changed files with 97 additions and 9 deletions
|
@ -493,6 +493,12 @@ void System::Shutdown() {
|
||||||
impl->Shutdown();
|
impl->Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::DetachDebugger() {
|
||||||
|
if (impl->debugger) {
|
||||||
|
impl->debugger->NotifyShutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_lock<std::mutex> System::StallCPU() {
|
std::unique_lock<std::mutex> System::StallCPU() {
|
||||||
return impl->StallCPU();
|
return impl->StallCPU();
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,9 @@ public:
|
||||||
/// Shutdown the emulated system.
|
/// Shutdown the emulated system.
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
|
/// Forcibly detach the debugger if it is running.
|
||||||
|
void DetachDebugger();
|
||||||
|
|
||||||
std::unique_lock<std::mutex> StallCPU();
|
std::unique_lock<std::mutex> StallCPU();
|
||||||
void UnstallCPU();
|
void UnstallCPU();
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,16 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
|
||||||
return received_data;
|
return received_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class SignalType {
|
||||||
|
Stopped,
|
||||||
|
ShuttingDown,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SignalInfo {
|
||||||
|
SignalType type;
|
||||||
|
Kernel::KThread* thread;
|
||||||
|
};
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
class DebuggerImpl : public DebuggerBackend {
|
class DebuggerImpl : public DebuggerBackend {
|
||||||
|
@ -56,7 +66,7 @@ public:
|
||||||
ShutdownServer();
|
ShutdownServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NotifyThreadStopped(Kernel::KThread* thread) {
|
bool SignalDebugger(SignalInfo signal_info) {
|
||||||
std::scoped_lock lk{connection_lock};
|
std::scoped_lock lk{connection_lock};
|
||||||
|
|
||||||
if (stopped) {
|
if (stopped) {
|
||||||
|
@ -64,9 +74,13 @@ public:
|
||||||
// It should be ignored.
|
// It should be ignored.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
stopped = true;
|
|
||||||
|
|
||||||
boost::asio::write(signal_pipe, boost::asio::buffer(&thread, sizeof(thread)));
|
// Set up the state.
|
||||||
|
stopped = true;
|
||||||
|
info = signal_info;
|
||||||
|
|
||||||
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +138,7 @@ private:
|
||||||
Common::SetCurrentThreadName("yuzu:Debugger");
|
Common::SetCurrentThreadName("yuzu:Debugger");
|
||||||
|
|
||||||
// Set up the client signals for new data.
|
// 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); });
|
AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
|
||||||
|
|
||||||
// Stop the emulated CPU.
|
// Stop the emulated CPU.
|
||||||
|
@ -142,9 +156,28 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipeData(std::span<const u8> data) {
|
void PipeData(std::span<const u8> data) {
|
||||||
AllCoreStop();
|
switch (info.type) {
|
||||||
UpdateActiveThread();
|
case SignalType::Stopped:
|
||||||
frontend->Stopped(active_thread);
|
// 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) {
|
void ClientData(std::span<const u8> data) {
|
||||||
|
@ -246,7 +279,9 @@ private:
|
||||||
boost::asio::ip::tcp::socket client_socket;
|
boost::asio::ip::tcp::socket client_socket;
|
||||||
std::optional<std::unique_lock<std::mutex>> suspend;
|
std::optional<std::unique_lock<std::mutex>> suspend;
|
||||||
|
|
||||||
|
SignalInfo info;
|
||||||
Kernel::KThread* active_thread;
|
Kernel::KThread* active_thread;
|
||||||
|
bool pipe_data;
|
||||||
bool stopped;
|
bool stopped;
|
||||||
|
|
||||||
std::array<u8, 4096> client_data;
|
std::array<u8, 4096> client_data;
|
||||||
|
@ -263,7 +298,13 @@ Debugger::Debugger(Core::System& system, u16 port) {
|
||||||
Debugger::~Debugger() = default;
|
Debugger::~Debugger() = default;
|
||||||
|
|
||||||
bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
|
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
|
} // namespace Core
|
||||||
|
|
|
@ -35,6 +35,11 @@ public:
|
||||||
*/
|
*/
|
||||||
bool NotifyThreadStopped(Kernel::KThread* thread);
|
bool NotifyThreadStopped(Kernel::KThread* thread);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the debugger that a shutdown is being performed now and disconnect.
|
||||||
|
*/
|
||||||
|
void NotifyShutdown();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<DebuggerImpl> impl;
|
std::unique_ptr<DebuggerImpl> impl;
|
||||||
};
|
};
|
||||||
|
|
|
@ -66,6 +66,11 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void Stopped(Kernel::KThread* thread) = 0;
|
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.
|
* Called when new data is asynchronously received on the client socket.
|
||||||
* A list of actions to perform is returned.
|
* A list of actions to perform is returned.
|
||||||
|
|
|
@ -106,6 +106,8 @@ GDBStub::~GDBStub() = default;
|
||||||
|
|
||||||
void GDBStub::Connected() {}
|
void GDBStub::Connected() {}
|
||||||
|
|
||||||
|
void GDBStub::ShuttingDown() {}
|
||||||
|
|
||||||
void GDBStub::Stopped(Kernel::KThread* thread) {
|
void GDBStub::Stopped(Kernel::KThread* thread) {
|
||||||
SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
|
SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ public:
|
||||||
|
|
||||||
void Connected() override;
|
void Connected() override;
|
||||||
void Stopped(Kernel::KThread* thread) override;
|
void Stopped(Kernel::KThread* thread) override;
|
||||||
|
void ShuttingDown() override;
|
||||||
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
|
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -1591,6 +1591,7 @@ void GMainWindow::ShutdownGame() {
|
||||||
|
|
||||||
AllowOSSleep();
|
AllowOSSleep();
|
||||||
|
|
||||||
|
system->DetachDebugger();
|
||||||
discord_rpc->Pause();
|
discord_rpc->Pause();
|
||||||
emu_thread->RequestStop();
|
emu_thread->RequestStop();
|
||||||
|
|
||||||
|
|
|
@ -344,6 +344,8 @@ void Config::ReadValues() {
|
||||||
ReadSetting("Debugging", Settings::values.use_debug_asserts);
|
ReadSetting("Debugging", Settings::values.use_debug_asserts);
|
||||||
ReadSetting("Debugging", Settings::values.use_auto_stub);
|
ReadSetting("Debugging", Settings::values.use_auto_stub);
|
||||||
ReadSetting("Debugging", Settings::values.disable_macro_jit);
|
ReadSetting("Debugging", Settings::values.disable_macro_jit);
|
||||||
|
ReadSetting("Debugging", Settings::values.use_gdbstub);
|
||||||
|
ReadSetting("Debugging", Settings::values.gdbstub_port);
|
||||||
|
|
||||||
const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
|
const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
|
||||||
std::stringstream ss(title_list);
|
std::stringstream ss(title_list);
|
||||||
|
|
|
@ -437,6 +437,11 @@ disable_macro_jit=false
|
||||||
# Presents guest frames as they become available. Experimental.
|
# Presents guest frames as they become available. Experimental.
|
||||||
# false: Disabled (default), true: Enabled
|
# false: Disabled (default), true: Enabled
|
||||||
disable_fps_limit=false
|
disable_fps_limit=false
|
||||||
|
# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
|
||||||
|
# false: Disabled (default), true: Enabled
|
||||||
|
use_gdbstub=false
|
||||||
|
# The port to use for the GDB server, if it is enabled.
|
||||||
|
gdbstub_port=6543
|
||||||
|
|
||||||
[WebService]
|
[WebService]
|
||||||
# Whether or not to enable telemetry
|
# Whether or not to enable telemetry
|
||||||
|
|
|
@ -162,7 +162,15 @@ void EmuWindow_SDL2::WaitEvent() {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
|
|
||||||
if (!SDL_WaitEvent(&event)) {
|
if (!SDL_WaitEvent(&event)) {
|
||||||
LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", SDL_GetError());
|
const char* error = SDL_GetError();
|
||||||
|
if (!error || strcmp(error, "") == 0) {
|
||||||
|
// https://github.com/libsdl-org/SDL/issues/5780
|
||||||
|
// Sometimes SDL will return without actually having hit an error condition;
|
||||||
|
// just ignore it in this case.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -217,10 +217,19 @@ int main(int argc, char** argv) {
|
||||||
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
|
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
system.RegisterExitCallback([&] {
|
||||||
|
// Just exit right away.
|
||||||
|
exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
void(system.Run());
|
void(system.Run());
|
||||||
|
if (system.DebuggerEnabled()) {
|
||||||
|
system.InitializeDebugger();
|
||||||
|
}
|
||||||
while (emu_window->IsOpen()) {
|
while (emu_window->IsOpen()) {
|
||||||
emu_window->WaitEvent();
|
emu_window->WaitEvent();
|
||||||
}
|
}
|
||||||
|
system.DetachDebugger();
|
||||||
void(system.Pause());
|
void(system.Pause());
|
||||||
system.Shutdown();
|
system.Shutdown();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue