Qt: Run GDB server on emulation thread
This commit is contained in:
parent
4494a2c73c
commit
7810e68a58
|
@ -7,28 +7,31 @@
|
||||||
#include "qthost.h"
|
#include "qthost.h"
|
||||||
Log_SetChannel(GDBConnection);
|
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);
|
if (!setSocketDescriptor(descriptor))
|
||||||
|
|
||||||
connect(&m_socket, &QTcpSocket::readyRead, this, &GDBConnection::receivedData);
|
|
||||||
connect(&m_socket, &QTcpSocket::disconnected, this, &GDBConnection::gotDisconnected);
|
|
||||||
|
|
||||||
if (m_socket.setSocketDescriptor(m_descriptor))
|
|
||||||
{
|
{
|
||||||
g_emu_thread->setSystemPaused(true, true);
|
Log_ErrorPrintf("(%" PRIdPTR ") Failed to set socket descriptor: %s", descriptor,
|
||||||
}
|
errorString().toUtf8().constData());
|
||||||
else
|
deleteLater();
|
||||||
{
|
return;
|
||||||
Log_ErrorPrintf("(%u) Failed to set socket descriptor: %s", m_descriptor,
|
|
||||||
m_socket.errorString().toUtf8().constData());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
void GDBConnection::gotDisconnected()
|
||||||
{
|
{
|
||||||
Log_InfoPrintf("(%u) Client disconnected", m_descriptor);
|
Log_InfoPrintf("(%" PRIdPTR ") Client disconnected", m_descriptor);
|
||||||
this->exit(0);
|
deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDBConnection::receivedData()
|
void GDBConnection::receivedData()
|
||||||
|
@ -36,7 +39,7 @@ void GDBConnection::receivedData()
|
||||||
qint64 bytesRead;
|
qint64 bytesRead;
|
||||||
char buffer[256];
|
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))
|
for (char c : std::string_view(buffer, bytesRead))
|
||||||
{
|
{
|
||||||
|
@ -44,19 +47,19 @@ void GDBConnection::receivedData()
|
||||||
|
|
||||||
if (GDBProtocol::IsPacketInterrupt(m_readBuffer))
|
if (GDBProtocol::IsPacketInterrupt(m_readBuffer))
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("(%u) > Interrupt request", m_descriptor);
|
Log_DebugPrintf("(%" PRIdPTR ") > Interrupt request", m_descriptor);
|
||||||
g_emu_thread->setSystemPaused(true, true);
|
g_emu_thread->setSystemPaused(true);
|
||||||
m_readBuffer.erase();
|
m_readBuffer.erase();
|
||||||
}
|
}
|
||||||
else if (GDBProtocol::IsPacketContinue(m_readBuffer))
|
else if (GDBProtocol::IsPacketContinue(m_readBuffer))
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("(%u) > Continue request", m_descriptor);
|
Log_DebugPrintf("(%" PRIdPTR ") > Continue request", m_descriptor);
|
||||||
g_emu_thread->setSystemPaused(false, false);
|
g_emu_thread->setSystemPaused(false);
|
||||||
m_readBuffer.erase();
|
m_readBuffer.erase();
|
||||||
}
|
}
|
||||||
else if (GDBProtocol::IsPacketComplete(m_readBuffer))
|
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));
|
writePacket(GDBProtocol::ProcessPacket(m_readBuffer));
|
||||||
m_readBuffer.erase();
|
m_readBuffer.erase();
|
||||||
}
|
}
|
||||||
|
@ -64,7 +67,8 @@ void GDBConnection::receivedData()
|
||||||
}
|
}
|
||||||
if (bytesRead == -1)
|
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)
|
void GDBConnection::writePacket(std::string_view packet)
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("(%u) < %*s", m_descriptor, packet.length(), packet.data());
|
Log_DebugPrintf("(%" PRIdPTR ") < %*s", m_descriptor, packet.length(), packet.data());
|
||||||
if (m_socket.write(packet.data(), packet.length()) == -1)
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,14 @@
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
#include <QtNetwork/QTcpSocket>
|
#include <QtNetwork/QTcpSocket>
|
||||||
|
|
||||||
class GDBConnection : public QThread
|
class GDBServer;
|
||||||
|
|
||||||
|
class GDBConnection : public QTcpSocket
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GDBConnection(QObject *parent, int descriptor);
|
GDBConnection(GDBServer *parent, intptr_t descriptor);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void gotDisconnected();
|
void gotDisconnected();
|
||||||
|
@ -21,8 +23,7 @@ public Q_SLOTS:
|
||||||
private:
|
private:
|
||||||
void writePacket(std::string_view data);
|
void writePacket(std::string_view data);
|
||||||
|
|
||||||
int m_descriptor;
|
intptr_t m_descriptor;
|
||||||
QTcpSocket m_socket;
|
|
||||||
std::string m_readBuffer;
|
std::string m_readBuffer;
|
||||||
bool m_seen_resume;
|
bool m_seen_resume;
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,33 +7,46 @@
|
||||||
#include "qthost.h"
|
#include "qthost.h"
|
||||||
Log_SetChannel(GDBServer);
|
Log_SetChannel(GDBServer);
|
||||||
|
|
||||||
GDBServer::GDBServer(QObject *parent, u16 port)
|
GDBServer::GDBServer(QObject *parent)
|
||||||
: QTcpServer(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()
|
GDBServer::~GDBServer()
|
||||||
{
|
{
|
||||||
|
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");
|
Log_InfoPrint("GDB server stopped");
|
||||||
for (auto* thread : m_connections) {
|
}
|
||||||
thread->quit();
|
|
||||||
thread->wait();
|
for (QObject* connection : children()) {
|
||||||
delete thread;
|
connection->deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDBServer::incomingConnection(qintptr descriptor)
|
void GDBServer::incomingConnection(qintptr descriptor)
|
||||||
{
|
{
|
||||||
Log_InfoPrint("Accepted connection on GDB server");
|
new GDBConnection(this, descriptor);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,13 @@ class GDBServer : public QTcpServer
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GDBServer(QObject* parent, u16 port);
|
GDBServer(QObject* parent = nullptr);
|
||||||
~GDBServer();
|
~GDBServer();
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void start(quint16 port);
|
||||||
|
void stop();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void incomingConnection(qintptr socketDescriptor) override;
|
void incomingConnection(qintptr socketDescriptor) override;
|
||||||
|
|
||||||
private:
|
|
||||||
std::list<GDBConnection*> m_connections;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "frontend-common/platform_misc.h"
|
#include "frontend-common/platform_misc.h"
|
||||||
#include "gamelistsettingswidget.h"
|
#include "gamelistsettingswidget.h"
|
||||||
#include "gamelistwidget.h"
|
#include "gamelistwidget.h"
|
||||||
#include "gdbserver.h"
|
|
||||||
#include "memorycardeditordialog.h"
|
#include "memorycardeditordialog.h"
|
||||||
#include "qthost.h"
|
#include "qthost.h"
|
||||||
#include "qtutils.h"
|
#include "qtutils.h"
|
||||||
|
@ -1723,17 +1722,13 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool cheevo
|
||||||
if ((!starting && !running) || running)
|
if ((!starting && !running) || running)
|
||||||
m_open_debugger_on_start = false;
|
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)
|
QMetaObject::invokeMethod(g_gdb_server, "start", Qt::QueuedConnection, Q_ARG(quint16, g_settings.debugging.gdb_server_port));
|
||||||
{
|
|
||||||
m_gdb_server = new GDBServer(this, g_settings.debugging.gdb_server_port);
|
|
||||||
}
|
}
|
||||||
else if (!running && m_gdb_server)
|
else if (g_gdb_server->isListening() && !running)
|
||||||
{
|
{
|
||||||
delete m_gdb_server;
|
QMetaObject::invokeMethod(g_gdb_server, "stop", Qt::QueuedConnection);
|
||||||
m_gdb_server = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ui.statusBar->clearMessage();
|
m_ui.statusBar->clearMessage();
|
||||||
|
|
|
@ -33,8 +33,6 @@ namespace GameList {
|
||||||
struct Entry;
|
struct Entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GDBServer;
|
|
||||||
|
|
||||||
class MainWindow final : public QMainWindow
|
class MainWindow final : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -286,8 +284,6 @@ private:
|
||||||
bool m_was_disc_change_request = false;
|
bool m_was_disc_change_request = false;
|
||||||
bool m_is_closing = false;
|
bool m_is_closing = false;
|
||||||
|
|
||||||
GDBServer* m_gdb_server = nullptr;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void* m_device_notification_handle = nullptr;
|
void* m_device_notification_handle = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -98,6 +98,7 @@ static bool s_start_fullscreen_ui = false;
|
||||||
static bool s_start_fullscreen_ui_fullscreen = false;
|
static bool s_start_fullscreen_ui_fullscreen = false;
|
||||||
|
|
||||||
EmuThread* g_emu_thread;
|
EmuThread* g_emu_thread;
|
||||||
|
GDBServer* g_gdb_server;
|
||||||
|
|
||||||
EmuThread::EmuThread(QThread* ui_thread) : QThread(), m_ui_thread(ui_thread) {}
|
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");
|
AssertMsg(!g_emu_thread, "Emu thread does not exist");
|
||||||
|
|
||||||
g_emu_thread = new EmuThread(QThread::currentThread());
|
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->QThread::start();
|
||||||
g_emu_thread->m_started_semaphore.acquire();
|
g_emu_thread->m_started_semaphore.acquire();
|
||||||
g_emu_thread->moveToThread(g_emu_thread);
|
g_emu_thread->moveToThread(g_emu_thread);
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "frontend-common/common_host.h"
|
#include "frontend-common/common_host.h"
|
||||||
#include "frontend-common/game_list.h"
|
#include "frontend-common/game_list.h"
|
||||||
#include "frontend-common/input_manager.h"
|
#include "frontend-common/input_manager.h"
|
||||||
|
#include "gdbserver.h"
|
||||||
#include "qtutils.h"
|
#include "qtutils.h"
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
#include <QtCore/QMetaType>
|
#include <QtCore/QMetaType>
|
||||||
|
@ -235,6 +236,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
extern EmuThread* g_emu_thread;
|
extern EmuThread* g_emu_thread;
|
||||||
|
extern GDBServer* g_gdb_server;
|
||||||
|
|
||||||
namespace QtHost {
|
namespace QtHost {
|
||||||
/// Sets batch mode (exit after game shutdown).
|
/// Sets batch mode (exit after game shutdown).
|
||||||
|
|
Loading…
Reference in New Issue