mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-30 01:25:51 +00:00
Qt: Implement GDB server
This commit is contained in:
parent
abd2399aaf
commit
7dcacc2cda
|
@ -52,6 +52,10 @@ set(SRCS
|
||||||
gamepropertiesdialog.cpp
|
gamepropertiesdialog.cpp
|
||||||
gamepropertiesdialog.h
|
gamepropertiesdialog.h
|
||||||
gamepropertiesdialog.ui
|
gamepropertiesdialog.ui
|
||||||
|
gdbconnection.cpp
|
||||||
|
gdbconnection.h
|
||||||
|
gdbserver.cpp
|
||||||
|
gdbserver.h
|
||||||
generalsettingswidget.cpp
|
generalsettingswidget.cpp
|
||||||
generalsettingswidget.h
|
generalsettingswidget.h
|
||||||
generalsettingswidget.ui
|
generalsettingswidget.ui
|
||||||
|
|
|
@ -75,6 +75,8 @@
|
||||||
<ClCompile Include="gamelistsettingswidget.cpp" />
|
<ClCompile Include="gamelistsettingswidget.cpp" />
|
||||||
<ClCompile Include="gamelistwidget.cpp" />
|
<ClCompile Include="gamelistwidget.cpp" />
|
||||||
<ClCompile Include="gamepropertiesdialog.cpp" />
|
<ClCompile Include="gamepropertiesdialog.cpp" />
|
||||||
|
<ClCompile Include="gdbconnection.cpp" />
|
||||||
|
<ClCompile Include="gdbserver.cpp" />
|
||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
<ClCompile Include="mainwindow.cpp" />
|
<ClCompile Include="mainwindow.cpp" />
|
||||||
<ClCompile Include="controllersettingswidget.cpp" />
|
<ClCompile Include="controllersettingswidget.cpp" />
|
||||||
|
@ -119,6 +121,8 @@
|
||||||
<QtMoc Include="gamelistsettingswidget.h" />
|
<QtMoc Include="gamelistsettingswidget.h" />
|
||||||
<QtMoc Include="gamelistwidget.h" />
|
<QtMoc Include="gamelistwidget.h" />
|
||||||
<QtMoc Include="gamepropertiesdialog.h" />
|
<QtMoc Include="gamepropertiesdialog.h" />
|
||||||
|
<QtMoc Include="gdbconnection.h" />
|
||||||
|
<QtMoc Include="gdbserver.h" />
|
||||||
<QtMoc Include="postprocessingchainconfigwidget.h" />
|
<QtMoc Include="postprocessingchainconfigwidget.h" />
|
||||||
<QtMoc Include="postprocessingshaderconfigwidget.h" />
|
<QtMoc Include="postprocessingshaderconfigwidget.h" />
|
||||||
<QtMoc Include="postprocessingsettingswidget.h" />
|
<QtMoc Include="postprocessingsettingswidget.h" />
|
||||||
|
@ -221,6 +225,8 @@
|
||||||
<ClCompile Include="$(IntDir)moc_gamelistsettingswidget.cpp" />
|
<ClCompile Include="$(IntDir)moc_gamelistsettingswidget.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_gamelistwidget.cpp" />
|
<ClCompile Include="$(IntDir)moc_gamelistwidget.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_gamepropertiesdialog.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_generalsettingswidget.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_debuggermodels.cpp" />
|
<ClCompile Include="$(IntDir)moc_debuggermodels.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_debuggerwindow.cpp" />
|
<ClCompile Include="$(IntDir)moc_debuggerwindow.cpp" />
|
||||||
|
|
|
@ -31,6 +31,10 @@
|
||||||
<ClCompile Include="$(IntDir)moc_advancedsettingswidget.cpp" />
|
<ClCompile Include="$(IntDir)moc_advancedsettingswidget.cpp" />
|
||||||
<ClCompile Include="gamepropertiesdialog.cpp" />
|
<ClCompile Include="gamepropertiesdialog.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_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="controllersettingswidget.cpp" />
|
||||||
<ClCompile Include="aboutdialog.cpp" />
|
<ClCompile Include="aboutdialog.cpp" />
|
||||||
<ClCompile Include="memorycardsettingswidget.cpp" />
|
<ClCompile Include="memorycardsettingswidget.cpp" />
|
||||||
|
@ -101,6 +105,8 @@
|
||||||
<QtMoc Include="qtprogresscallback.h" />
|
<QtMoc Include="qtprogresscallback.h" />
|
||||||
<QtMoc Include="advancedsettingswidget.h" />
|
<QtMoc Include="advancedsettingswidget.h" />
|
||||||
<QtMoc Include="gamepropertiesdialog.h" />
|
<QtMoc Include="gamepropertiesdialog.h" />
|
||||||
|
<QtMoc Include="gdbconnection.h" />
|
||||||
|
<QtMoc Include="gdbserver.h" />
|
||||||
<QtMoc Include="controllersettingswidget.h" />
|
<QtMoc Include="controllersettingswidget.h" />
|
||||||
<QtMoc Include="aboutdialog.h" />
|
<QtMoc Include="aboutdialog.h" />
|
||||||
<QtMoc Include="memorycardsettingswidget.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 "gamelistsettingswidget.h"
|
||||||
#include "gamelistwidget.h"
|
#include "gamelistwidget.h"
|
||||||
#include "gamepropertiesdialog.h"
|
#include "gamepropertiesdialog.h"
|
||||||
|
#include "gdbserver.h"
|
||||||
#include "memorycardeditordialog.h"
|
#include "memorycardeditordialog.h"
|
||||||
#include "qtdisplaywidget.h"
|
#include "qtdisplaywidget.h"
|
||||||
#include "qthostinterface.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();
|
m_ui.statusBar->clearMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ class DebuggerWindow;
|
||||||
class HostDisplay;
|
class HostDisplay;
|
||||||
struct GameListEntry;
|
struct GameListEntry;
|
||||||
|
|
||||||
|
class GDBServer;
|
||||||
|
|
||||||
class MainWindow final : public QMainWindow
|
class MainWindow final : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -145,4 +147,6 @@ private:
|
||||||
|
|
||||||
bool m_emulation_running = false;
|
bool m_emulation_running = false;
|
||||||
bool m_was_paused_by_focus_loss = false;
|
bool m_was_paused_by_focus_loss = false;
|
||||||
|
|
||||||
|
GDBServer* m_gdb_server = nullptr;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue