mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 10:00:16 +02:00
GdbServer: Breakpoint intergation
This commit is contained in:
1 parent
9a89741ff2
commit
2babc1292c
8 files changed
+111
-45
No files matched your search
@@ -21,6 +21,9 @@ namespace FEXCore::Config {
|
||||
case FEXCore::Config::CONFIG_SINGLESTEP:
|
||||
CTX->RunningMode = Config != 0 ? FEXCore::Context::CoreRunningMode::MODE_SINGLESTEP : FEXCore::Context::CoreRunningMode::MODE_RUN;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_GDBSERVER:
|
||||
Config != 0 ? CTX->StartGdbServer() : CTX->StopGdbServer();
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown configuration option");
|
||||
}
|
||||
}
|
||||
@@ -41,6 +44,8 @@ namespace FEXCore::Config {
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_SINGLESTEP:
|
||||
return CTX->RunningMode == FEXCore::Context::CoreRunningMode::MODE_SINGLESTEP ? 1 : 0;
|
||||
case FEXCore::Config::CONFIG_GDBSERVER:
|
||||
return CTX->GetGdbServerStatus();
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown configuration option");
|
||||
}
|
||||
|
||||
@@ -11,11 +11,13 @@
|
||||
#include <FEXCore/Utils/Event.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace FEXCore {
|
||||
class SyscallHandler;
|
||||
class BlockSamplingData;
|
||||
class GdbServer;
|
||||
|
||||
namespace CPU {
|
||||
class JITCore;
|
||||
@@ -42,6 +44,7 @@ namespace FEXCore::Context {
|
||||
int64_t MaxInstPerBlock {-1LL};
|
||||
uint64_t VirtualMemSize {1ULL << 36};
|
||||
FEXCore::Config::ConfigCore Core {FEXCore::Config::CONFIG_INTERPRETER};
|
||||
bool GdbServer {false};
|
||||
|
||||
// LLVM JIT options
|
||||
bool LLVM_MemoryValidation {false};
|
||||
@@ -68,6 +71,8 @@ namespace FEXCore::Context {
|
||||
CustomCPUFactoryType FallbackCPUFactory;
|
||||
std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> CustomExitHandler;
|
||||
|
||||
|
||||
|
||||
#ifdef BLOCKSTATS
|
||||
std::unique_ptr<FEXCore::BlockSamplingData> BlockData;
|
||||
#endif
|
||||
@@ -82,6 +87,10 @@ namespace FEXCore::Context {
|
||||
void Run();
|
||||
void Step();
|
||||
|
||||
bool GetGdbServerStatus() { return (bool)DebugServer; }
|
||||
void StartGdbServer();
|
||||
void StopGdbServer();
|
||||
|
||||
// Debugger interface
|
||||
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
|
||||
uint64_t GetThreadCount() const;
|
||||
@@ -128,6 +137,7 @@ namespace FEXCore::Context {
|
||||
uint64_t StartingRIP;
|
||||
IR::RegisterAllocationPass *RAPass {};
|
||||
std::mutex ExitMutex;
|
||||
std::unique_ptr<GdbServer> DebugServer;
|
||||
|
||||
bool StartPaused = false;
|
||||
#if ENABLE_JITSYMBOLS
|
||||
|
||||
@@ -284,17 +284,22 @@ namespace FEXCore::Context {
|
||||
uint64_t SlotSize = Loader->InitializeThreadSlot(TLSSlotWriter);
|
||||
Thread->State.State.rip = StartingRIP = Loader->DefaultRIP();
|
||||
|
||||
if (false) {
|
||||
auto gdb = new GdbServer(this, Loader);
|
||||
gdb->StartAndBlock();
|
||||
StartPaused = true;
|
||||
}
|
||||
|
||||
InitializeThread(Thread);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Context::StartGdbServer() {
|
||||
if (!DebugServer) {
|
||||
DebugServer = std::make_unique<GdbServer>(this);
|
||||
StartPaused = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Context::StopGdbServer() {
|
||||
DebugServer.reset();
|
||||
}
|
||||
|
||||
void Context::WaitForIdle() {
|
||||
do {
|
||||
bool AllPaused = true;
|
||||
|
||||
@@ -22,8 +22,22 @@
|
||||
namespace FEXCore
|
||||
{
|
||||
|
||||
GdbServer::GdbServer(FEXCore::Context::Context *ctx, FEXCore::CodeLoader *Loader) : CTX(ctx) {
|
||||
std::tie(std::ignore, std::ignore, data_offset) = Loader->GetLayout();
|
||||
static const std::string NO_REPLY = "}{\x7f}{"; // An "unlikely" packet
|
||||
|
||||
void GdbServer::Break() {
|
||||
std::lock_guard lk(sendMutex);
|
||||
if (CommsStream)
|
||||
SendPacket(*CommsStream, "S05");
|
||||
}
|
||||
|
||||
GdbServer::GdbServer(FEXCore::Context::Context *ctx) : CTX(ctx) {
|
||||
ctx->CustomExitHandler = [&](uint64_t ThreadId, FEXCore::Context::ExitReason ExitReason) {
|
||||
if (ExitReason == FEXCore::Context::ExitReason::EXIT_DEBUG) {
|
||||
this->Break();
|
||||
}
|
||||
};
|
||||
|
||||
StartThread();
|
||||
}
|
||||
|
||||
static int calculateChecksum(std::string &packet) {
|
||||
@@ -100,7 +114,7 @@ std::string GdbServer::ReadPacket(std::iostream &stream) {
|
||||
int expected_checksum = std::strtoul(hexString, nullptr, 16);
|
||||
|
||||
if (calculateChecksum(packet) == expected_checksum) {
|
||||
LogMan::Msg::E("Received Packet: \"%s\"", packet.c_str());
|
||||
LogMan::Msg::I("Received Packet: \"%s\"", packet.c_str());
|
||||
stream << "+" << std::flush;
|
||||
return packet;
|
||||
} else {
|
||||
@@ -141,8 +155,12 @@ static std::string escapePacket(std::string packet) {
|
||||
}
|
||||
|
||||
void GdbServer::SendPacket(std::ostream &stream, std::string packet) {
|
||||
// In-band signaling, not a great design
|
||||
if (packet == NO_REPLY)
|
||||
return;
|
||||
|
||||
auto escaped = escapePacket(packet);
|
||||
LogMan::Msg::E("GdbServer Reply: %s", escaped.c_str());
|
||||
//LogMan::Msg::E("GdbServer Reply: %s", escaped.c_str());
|
||||
stream << '$' << escaped << '#';
|
||||
stream << std::setfill('0') << std::setw(2) << std::hex << (int)calculateChecksum(escaped);
|
||||
stream << std::flush;
|
||||
@@ -150,7 +168,7 @@ void GdbServer::SendPacket(std::ostream &stream, std::string packet) {
|
||||
|
||||
std::string GdbServer::readRegs() {
|
||||
auto state = CTX->GetCPUState();
|
||||
state.rip = 0x47c990;
|
||||
// state.rip = 0x47c990;
|
||||
|
||||
return encodeHex((unsigned char *)&state, sizeof(state)).substr(0, 572*2);
|
||||
}
|
||||
@@ -432,14 +450,14 @@ std::string GdbServer::handleV(std::string& packet) {
|
||||
switch (action) {
|
||||
case 'c':
|
||||
CTX->Run();
|
||||
return ""; // fixme
|
||||
return NO_REPLY;
|
||||
case 's':
|
||||
CTX->Step();
|
||||
return ""; // fixme
|
||||
return NO_REPLY;
|
||||
|
||||
case 't':
|
||||
CTX->ShouldStop = true;
|
||||
return "";
|
||||
return NO_REPLY;
|
||||
default:
|
||||
return "E00";
|
||||
}
|
||||
@@ -466,19 +484,26 @@ std::string GdbServer::ProcessPacket(std::string &packet) {
|
||||
}
|
||||
}
|
||||
|
||||
void GdbServer::GdbServerLoop(std::unique_ptr<std::iostream> stream) {
|
||||
void GdbServer::GdbServerLoop() {
|
||||
CommsStream = OpenSocket();
|
||||
|
||||
std::string responce;
|
||||
|
||||
// Outer server loop. Handles packet start, ACK/NAK and break
|
||||
|
||||
int c;
|
||||
while ((c = stream->get()) >= 0 ) {
|
||||
while ((c = CommsStream->get()) >= 0 ) {
|
||||
switch (c) {
|
||||
case '$': {
|
||||
std::string packet = ReadPacket(*stream);
|
||||
std::string packet = ReadPacket(*CommsStream);
|
||||
responce = ProcessPacket(packet);
|
||||
std::cout << responce;
|
||||
SendPacket(*stream, responce);
|
||||
{
|
||||
std::lock_guard lk(sendMutex);
|
||||
SendPacket(*CommsStream, responce);
|
||||
}
|
||||
if (responce == "") {
|
||||
LogMan::Msg::D("Unknown packet %s", packet.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '+':
|
||||
@@ -486,19 +511,29 @@ void GdbServer::GdbServerLoop(std::unique_ptr<std::iostream> stream) {
|
||||
break;
|
||||
case '-':
|
||||
// NAK, Resend requested
|
||||
SendPacket(*stream, responce);
|
||||
{
|
||||
std::lock_guard lk(sendMutex);
|
||||
SendPacket(*CommsStream, responce);
|
||||
}
|
||||
break;
|
||||
case '\x03': // ASCII EOT
|
||||
LogMan::Msg::E("GdbServer: Break");
|
||||
LogMan::Msg::D("GdbServer: Break");
|
||||
CTX->Pause();
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::E("GdbServer: Unexpected byte %c (%02x)", c, c);
|
||||
LogMan::Msg::D("GdbServer: Unexpected byte %c (%02x)", c, c);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lk(sendMutex);
|
||||
CommsStream.release();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GdbServer::StartThread(std::unique_ptr<std::iostream> stream) {
|
||||
gdbServerThread = std::thread(&GdbServer::GdbServerLoop, this, std::move(stream));
|
||||
void GdbServer::StartThread() {
|
||||
gdbServerThread = std::thread(&GdbServer::GdbServerLoop, this);
|
||||
}
|
||||
|
||||
std::unique_ptr<std::iostream> GdbServer::OpenSocket() {
|
||||
@@ -535,7 +570,7 @@ std::unique_ptr<std::iostream> GdbServer::OpenSocket() {
|
||||
|
||||
// Block until a connection arrives
|
||||
|
||||
LogMan::Msg::E("GdbServer, waiting for connection");
|
||||
LogMan::Msg::E("GdbServer, waiting for connection on localhost:8086");
|
||||
listen(sockfd, 1);
|
||||
|
||||
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
|
||||
@@ -10,16 +10,14 @@ namespace FEXCore {
|
||||
|
||||
class GdbServer {
|
||||
public:
|
||||
GdbServer(FEXCore::Context::Context *ctx, FEXCore::CodeLoader *Loader);
|
||||
void StartAndBlock() { StartThread(OpenSocket()); Wait(); }
|
||||
GdbServer(FEXCore::Context::Context *ctx);
|
||||
|
||||
private:
|
||||
std::unique_ptr<std::iostream> OpenSocket();
|
||||
void StartThread(std::unique_ptr<std::iostream> stream);
|
||||
void GdbServerLoop(std::unique_ptr<std::iostream> stream);
|
||||
void Wait() {
|
||||
void Break();
|
||||
|
||||
}
|
||||
std::unique_ptr<std::iostream> OpenSocket();
|
||||
void StartThread();
|
||||
void GdbServerLoop();
|
||||
std::string ReadPacket(std::iostream &stream);
|
||||
void SendPacket(std::ostream &stream, std::string packet);
|
||||
|
||||
@@ -34,9 +32,8 @@ private:
|
||||
FEXCore::Context::Context *CTX;
|
||||
std::thread gdbServerThread;
|
||||
uint64_t data_offset;
|
||||
|
||||
std::condition_variable waiting;
|
||||
|
||||
std::unique_ptr<std::iostream> CommsStream;
|
||||
std::mutex sendMutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -378,8 +378,14 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const
|
||||
ud2();
|
||||
break;
|
||||
case 4: // HLT
|
||||
case 6: // INT3
|
||||
{
|
||||
mov(al, 1);
|
||||
xchg(byte [STATE + offsetof(FEXCore::Core::ThreadState, RunningEvents.ShouldStop)], al);
|
||||
|
||||
auto offset = Op->Reason == 4 ?
|
||||
offsetof(FEXCore::Core::ThreadState, RunningEvents.ShouldStop) // HLT
|
||||
: offsetof(FEXCore::Core::ThreadState, RunningEvents.ShouldPause); // INT3
|
||||
xchg(byte [STATE + offset], al);
|
||||
|
||||
// This code matches what is in EXITFUNCTION
|
||||
if (SpillSlots) {
|
||||
@@ -398,6 +404,7 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const
|
||||
#endif
|
||||
ret();
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Break reason: %d", Op->Reason);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3060,12 +3060,9 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs) {
|
||||
void OpDispatchBuilder::INTOp(OpcodeArgs) {
|
||||
uint8_t Reason{};
|
||||
uint8_t Literal{};
|
||||
BlockSetRIP = true;
|
||||
bool setRIP = false;
|
||||
|
||||
switch (Op->OP) {
|
||||
case 0xCC:
|
||||
Reason = 0;
|
||||
break;
|
||||
case 0xCD:
|
||||
Reason = 1;
|
||||
Literal = Op->Src1.TypeLiteral.Literal;
|
||||
@@ -3078,15 +3075,24 @@ void OpDispatchBuilder::INTOp(OpcodeArgs) {
|
||||
break;
|
||||
case 0xF4: {
|
||||
Reason = 4;
|
||||
|
||||
// We want to set RIP to the next instruction after HLT
|
||||
auto NewRIP = _Constant(Op->PC + Op->InstSize);
|
||||
_StoreContext(8, offsetof(FEXCore::Core::CPUState, rip), NewRIP);
|
||||
setRIP = true;
|
||||
break;
|
||||
}
|
||||
case 0x0B:
|
||||
Reason = 5;
|
||||
case 0xCC:
|
||||
Reason = 6;
|
||||
setRIP = true;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
if (setRIP) {
|
||||
BlockSetRIP = setRIP;
|
||||
|
||||
// We want to set RIP to the next instruction after HLT/INT3
|
||||
auto NewRIP = _Constant(Op->PC + Op->InstSize);
|
||||
_StoreContext(8, offsetof(FEXCore::Core::CPUState, rip), NewRIP);
|
||||
}
|
||||
|
||||
if (Op->OP == 0xCE) { // Conditional to only break if Overflow == 1
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace FEXCore::Config {
|
||||
CONFIG_DEFAULTCORE,
|
||||
CONFIG_VIRTUALMEMSIZE,
|
||||
CONFIG_SINGLESTEP,
|
||||
CONFIG_GDBSERVER,
|
||||
};
|
||||
|
||||
enum ConfigCore {
|
||||
|
||||
Reference in new issue
Block a user