System: Move GDB server into core

This commit is contained in:
Stenzek 2024-05-27 00:10:39 +10:00
parent 55d96f86f0
commit 015804c434
No known key found for this signature in database
20 changed files with 307 additions and 274 deletions

View file

@ -32,7 +32,7 @@
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalLibraryDirectories>$(QtLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <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> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
@ -118,24 +118,21 @@
<!--Copy the needed dlls--> <!--Copy the needed dlls-->
<ItemGroup> <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')" /> <QtDlls Include="@(QtLibNames -> '$(QtBinDir)%(Identity).dll')" />
<!--Filter plugins to copy based on the observation that all debug versions end in "d"--> <!--Filter plugins to copy based on the observation that all debug versions end in "d"-->
<QtAllPlugins Include="$(QtPluginsDir)**\*$(QtLibSuffix).dll" /> <QtAllPlugins Include="$(QtPluginsDir)**\*$(QtLibSuffix).dll" />
<QtPlugins Condition="$(Configuration.Contains(Debug))" Include="@(QtAllPlugins)" /> <QtPlugins Condition="$(Configuration.Contains(Debug))" Include="@(QtAllPlugins)" />
<QtPlugins Condition="!$(Configuration.Contains(Debug))" Exclude="$(QtPluginsDir)**\*$(QtDebugSuffix).dll" Include="@(QtAllPlugins)" /> <QtPlugins Condition="!$(Configuration.Contains(Debug))" Exclude="$(QtPluginsDir)**\*$(QtDebugSuffix).dll" Include="@(QtAllPlugins)" />
<QtPluginsDest Include="@(QtPlugins -> '$(BinaryOutputDir)$(QtPluginFolder)\%(RecursiveDir)%(Filename)%(Extension)')" /> <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> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<QtConfFile>$(BinaryOutputDir)qt.conf</QtConfFile> <QtConfFile>$(BinaryOutputDir)qt.conf</QtConfFile>
</PropertyGroup> </PropertyGroup>
<Target Name="QtCopyBinaries" <Target Name="QtCopyBinaries"
AfterTargets="Build" AfterTargets="Build"
Inputs="@(QtDlls);@(QtPlugins);@(QtTLSDlls)" Inputs="@(QtDlls);@(QtPlugins)"
Outputs="@(QtDlls -> '$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(Extension)');@(QtPluginsDest);@(QtTLSDllsDest)"> Outputs="@(QtDlls -> '$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(Extension)');@(QtPluginsDest)">
<Message Text="Copying Qt .dlls" /> <Message Text="Copying Qt .dlls" />
<Copy <Copy
SourceFiles="@(QtDlls)" SourceFiles="@(QtDlls)"
@ -147,11 +144,6 @@
DestinationFiles="@(QtPluginsDest)" DestinationFiles="@(QtPluginsDest)"
SkipUnchangedFiles="true" SkipUnchangedFiles="true"
/> />
<Copy
SourceFiles="@(QtTLSDlls)"
DestinationFiles="@(QtTLSDllsDest)"
SkipUnchangedFiles="true"
/>
</Target> </Target>
<Target Name="QtCreateConf" <Target Name="QtCreateConf"
BeforeTargets="QtCopyBinaries" BeforeTargets="QtCopyBinaries"

View file

@ -40,8 +40,8 @@ add_library(core
game_database.h game_database.h
game_list.cpp game_list.cpp
game_list.h game_list.h
gdb_protocol.cpp gdb_server.cpp
gdb_protocol.h gdb_server.h
gpu.cpp gpu.cpp
gpu.h gpu.h
gpu_backend.cpp gpu_backend.cpp

View file

@ -43,6 +43,7 @@
<ClCompile Include="fullscreen_ui.cpp" /> <ClCompile Include="fullscreen_ui.cpp" />
<ClCompile Include="game_database.cpp" /> <ClCompile Include="game_database.cpp" />
<ClCompile Include="game_list.cpp" /> <ClCompile Include="game_list.cpp" />
<ClCompile Include="gdb_server.cpp" />
<ClCompile Include="gpu_backend.cpp" /> <ClCompile Include="gpu_backend.cpp" />
<ClCompile Include="gpu_commands.cpp" /> <ClCompile Include="gpu_commands.cpp" />
<ClCompile Include="gpu_hw_shadergen.cpp" /> <ClCompile Include="gpu_hw_shadergen.cpp" />
@ -51,7 +52,6 @@
<ClCompile Include="gpu_sw_backend.cpp" /> <ClCompile Include="gpu_sw_backend.cpp" />
<ClCompile Include="gte.cpp" /> <ClCompile Include="gte.cpp" />
<ClCompile Include="dma.cpp" /> <ClCompile Include="dma.cpp" />
<ClCompile Include="gdb_protocol.cpp" />
<ClCompile Include="gpu.cpp" /> <ClCompile Include="gpu.cpp" />
<ClCompile Include="gpu_hw.cpp" /> <ClCompile Include="gpu_hw.cpp" />
<ClCompile Include="host.cpp" /> <ClCompile Include="host.cpp" />
@ -122,6 +122,7 @@
<ClInclude Include="fullscreen_ui.h" /> <ClInclude Include="fullscreen_ui.h" />
<ClInclude Include="game_database.h" /> <ClInclude Include="game_database.h" />
<ClInclude Include="game_list.h" /> <ClInclude Include="game_list.h" />
<ClInclude Include="gdb_server.h" />
<ClInclude Include="gpu_backend.h" /> <ClInclude Include="gpu_backend.h" />
<ClInclude Include="gpu_hw_shadergen.h" /> <ClInclude Include="gpu_hw_shadergen.h" />
<ClInclude Include="gpu_shadergen.h" /> <ClInclude Include="gpu_shadergen.h" />
@ -131,7 +132,6 @@
<ClInclude Include="gte.h" /> <ClInclude Include="gte.h" />
<ClInclude Include="cpu_types.h" /> <ClInclude Include="cpu_types.h" />
<ClInclude Include="dma.h" /> <ClInclude Include="dma.h" />
<ClInclude Include="gdb_protocol.h" />
<ClInclude Include="gpu.h" /> <ClInclude Include="gpu.h" />
<ClInclude Include="gpu_hw.h" /> <ClInclude Include="gpu_hw.h" />
<ClInclude Include="gte_types.h" /> <ClInclude Include="gte_types.h" />

View file

@ -6,7 +6,6 @@
<ClCompile Include="cpu_disasm.cpp" /> <ClCompile Include="cpu_disasm.cpp" />
<ClCompile Include="bus.cpp" /> <ClCompile Include="bus.cpp" />
<ClCompile Include="dma.cpp" /> <ClCompile Include="dma.cpp" />
<ClCompile Include="gdb_protocol.cpp" />
<ClCompile Include="gpu.cpp" /> <ClCompile Include="gpu.cpp" />
<ClCompile Include="gpu_hw.cpp" /> <ClCompile Include="gpu_hw.cpp" />
<ClCompile Include="interrupt_controller.cpp" /> <ClCompile Include="interrupt_controller.cpp" />
@ -68,6 +67,7 @@
<ClCompile Include="cpu_newrec_compiler_aarch32.cpp" /> <ClCompile Include="cpu_newrec_compiler_aarch32.cpp" />
<ClCompile Include="justifier.cpp" /> <ClCompile Include="justifier.cpp" />
<ClCompile Include="pine_server.cpp" /> <ClCompile Include="pine_server.cpp" />
<ClCompile Include="gdb_server.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="types.h" /> <ClInclude Include="types.h" />
@ -120,7 +120,6 @@
<ClInclude Include="gpu_sw_backend.h" /> <ClInclude Include="gpu_sw_backend.h" />
<ClInclude Include="texture_replacements.h" /> <ClInclude Include="texture_replacements.h" />
<ClInclude Include="multitap.h" /> <ClInclude Include="multitap.h" />
<ClInclude Include="gdb_protocol.h" />
<ClInclude Include="host.h" /> <ClInclude Include="host.h" />
<ClInclude Include="achievements.h" /> <ClInclude Include="achievements.h" />
<ClInclude Include="game_database.h" /> <ClInclude Include="game_database.h" />
@ -142,5 +141,6 @@
<ClInclude Include="achievements_private.h" /> <ClInclude Include="achievements_private.h" />
<ClInclude Include="justifier.h" /> <ClInclude Include="justifier.h" />
<ClInclude Include="pine_server.h" /> <ClInclude Include="pine_server.h" />
<ClInclude Include="gdb_server.h" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -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

View file

@ -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) // 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 "bus.h"
#include "cpu_core.h" #include "cpu_core.h"
#include "cpu_core_private.h" #include "cpu_core_private.h"
@ -11,6 +11,8 @@
#include "common/small_string.h" #include "common/small_string.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "util/sockets.h"
#include <functional> #include <functional>
#include <iomanip> #include <iomanip>
#include <map> #include <map>
@ -20,6 +22,14 @@
Log_SetChannel(GDBProtocol); 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 { namespace GDBProtocol {
static u8* GetMemoryPointer(PhysicalMemoryAddress address, u32 length) static u8* GetMemoryPointer(PhysicalMemoryAddress address, u32 length)
@ -345,3 +355,210 @@ std::string ProcessPacket(std::string_view data)
} }
} // namespace GDBProtocol } // 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
View 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

