mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 15:45:42 +00:00
Qt: Implement GDB server
This commit is contained in:
parent
abd2399aaf
commit
7dcacc2cda
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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" />
|
||||
|
|
82
src/duckstation-qt/gdbconnection.cpp
Normal file
82
src/duckstation-qt/gdbconnection.cpp
Normal 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());
|
||||
}
|
||||
}
|
24
src/duckstation-qt/gdbconnection.h
Normal file
24
src/duckstation-qt/gdbconnection.h
Normal 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;
|
||||
};
|
35
src/duckstation-qt/gdbserver.cpp
Normal file
35
src/duckstation-qt/gdbserver.cpp
Normal 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);
|
||||
}
|
19
src/duckstation-qt/gdbserver.h
Normal file
19
src/duckstation-qt/gdbserver.h
Normal 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;
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue