mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +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
	
	 Jean-Baptiste Boric
						Jean-Baptiste Boric