View file

@ -82,8 +82,9 @@ Log_SetChannel(System);
#ifndef __ANDROID__ #ifndef __ANDROID__
#define ENABLE_PINE_SERVER 1 #define ENABLE_PINE_SERVER 1
// #define ENABLE_GDB_SERVER 1 #define ENABLE_GDB_SERVER 1
#define ENABLE_SOCKET_MULTIPLEXER 1 #define ENABLE_SOCKET_MULTIPLEXER 1
#include "gdb_server.h"
#include "pine_server.h" #include "pine_server.h"
#endif #endif
@ -1289,6 +1290,10 @@ void System::PauseSystem(bool paused)
if (g_settings.inhibit_screensaver) if (g_settings.inhibit_screensaver)
PlatformMisc::ResumeScreensaver(); PlatformMisc::ResumeScreensaver();
#ifdef ENABLE_GDB_SERVER
GDBServer::OnSystemPaused();
#endif
Host::OnSystemPaused(); Host::OnSystemPaused();
Host::OnIdleStateChanged(); Host::OnIdleStateChanged();
UpdateDisplayVSync(); UpdateDisplayVSync();
@ -1303,6 +1308,10 @@ void System::PauseSystem(bool paused)
if (g_settings.inhibit_screensaver) if (g_settings.inhibit_screensaver)
PlatformMisc::SuspendScreensaver(); PlatformMisc::SuspendScreensaver();
#ifdef ENABLE_GDB_SERVER
GDBServer::OnSystemResumed();
#endif
Host::OnSystemResumed(); Host::OnSystemResumed();
Host::OnIdleStateChanged(); Host::OnIdleStateChanged();
@ -1670,6 +1679,11 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
if (g_settings.inhibit_screensaver) if (g_settings.inhibit_screensaver)
PlatformMisc::SuspendScreensaver(); 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::OnSystemStarted();
Host::OnIdleStateChanged(); Host::OnIdleStateChanged();
@ -1816,6 +1830,10 @@ void System::DestroySystem()
if (s_state == State::Shutdown) if (s_state == State::Shutdown)
return; return;
#ifdef ENABLE_GDB_SERVER
GDBServer::Shutdown();
#endif
Host::ClearOSDMessages(); Host::ClearOSDMessages();
PostProcessing::Shutdown(); PostProcessing::Shutdown();
@ -4072,6 +4090,16 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
} }
PostProcessing::UpdateSettings(); 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 else
{ {

View file

@ -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) include(CopyBaseTranslations)
@ -96,10 +96,6 @@ set(SRCS
gamesummarywidget.cpp gamesummarywidget.cpp
gamesummarywidget.h gamesummarywidget.h
gamesummarywidget.ui gamesummarywidget.ui
gdbconnection.cpp
gdbconnection.h
gdbserver.cpp
gdbserver.h
graphicssettingswidget.cpp graphicssettingswidget.cpp
graphicssettingswidget.h graphicssettingswidget.h
graphicssettingswidget.ui graphicssettingswidget.ui
@ -175,7 +171,7 @@ set(TS_FILES
add_executable(duckstation-qt ${SRCS} ${QM_FILES}) add_executable(duckstation-qt ${SRCS} ${QM_FILES})
target_precompile_headers(duckstation-qt PRIVATE "pch.h") target_precompile_headers(duckstation-qt PRIVATE "pch.h")
target_include_directories(duckstation-qt PRIVATE "${Qt6Gui_PRIVATE_INCLUDE_DIRS}" "${CMAKE_CURRENT_SOURCE_DIR}") 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. # Our Qt builds may have exceptions on, so force them off.
target_compile_definitions(duckstation-qt PRIVATE QT_NO_EXCEPTIONS) target_compile_definitions(duckstation-qt PRIVATE QT_NO_EXCEPTIONS)

View file

@ -36,8 +36,6 @@
<ClCompile Include="gamelistrefreshthread.cpp" /> <ClCompile Include="gamelistrefreshthread.cpp" />
<ClCompile Include="gamelistwidget.cpp" /> <ClCompile Include="gamelistwidget.cpp" />
<ClCompile Include="gamesummarywidget.cpp" /> <ClCompile Include="gamesummarywidget.cpp" />
<ClCompile Include="gdbconnection.cpp" />
<ClCompile Include="gdbserver.cpp" />
<ClCompile Include="mainwindow.cpp" /> <ClCompile Include="mainwindow.cpp" />
<ClCompile Include="memorycardsettingswidget.cpp" /> <ClCompile Include="memorycardsettingswidget.cpp" />
<ClCompile Include="memorycardeditorwindow.cpp" /> <ClCompile Include="memorycardeditorwindow.cpp" />
@ -97,8 +95,6 @@
<QtMoc Include="gamelistrefreshthread.h" /> <QtMoc Include="gamelistrefreshthread.h" />
<QtMoc Include="gamelistwidget.h" /> <QtMoc Include="gamelistwidget.h" />
<QtMoc Include="gamesummarywidget.h" /> <QtMoc Include="gamesummarywidget.h" />
<QtMoc Include="gdbconnection.h" />
<QtMoc Include="gdbserver.h" />
<QtMoc Include="postprocessingsettingswidget.h" /> <QtMoc Include="postprocessingsettingswidget.h" />
<QtMoc Include="mainwindow.h" /> <QtMoc Include="mainwindow.h" />
<QtMoc Include="qthost.h" /> <QtMoc Include="qthost.h" />
@ -243,8 +239,6 @@
<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_gamesummarywidget.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_graphicssettingswidget.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" />

View file

@ -15,8 +15,6 @@
<ClCompile Include="qtprogresscallback.cpp" /> <ClCompile Include="qtprogresscallback.cpp" />
<ClCompile Include="interfacesettingswidget.cpp" /> <ClCompile Include="interfacesettingswidget.cpp" />
<ClCompile Include="advancedsettingswidget.cpp" /> <ClCompile Include="advancedsettingswidget.cpp" />
<ClCompile Include="gdbconnection.cpp" />
<ClCompile Include="gdbserver.cpp" />
<ClCompile Include="aboutdialog.cpp" /> <ClCompile Include="aboutdialog.cpp" />
<ClCompile Include="memorycardsettingswidget.cpp" /> <ClCompile Include="memorycardsettingswidget.cpp" />
<ClCompile Include="$(IntDir)qrc_resources.cpp" /> <ClCompile Include="$(IntDir)qrc_resources.cpp" />
@ -126,12 +124,6 @@
<ClCompile Include="$(IntDir)moc_gamesummarywidget.cpp"> <ClCompile Include="$(IntDir)moc_gamesummarywidget.cpp">
<Filter>moc</Filter> <Filter>moc</Filter>
</ClCompile> </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"> <ClCompile Include="$(IntDir)moc_hotkeysettingswidget.cpp">
<Filter>moc</Filter> <Filter>moc</Filter>
</ClCompile> </ClCompile>
@ -219,8 +211,6 @@
<QtMoc Include="interfacesettingswidget.h" /> <QtMoc Include="interfacesettingswidget.h" />
<QtMoc Include="qtprogresscallback.h" /> <QtMoc Include="qtprogresscallback.h" />
<QtMoc Include="advancedsettingswidget.h" /> <QtMoc Include="advancedsettingswidget.h" />
<QtMoc Include="gdbconnection.h" />
<QtMoc Include="gdbserver.h" />
<QtMoc Include="aboutdialog.h" /> <QtMoc Include="aboutdialog.h" />
<QtMoc Include="memorycardsettingswidget.h" /> <QtMoc Include="memorycardsettingswidget.h" />
<QtMoc Include="inputbindingdialog.h" /> <QtMoc Include="inputbindingdialog.h" />

View file

@ -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());
}

View file

@ -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;
};

