Merge pull request #3268 from Sonicadvance1/more_gdbserver

More GdbServer improvements
This commit is contained in:
Ryan Houdek authored and GitHub committed 2023-11-15 21:15:14 -08:00
commit 1c115096c4
4 files changed
+143 -20

No files matched your search

+12 -11
View File
@@ -104,6 +104,17 @@ namespace FEXServerClient {
return GetServerLockFolder() + "RootFS.lock";
}
fextl::string GetTempFolder() {
auto XDGRuntimeEnv = getenv("XDG_RUNTIME_DIR");
if (XDGRuntimeEnv) {
// If the XDG runtime directory works then use that.
return XDGRuntimeEnv;
}
// Fallback to `/tmp/` if XDG_RUNTIME_DIR doesn't exist.
// Might not be ideal but we don't have much of a choice.
return fextl::string{std::filesystem::temp_directory_path().string()};
}
fextl::string GetServerMountFolder() {
// We need a FEXServer mount directory that has some tricky requirements.
// - We don't want to use `/tmp/` if possible.
@@ -119,17 +130,7 @@ namespace FEXServerClient {
// - If this path doesn't exist then fallback to `/tmp/` as a last resort.
// - pressure-vessel explicitly creates an internal XDG_RUNTIME_DIR inside its chroot.
// - This is okay since pressure-vessel rbinds the FEX rootfs from the host to `/run/pressure-vessel/interpreter-root`.
fextl::string Folder{};
auto XDGRuntimeEnv = getenv("XDG_RUNTIME_DIR");
if (XDGRuntimeEnv) {
// If the XDG runtime directory works then use that.
Folder = XDGRuntimeEnv;
}
else {
// Fallback to `/tmp/` if XDG_RUNTIME_DIR doesn't exist.
// Might not be ideal but we don't have much of a choice.
Folder = std::filesystem::temp_directory_path().string();
}
auto Folder = GetTempFolder();
if (FEXCore::Config::FindContainer() == "pressure-vessel") {
// In pressure-vessel the mount point changes location.
+1
View File
@@ -50,6 +50,7 @@ namespace FEXServerClient {
fextl::string GetServerLockFolder();
fextl::string GetServerLockFile();
fextl::string GetServerRootFSLockFile();
fextl::string GetTempFolder();
fextl::string GetServerMountFolder();
fextl::string GetServerSocketName();
int GetServerFD();
@@ -44,12 +44,13 @@ $end_info$
#endif
#include <errno.h>
#include <fcntl.h>
#include <fmt/format.h>
#include <poll.h>
#include <signal.h>
#include <stddef.h>
#include <string_view>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <utility>
@@ -73,6 +74,11 @@ void GdbServer::WaitForThreadWakeup() {
ThreadBreakEvent.Wait();
}
GdbServer::~GdbServer() {
CoreShuttingDown = true;
close(ListenSocket);
}
GdbServer::GdbServer(FEXCore::Context::Context *ctx, FEXCore::SignalDelegator *SignalDelegation, FEXCore::HLE::SyscallHandler *const SyscallHandler)
: CTX(ctx)
, SyscallHandler {SyscallHandler} {
@@ -143,6 +149,10 @@ static fextl::string encodeHex(const unsigned char *data, size_t length) {
return ss.str();
}
static fextl::string encodeHex(std::string_view str) {
return encodeHex(reinterpret_cast<const unsigned char*>(str.data()), str.size());
}
static fextl::string getThreadName(uint32_t ThreadID) {
const auto ThreadFile = fextl::fmt::format("/proc/{}/task/{}/comm", getpid(), ThreadID);
fextl::string ThreadName;
@@ -818,7 +828,6 @@ GdbServer::HandledPacketType GdbServer::handleMemory(const fextl::string &packet
}
}
GdbServer::HandledPacketType GdbServer::handleQuery(const fextl::string &packet) {
const auto match = [&](const char *str) -> bool { return packet.rfind(str, 0) == 0; };
const auto MatchStr = [](const fextl::string &Str, const char *str) -> bool { return Str.rfind(str, 0) == 0; };
@@ -870,12 +879,17 @@ GdbServer::HandledPacketType GdbServer::handleQuery(const fextl::string &packet)
SupportedFeatures += "QNonStop+;";
SupportedFeatures += "qXfer:osdata:read+;";
SupportedFeatures += "QStartNoAckMode+;";
// Causes GDB to crash?
// SupportedFeatures += "QStartNoAckMode+;";
// TODO: Support breakpoints
// SupportedFeatures += "swbreak+;";
// SupportedFeatures += "hwbreak+;";
// SupportedFeatures += "BreakpointCommands+;";
// TODO: If we want to support conditional breakpoints then we need to support single stepping.
// SupportedFeatures += "ConditionalBreakpoints+;";
for (auto &Feature : Features) {
if (MatchStr(Feature, "swbreak+")) {
SupportedFeatures += "swbreak+;";
}
@@ -982,6 +996,61 @@ GdbServer::HandledPacketType GdbServer::handleQuery(const fextl::string &packet)
return {"OK", HandledPacketType::TYPE_ACK};
}
// lldb specific queries
if (match("qHostInfo")) {
// Returns Key:Value pairs separated by ;
// eg:
// triple:7838365f36342d70632d6c696e75782d676e75;
// ptrsize:8;
// distribution_id:7562756e7475;
// watchpoint_exceptions_received:after;
// endian:little;
// os_version:6.3.3;
// os_build:362e332e332d3036303330332d67656e65726963;
// os_kernel:2332303233303531373133333620534d5020505245454d50545f44594e414d494320576564204d61792031372031333a34353a3139205554432032303233;
// hostname:7279616e682d545235303030;
fextl::string HostFeatures{};
// 64-bit always returned for the host environment.
// qProcessInfo will return i386 or not.
HostFeatures += fextl::fmt::format("triple:{};", encodeHex("x86_64-pc-linux-gnu"));
HostFeatures += "ptrsize:8;";
// Always little-endian.
HostFeatures += "endian:little;";
struct utsname buf{};
if (uname(&buf) != -1) {
uint32_t Major{};
uint32_t Minor{};
uint32_t Patch{};
// Parse kernel version in the form of `<Major>.<Minor>.<Patch>[Optional Data]`
const auto End = buf.release + sizeof(buf.release);
auto Results = std::from_chars(buf.release, End, Major, 10);
Results = std::from_chars(Results.ptr + 1, End, Minor, 10);
Results = std::from_chars(Results.ptr + 1, End, Patch, 10);
HostFeatures += fextl::fmt::format("os_version:{}.{}.{};", Major, Minor, Patch);
// os_build returns the release untouched.
HostFeatures += fextl::fmt::format("os_build:{};", encodeHex(buf.release));
HostFeatures += fextl::fmt::format("os_kernel:{};", encodeHex(buf.version));
HostFeatures += fextl::fmt::format("hostname:{};", encodeHex(buf.nodename));
}
// TODO: distribution_id should be fetched with `lsb_release -i`
// TODO: watchpoint_exceptions_received is unsupported
return {std::move(HostFeatures), HandledPacketType::TYPE_ACK};
}
if (match("qGetWorkingDir")) {
char Tmp[PATH_MAX];
if (getcwd(Tmp, PATH_MAX)) {
return {encodeHex(Tmp), HandledPacketType::TYPE_ACK};
}
return {"E00", HandledPacketType::TYPE_ACK};
}
return {"", HandledPacketType::TYPE_UNKNOWN};
}
@@ -1224,11 +1293,45 @@ void GdbServer::SendPacketPair(const HandledPacketType& response) {
}
}
GdbServer::WaitForConnectionResult GdbServer::WaitForConnection() {
while (!CoreShuttingDown.load()) {
struct pollfd PollFD {
.fd = ListenSocket,
.events = POLLIN | POLLPRI | POLLRDHUP,
.revents = 0,
};
int Result = ppoll(&PollFD, 1, nullptr, nullptr);
if (Result > 0) {
if (PollFD.revents & POLLIN) {
CommsStream = OpenSocket();
return WaitForConnectionResult::CONNECTION;
}
else if (PollFD.revents & (POLLHUP | POLLERR | POLLNVAL)) {
// Listen socket error or shutting down
LogMan::Msg::EFmt("[GdbServer] gdbserver shutting down: {}");
return WaitForConnectionResult::ERROR;
}
}
else if (Result == -1) {
LogMan::Msg::EFmt("[GdbServer] poll failure: {}", errno);
}
}
LogMan::Msg::EFmt("[GdbServer] Shutting Down");
return WaitForConnectionResult::ERROR;
}
void GdbServer::GdbServerLoop() {
OpenListenSocket();
if (ListenSocket == -1) {
// Couldn't open socket, just exit.
return;
}
while (!CoreShuttingDown.load()) {
CommsStream = OpenSocket();
if (WaitForConnection() == WaitForConnectionResult::ERROR) {
break;
}
HandledPacketType response{};
@@ -1281,6 +1384,7 @@ void GdbServer::GdbServerLoop() {
unlink(GdbUnixSocketPath.c_str());
}
static void* ThreadHandler(void *Arg) {
FEXCore::Threads::SetThreadName("FEX:gdbserver");
auto This = reinterpret_cast<FEX::GdbServer*>(Arg);
This->GdbServerLoop();
return nullptr;
@@ -1293,7 +1397,7 @@ void GdbServer::StartThread() {
}
void GdbServer::OpenListenSocket() {
const auto GdbUnixPath = fextl::fmt::format("{}/FEX_gdbserver/", FEXServerClient::GetServerMountFolder());
const auto GdbUnixPath = fextl::fmt::format("{}/FEX_gdbserver/", FEXServerClient::GetTempFolder());
if (FHU::Filesystem::CreateDirectory(GdbUnixPath) == FHU::Filesystem::CreateDirectoryResult::ERROR) {
LogMan::Msg::EFmt("[GdbServer] Couldn't create gdbserver folder {}", GdbUnixPath);
return;
@@ -1313,8 +1417,19 @@ void GdbServer::OpenListenSocket() {
size_t SizeOfAddr = offsetof(sockaddr_un, sun_path) + GdbUnixSocketPath.size();
// Bind the socket to the path
int Result = bind(ListenSocket, reinterpret_cast<struct sockaddr*>(&addr), SizeOfAddr);
if (Result == -1) {
int Result{};
for (int attempt = 0; attempt < 2; ++attempt) {
Result = bind(ListenSocket, reinterpret_cast<struct sockaddr*>(&addr), SizeOfAddr);
if (Result == 0) {
break;
}
// This can happen periodically with execve. unlink the path and try again.
// The PID is reused but FEX likely started a gdbserver thread for the PID before execve.
unlink(GdbUnixSocketPath.c_str());
}
if (Result != 0) {
LogMan::Msg::EFmt("[GdbServer] Couldn't bind AF_UNIX socket '{}': {} {}\n", addr.sun_path, errno, strerror(errno));
close(ListenSocket);
ListenSocket = -1;
@@ -24,6 +24,7 @@ namespace FEX {
class GdbServer {
public:
GdbServer(FEXCore::Context::Context *ctx, FEXCore::SignalDelegator *SignalDelegation, FEXCore::HLE::SyscallHandler *const SyscallHandler);
~GdbServer();
// Public for threading
void GdbServerLoop();
@@ -36,6 +37,11 @@ private:
void Break(int signal);
void OpenListenSocket();
enum class WaitForConnectionResult {
CONNECTION,
ERROR,
};
WaitForConnectionResult WaitForConnection();
fextl::unique_ptr<std::iostream> OpenSocket();
void StartThread();
fextl::string ReadPacket(std::iostream &stream);