From 7810e68a5839d12b9b9a50e669e6a940fccdd449 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric <jblbeurope@gmail.com> Date: Sat, 25 Feb 2023 17:27:03 +0100 Subject: [PATCH 1/2] Qt: Run GDB server on emulation thread --- src/duckstation-qt/gdbconnection.cpp | 55 +++++++++++++++------------- src/duckstation-qt/gdbconnection.h | 9 +++-- src/duckstation-qt/gdbserver.cpp | 49 ++++++++++++++++--------- src/duckstation-qt/gdbserver.h | 9 +++-- src/duckstation-qt/mainwindow.cpp | 17 +++------ src/duckstation-qt/mainwindow.h | 4 -- src/duckstation-qt/qthost.cpp | 3 ++ src/duckstation-qt/qthost.h | 2 + 8 files changed, 82 insertions(+), 66 deletions(-) diff --git a/src/duckstation-qt/gdbconnection.cpp b/src/duckstation-qt/gdbconnection.cpp index 2be99414b..f3f91d878 100644 --- a/src/duckstation-qt/gdbconnection.cpp +++ b/src/duckstation-qt/gdbconnection.cpp @@ -7,28 +7,31 @@ #include "qthost.h" Log_SetChannel(GDBConnection); -GDBConnection::GDBConnection(QObject* parent, int descriptor) : QThread(parent), m_descriptor(descriptor) +GDBConnection::GDBConnection(GDBServer* parent, intptr_t descriptor) : QTcpSocket(parent), m_descriptor(descriptor) { - Log_InfoPrintf("(%u) Accepted new connection on GDB server", m_descriptor); - - connect(&m_socket, &QTcpSocket::readyRead, this, &GDBConnection::receivedData); - connect(&m_socket, &QTcpSocket::disconnected, this, &GDBConnection::gotDisconnected); - - if (m_socket.setSocketDescriptor(m_descriptor)) + if (!setSocketDescriptor(descriptor)) { - g_emu_thread->setSystemPaused(true, true); - } - else - { - Log_ErrorPrintf("(%u) Failed to set socket descriptor: %s", m_descriptor, - m_socket.errorString().toUtf8().constData()); + Log_ErrorPrintf("(%" PRIdPTR ") Failed to set socket descriptor: %s", descriptor, + errorString().toUtf8().constData()); + deleteLater(); + return; } + + connect(g_emu_thread, &EmuThread::systemPaused, this, &GDBConnection::onEmulationPaused); + connect(g_emu_thread, &EmuThread::systemResumed, this, &GDBConnection::onEmulationResumed); + connect(this, &QTcpSocket::readyRead, this, &GDBConnection::receivedData); + connect(this, &QTcpSocket::disconnected, this, &GDBConnection::gotDisconnected); + + Log_InfoPrintf("(%" PRIdPTR ") Client connected", m_descriptor); + + m_seen_resume = System::IsPaused(); + g_emu_thread->setSystemPaused(true); } void GDBConnection::gotDisconnected() { - Log_InfoPrintf("(%u) Client disconnected", m_descriptor); - this->exit(0); + Log_InfoPrintf("(%" PRIdPTR ") Client disconnected", m_descriptor); + deleteLater(); } void GDBConnection::receivedData() @@ -36,7 +39,7 @@ void GDBConnection::receivedData() qint64 bytesRead; char buffer[256]; - while ((bytesRead = m_socket.read(buffer, sizeof(buffer))) > 0) + while ((bytesRead = read(buffer, sizeof(buffer))) > 0) { for (char c : std::string_view(buffer, bytesRead)) { @@ -44,19 +47,19 @@ void GDBConnection::receivedData() if (GDBProtocol::IsPacketInterrupt(m_readBuffer)) { - Log_DebugPrintf("(%u) > Interrupt request", m_descriptor); - g_emu_thread->setSystemPaused(true, true); + Log_DebugPrintf("(%" PRIdPTR ") > Interrupt request", m_descriptor); + g_emu_thread->setSystemPaused(true); m_readBuffer.erase(); } else if (GDBProtocol::IsPacketContinue(m_readBuffer)) { - Log_DebugPrintf("(%u) > Continue request", m_descriptor); - g_emu_thread->setSystemPaused(false, false); + Log_DebugPrintf("(%" PRIdPTR ") > Continue request", m_descriptor); + g_emu_thread->setSystemPaused(false); m_readBuffer.erase(); } else if (GDBProtocol::IsPacketComplete(m_readBuffer)) { - Log_DebugPrintf("(%u) > %s", m_descriptor, m_readBuffer.c_str()); + Log_DebugPrintf("(%" PRIdPTR ") > %s", m_descriptor, m_readBuffer.c_str()); writePacket(GDBProtocol::ProcessPacket(m_readBuffer)); m_readBuffer.erase(); } @@ -64,7 +67,8 @@ void GDBConnection::receivedData() } if (bytesRead == -1) { - Log_ErrorPrintf("(%u) Failed to read from socket: %s", m_descriptor, m_socket.errorString().toUtf8().constData()); + Log_ErrorPrintf("(%" PRIdPTR ") Failed to read from socket: %s", m_descriptor, + errorString().toUtf8().constData()); } } @@ -87,9 +91,10 @@ void GDBConnection::onEmulationResumed() void GDBConnection::writePacket(std::string_view packet) { - Log_DebugPrintf("(%u) < %*s", m_descriptor, packet.length(), packet.data()); - if (m_socket.write(packet.data(), packet.length()) == -1) + Log_DebugPrintf("(%" PRIdPTR ") < %*s", m_descriptor, packet.length(), packet.data()); + if (write(packet.data(), packet.length()) == -1) { - Log_ErrorPrintf("(%u) Failed to write to socket: %s", m_descriptor, m_socket.errorString().toUtf8().constData()); + Log_ErrorPrintf("(%" PRIdPTR ") Failed to write to socket: %s", m_descriptor, + errorString().toUtf8().constData()); } } diff --git a/src/duckstation-qt/gdbconnection.h b/src/duckstation-qt/gdbconnection.h index 0cea44fec..cea8ced8c 100644 --- a/src/duckstation-qt/gdbconnection.h +++ b/src/duckstation-qt/gdbconnection.h @@ -5,12 +5,14 @@ #include <QtCore/QThread> #include <QtNetwork/QTcpSocket> -class GDBConnection : public QThread +class GDBServer; + +class GDBConnection : public QTcpSocket { Q_OBJECT public: - GDBConnection(QObject *parent, int descriptor); + GDBConnection(GDBServer *parent, intptr_t descriptor); public Q_SLOTS: void gotDisconnected(); @@ -21,8 +23,7 @@ public Q_SLOTS: private: void writePacket(std::string_view data); - int m_descriptor; - QTcpSocket m_socket; + intptr_t m_descriptor; std::string m_readBuffer; bool m_seen_resume; }; diff --git a/src/duckstation-qt/gdbserver.cpp b/src/duckstation-qt/gdbserver.cpp index f47ef1dbe..43e717d0c 100644 --- a/src/duckstation-qt/gdbserver.cpp +++ b/src/duckstation-qt/gdbserver.cpp @@ -7,33 +7,46 @@ #include "qthost.h" Log_SetChannel(GDBServer); -GDBServer::GDBServer(QObject *parent, u16 port) +GDBServer::GDBServer(QObject *parent) : QTcpServer(parent) { - if (listen(QHostAddress::LocalHost, port)) { - Log_InfoPrintf("GDB server listening on TCP port %u", port); - } - else { - Log_InfoPrintf("Failed to listen on TCP port %u for GDB server: %s", port, errorString().toUtf8().constData()); - } } GDBServer::~GDBServer() { - Log_InfoPrint("GDB server stopped"); - for (auto* thread : m_connections) { - thread->quit(); - thread->wait(); - delete thread; + stop(); +} + +void GDBServer::start(quint16 port) { + if (isListening()) + { + return; + } + + if (!listen(QHostAddress::LocalHost, port)) + { + Log_ErrorPrintf("Failed to listen on TCP port %u for GDB server: %s", port, + errorString().toUtf8().constData()); + return; + } + + Log_InfoPrintf("GDB server listening on TCP port %u", port); +} + +void GDBServer::stop() +{ + if (isListening()) + { + close(); + Log_InfoPrint("GDB server stopped"); + } + + for (QObject* connection : children()) { + connection->deleteLater(); } } void GDBServer::incomingConnection(qintptr descriptor) { - Log_InfoPrint("Accepted connection on GDB server"); - GDBConnection *thread = new GDBConnection(this, descriptor); - connect(g_emu_thread, &EmuThread::systemPaused, thread, &GDBConnection::onEmulationPaused); - connect(g_emu_thread, &EmuThread::systemResumed, thread, &GDBConnection::onEmulationResumed); - thread->start(); - m_connections.push_back(thread); + new GDBConnection(this, descriptor); } diff --git a/src/duckstation-qt/gdbserver.h b/src/duckstation-qt/gdbserver.h index 5407e31b8..5da9f432a 100644 --- a/src/duckstation-qt/gdbserver.h +++ b/src/duckstation-qt/gdbserver.h @@ -11,12 +11,13 @@ class GDBServer : public QTcpServer Q_OBJECT public: - GDBServer(QObject* parent, u16 port); + GDBServer(QObject* parent = nullptr); ~GDBServer(); +public Q_SLOTS: + void start(quint16 port); + void stop(); + protected: void incomingConnection(qintptr socketDescriptor) override; - -private: - std::list<GDBConnection*> m_connections; }; diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 74389cff8..eb0c6d152 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -21,7 +21,6 @@ #include "frontend-common/platform_misc.h" #include "gamelistsettingswidget.h" #include "gamelistwidget.h" -#include "gdbserver.h" #include "memorycardeditordialog.h" #include "qthost.h" #include "qtutils.h" @@ -1723,17 +1722,13 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool cheevo if ((!starting && !running) || running) m_open_debugger_on_start = false; - if (g_settings.debugging.enable_gdb_server) + if (!g_gdb_server->isListening() && g_settings.debugging.enable_gdb_server && starting) { - if (starting && !m_gdb_server) - { - m_gdb_server = new GDBServer(this, g_settings.debugging.gdb_server_port); - } - else if (!running && m_gdb_server) - { - delete m_gdb_server; - m_gdb_server = nullptr; - } + QMetaObject::invokeMethod(g_gdb_server, "start", Qt::QueuedConnection, Q_ARG(quint16, g_settings.debugging.gdb_server_port)); + } + else if (g_gdb_server->isListening() && !running) + { + QMetaObject::invokeMethod(g_gdb_server, "stop", Qt::QueuedConnection); } m_ui.statusBar->clearMessage(); diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index ec1aa123f..279f6625a 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -33,8 +33,6 @@ namespace GameList { struct Entry; } -class GDBServer; - class MainWindow final : public QMainWindow { Q_OBJECT @@ -286,8 +284,6 @@ private: bool m_was_disc_change_request = false; bool m_is_closing = false; - GDBServer* m_gdb_server = nullptr; - #ifdef _WIN32 void* m_device_notification_handle = nullptr; #endif diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index fd235b618..3a7fd50ad 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -98,6 +98,7 @@ static bool s_start_fullscreen_ui = false; static bool s_start_fullscreen_ui_fullscreen = false; EmuThread* g_emu_thread; +GDBServer* g_gdb_server; EmuThread::EmuThread(QThread* ui_thread) : QThread(), m_ui_thread(ui_thread) {} @@ -1373,6 +1374,8 @@ void EmuThread::start() AssertMsg(!g_emu_thread, "Emu thread does not exist"); g_emu_thread = new EmuThread(QThread::currentThread()); + g_gdb_server = new GDBServer(); + g_gdb_server->moveToThread(g_emu_thread); g_emu_thread->QThread::start(); g_emu_thread->m_started_semaphore.acquire(); g_emu_thread->moveToThread(g_emu_thread); diff --git a/src/duckstation-qt/qthost.h b/src/duckstation-qt/qthost.h index cd0bac444..d45be2460 100644 --- a/src/duckstation-qt/qthost.h +++ b/src/duckstation-qt/qthost.h @@ -10,6 +10,7 @@ #include "frontend-common/common_host.h" #include "frontend-common/game_list.h" #include "frontend-common/input_manager.h" +#include "gdbserver.h" #include "qtutils.h" #include <QtCore/QByteArray> #include <QtCore/QMetaType> @@ -235,6 +236,7 @@ private: }; extern EmuThread* g_emu_thread; +extern GDBServer* g_gdb_server; namespace QtHost { /// Sets batch mode (exit after game shutdown). From d65fb0e86a40b3eb4f8a4127379d420fbca14d33 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric <jblbeurope@gmail.com> Date: Sat, 25 Feb 2023 17:46:37 +0100 Subject: [PATCH 2/2] Qt: Add toggle to enable GDB server --- src/duckstation-qt/mainwindow.cpp | 1 + src/duckstation-qt/mainwindow.ui | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index eb0c6d152..2e442ccea 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -1972,6 +1972,7 @@ void MainWindow::connectSignals() connect(m_ui.actionCoverDownloader, &QAction::triggered, this, &MainWindow::onToolsCoverDownloaderTriggered); connect(m_ui.actionCheatManager, &QAction::triggered, this, &MainWindow::onToolsCheatManagerTriggered); connect(m_ui.actionCPUDebugger, &QAction::triggered, this, &MainWindow::openCPUDebugger); + SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableGDBServer, "Debug", "EnableGDBServer", false); connect(m_ui.actionOpenDataDirectory, &QAction::triggered, this, &MainWindow::onToolsOpenDataDirectoryTriggered); connect(m_ui.actionGridViewShowTitles, &QAction::triggered, m_game_list_widget, &GameListWidget::setShowCoverTitles); connect(m_ui.actionGridViewZoomIn, &QAction::triggered, m_game_list_widget, [this]() { diff --git a/src/duckstation-qt/mainwindow.ui b/src/duckstation-qt/mainwindow.ui index 03ad4266a..98248d624 100644 --- a/src/duckstation-qt/mainwindow.ui +++ b/src/duckstation-qt/mainwindow.ui @@ -183,6 +183,7 @@ <addaction name="actionForceNTSCTimings"/> <addaction name="separator"/> <addaction name="actionCPUDebugger"/> + <addaction name="actionEnableGDBServer"/> <addaction name="separator"/> <addaction name="actionDumpRAM"/> <addaction name="actionDumpVRAM"/> @@ -865,6 +866,14 @@ <string>CPU D&ebugger</string> </property> </action> + <action name="actionEnableGDBServer"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Enable GDB server</string> + </property> + </action> <action name="actionViewGameGrid"> <property name="icon"> <iconset theme="function-line">