View file

@ -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);
}

View file

@ -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;
};

View file

@ -1826,16 +1826,6 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool cheevo
if ((!starting && !running) || running) if ((!starting && !running) || running)
m_open_debugger_on_start = false; 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(); m_ui.statusBar->clearMessage();
} }

View file

@ -16,6 +16,7 @@
#include "core/fullscreen_ui.h" #include "core/fullscreen_ui.h"
#include "core/game_database.h" #include "core/game_database.h"
#include "core/game_list.h" #include "core/game_list.h"
#include "core/gdb_server.h"
#include "core/gpu.h" #include "core/gpu.h"
#include "core/host.h" #include "core/host.h"
#include "core/imgui_overlays.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. /// 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; 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 // 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_run_setup_wizard = false;
static bool s_cleanup_after_update = false; static bool s_cleanup_after_update = false;
EmuThread* g_emu_thread; EmuThread* g_emu_thread = nullptr;
GDBServer* g_gdb_server;
EmuThread::EmuThread(QThread* ui_thread) : QThread(), m_ui_thread(ui_thread) 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()) if (m_background_controller_polling_timer->isActive())
return; return;
m_background_controller_polling_timer->start( u32 poll_interval = BACKGROUND_CONTROLLER_POLLING_INTERVAL;
FullscreenUI::IsInitialized() ? FULLSCREEN_UI_CONTROLLER_POLLING_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() void EmuThread::stopBackgroundControllerPollTimer()
@ -1687,8 +1695,6 @@ void EmuThread::start()
AssertMsg(!g_emu_thread, "Emu thread does not exist"); AssertMsg(!g_emu_thread, "Emu thread does not exist");
g_emu_thread = new EmuThread(QThread::currentThread()); 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->QThread::start();
g_emu_thread->m_started_semaphore.acquire(); g_emu_thread->m_started_semaphore.acquire();
g_emu_thread->moveToThread(g_emu_thread); g_emu_thread->moveToThread(g_emu_thread);

View file

@ -3,7 +3,6 @@
#pragma once #pragma once
#include "gdbserver.h"
#include "qtutils.h" #include "qtutils.h"
#include "core/game_list.h" #include "core/game_list.h"
@ -246,7 +245,6 @@ private:
}; };
extern EmuThread* g_emu_thread; extern EmuThread* g_emu_thread;
extern GDBServer* g_gdb_server;
namespace QtHost { namespace QtHost {
/// Sets batch mode (exit after game shutdown). /// Sets batch mode (exit after game shutdown).

View file

@ -755,6 +755,9 @@ void BufferedStreamSocket::ReleaseReadBuffer(size_t bytes_consumed)
std::span<u8> BufferedStreamSocket::AcquireWriteBuffer(size_t wanted_bytes, bool allow_smaller /* = false */) 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 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()) 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 */) 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()); DebugAssert((m_send_buffer_offset + m_send_buffer_size + bytes_written) <= m_send_buffer.size());
m_send_buffer_size += static_cast<u32>(bytes_written); 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) size_t BufferedStreamSocket::Write(const void* buffer, size_t buffer_size)
{ {
if (!m_connected)
return 0;
// Read from receive buffer. // Read from receive buffer.
const std::span<u8> wrbuf = AcquireWriteBuffer(buffer_size, true); const std::span<u8> wrbuf = AcquireWriteBuffer(buffer_size, true);
if (wrbuf.empty()) if (wrbuf.empty())
@ -857,6 +866,16 @@ size_t BufferedStreamSocket::WriteVector(const void** buffers, const size_t* buf
return written_bytes; 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() void BufferedStreamSocket::OnReadEvent()
{ {
std::unique_lock lock(m_lock); std::unique_lock lock(m_lock);

View file

@ -197,7 +197,7 @@ public:
static u32 GetSocketProtocolForAddress(const SocketAddress& sa); static u32 GetSocketProtocolForAddress(const SocketAddress& sa);
virtual void Close() override final; virtual void Close() override;
// Accessors // Accessors
const SocketAddress& GetLocalAddress() const { return m_local_address; } const SocketAddress& GetLocalAddress() const { return m_local_address; }
@ -251,6 +251,7 @@ public:
size_t Read(void* buffer, size_t buffer_size); size_t Read(void* buffer, size_t buffer_size);
size_t Write(const 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); size_t WriteVector(const void** buffers, const size_t* buffer_lengths, size_t num_buffers);
virtual void Close() override;
protected: protected:
void OnReadEvent() override final; void OnReadEvent() override final;