Qt: Implement GDB server

This commit is contained in:
Jean-Baptiste Boric 2020-12-17 18:32:29 +01:00 committed by Connor McLaughlin
parent abd2399aaf
commit 7dcacc2cda
9 changed files with 191 additions and 0 deletions

View file

@ -52,6 +52,10 @@ set(SRCS
gamepropertiesdialog.cpp
gamepropertiesdialog.h
gamepropertiesdialog.ui
gdbconnection.cpp
gdbconnection.h
gdbserver.cpp
gdbserver.h
generalsettingswidget.cpp
generalsettingswidget.h
generalsettingswidget.ui

View file

@ -75,6 +75,8 @@
<ClCompile Include="gamelistsettingswidget.cpp" />
<ClCompile Include="gamelistwidget.cpp" />
<ClCompile Include="gamepropertiesdialog.cpp" />
<ClCompile Include="gdbconnection.cpp" />
<ClCompile Include="gdbserver.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="mainwindow.cpp" />
<ClCompile Include="controllersettingswidget.cpp" />
@ -119,6 +121,8 @@
<QtMoc Include="gamelistsettingswidget.h" />
<QtMoc Include="gamelistwidget.h" />
<QtMoc Include="gamepropertiesdialog.h" />
<QtMoc Include="gdbconnection.h" />
<QtMoc Include="gdbserver.h" />
<QtMoc Include="postprocessingchainconfigwidget.h" />
<QtMoc Include="postprocessingshaderconfigwidget.h" />
<QtMoc Include="postprocessingsettingswidget.h" />
@ -221,6 +225,8 @@
<ClCompile Include="$(IntDir)moc_gamelistsettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_gamelistwidget.cpp" />
<ClCompile Include="$(IntDir)moc_gamepropertiesdialog.cpp" />
<ClCompile Include="$(IntDir)moc_gdbconnection.cpp" />
<ClCompile Include="$(IntDir)moc_gdbserver.cpp" />
<ClCompile Include="$(IntDir)moc_generalsettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_debuggermodels.cpp" />
<ClCompile Include="$(IntDir)moc_debuggerwindow.cpp" />

View file

@ -31,6 +31,10 @@
<ClCompile Include="$(IntDir)moc_advancedsettingswidget.cpp" />
<ClCompile Include="gamepropertiesdialog.cpp" />
<ClCompile Include="$(IntDir)moc_gamepropertiesdialog.cpp" />
<ClCompile Include="gdbconnection.cpp" />
<ClCompile Include="$(IntDir)moc_gdbconnection.cpp" />
<ClCompile Include="gdbserver.cpp" />
<ClCompile Include="$(IntDir)moc_gdbserver.cpp" />
<ClCompile Include="controllersettingswidget.cpp" />
<ClCompile Include="aboutdialog.cpp" />
<ClCompile Include="memorycardsettingswidget.cpp" />
@ -101,6 +105,8 @@
<QtMoc Include="qtprogresscallback.h" />
<QtMoc Include="advancedsettingswidget.h" />
<QtMoc Include="gamepropertiesdialog.h" />
<QtMoc Include="gdbconnection.h" />
<QtMoc Include="gdbserver.h" />
<QtMoc Include="controllersettingswidget.h" />
<QtMoc Include="aboutdialog.h" />
<QtMoc Include="memorycardsettingswidget.h" />

View file

@ -0,0 +1,82 @@
#include "gdbconnection.h"
#include "qthostinterface.h"
#include "common/log.h"
#include "core/gdb_protocol.h"
Log_SetChannel(GDBConnection);
GDBConnection::GDBConnection(QObject *parent, int descriptor)
: QThread(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)) {
QtHostInterface::GetInstance()->pauseSystem(true, true);
}
else {
Log_ErrorPrintf("(%u) Failed to set socket descriptor: %s", m_descriptor, m_socket.errorString().toUtf8().constData());
}
}
void GDBConnection::gotDisconnected()
{
Log_InfoPrintf("(%u) Client disconnected", m_descriptor);
this->exit(0);
}
void GDBConnection::receivedData()
{
qint64 bytesRead;
char buffer[256];
while ((bytesRead = m_socket.read(buffer, sizeof(buffer))) > 0) {
for (char c : std::string_view(buffer, bytesRead)) {
m_readBuffer.push_back(c);
if (GDBProtocol::IsPacketInterrupt(m_readBuffer)) {
Log_DebugPrintf("(%u) > Interrupt request", m_descriptor);
QtHostInterface::GetInstance()->pauseSystem(true, true);
m_readBuffer.erase();
}
else if (GDBProtocol::IsPacketContinue(m_readBuffer)) {
Log_DebugPrintf("(%u) > Continue request", m_descriptor);
QtHostInterface::GetInstance()->pauseSystem(false, false);
m_readBuffer.erase();
}
else if (GDBProtocol::IsPacketComplete(m_readBuffer)) {
Log_DebugPrintf("(%u) > %s", m_descriptor, m_readBuffer.c_str());
writePacket(GDBProtocol::ProcessPacket(m_readBuffer));
m_readBuffer.erase();
}
}
}
if (bytesRead == -1) {
Log_ErrorPrintf("(%u) Failed to read from socket: %s", m_descriptor, m_socket.errorString().toUtf8().constData());
}
}
void GDBConnection::onEmulationPaused(bool paused)
{
if (paused) {
if (m_seen_resume) {
m_seen_resume = false;
// Generate a stop reply packet, insert '?' command to generate it.
writePacket(GDBProtocol::ProcessPacket("$?#3f"));
}
}
else {
m_seen_resume = true;
// Send ack, in case GDB sent a continue request.
writePacket("+");
}
}
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_ErrorPrintf("(%u) Failed to write to socket: %s", m_descriptor, m_socket.errorString().toUtf8().constData());
}
}

