System: Move GDB server into core
This commit is contained in:
@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user