Qt: Run GDB server on emulation thread

This commit is contained in:
Jean-Baptiste Boric 2023-02-25 17:27:03 +01:00
parent 4494a2c73c
commit 7810e68a58
8 changed files with 82 additions and 66 deletions

View file

@ -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());
}
}

View file

@ -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;
};

View file

@ -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);
}

View file

@ -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;
};

View file

@ -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();

View file

@ -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

View file

@ -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);

View file

@ -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).