View file

@ -0,0 +1,24 @@
#pragma once
#include <QtCore/QThread>
#include <QtNetwork/QTcpSocket>
class GDBConnection : public QThread
{
Q_OBJECT
public:
GDBConnection(QObject *parent, int descriptor);
public Q_SLOTS:
void gotDisconnected();
void receivedData();
void onEmulationPaused(bool paused);
private:
void writePacket(std::string_view data);
int m_descriptor;
QTcpSocket m_socket;
std::string m_readBuffer;
bool m_seen_resume;
};

View file

@ -0,0 +1,35 @@
#include "gdbserver.h"
#include "gdbconnection.h"
#include "common/log.h"
#include "qthostinterface.h"
Log_SetChannel(GDBServer);
GDBServer::GDBServer(QObject *parent, u16 port)
: 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;
}
}
void GDBServer::incomingConnection(qintptr descriptor)
{
Log_InfoPrint("Accepted connection on GDB server");
GDBConnection *thread = new GDBConnection(this, descriptor);
connect(QtHostInterface::GetInstance(), &QtHostInterface::emulationPaused, thread, &GDBConnection::onEmulationPaused);
thread->start();
m_connections.push_back(thread);
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "core/types.h"
#include "gdbconnection.h"
#include <QtNetwork/QTcpServer>
class GDBServer : public QTcpServer
{
Q_OBJECT
public:
GDBServer(QObject* parent, u16 port);
~GDBServer();
protected:
void incomingConnection(qintptr socketDescriptor) override;
private:
std::list<GDBConnection*> m_connections;
};

View file

@ -11,6 +11,7 @@
#include "gamelistsettingswidget.h"
#include "gamelistwidget.h"
#include "gamepropertiesdialog.h"
#include "gdbserver.h"
#include "memorycardeditordialog.h"
#include "qtdisplaywidget.h"
#include "qthostinterface.h"
@ -814,6 +815,16 @@ void MainWindow::updateEmulationActions(bool starting, bool running)
}
}
if (g_settings.debugging.enable_gdb_server) {
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;
}
}
m_ui.statusBar->clearMessage();
}

View file

@ -21,6 +21,8 @@ class DebuggerWindow;
class HostDisplay;
struct GameListEntry;
class GDBServer;
class MainWindow final : public QMainWindow
{
Q_OBJECT
@ -145,4 +147,6 @@ private:
bool m_emulation_running = false;
bool m_was_paused_by_focus_loss = false;
GDBServer* m_gdb_server = nullptr;
};