mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	System: Move GDB server into core
This commit is contained in:
		
							parent
							
								
									55d96f86f0
								
							
						
					
					
						commit
						015804c434
					
				|  | @ -32,7 +32,7 @@ | |||
|     </ClCompile> | ||||
|     <Link> | ||||
|       <AdditionalLibraryDirectories>$(QtLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||||
|       <AdditionalDependencies>Qt6Core$(QtLibSuffix).lib;Qt6Gui$(QtLibSuffix).lib;Qt6Widgets$(QtLibSuffix).lib;Qt6Network$(QtLibSuffix).lib;Qt6Concurrent$(QtLibSuffix).lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|       <AdditionalDependencies>Qt6Core$(QtLibSuffix).lib;Qt6Gui$(QtLibSuffix).lib;Qt6Widgets$(QtLibSuffix).lib;Qt6Concurrent$(QtLibSuffix).lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|     </Link> | ||||
|   </ItemDefinitionGroup> | ||||
| 
 | ||||
|  | @ -118,24 +118,21 @@ | |||
| 
 | ||||
|   <!--Copy the needed dlls--> | ||||
|   <ItemGroup> | ||||
|     <QtLibNames Include="Qt6Core$(QtLibSuffix);Qt6Gui$(QtLibSuffix);Qt6Widgets$(QtLibSuffix);Qt6Network$(QtLibSuffix);Qt6Svg$(QtLibSuffix);Qt6Concurrent$(QtLibSuffix)" /> | ||||
|     <QtLibNames Include="Qt6Core$(QtLibSuffix);Qt6Gui$(QtLibSuffix);Qt6Widgets$(QtLibSuffix);Qt6Svg$(QtLibSuffix);Qt6Concurrent$(QtLibSuffix)" /> | ||||
|     <QtDlls Include="@(QtLibNames -> '$(QtBinDir)%(Identity).dll')" /> | ||||
|     <!--Filter plugins to copy based on the observation that all debug versions end in "d"--> | ||||
|     <QtAllPlugins Include="$(QtPluginsDir)**\*$(QtLibSuffix).dll" /> | ||||
|     <QtPlugins Condition="$(Configuration.Contains(Debug))" Include="@(QtAllPlugins)" /> | ||||
|     <QtPlugins Condition="!$(Configuration.Contains(Debug))" Exclude="$(QtPluginsDir)**\*$(QtDebugSuffix).dll" Include="@(QtAllPlugins)" /> | ||||
|     <QtPluginsDest Include="@(QtPlugins -> '$(BinaryOutputDir)$(QtPluginFolder)\%(RecursiveDir)%(Filename)%(Extension)')" /> | ||||
|     <!--Our normal *d filter fails for the TLS DLLs, because backend ends in d. --> | ||||
|     <QtTLSDlls Include="$(QtPluginsDir)tls\qcertonlybackend$(QtLibSuffix).dll;$(QtPluginsDir)tls\qschannelbackend$(QtLibSuffix).dll" /> | ||||
|     <QtTLSDllsDest Include="@(QtTLSDlls -> '$(BinaryOutputDir)$(QtPluginFolder)\tls\%(Filename)%(Extension)')" /> | ||||
|   </ItemGroup> | ||||
|   <PropertyGroup> | ||||
|     <QtConfFile>$(BinaryOutputDir)qt.conf</QtConfFile> | ||||
|   </PropertyGroup> | ||||
|   <Target Name="QtCopyBinaries" | ||||
|     AfterTargets="Build" | ||||
|     Inputs="@(QtDlls);@(QtPlugins);@(QtTLSDlls)" | ||||
|     Outputs="@(QtDlls -> '$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(Extension)');@(QtPluginsDest);@(QtTLSDllsDest)"> | ||||
|     Inputs="@(QtDlls);@(QtPlugins)" | ||||
|     Outputs="@(QtDlls -> '$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(Extension)');@(QtPluginsDest)"> | ||||
|     <Message Text="Copying Qt .dlls" /> | ||||
|     <Copy | ||||
|       SourceFiles="@(QtDlls)" | ||||
|  | @ -147,11 +144,6 @@ | |||
|       DestinationFiles="@(QtPluginsDest)" | ||||
|       SkipUnchangedFiles="true" | ||||
|     /> | ||||
|     <Copy | ||||
|       SourceFiles="@(QtTLSDlls)" | ||||
|       DestinationFiles="@(QtTLSDllsDest)" | ||||
|       SkipUnchangedFiles="true" | ||||
|     /> | ||||
|   </Target> | ||||
|   <Target Name="QtCreateConf" | ||||
|     BeforeTargets="QtCopyBinaries" | ||||
|  |  | |||
|  | @ -40,8 +40,8 @@ add_library(core | |||
|   game_database.h | ||||
|   game_list.cpp | ||||
|   game_list.h | ||||
|   gdb_protocol.cpp | ||||
|   gdb_protocol.h | ||||
|   gdb_server.cpp | ||||
|   gdb_server.h | ||||
|   gpu.cpp | ||||
|   gpu.h | ||||
|   gpu_backend.cpp | ||||
|  |  | |||
|  | @ -43,6 +43,7 @@ | |||
|     <ClCompile Include="fullscreen_ui.cpp" /> | ||||
|     <ClCompile Include="game_database.cpp" /> | ||||
|     <ClCompile Include="game_list.cpp" /> | ||||
|     <ClCompile Include="gdb_server.cpp" /> | ||||
|     <ClCompile Include="gpu_backend.cpp" /> | ||||
|     <ClCompile Include="gpu_commands.cpp" /> | ||||
|     <ClCompile Include="gpu_hw_shadergen.cpp" /> | ||||
|  | @ -51,7 +52,6 @@ | |||
|     <ClCompile Include="gpu_sw_backend.cpp" /> | ||||
|     <ClCompile Include="gte.cpp" /> | ||||
|     <ClCompile Include="dma.cpp" /> | ||||
|     <ClCompile Include="gdb_protocol.cpp" /> | ||||
|     <ClCompile Include="gpu.cpp" /> | ||||
|     <ClCompile Include="gpu_hw.cpp" /> | ||||
|     <ClCompile Include="host.cpp" /> | ||||
|  | @ -122,6 +122,7 @@ | |||
|     <ClInclude Include="fullscreen_ui.h" /> | ||||
|     <ClInclude Include="game_database.h" /> | ||||
|     <ClInclude Include="game_list.h" /> | ||||
|     <ClInclude Include="gdb_server.h" /> | ||||
|     <ClInclude Include="gpu_backend.h" /> | ||||
|     <ClInclude Include="gpu_hw_shadergen.h" /> | ||||
|     <ClInclude Include="gpu_shadergen.h" /> | ||||
|  | @ -131,7 +132,6 @@ | |||
|     <ClInclude Include="gte.h" /> | ||||
|     <ClInclude Include="cpu_types.h" /> | ||||
|     <ClInclude Include="dma.h" /> | ||||
|     <ClInclude Include="gdb_protocol.h" /> | ||||
|     <ClInclude Include="gpu.h" /> | ||||
|     <ClInclude Include="gpu_hw.h" /> | ||||
|     <ClInclude Include="gte_types.h" /> | ||||
|  |  | |||
|  | @ -6,7 +6,6 @@ | |||
|     <ClCompile Include="cpu_disasm.cpp" /> | ||||
|     <ClCompile Include="bus.cpp" /> | ||||
|     <ClCompile Include="dma.cpp" /> | ||||
|     <ClCompile Include="gdb_protocol.cpp" /> | ||||
|     <ClCompile Include="gpu.cpp" /> | ||||
|     <ClCompile Include="gpu_hw.cpp" /> | ||||
|     <ClCompile Include="interrupt_controller.cpp" /> | ||||
|  | @ -68,6 +67,7 @@ | |||
|     <ClCompile Include="cpu_newrec_compiler_aarch32.cpp" /> | ||||
|     <ClCompile Include="justifier.cpp" /> | ||||
|     <ClCompile Include="pine_server.cpp" /> | ||||
|     <ClCompile Include="gdb_server.cpp" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClInclude Include="types.h" /> | ||||
|  | @ -120,7 +120,6 @@ | |||
|     <ClInclude Include="gpu_sw_backend.h" /> | ||||
|     <ClInclude Include="texture_replacements.h" /> | ||||
|     <ClInclude Include="multitap.h" /> | ||||
|     <ClInclude Include="gdb_protocol.h" /> | ||||
|     <ClInclude Include="host.h" /> | ||||
|     <ClInclude Include="achievements.h" /> | ||||
|     <ClInclude Include="game_database.h" /> | ||||
|  | @ -142,5 +141,6 @@ | |||
|     <ClInclude Include="achievements_private.h" /> | ||||
|     <ClInclude Include="justifier.h" /> | ||||
|     <ClInclude Include="pine_server.h" /> | ||||
|     <ClInclude Include="gdb_server.h" /> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
|  | @ -1,13 +0,0 @@ | |||
| // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> and contributors.
 | ||||
| // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | ||||
| 
 | ||||
| #pragma once | ||||
| #include <string_view> | ||||
| 
 | ||||
| namespace GDBProtocol { | ||||
| bool IsPacketInterrupt(std::string_view data); | ||||
| bool IsPacketContinue(std::string_view data); | ||||
| 
 | ||||
| bool IsPacketComplete(std::string_view data); | ||||
| std::string ProcessPacket(std::string_view data); | ||||
| } // namespace GDBProtocol
 | ||||
|  | @ -1,7 +1,7 @@ | |||
| // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> and contributors.
 | ||||
| // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
 | ||||
| // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | ||||
| 
 | ||||
| #include "gdb_protocol.h" | ||||
| #include "gdb_server.h" | ||||
| #include "bus.h" | ||||
| #include "cpu_core.h" | ||||
| #include "cpu_core_private.h" | ||||
|  | @ -11,6 +11,8 @@ | |||
| #include "common/small_string.h" | ||||
| #include "common/string_util.h" | ||||
| 
 | ||||
| #include "util/sockets.h" | ||||
| 
 | ||||
| #include <functional> | ||||
| #include <iomanip> | ||||
| #include <map> | ||||
|  | @ -20,6 +22,14 @@ | |||
| 
 | ||||
| Log_SetChannel(GDBProtocol); | ||||
| 
 | ||||
| namespace GDBProtocol { | ||||
| static bool IsPacketInterrupt(std::string_view data); | ||||
| static bool IsPacketContinue(std::string_view data); | ||||
| 
 | ||||
| static bool IsPacketComplete(std::string_view data); | ||||
| static std::string ProcessPacket(std::string_view data); | ||||
| } // namespace GDBProtocol
 | ||||
| 
 | ||||
| namespace GDBProtocol { | ||||
| 
 | ||||
| static u8* GetMemoryPointer(PhysicalMemoryAddress address, u32 length) | ||||
|  | @ -345,3 +355,210 @@ std::string ProcessPacket(std::string_view data) | |||
| } | ||||
| 
 | ||||
| } // namespace GDBProtocol
 | ||||
| 
 | ||||
| namespace GDBServer { | ||||
| 
 | ||||
| namespace { | ||||
| class ClientSocket final : public BufferedStreamSocket | ||||
| { | ||||
| public: | ||||
|   ClientSocket(SocketMultiplexer& multiplexer, SocketDescriptor descriptor); | ||||
|   ~ClientSocket() override; | ||||
| 
 | ||||
|   void OnSystemPaused(); | ||||
|   void OnSystemResumed(); | ||||
| 
 | ||||
| protected: | ||||
|   void OnConnected() override; | ||||
|   void OnDisconnected(const Error& error) override; | ||||
|   void OnRead() override; | ||||
| 
 | ||||
| private: | ||||
|   void SendPacket(std::string_view sv); | ||||
| 
 | ||||
|   bool m_seen_resume = false; | ||||
| }; | ||||
| } // namespace
 | ||||
| 
 | ||||
| static std::shared_ptr<ListenSocket> s_gdb_listen_socket; | ||||
| static std::vector<std::shared_ptr<ClientSocket>> s_gdb_clients; | ||||
| } // namespace GDBServer
 | ||||
| 
 | ||||
| GDBServer::ClientSocket::ClientSocket(SocketMultiplexer& multiplexer, SocketDescriptor descriptor) | ||||
|   : BufferedStreamSocket(multiplexer, descriptor, 65536, 65536) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| GDBServer::ClientSocket::~ClientSocket() = default; | ||||
| 
 | ||||
| void GDBServer::ClientSocket::OnConnected() | ||||
| { | ||||
|   INFO_LOG("Client {} connected.", GetRemoteAddress().ToString()); | ||||
| 
 | ||||
|   m_seen_resume = System::IsPaused(); | ||||
|   System::PauseSystem(true); | ||||
| 
 | ||||
|   s_gdb_clients.push_back(std::static_pointer_cast<ClientSocket>(shared_from_this())); | ||||
| } | ||||
| 
 | ||||
| void GDBServer::ClientSocket::OnDisconnected(const Error& error) | ||||
| { | ||||
|   INFO_LOG("Client {} disconnected: {}", GetRemoteAddress().ToString(), error.GetDescription()); | ||||
| 
 | ||||
|   const auto iter = std::find_if(s_gdb_clients.begin(), s_gdb_clients.end(), | ||||
|                                  [this](const std::shared_ptr<ClientSocket>& rhs) { return (rhs.get() == this); }); | ||||
|   if (iter == s_gdb_clients.end()) | ||||
|   { | ||||
|     ERROR_LOG("Unknown GDB client disconnected? This should never happen."); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   s_gdb_clients.erase(iter); | ||||
| } | ||||
| 
 | ||||
| void GDBServer::ClientSocket::OnRead() | ||||
| { | ||||
|   const std::span<const u8> buffer = AcquireReadBuffer(); | ||||
|   if (buffer.empty()) | ||||
|     return; | ||||
| 
 | ||||
|   size_t buffer_offset = 0; | ||||
|   while (buffer_offset < buffer.size()) | ||||
|   { | ||||
|     size_t current_packet_size = 1; | ||||
|     bool packet_complete = false; | ||||
|     for (; (buffer_offset + current_packet_size) <= buffer.size(); current_packet_size++) | ||||
|     { | ||||
|       const std::string_view current_packet(reinterpret_cast<const char*>(buffer.data() + buffer_offset), | ||||
|                                             current_packet_size); | ||||
| 
 | ||||
|       if (GDBProtocol::IsPacketInterrupt(current_packet)) | ||||
|       { | ||||
|         DEV_LOG("{} > Interrupt request", GetRemoteAddress().ToString()); | ||||
|         System::PauseSystem(true); | ||||
|         packet_complete = true; | ||||
|         break; | ||||
|       } | ||||
|       else if (GDBProtocol::IsPacketContinue(current_packet)) | ||||
|       { | ||||
|         DEV_LOG("{} > Continue request", GetRemoteAddress().ToString()); | ||||
|         System::PauseSystem(false); | ||||
|         packet_complete = true; | ||||
|         break; | ||||
|       } | ||||
|       else if (GDBProtocol::IsPacketComplete(current_packet)) | ||||
|       { | ||||
|         // TODO: Make this not copy.
 | ||||
|         DEV_LOG("{} > {}", GetRemoteAddress().ToString(), current_packet); | ||||
|         SendPacket(GDBProtocol::ProcessPacket(current_packet)); | ||||
|         packet_complete = true; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     if (!packet_complete) | ||||
|     { | ||||
|       WARNING_LOG("Incomplete packet, got {} bytes.", buffer.size() - buffer_offset); | ||||
|       break; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       buffer_offset += current_packet_size; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ReleaseReadBuffer(buffer_offset); | ||||
| } | ||||
| 
 | ||||
| void GDBServer::ClientSocket::SendPacket(std::string_view sv) | ||||
| { | ||||
|   if (sv.empty()) | ||||
|     return; | ||||
| 
 | ||||
|   WARNING_LOG("Write: {}", sv); | ||||
|   if (size_t written = Write(sv.data(), sv.length()); written != sv.length()) | ||||
|     ERROR_LOG("Only wrote {} of {} bytes.", written, sv.length()); | ||||
| } | ||||
| 
 | ||||
| void GDBServer::ClientSocket::OnSystemPaused() | ||||
| { | ||||
|   if (!m_seen_resume) | ||||
|     return; | ||||
| 
 | ||||
|   m_seen_resume = false; | ||||
| 
 | ||||
|   // Generate a stop reply packet, insert '?' command to generate it.
 | ||||
|   SendPacket(GDBProtocol::ProcessPacket("$?#3f")); | ||||
| } | ||||
| 
 | ||||
| void GDBServer::ClientSocket::OnSystemResumed() | ||||
| { | ||||
|   m_seen_resume = true; | ||||
| 
 | ||||
|   // Send ack, in case GDB sent a continue request.
 | ||||
|   SendPacket("+"); | ||||
| } | ||||
| 
 | ||||
| bool GDBServer::Initialize(u16 port) | ||||
| { | ||||
|   Error error; | ||||
|   Assert(!s_gdb_listen_socket); | ||||
| 
 | ||||
|   const std::optional<SocketAddress> address = | ||||
|     SocketAddress::Parse(SocketAddress::Type::IPv4, "127.0.0.1", port, &error); | ||||
|   if (!address.has_value()) | ||||
|   { | ||||
|     ERROR_LOG("Failed to parse address: {}", error.GetDescription()); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   SocketMultiplexer* multiplexer = System::GetSocketMultiplexer(); | ||||
|   if (!multiplexer) | ||||
|     return false; | ||||
| 
 | ||||
|   s_gdb_listen_socket = multiplexer->CreateListenSocket<ClientSocket>(address.value(), &error); | ||||
|   if (!s_gdb_listen_socket) | ||||
|   { | ||||
|     ERROR_LOG("Failed to create listen socket: {}", error.GetDescription()); | ||||
|     System::ReleaseSocketMultiplexer(); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   INFO_LOG("GDB server is now listening on {}.", address->ToString()); | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool GDBServer::HasAnyClients() | ||||
| { | ||||
|   return !s_gdb_clients.empty(); | ||||
| } | ||||
| 
 | ||||
| void GDBServer::Shutdown() | ||||
| { | ||||
|   if (!s_gdb_listen_socket) | ||||
|     return; | ||||
| 
 | ||||
|   INFO_LOG("Disconnecting {} GDB clients...", s_gdb_clients.size()); | ||||
|   while (!s_gdb_clients.empty()) | ||||
|   { | ||||
|     // maintain a reference so we don't delete while in scope
 | ||||
|     std::shared_ptr<ClientSocket> client = s_gdb_clients.back(); | ||||
|     client->Close(); | ||||
|   } | ||||
| 
 | ||||
|   INFO_LOG("Stopping GDB server."); | ||||
|   s_gdb_listen_socket.reset(); | ||||
|   System::ReleaseSocketMultiplexer(); | ||||
| } | ||||
| 
 | ||||
| void GDBServer::OnSystemPaused() | ||||
| { | ||||
|   for (auto& it : s_gdb_clients) | ||||
|     it->OnSystemPaused(); | ||||
| } | ||||
| 
 | ||||
| void GDBServer::OnSystemResumed() | ||||
| { | ||||
|   for (auto& it : s_gdb_clients) | ||||
|     it->OnSystemResumed(); | ||||
| } | ||||
							
								
								
									
										14
									
								
								src/core/gdb_server.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/core/gdb_server.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
 | ||||
| // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | ||||
| 
 | ||||
| #pragma once | ||||
| #include <string_view> | ||||
| 
 | ||||
| namespace GDBServer { | ||||
| bool Initialize(u16 port); | ||||
| bool HasAnyClients(); | ||||
| void Shutdown(); | ||||
| 
 | ||||
| void OnSystemPaused(); | ||||
| void OnSystemResumed(); | ||||
| } // namespace GDBServer
 | ||||
|  | @ -82,8 +82,9 @@ Log_SetChannel(System); | |||
| 
 | ||||
| #ifndef __ANDROID__ | ||||
| #define ENABLE_PINE_SERVER 1 | ||||
| // #define ENABLE_GDB_SERVER 1
 | ||||
| #define ENABLE_GDB_SERVER 1 | ||||
| #define ENABLE_SOCKET_MULTIPLEXER 1 | ||||
| #include "gdb_server.h" | ||||
| #include "pine_server.h" | ||||
| #endif | ||||
| 
 | ||||
|  | @ -1289,6 +1290,10 @@ void System::PauseSystem(bool paused) | |||
|     if (g_settings.inhibit_screensaver) | ||||
|       PlatformMisc::ResumeScreensaver(); | ||||
| 
 | ||||
| #ifdef ENABLE_GDB_SERVER | ||||
|     GDBServer::OnSystemPaused(); | ||||
| #endif | ||||
| 
 | ||||
|     Host::OnSystemPaused(); | ||||
|     Host::OnIdleStateChanged(); | ||||
|     UpdateDisplayVSync(); | ||||
|  | @ -1303,6 +1308,10 @@ void System::PauseSystem(bool paused) | |||
|     if (g_settings.inhibit_screensaver) | ||||
|       PlatformMisc::SuspendScreensaver(); | ||||
| 
 | ||||
| #ifdef ENABLE_GDB_SERVER | ||||
|     GDBServer::OnSystemResumed(); | ||||
| #endif | ||||
| 
 | ||||
|     Host::OnSystemResumed(); | ||||
|     Host::OnIdleStateChanged(); | ||||
| 
 | ||||
|  | @ -1670,6 +1679,11 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error) | |||
|   if (g_settings.inhibit_screensaver) | ||||
|     PlatformMisc::SuspendScreensaver(); | ||||
| 
 | ||||
| #ifdef ENABLE_GDB_SERVER | ||||
|   if (g_settings.debugging.enable_gdb_server) | ||||
|     GDBServer::Initialize(g_settings.debugging.gdb_server_port); | ||||
| #endif | ||||
| 
 | ||||
|   Host::OnSystemStarted(); | ||||
|   Host::OnIdleStateChanged(); | ||||
| 
 | ||||
|  | @ -1816,6 +1830,10 @@ void System::DestroySystem() | |||
|   if (s_state == State::Shutdown) | ||||
|     return; | ||||
| 
 | ||||
| #ifdef ENABLE_GDB_SERVER | ||||
|   GDBServer::Shutdown(); | ||||
| #endif | ||||
| 
 | ||||
|   Host::ClearOSDMessages(); | ||||
| 
 | ||||
|   PostProcessing::Shutdown(); | ||||
|  | @ -4072,6 +4090,16 @@ void System::CheckForSettingsChanges(const Settings& old_settings) | |||
|     } | ||||
| 
 | ||||
|     PostProcessing::UpdateSettings(); | ||||
| 
 | ||||
| #ifdef ENABLE_GDB_SERVER | ||||
|     if (g_settings.debugging.enable_gdb_server != old_settings.debugging.enable_gdb_server || | ||||
|         g_settings.debugging.gdb_server_port != old_settings.debugging.gdb_server_port) | ||||
|     { | ||||
|       GDBServer::Shutdown(); | ||||
|       if (g_settings.debugging.enable_gdb_server) | ||||
|         GDBServer::Initialize(g_settings.debugging.gdb_server_port); | ||||
|     } | ||||
| #endif | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| find_package(Qt6 6.6.0 COMPONENTS Core Gui Widgets Network LinguistTools REQUIRED) | ||||
| find_package(Qt6 6.7.0 COMPONENTS Core Gui Widgets LinguistTools REQUIRED) | ||||
| 
 | ||||
| include(CopyBaseTranslations) | ||||
| 
 | ||||
|  | @ -96,10 +96,6 @@ set(SRCS | |||
|   gamesummarywidget.cpp | ||||
|   gamesummarywidget.h | ||||
|   gamesummarywidget.ui | ||||
|   gdbconnection.cpp | ||||
|   gdbconnection.h | ||||
|   gdbserver.cpp | ||||
|   gdbserver.h | ||||
|   graphicssettingswidget.cpp | ||||
|   graphicssettingswidget.h | ||||
|   graphicssettingswidget.ui | ||||
|  | @ -175,7 +171,7 @@ set(TS_FILES | |||
| add_executable(duckstation-qt ${SRCS} ${QM_FILES}) | ||||
| target_precompile_headers(duckstation-qt PRIVATE "pch.h") | ||||
| target_include_directories(duckstation-qt PRIVATE "${Qt6Gui_PRIVATE_INCLUDE_DIRS}" "${CMAKE_CURRENT_SOURCE_DIR}") | ||||
| target_link_libraries(duckstation-qt PRIVATE core common imgui minizip scmversion Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network) | ||||
| target_link_libraries(duckstation-qt PRIVATE core common imgui minizip scmversion Qt6::Core Qt6::Gui Qt6::Widgets) | ||||
| 
 | ||||
| # Our Qt builds may have exceptions on, so force them off. | ||||
| target_compile_definitions(duckstation-qt PRIVATE QT_NO_EXCEPTIONS) | ||||
|  |  | |||
|  | @ -36,8 +36,6 @@ | |||
|     <ClCompile Include="gamelistrefreshthread.cpp" /> | ||||
|     <ClCompile Include="gamelistwidget.cpp" /> | ||||
|     <ClCompile Include="gamesummarywidget.cpp" /> | ||||
|     <ClCompile Include="gdbconnection.cpp" /> | ||||
|     <ClCompile Include="gdbserver.cpp" /> | ||||
|     <ClCompile Include="mainwindow.cpp" /> | ||||
|     <ClCompile Include="memorycardsettingswidget.cpp" /> | ||||
|     <ClCompile Include="memorycardeditorwindow.cpp" /> | ||||
|  | @ -97,8 +95,6 @@ | |||
|     <QtMoc Include="gamelistrefreshthread.h" /> | ||||
|     <QtMoc Include="gamelistwidget.h" /> | ||||
|     <QtMoc Include="gamesummarywidget.h" /> | ||||
|     <QtMoc Include="gdbconnection.h" /> | ||||
|     <QtMoc Include="gdbserver.h" /> | ||||
|     <QtMoc Include="postprocessingsettingswidget.h" /> | ||||
|     <QtMoc Include="mainwindow.h" /> | ||||
|     <QtMoc Include="qthost.h" /> | ||||
|  | @ -243,8 +239,6 @@ | |||
|     <ClCompile Include="$(IntDir)moc_gamelistsettingswidget.cpp" /> | ||||
|     <ClCompile Include="$(IntDir)moc_gamelistwidget.cpp" /> | ||||
|     <ClCompile Include="$(IntDir)moc_gamesummarywidget.cpp" /> | ||||
|     <ClCompile Include="$(IntDir)moc_gdbconnection.cpp" /> | ||||
|     <ClCompile Include="$(IntDir)moc_gdbserver.cpp" /> | ||||
|     <ClCompile Include="$(IntDir)moc_graphicssettingswidget.cpp" /> | ||||
|     <ClCompile Include="$(IntDir)moc_debuggermodels.cpp" /> | ||||
|     <ClCompile Include="$(IntDir)moc_debuggerwindow.cpp" /> | ||||
|  |  | |||
|  | @ -15,8 +15,6 @@ | |||
|     <ClCompile Include="qtprogresscallback.cpp" /> | ||||
|     <ClCompile Include="interfacesettingswidget.cpp" /> | ||||
|     <ClCompile Include="advancedsettingswidget.cpp" /> | ||||
|     <ClCompile Include="gdbconnection.cpp" /> | ||||
|     <ClCompile Include="gdbserver.cpp" /> | ||||
|     <ClCompile Include="aboutdialog.cpp" /> | ||||
|     <ClCompile Include="memorycardsettingswidget.cpp" /> | ||||
|     <ClCompile Include="$(IntDir)qrc_resources.cpp" /> | ||||
|  | @ -126,12 +124,6 @@ | |||
|     <ClCompile Include="$(IntDir)moc_gamesummarywidget.cpp"> | ||||
|       <Filter>moc</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="$(IntDir)moc_gdbconnection.cpp"> | ||||
|       <Filter>moc</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="$(IntDir)moc_gdbserver.cpp"> | ||||
|       <Filter>moc</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="$(IntDir)moc_hotkeysettingswidget.cpp"> | ||||
|       <Filter>moc</Filter> | ||||
|     </ClCompile> | ||||
|  | @ -219,8 +211,6 @@ | |||
|     <QtMoc Include="interfacesettingswidget.h" /> | ||||
|     <QtMoc Include="qtprogresscallback.h" /> | ||||
|     <QtMoc Include="advancedsettingswidget.h" /> | ||||
|     <QtMoc Include="gdbconnection.h" /> | ||||
|     <QtMoc Include="gdbserver.h" /> | ||||
|     <QtMoc Include="aboutdialog.h" /> | ||||
|     <QtMoc Include="memorycardsettingswidget.h" /> | ||||
|     <QtMoc Include="inputbindingdialog.h" /> | ||||
|  |  | |||
|  | @ -1,95 +0,0 @@ | |||
| // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> and contributors.
 | ||||
| // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | ||||
| 
 | ||||
| #include "gdbconnection.h" | ||||
| #include "common/log.h" | ||||
| #include "core/gdb_protocol.h" | ||||
| #include "qthost.h" | ||||
| Log_SetChannel(GDBConnection); | ||||
| 
 | ||||
| GDBConnection::GDBConnection(GDBServer* parent, intptr_t descriptor) : QTcpSocket(parent), m_descriptor(descriptor) | ||||
| { | ||||
|   if (!setSocketDescriptor(descriptor)) | ||||
|   { | ||||
|     ERROR_LOG("{} failed to set socket descriptor: {}", descriptor, errorString().toStdString()); | ||||
|     deleteLater(); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   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); | ||||
| 
 | ||||
|   INFO_LOG("{} client connected", m_descriptor); | ||||
| 
 | ||||
|   m_seen_resume = System::IsPaused(); | ||||
|   g_emu_thread->setSystemPaused(true); | ||||
| } | ||||
| 
 | ||||
| void GDBConnection::gotDisconnected() | ||||
| { | ||||
|   INFO_LOG("{} client disconnected", m_descriptor); | ||||
|   deleteLater(); | ||||
| } | ||||
| 
 | ||||
| void GDBConnection::receivedData() | ||||
| { | ||||
|   qint64 bytesRead; | ||||
|   char buffer[256]; | ||||
| 
 | ||||
|   while ((bytesRead = read(buffer, sizeof(buffer))) > 0) | ||||
|   { | ||||
|     for (char c : std::string_view(buffer, bytesRead)) | ||||
|     { | ||||
|       m_readBuffer.push_back(c); | ||||
| 
 | ||||
|       if (GDBProtocol::IsPacketInterrupt(m_readBuffer)) | ||||
|       { | ||||
|         DEBUG_LOG("{} > Interrupt request", m_descriptor); | ||||
|         g_emu_thread->setSystemPaused(true); | ||||
|         m_readBuffer.erase(); | ||||
|       } | ||||
|       else if (GDBProtocol::IsPacketContinue(m_readBuffer)) | ||||
|       { | ||||
|         DEBUG_LOG("{} > Continue request", m_descriptor); | ||||
|         g_emu_thread->setSystemPaused(false); | ||||
|         m_readBuffer.erase(); | ||||
|       } | ||||
|       else if (GDBProtocol::IsPacketComplete(m_readBuffer)) | ||||
|       { | ||||
|         DEBUG_LOG("{} > {}", m_descriptor, m_readBuffer); | ||||
|         writePacket(GDBProtocol::ProcessPacket(m_readBuffer)); | ||||
|         m_readBuffer.erase(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   if (bytesRead == -1) | ||||
|   { | ||||
|     ERROR_LOG("{} failed to read from socket: {}", m_descriptor, errorString().toStdString()); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void GDBConnection::onEmulationPaused() | ||||
| { | ||||
|   if (m_seen_resume) | ||||
|   { | ||||
|     m_seen_resume = false; | ||||
|     // Generate a stop reply packet, insert '?' command to generate it.
 | ||||
|     writePacket(GDBProtocol::ProcessPacket("$?#3f")); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void GDBConnection::onEmulationResumed() | ||||
| { | ||||
|   m_seen_resume = true; | ||||
|   // Send ack, in case GDB sent a continue request.
 | ||||
|   writePacket("+"); | ||||
| } | ||||
| 
 | ||||
| void GDBConnection::writePacket(std::string_view packet) | ||||
| { | ||||
|   DEBUG_LOG("{} < {}", m_descriptor, packet); | ||||
|   if (write(packet.data(), packet.length()) == -1) | ||||
|     ERROR_LOG("{} failed to write to socket: {}", m_descriptor, errorString().toStdString()); | ||||
| } | ||||
|  | @ -1,29 +0,0 @@ | |||
| // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> and contributors.
 | ||||
| // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | ||||
| 
 | ||||
| #pragma once | ||||
| #include <QtCore/QThread> | ||||
| #include <QtNetwork/QTcpSocket> | ||||
| 
 | ||||
| class GDBServer; | ||||
| 
 | ||||
| class GDBConnection : public QTcpSocket | ||||
| { | ||||
|   Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   GDBConnection(GDBServer *parent, intptr_t descriptor); | ||||
| 
 | ||||
| public Q_SLOTS: | ||||
|   void gotDisconnected(); | ||||
|   void receivedData(); | ||||
|   void onEmulationPaused(); | ||||
|   void onEmulationResumed(); | ||||
| 
 | ||||
| private: | ||||
|   void writePacket(std::string_view data); | ||||
| 
 | ||||
|   intptr_t m_descriptor; | ||||
|   std::string m_readBuffer; | ||||
|   bool m_seen_resume; | ||||
| }; | ||||
|  | @ -1,52 +0,0 @@ | |||
| // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> and contributors.
 | ||||
| // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | ||||
| 
 | ||||
| #include "gdbserver.h" | ||||
| #include "common/log.h" | ||||
| #include "gdbconnection.h" | ||||
| #include "qthost.h" | ||||
| Log_SetChannel(GDBServer); | ||||
| 
 | ||||
| GDBServer::GDBServer(QObject* parent) : QTcpServer(parent) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| GDBServer::~GDBServer() | ||||
| { | ||||
|   stop(); | ||||
| } | ||||
| 
 | ||||
| void GDBServer::start(quint16 port) | ||||
| { | ||||
|   if (isListening()) | ||||
|   { | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   if (!listen(QHostAddress::LocalHost, port)) | ||||
|   { | ||||
|     ERROR_LOG("Failed to listen on TCP port {} for GDB server: {}", port, errorString().toUtf8().constData()); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   INFO_LOG("GDB server listening on TCP port {}", port); | ||||
| } | ||||
| 
 | ||||
| void GDBServer::stop() | ||||
| { | ||||
|   if (isListening()) | ||||
|   { | ||||
|     close(); | ||||
|     INFO_LOG("GDB server stopped"); | ||||
|   } | ||||
| 
 | ||||
|   for (QObject* connection : children()) | ||||
|   { | ||||
|     connection->deleteLater(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void GDBServer::incomingConnection(qintptr descriptor) | ||||
| { | ||||
|   new GDBConnection(this, descriptor); | ||||
| } | ||||
|  | @ -1,23 +0,0 @@ | |||
| // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> and contributors.
 | ||||
| // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | ||||
| 
 | ||||
| #pragma once | ||||
| #include "core/types.h" | ||||
| #include "gdbconnection.h" | ||||
| #include <QtNetwork/QTcpServer> | ||||
| 
 | ||||
| class GDBServer : public QTcpServer | ||||
| { | ||||
|   Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   GDBServer(QObject* parent = nullptr); | ||||
|   ~GDBServer(); | ||||
| 
 | ||||
| public Q_SLOTS: | ||||
|   void start(quint16 port); | ||||
|   void stop(); | ||||
| 
 | ||||
| protected: | ||||
|   void incomingConnection(qintptr socketDescriptor) override; | ||||
| }; | ||||
|  | @ -1826,16 +1826,6 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool cheevo | |||
|   if ((!starting && !running) || running) | ||||
|     m_open_debugger_on_start = false; | ||||
| 
 | ||||
|   if (!g_gdb_server->isListening() && g_settings.debugging.enable_gdb_server && starting) | ||||
|   { | ||||
|     QMetaObject::invokeMethod(g_gdb_server, "start", Qt::QueuedConnection, | ||||
|                               Q_ARG(quint16, g_settings.debugging.gdb_server_port)); | ||||
|   } | ||||
|   else if (g_gdb_server->isListening() && !running) | ||||
|   { | ||||
|     QMetaObject::invokeMethod(g_gdb_server, "stop", Qt::QueuedConnection); | ||||
|   } | ||||
| 
 | ||||
|   m_ui.statusBar->clearMessage(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| #include "core/fullscreen_ui.h" | ||||
| #include "core/game_database.h" | ||||
| #include "core/game_list.h" | ||||
| #include "core/gdb_server.h" | ||||
| #include "core/gpu.h" | ||||
| #include "core/host.h" | ||||
| #include "core/imgui_overlays.h" | ||||
|  | @ -81,6 +82,9 @@ static constexpr u32 BACKGROUND_CONTROLLER_POLLING_INTERVAL = 100; | |||
| /// Poll at half the vsync rate for FSUI to reduce the chance of getting a press+release in the same frame.
 | ||||
| static constexpr u32 FULLSCREEN_UI_CONTROLLER_POLLING_INTERVAL = 8; | ||||
| 
 | ||||
| /// Poll at 1ms when running GDB server. We can get rid of this once we move networking to its own thread.
 | ||||
| static constexpr u32 GDB_SERVER_POLLING_INTERVAL = 1; | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////
 | ||||
| // Local function declarations
 | ||||
| //////////////////////////////////////////////////////////////////////////
 | ||||
|  | @ -116,8 +120,7 @@ static bool s_start_fullscreen_ui_fullscreen = false; | |||
| static bool s_run_setup_wizard = false; | ||||
| static bool s_cleanup_after_update = false; | ||||
| 
 | ||||
| EmuThread* g_emu_thread; | ||||
| GDBServer* g_gdb_server; | ||||
| EmuThread* g_emu_thread = nullptr; | ||||
| 
 | ||||
| EmuThread::EmuThread(QThread* ui_thread) : QThread(), m_ui_thread(ui_thread) | ||||
| { | ||||
|  | @ -1670,8 +1673,13 @@ void EmuThread::startBackgroundControllerPollTimer() | |||
|   if (m_background_controller_polling_timer->isActive()) | ||||
|     return; | ||||
| 
 | ||||
|   m_background_controller_polling_timer->start( | ||||
|     FullscreenUI::IsInitialized() ? FULLSCREEN_UI_CONTROLLER_POLLING_INTERVAL : BACKGROUND_CONTROLLER_POLLING_INTERVAL); | ||||
|   u32 poll_interval = BACKGROUND_CONTROLLER_POLLING_INTERVAL; | ||||
|   if (FullscreenUI::IsInitialized()) | ||||
|     poll_interval = FULLSCREEN_UI_CONTROLLER_POLLING_INTERVAL; | ||||
|   if (GDBServer::HasAnyClients()) | ||||
|     poll_interval = GDB_SERVER_POLLING_INTERVAL; | ||||
| 
 | ||||
|   m_background_controller_polling_timer->start(poll_interval); | ||||
| } | ||||
| 
 | ||||
| void EmuThread::stopBackgroundControllerPollTimer() | ||||
|  | @ -1687,8 +1695,6 @@ void EmuThread::start() | |||
|   AssertMsg(!g_emu_thread, "Emu thread does not exist"); | ||||
| 
 | ||||
|   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->m_started_semaphore.acquire(); | ||||
|   g_emu_thread->moveToThread(g_emu_thread); | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "gdbserver.h" | ||||
| #include "qtutils.h" | ||||
| 
 | ||||
| #include "core/game_list.h" | ||||
|  | @ -246,7 +245,6 @@ private: | |||
| }; | ||||
| 
 | ||||
| extern EmuThread* g_emu_thread; | ||||
| extern GDBServer* g_gdb_server; | ||||
| 
 | ||||
| namespace QtHost { | ||||
| /// Sets batch mode (exit after game shutdown).
 | ||||
|  |  | |||
|  | @ -755,6 +755,9 @@ void BufferedStreamSocket::ReleaseReadBuffer(size_t bytes_consumed) | |||
| 
 | ||||
| std::span<u8> BufferedStreamSocket::AcquireWriteBuffer(size_t wanted_bytes, bool allow_smaller /* = false */) | ||||
| { | ||||
|   if (!m_connected) | ||||
|     return {}; | ||||
| 
 | ||||
|   // If to get the desired space, we need to move backwards, do so.
 | ||||
|   if ((m_send_buffer_offset + m_send_buffer_size + wanted_bytes) > m_send_buffer.size()) | ||||
|   { | ||||
|  | @ -776,6 +779,9 @@ std::span<u8> BufferedStreamSocket::AcquireWriteBuffer(size_t wanted_bytes, bool | |||
| 
 | ||||
| void BufferedStreamSocket::ReleaseWriteBuffer(size_t bytes_written, bool commit /* = true */) | ||||
| { | ||||
|   if (!m_connected) | ||||
|     return; | ||||
| 
 | ||||
|   DebugAssert((m_send_buffer_offset + m_send_buffer_size + bytes_written) <= m_send_buffer.size()); | ||||
|   m_send_buffer_size += static_cast<u32>(bytes_written); | ||||
| 
 | ||||
|  | @ -819,6 +825,9 @@ size_t BufferedStreamSocket::Read(void* buffer, size_t buffer_size) | |||
| 
 | ||||
| size_t BufferedStreamSocket::Write(const void* buffer, size_t buffer_size) | ||||
| { | ||||
|   if (!m_connected) | ||||
|     return 0; | ||||
| 
 | ||||
|   // Read from receive buffer.
 | ||||
|   const std::span<u8> wrbuf = AcquireWriteBuffer(buffer_size, true); | ||||
|   if (wrbuf.empty()) | ||||
|  | @ -857,6 +866,16 @@ size_t BufferedStreamSocket::WriteVector(const void** buffers, const size_t* buf | |||
|   return written_bytes; | ||||
| } | ||||
| 
 | ||||
| void BufferedStreamSocket::Close() | ||||
| { | ||||
|   StreamSocket::Close(); | ||||
| 
 | ||||
|   m_receive_buffer_offset = 0; | ||||
|   m_receive_buffer_size = 0; | ||||
|   m_send_buffer_offset = 0; | ||||
|   m_send_buffer_size = 0; | ||||
| } | ||||
| 
 | ||||
| void BufferedStreamSocket::OnReadEvent() | ||||
| { | ||||
|   std::unique_lock lock(m_lock); | ||||
|  |  | |||
|  | @ -197,7 +197,7 @@ public: | |||
| 
 | ||||
|   static u32 GetSocketProtocolForAddress(const SocketAddress& sa); | ||||
| 
 | ||||
|   virtual void Close() override final; | ||||
|   virtual void Close() override; | ||||
| 
 | ||||
|   // Accessors
 | ||||
|   const SocketAddress& GetLocalAddress() const { return m_local_address; } | ||||
|  | @ -251,6 +251,7 @@ public: | |||
|   size_t Read(void* buffer, size_t buffer_size); | ||||
|   size_t Write(const void* buffer, size_t buffer_size); | ||||
|   size_t WriteVector(const void** buffers, const size_t* buffer_lengths, size_t num_buffers); | ||||
|   virtual void Close() override; | ||||
| 
 | ||||
| protected: | ||||
|   void OnReadEvent() override final; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Stenzek
						Stenzek