GDBStub: Refactor the whole code

This commit is contained in:
aldelaro5 2021-10-01 11:15:30 -04:00
parent 94a0f416eb
commit 1b92f81379
8 changed files with 279 additions and 333 deletions

View File

@ -318,7 +318,8 @@ static void CPUSetInitialExecutionState(bool force_paused = false)
// The CPU starts in stepping state, and will wait until a new state is set before executing. // The CPU starts in stepping state, and will wait until a new state is set before executing.
// SetState must be called on the host thread, so we defer it for later. // SetState must be called on the host thread, so we defer it for later.
QueueHostJob([force_paused]() { QueueHostJob([force_paused]() {
SetState(SConfig::GetInstance().bBootToPause || force_paused ? State::Paused : State::Running); bool paused = SConfig::GetInstance().bBootToPause || force_paused;
SetState(paused ? State::Paused : State::Running);
Host_UpdateDisasmDialog(); Host_UpdateDisasmDialog();
Host_UpdateMainFrame(); Host_UpdateMainFrame();
Host_Message(HostMessageID::WMUserCreate); Host_Message(HostMessageID::WMUserCreate);
@ -390,9 +391,9 @@ static void CpuThread(const std::optional<std::string>& savestate_path, bool del
if (_CoreParameter.bFastmem) if (_CoreParameter.bFastmem)
EMM::UninstallExceptionHandler(); EMM::UninstallExceptionHandler();
if (gdb_active()) if (GDBStub::IsActive())
{ {
gdb_deinit(); GDBStub::Deinit();
INFO_LOG_FMT(GDB_STUB, "Killed by CPU shutdown"); INFO_LOG_FMT(GDB_STUB, "Killed by CPU shutdown");
return; return;
} }
@ -657,7 +658,7 @@ static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi
} }
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stopping GDB ...")); INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stopping GDB ..."));
gdb_deinit(); GDBStub::Deinit();
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GDB stopped.")); INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GDB stopped."));
} }

View File

@ -97,6 +97,7 @@ void Run()
s_state_cpu_cvar.wait(state_lock, [] { return !s_state_paused_and_locked; }); s_state_cpu_cvar.wait(state_lock, [] { return !s_state_paused_and_locked; });
ExecutePendingJobs(state_lock); ExecutePendingJobs(state_lock);
Common::Event gdb_step_sync_event;
switch (s_state) switch (s_state)
{ {
case State::Running: case State::Running:
@ -130,16 +131,24 @@ void Run()
case State::Stepping: case State::Stepping:
// Wait for step command. // Wait for step command.
s_state_cpu_cvar.wait(state_lock, [&state_lock] { s_state_cpu_cvar.wait(state_lock, [&state_lock, &gdb_step_sync_event] {
ExecutePendingJobs(state_lock); ExecutePendingJobs(state_lock);
state_lock.unlock(); state_lock.unlock();
if (gdb_active() && gdb_hasControl()) if (GDBStub::IsActive() && GDBStub::HasControl())
{ {
gdb_signal(GDB_SIGTRAP); GDBStub::SendSignal(GDBStub::Signal::Sigtrap);
gdb_handle_exception(true); GDBStub::ProcessCommands(true);
// If we are still going to step, emulate the fact we just sent a step command // If we are still going to step, emulate the fact we just sent a step command
if (gdb_hasControl()) if (GDBStub::HasControl())
{
// Make sure the previous step by gdb was serviced
if (s_state_cpu_step_instruction_sync &&
s_state_cpu_step_instruction_sync != &gdb_step_sync_event)
s_state_cpu_step_instruction_sync->Set();
s_state_cpu_step_instruction = true; s_state_cpu_step_instruction = true;
s_state_cpu_step_instruction_sync = &gdb_step_sync_event;
}
} }
state_lock.lock(); state_lock.lock();
return s_state_cpu_step_instruction || !IsStepping(); return s_state_cpu_step_instruction || !IsStepping();
@ -293,6 +302,12 @@ void Break()
RunAdjacentSystems(false); RunAdjacentSystems(false);
} }
void Continue()
{
CPU::EnableStepping(false);
Core::CallOnStateChangedCallbacks(Core::State::Running);
}
bool PauseAndLock(bool do_lock, bool unpause_on_unlock, bool control_adjacent) bool PauseAndLock(bool do_lock, bool unpause_on_unlock, bool control_adjacent)
{ {
// NOTE: This is protected by s_stepping_lock. // NOTE: This is protected by s_stepping_lock.

View File

@ -53,6 +53,9 @@ void EnableStepping(bool stepping);
// should not be used by the Host. // should not be used by the Host.
void Break(); void Break();
// This should only be called from the CPU thread
void Continue();
// Shorthand for GetState() == State::Stepping. // Shorthand for GetState() == State::Stepping.
// WARNING: State::PowerDown will return false, not just State::Running. // WARNING: State::PowerDown will return false, not just State::Running.
bool IsStepping(); bool IsStepping();

View File

@ -3,6 +3,7 @@
// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic. // Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
#include <fmt/format.h>
#include <optional> #include <optional>
#include <signal.h> #include <signal.h>
#include <stdint.h> #include <stdint.h>
@ -35,10 +36,9 @@ typedef SSIZE_T ssize_t;
#include "Core/PowerPC/PPCCache.h" #include "Core/PowerPC/PPCCache.h"
#include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/PowerPC.h"
namespace namespace GDBStub
{ {
std::optional<Common::SocketContext> s_socket_context; std::optional<Common::SocketContext> s_socket_context;
} // namespace
#define GDB_BFR_MAX 10000 #define GDB_BFR_MAX 10000
@ -47,27 +47,35 @@ std::optional<Common::SocketContext> s_socket_context;
#define GDB_STUB_ACK '+' #define GDB_STUB_ACK '+'
#define GDB_STUB_NAK '-' #define GDB_STUB_NAK '-'
static bool hasControl = false; // We are treating software breakpoints and hardware breakpoints the same way
enum class BreakpointType
{
ExecuteSoft = 0,
ExecuteHard,
Read,
Write,
Access,
};
static int tmpsock = -1; const s64 GDB_UPDATE_CYCLES = 100000;
static int sock = -1;
static u8 cmd_bfr[GDB_BFR_MAX]; static bool s_has_control = false;
static u32 cmd_len;
static u32 sig = 0; static int s_tmpsock = -1;
static u32 send_signal = 0; static int s_sock = -1;
static u32 step_break = 0;
static CoreTiming::EventType* m_gdbStubUpdateEvent; static u8 s_cmd_bfr[GDB_BFR_MAX];
static u32 s_cmd_len;
static CoreTiming::EventType* s_update_event;
static const char* CommandBufferAsString() static const char* CommandBufferAsString()
{ {
return reinterpret_cast<const char*>(cmd_bfr); return reinterpret_cast<const char*>(s_cmd_bfr);
} }
// private helpers // private helpers
static u8 hex2char(u8 hex) static u8 Hex2char(u8 hex)
{ {
if (hex >= '0' && hex <= '9') if (hex >= '0' && hex <= '9')
return hex - '0'; return hex - '0';
@ -80,7 +88,7 @@ static u8 hex2char(u8 hex)
return 0; return 0;
} }
static u8 nibble2hex(u8 n) static u8 Nibble2hex(u8 n)
{ {
n &= 0xf; n &= 0xf;
if (n < 0xa) if (n < 0xa)
@ -89,49 +97,50 @@ static u8 nibble2hex(u8 n)
return 'A' + n - 0xa; return 'A' + n - 0xa;
} }
static void mem2hex(u8* dst, u8* src, u32 len) static void Mem2hex(u8* dst, u8* src, u32 len)
{ {
while (len-- > 0) while (len-- > 0)
{ {
const u8 tmp = *src++; const u8 tmp = *src++;
*dst++ = nibble2hex(tmp >> 4); *dst++ = Nibble2hex(tmp >> 4);
*dst++ = nibble2hex(tmp); *dst++ = Nibble2hex(tmp);
} }
} }
static void hex2mem(u8* dst, u8* src, u32 len) static void Hex2mem(u8* dst, u8* src, u32 len)
{ {
while (len-- > 0) while (len-- > 0)
{ {
*dst++ = (hex2char(*src) << 4) | hex2char(*(src + 1)); *dst++ = (Hex2char(*src) << 4) | Hex2char(*(src + 1));
src += 2; src += 2;
} }
} }
void GDBStubUpdateCallback(u64 userdata, s64 cycles_late) static void UpdateCallback(u64 userdata, s64 cycles_late)
{ {
gdb_handle_exception(false); ProcessCommands(false);
CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, m_gdbStubUpdateEvent); if (IsActive())
CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, s_update_event);
} }
static u8 gdb_read_byte() static u8 ReadByte()
{ {
u8 c = '+'; u8 c = '+';
const ssize_t res = recv(sock, (char*)&c, 1, MSG_WAITALL); const ssize_t res = recv(s_sock, (char*)&c, 1, MSG_WAITALL);
if (res != 1) if (res != 1)
{ {
ERROR_LOG_FMT(GDB_STUB, "recv failed : {}", res); ERROR_LOG_FMT(GDB_STUB, "recv failed : {}", res);
gdb_deinit(); Deinit();
} }
return c; return c;
} }
static u8 gdb_calc_chksum() static u8 CalculateChecksum()
{ {
u32 len = cmd_len; u32 len = s_cmd_len;
u8* ptr = cmd_bfr; u8* ptr = s_cmd_bfr;
u8 c = 0; u8 c = 0;
while (len-- > 0) while (len-- > 0)
@ -140,9 +149,9 @@ static u8 gdb_calc_chksum()
return c; return c;
} }
static void gdb_bp_remove(u32 type, u32 addr, u32 len) static void RemoveBreakpoint(BreakpointType type, u32 addr, u32 len)
{ {
if (type == GDB_BP_TYPE_X) if (type == BreakpointType::ExecuteHard || type == BreakpointType::ExecuteSoft)
{ {
while (PowerPC::breakpoints.IsAddressBreakPoint(addr)) while (PowerPC::breakpoints.IsAddressBreakPoint(addr))
{ {
@ -160,30 +169,30 @@ static void gdb_bp_remove(u32 type, u32 addr, u32 len)
} }
} }
static void gdb_nak() static void Nack()
{ {
const char nak = GDB_STUB_NAK; const char nak = GDB_STUB_NAK;
const ssize_t res = send(sock, &nak, 1, 0); const ssize_t res = send(s_sock, &nak, 1, 0);
if (res != 1) if (res != 1)
ERROR_LOG_FMT(GDB_STUB, "send failed"); ERROR_LOG_FMT(GDB_STUB, "send failed");
} }
static void gdb_ack() static void Ack()
{ {
const char ack = GDB_STUB_ACK; const char ack = GDB_STUB_ACK;
const ssize_t res = send(sock, &ack, 1, 0); const ssize_t res = send(s_sock, &ack, 1, 0);
if (res != 1) if (res != 1)
ERROR_LOG_FMT(GDB_STUB, "send failed"); ERROR_LOG_FMT(GDB_STUB, "send failed");
} }
static void gdb_read_command() static void ReadCommand()
{ {
cmd_len = 0; s_cmd_len = 0;
memset(cmd_bfr, 0, sizeof cmd_bfr); memset(s_cmd_bfr, 0, sizeof s_cmd_bfr);
u8 c = gdb_read_byte(); u8 c = ReadByte();
if (c == '+') if (c == '+')
{ {
// ignore ack // ignore ack
@ -192,8 +201,8 @@ static void gdb_read_command()
else if (c == 0x03) else if (c == 0x03)
{ {
CPU::Break(); CPU::Break();
gdb_signal(GDB_SIGTRAP); SendSignal(Signal::Sigtrap);
hasControl = true; s_has_control = true;
return; return;
} }
else if (c != GDB_STUB_START) else if (c != GDB_STUB_START)
@ -202,146 +211,138 @@ static void gdb_read_command()
return; return;
} }
while ((c = gdb_read_byte()) != GDB_STUB_END) while ((c = ReadByte()) != GDB_STUB_END)
{ {
cmd_bfr[cmd_len++] = c; s_cmd_bfr[s_cmd_len++] = c;
if (cmd_len == sizeof cmd_bfr) if (s_cmd_len == sizeof s_cmd_bfr)
{ {
ERROR_LOG_FMT(GDB_STUB, "gdb: cmd_bfr overflow"); ERROR_LOG_FMT(GDB_STUB, "gdb: cmd_bfr overflow");
gdb_nak(); Nack();
return; return;
} }
} }
u8 chk_read = hex2char(gdb_read_byte()) << 4; u8 chk_read = Hex2char(ReadByte()) << 4;
chk_read |= hex2char(gdb_read_byte()); chk_read |= Hex2char(ReadByte());
const u8 chk_calc = gdb_calc_chksum(); const u8 chk_calc = CalculateChecksum();
if (chk_calc != chk_read) if (chk_calc != chk_read)
{ {
ERROR_LOG_FMT(GDB_STUB, ERROR_LOG_FMT(GDB_STUB,
"gdb: invalid checksum: calculated {:02x} and read {:02x} for ${}# (length: {})", "gdb: invalid checksum: calculated {:02x} and read {:02x} for ${}# (length: {})",
chk_calc, chk_read, CommandBufferAsString(), cmd_len); chk_calc, chk_read, CommandBufferAsString(), s_cmd_len);
cmd_len = 0; s_cmd_len = 0;
gdb_nak(); Nack();
return; return;
} }
DEBUG_LOG_FMT(GDB_STUB, "gdb: read command {} with a length of {}: {}", DEBUG_LOG_FMT(GDB_STUB, "gdb: read command {} with a length of {}: {}",
static_cast<char>(cmd_bfr[0]), cmd_len, CommandBufferAsString()); static_cast<char>(s_cmd_bfr[0]), s_cmd_len, CommandBufferAsString());
gdb_ack(); Ack();
} }
static int gdb_data_available() static bool IsDataAvailable()
{ {
struct timeval t; struct timeval t;
fd_set _fds, *fds = &_fds; fd_set _fds, *fds = &_fds;
FD_ZERO(fds); FD_ZERO(fds);
FD_SET(sock, fds); FD_SET(s_sock, fds);
t.tv_sec = 0; t.tv_sec = 0;
t.tv_usec = 20; t.tv_usec = 20;
if (select(sock + 1, fds, nullptr, nullptr, &t) < 0) if (select(s_sock + 1, fds, nullptr, nullptr, &t) < 0)
{ {
ERROR_LOG_FMT(GDB_STUB, "select failed"); ERROR_LOG_FMT(GDB_STUB, "select failed");
return 0; return false;
} }
if (FD_ISSET(sock, fds)) if (FD_ISSET(s_sock, fds))
return 1; return true;
return 0; return false;
} }
static void gdb_reply(const char* reply) static void SendReply(const char* reply)
{ {
if (!gdb_active()) if (!IsActive())
return; return;
memset(cmd_bfr, 0, sizeof cmd_bfr); memset(s_cmd_bfr, 0, sizeof s_cmd_bfr);
cmd_len = (u32)strlen(reply); s_cmd_len = (u32)strlen(reply);
if (cmd_len + 4 > sizeof cmd_bfr) if (s_cmd_len + 4 > sizeof s_cmd_bfr)
ERROR_LOG_FMT(GDB_STUB, "cmd_bfr overflow in gdb_reply"); ERROR_LOG_FMT(GDB_STUB, "cmd_bfr overflow in gdb_reply");
memcpy(cmd_bfr + 1, reply, cmd_len); memcpy(s_cmd_bfr + 1, reply, s_cmd_len);
cmd_len++; s_cmd_len++;
const u8 chk = gdb_calc_chksum(); const u8 chk = CalculateChecksum();
cmd_len--; s_cmd_len--;
cmd_bfr[0] = GDB_STUB_START; s_cmd_bfr[0] = GDB_STUB_START;
cmd_bfr[cmd_len + 1] = GDB_STUB_END; s_cmd_bfr[s_cmd_len + 1] = GDB_STUB_END;
cmd_bfr[cmd_len + 2] = nibble2hex(chk >> 4); s_cmd_bfr[s_cmd_len + 2] = Nibble2hex(chk >> 4);
cmd_bfr[cmd_len + 3] = nibble2hex(chk); s_cmd_bfr[s_cmd_len + 3] = Nibble2hex(chk);
DEBUG_LOG_FMT(GDB_STUB, "gdb: reply (len: {}): {}", cmd_len, CommandBufferAsString()); DEBUG_LOG_FMT(GDB_STUB, "gdb: reply (len: {}): {}", s_cmd_len, CommandBufferAsString());
const char* ptr = (const char*)cmd_bfr; const char* ptr = (const char*)s_cmd_bfr;
u32 left = cmd_len + 4; u32 left = s_cmd_len + 4;
while (left > 0) while (left > 0)
{ {
const int n = send(sock, ptr, left, 0); const int n = send(s_sock, ptr, left, 0);
if (n < 0) if (n < 0)
{ {
ERROR_LOG_FMT(GDB_STUB, "gdb: send failed"); ERROR_LOG_FMT(GDB_STUB, "gdb: send failed");
return gdb_deinit(); return Deinit();
} }
left -= n; left -= n;
ptr += n; ptr += n;
} }
} }
static void gdb_handle_query() static void HandleQuery()
{ {
DEBUG_LOG_FMT(GDB_STUB, "gdb: query '{}'", CommandBufferAsString() + 1); DEBUG_LOG_FMT(GDB_STUB, "gdb: query '{}'", CommandBufferAsString() + 1);
if (!strcmp((const char*)(cmd_bfr + 1), "TStatus")) if (!strcmp((const char*)(s_cmd_bfr + 1), "TStatus"))
{ {
return gdb_reply("T0"); return SendReply("T0");
} }
gdb_reply(""); SendReply("");
} }
static void gdb_handle_set_thread() static void HandleSetThread()
{ {
if (memcmp(cmd_bfr, "Hg0", 3) == 0 || memcmp(cmd_bfr, "Hc-1", 4) == 0 || if (memcmp(s_cmd_bfr, "Hg0", 3) == 0 || memcmp(s_cmd_bfr, "Hc-1", 4) == 0 ||
memcmp(cmd_bfr, "Hc0", 3) == 0 || memcmp(cmd_bfr, "Hc1", 3) == 0) memcmp(s_cmd_bfr, "Hc0", 3) == 0 || memcmp(s_cmd_bfr, "Hc1", 3) == 0)
return gdb_reply("OK"); return SendReply("OK");
gdb_reply("E01"); SendReply("E01");
} }
static void gdb_handle_thread_alive() static void HandleIsThreadAlive()
{ {
if (memcmp(cmd_bfr, "T0", 2) == 0 || memcmp(cmd_bfr, "T1", 4) == 0 || if (memcmp(s_cmd_bfr, "T0", 2) == 0 || memcmp(s_cmd_bfr, "T1", 4) == 0 ||
memcmp(cmd_bfr, "T-1", 3) == 0) memcmp(s_cmd_bfr, "T-1", 3) == 0)
return gdb_reply("OK"); return SendReply("OK");
gdb_reply("E01"); SendReply("E01");
}
static void gdb_handle_signal()
{
char bfr[128];
memset(bfr, 0, sizeof bfr);
sprintf(bfr, "T%02x%02x:%08x;%02x:%08x;", sig, 64, PC, 1, GPR(1));
gdb_reply(bfr);
} }
static void wbe32hex(u8* p, u32 v) static void wbe32hex(u8* p, u32 v)
{ {
u32 i; u32 i;
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
p[i] = nibble2hex(v >> (28 - 4 * i)); p[i] = Nibble2hex(v >> (28 - 4 * i));
} }
static void wbe64hex(u8* p, u64 v) static void wbe64hex(u8* p, u64 v)
{ {
u32 i; u32 i;
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
p[i] = nibble2hex(v >> (60 - 4 * i)); p[i] = Nibble2hex(v >> (60 - 4 * i));
} }
static u32 re32hex(u8* p) static u32 re32hex(u8* p)
@ -350,7 +351,7 @@ static u32 re32hex(u8* p)
u32 res = 0; u32 res = 0;
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
res = (res << 4) | hex2char(p[i]); res = (res << 4) | Hex2char(p[i]);
return res; return res;
} }
@ -361,22 +362,22 @@ static u64 re64hex(u8* p)
u64 res = 0; u64 res = 0;
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
res = (res << 4) | hex2char(p[i]); res = (res << 4) | Hex2char(p[i]);
return res; return res;
} }
static void gdb_read_register() static void ReadRegister()
{ {
static u8 reply[64]; static u8 reply[64];
u32 id; u32 id;
memset(reply, 0, sizeof reply); memset(reply, 0, sizeof reply);
id = hex2char(cmd_bfr[1]); id = Hex2char(s_cmd_bfr[1]);
if (cmd_bfr[2] != '\0') if (s_cmd_bfr[2] != '\0')
{ {
id <<= 4; id <<= 4;
id |= hex2char(cmd_bfr[2]); id |= Hex2char(s_cmd_bfr[2]);
} }
if (id < 32) if (id < 32)
@ -416,15 +417,15 @@ static void gdb_read_register()
wbe32hex(reply, FPSCR.Hex); wbe32hex(reply, FPSCR.Hex);
break; break;
default: default:
return gdb_reply("E01"); return SendReply("E01");
break; break;
} }
} }
gdb_reply((char*)reply); SendReply((char*)reply);
} }
static void gdb_read_registers() static void ReadRegisters()
{ {
static u8 bfr[GDB_BFR_MAX - 4]; static u8 bfr[GDB_BFR_MAX - 4];
u8* bufptr = bfr; u8* bufptr = bfr;
@ -438,13 +439,13 @@ static void gdb_read_registers()
} }
bufptr += 32 * 8; bufptr += 32 * 8;
gdb_reply((char*)bfr); SendReply((char*)bfr);
} }
static void gdb_write_registers() static void WriteRegisters()
{ {
u32 i; u32 i;
u8* bufptr = cmd_bfr; u8* bufptr = s_cmd_bfr;
for (i = 0; i < 32; i++) for (i = 0; i < 32; i++)
{ {
@ -452,21 +453,21 @@ static void gdb_write_registers()
} }
bufptr += 32 * 8; bufptr += 32 * 8;
gdb_reply("OK"); SendReply("OK");
} }
static void gdb_write_register() static void WriteRegister()
{ {
u32 id; u32 id;
u8* bufptr = cmd_bfr + 3; u8* bufptr = s_cmd_bfr + 3;
id = hex2char(cmd_bfr[1]); id = Hex2char(s_cmd_bfr[1]);
if (cmd_bfr[2] != '=') if (s_cmd_bfr[2] != '=')
{ {
++bufptr; ++bufptr;
id <<= 4; id <<= 4;
id |= hex2char(cmd_bfr[2]); id |= Hex2char(s_cmd_bfr[2]);
} }
if (id < 32) if (id < 32)
@ -506,15 +507,15 @@ static void gdb_write_register()
FPSCR.Hex = re32hex(bufptr); FPSCR.Hex = re32hex(bufptr);
break; break;
default: default:
return gdb_reply("E01"); return SendReply("E01");
break; break;
} }
} }
gdb_reply("OK"); SendReply("OK");
} }
static void gdb_read_mem() static void ReadMemory()
{ {
static u8 reply[GDB_BFR_MAX - 4]; static u8 reply[GDB_BFR_MAX - 4];
u32 addr, len; u32 addr, len;
@ -522,70 +523,57 @@ static void gdb_read_mem()
i = 1; i = 1;
addr = 0; addr = 0;
while (cmd_bfr[i] != ',') while (s_cmd_bfr[i] != ',')
addr = (addr << 4) | hex2char(cmd_bfr[i++]); addr = (addr << 4) | Hex2char(s_cmd_bfr[i++]);
i++; i++;
len = 0; len = 0;
while (i < cmd_len) while (i < s_cmd_len)
len = (len << 4) | hex2char(cmd_bfr[i++]); len = (len << 4) | Hex2char(s_cmd_bfr[i++]);
DEBUG_LOG_FMT(GDB_STUB, "gdb: read memory: {:08x} bytes from {:08x}", len, addr); DEBUG_LOG_FMT(GDB_STUB, "gdb: read memory: {:08x} bytes from {:08x}", len, addr);
if (len * 2 > sizeof reply) if (len * 2 > sizeof reply)
gdb_reply("E01"); SendReply("E01");
u8* data = Memory::GetPointer(addr); u8* data = Memory::GetPointer(addr);
if (!data) if (!data)
return gdb_reply("E0"); return SendReply("E0");
mem2hex(reply, data, len); Mem2hex(reply, data, len);
reply[len * 2] = '\0'; reply[len * 2] = '\0';
gdb_reply((char*)reply); SendReply((char*)reply);
} }
static void gdb_write_mem() static void WriteMemory()
{ {
u32 addr, len; u32 addr, len;
u32 i; u32 i;
i = 1; i = 1;
addr = 0; addr = 0;
while (cmd_bfr[i] != ',') while (s_cmd_bfr[i] != ',')
addr = (addr << 4) | hex2char(cmd_bfr[i++]); addr = (addr << 4) | Hex2char(s_cmd_bfr[i++]);
i++; i++;
len = 0; len = 0;
while (cmd_bfr[i] != ':') while (s_cmd_bfr[i] != ':')
len = (len << 4) | hex2char(cmd_bfr[i++]); len = (len << 4) | Hex2char(s_cmd_bfr[i++]);
DEBUG_LOG_FMT(GDB_STUB, "gdb: write memory: {:08x} bytes to {:08x}", len, addr); DEBUG_LOG_FMT(GDB_STUB, "gdb: write memory: {:08x} bytes to {:08x}", len, addr);
u8* dst = Memory::GetPointer(addr); u8* dst = Memory::GetPointer(addr);
if (!dst) if (!dst)
return gdb_reply("E00"); return SendReply("E00");
hex2mem(dst, cmd_bfr + i + 1, len); Hex2mem(dst, s_cmd_bfr + i + 1, len);
gdb_reply("OK"); SendReply("OK");
} }
// forces a break on next instruction check static void Step()
void gdb_break()
{ {
step_break = 1; CPU::EnableStepping(true);
send_signal = 1; Core::CallOnStateChangedCallbacks(Core::State::Paused);
} }
static void gdb_step() static bool AddBreakpoint(BreakpointType type, u32 addr, u32 len)
{ {
send_signal = 1; if (type == BreakpointType::ExecuteHard || type == BreakpointType::ExecuteSoft)
}
static void gdb_continue()
{
send_signal = 1;
CPU::EnableStepping(false);
Core::CallOnStateChangedCallbacks(Core::State::Running);
}
bool gdb_add_bp(u32 type, u32 addr, u32 len)
{
if (type == GDB_BP_TYPE_X)
{ {
PowerPC::breakpoints.Add(addr); PowerPC::breakpoints.Add(addr);
DEBUG_LOG_FMT(GDB_STUB, "gdb: added {} breakpoint: {:08x} bytes at {:08x}", type, len, addr); DEBUG_LOG_FMT(GDB_STUB, "gdb: added {} breakpoint: {:08x} bytes at {:08x}", type, len, addr);
@ -596,8 +584,10 @@ bool gdb_add_bp(u32 type, u32 addr, u32 len)
new_memcheck.start_address = addr; new_memcheck.start_address = addr;
new_memcheck.end_address = addr + len - 1; new_memcheck.end_address = addr + len - 1;
new_memcheck.is_ranged = (len > 1); new_memcheck.is_ranged = (len > 1);
new_memcheck.is_break_on_read = (type == GDB_BP_TYPE_R || type == GDB_BP_TYPE_A); new_memcheck.is_break_on_read =
new_memcheck.is_break_on_write = (type == GDB_BP_TYPE_W || type == GDB_BP_TYPE_A); (type == BreakpointType::Read || type == BreakpointType::Access);
new_memcheck.is_break_on_write =
(type == BreakpointType::Write || type == BreakpointType::Access);
new_memcheck.break_on_hit = true; new_memcheck.break_on_hit = true;
new_memcheck.log_on_hit = false; new_memcheck.log_on_hit = false;
new_memcheck.is_enabled = true; new_memcheck.is_enabled = true;
@ -607,161 +597,127 @@ bool gdb_add_bp(u32 type, u32 addr, u32 len)
return true; return true;
} }
static void _gdb_add_bp() static void HandleAddBreakpoint()
{ {
u32 type; u32 type;
u32 i, addr = 0, len = 0; u32 i, addr = 0, len = 0;
type = hex2char(cmd_bfr[1]); type = Hex2char(s_cmd_bfr[1]);
switch (type) if (type > 4)
{ return SendReply("E01");
case 0:
case 1:
type = GDB_BP_TYPE_X;
break;
case 2:
type = GDB_BP_TYPE_W;
break;
case 3:
type = GDB_BP_TYPE_R;
break;
case 4:
type = GDB_BP_TYPE_A;
break;
default:
return gdb_reply("E01");
}
i = 3; i = 3;
while (cmd_bfr[i] != ',') while (s_cmd_bfr[i] != ',')
addr = addr << 4 | hex2char(cmd_bfr[i++]); addr = addr << 4 | Hex2char(s_cmd_bfr[i++]);
i++; i++;
while (i < cmd_len) while (i < s_cmd_len)
len = len << 4 | hex2char(cmd_bfr[i++]); len = len << 4 | Hex2char(s_cmd_bfr[i++]);
if (!gdb_add_bp(type, addr, len)) if (!AddBreakpoint(static_cast<BreakpointType>(type), addr, len))
return gdb_reply("E02"); return SendReply("E02");
gdb_reply("OK"); SendReply("OK");
} }
static void gdb_remove_bp() static void HandleRemoveBreakpoint()
{ {
u32 type, addr, len, i; u32 type, addr, len, i;
type = hex2char(cmd_bfr[1]); type = Hex2char(s_cmd_bfr[1]);
switch (type) if (type >= 4)
{ return SendReply("E01");
case 0:
case 1:
type = GDB_BP_TYPE_X;
break;
case 2:
type = GDB_BP_TYPE_W;
break;
case 3:
type = GDB_BP_TYPE_R;
break;
case 4:
type = GDB_BP_TYPE_A;
break;
default:
return gdb_reply("E01");
}
addr = 0; addr = 0;
len = 0; len = 0;
i = 3; i = 3;
while (cmd_bfr[i] != ',') while (s_cmd_bfr[i] != ',')
addr = (addr << 4) | hex2char(cmd_bfr[i++]); addr = (addr << 4) | Hex2char(s_cmd_bfr[i++]);
i++; i++;
while (i < cmd_len) while (i < s_cmd_len)
len = (len << 4) | hex2char(cmd_bfr[i++]); len = (len << 4) | Hex2char(s_cmd_bfr[i++]);
gdb_bp_remove(type, addr, len); RemoveBreakpoint(static_cast<BreakpointType>(type), addr, len);
gdb_reply("OK"); SendReply("OK");
} }
void ProcessCommands(bool loop_until_continue)
void gdb_handle_exception(bool loop_until_continue)
{ {
while (gdb_active()) while (IsActive())
{ {
if (CPU::GetState() == CPU::State::PowerDown) if (CPU::GetState() == CPU::State::PowerDown)
{ {
gdb_deinit(); Deinit();
INFO_LOG_FMT(GDB_STUB, "killed by power down"); INFO_LOG_FMT(GDB_STUB, "killed by power down");
return; return;
} }
if (!gdb_data_available()) if (!IsDataAvailable())
{ {
if (loop_until_continue) if (loop_until_continue)
continue; continue;
else else
return; return;
} }
gdb_read_command(); ReadCommand();
// No more commands // No more commands
if (cmd_len == 0) if (s_cmd_len == 0)
continue; continue;
switch (cmd_bfr[0]) switch (s_cmd_bfr[0])
{ {
case 'q': case 'q':
gdb_handle_query(); HandleQuery();
break; break;
case 'H': case 'H':
gdb_handle_set_thread(); HandleSetThread();
break; break;
case 'T': case 'T':
gdb_handle_thread_alive(); HandleIsThreadAlive();
break; break;
case '?': case '?':
gdb_handle_signal(); SendSignal(Signal::Sigterm);
break; break;
case 'k': case 'k':
gdb_deinit(); Deinit();
INFO_LOG_FMT(GDB_STUB, "killed by gdb"); INFO_LOG_FMT(GDB_STUB, "killed by gdb");
return; return;
case 'g': case 'g':
gdb_read_registers(); ReadRegisters();
break; break;
case 'G': case 'G':
gdb_write_registers(); WriteRegisters();
break; break;
case 'p': case 'p':
gdb_read_register(); ReadRegister();
break; break;
case 'P': case 'P':
gdb_write_register(); WriteRegister();
break; break;
case 'm': case 'm':
gdb_read_mem(); ReadMemory();
break; break;
case 'M': case 'M':
gdb_write_mem(); WriteMemory();
PowerPC::ppcState.iCache.Reset(); PowerPC::ppcState.iCache.Reset();
Host_UpdateDisasmDialog(); Host_UpdateDisasmDialog();
break; break;
case 's': case 's':
gdb_step();
return; return;
case 'C': case 'C':
case 'c': case 'c':
gdb_continue(); CPU::Continue();
hasControl = false; s_has_control = false;
return; return;
case 'z': case 'z':
gdb_remove_bp(); HandleRemoveBreakpoint();
break; break;
case 'Z': case 'Z':
_gdb_add_bp(); HandleAddBreakpoint();
break; break;
default: default:
gdb_reply(""); SendReply("");
break; break;
} }
} }
@ -769,11 +725,11 @@ void gdb_handle_exception(bool loop_until_continue)
// exported functions // exported functions
static void gdb_init_generic(int domain, const sockaddr* server_addr, socklen_t server_addrlen, static void InitGeneric(int domain, const sockaddr* server_addr, socklen_t server_addrlen,
sockaddr* client_addr, socklen_t* client_addrlen); sockaddr* client_addr, socklen_t* client_addrlen);
#ifndef _WIN32 #ifndef _WIN32
void gdb_init_local(const char* socket) void InitLocal(const char* socket)
{ {
unlink(socket); unlink(socket);
@ -781,11 +737,11 @@ void gdb_init_local(const char* socket)
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, socket); strcpy(addr.sun_path, socket);
gdb_init_generic(PF_LOCAL, (const sockaddr*)&addr, sizeof(addr), NULL, NULL); InitGeneric(PF_LOCAL, (const sockaddr*)&addr, sizeof(addr), NULL, NULL);
} }
#endif #endif
void gdb_init(u32 port) void Init(u32 port)
{ {
sockaddr_in saddr_server = {}; sockaddr_in saddr_server = {};
sockaddr_in saddr_client; sockaddr_in saddr_client;
@ -796,94 +752,86 @@ void gdb_init(u32 port)
socklen_t client_addrlen = sizeof(saddr_client); socklen_t client_addrlen = sizeof(saddr_client);
gdb_init_generic(PF_INET, (const sockaddr*)&saddr_server, sizeof(saddr_server), InitGeneric(PF_INET, (const sockaddr*)&saddr_server, sizeof(saddr_server),
(sockaddr*)&saddr_client, &client_addrlen); (sockaddr*)&saddr_client, &client_addrlen);
saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr); saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr);
} }
static void gdb_init_generic(int domain, const sockaddr* server_addr, socklen_t server_addrlen, static void InitGeneric(int domain, const sockaddr* server_addr, socklen_t server_addrlen,
sockaddr* client_addr, socklen_t* client_addrlen) sockaddr* client_addr, socklen_t* client_addrlen)
{ {
s_socket_context.emplace(); s_socket_context.emplace();
tmpsock = socket(domain, SOCK_STREAM, 0); s_tmpsock = socket(domain, SOCK_STREAM, 0);
if (tmpsock == -1) if (s_tmpsock == -1)
ERROR_LOG_FMT(GDB_STUB, "Failed to create gdb socket"); ERROR_LOG_FMT(GDB_STUB, "Failed to create gdb socket");
int on = 1; int on = 1;
if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof on) < 0) if (setsockopt(s_tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof on) < 0)
ERROR_LOG_FMT(GDB_STUB, "Failed to setsockopt"); ERROR_LOG_FMT(GDB_STUB, "Failed to setsockopt");
if (bind(tmpsock, server_addr, server_addrlen) < 0) if (bind(s_tmpsock, server_addr, server_addrlen) < 0)
ERROR_LOG_FMT(GDB_STUB, "Failed to bind gdb socket"); ERROR_LOG_FMT(GDB_STUB, "Failed to bind gdb socket");
if (listen(tmpsock, 1) < 0) if (listen(s_tmpsock, 1) < 0)
ERROR_LOG_FMT(GDB_STUB, "Failed to listen to gdb socket"); ERROR_LOG_FMT(GDB_STUB, "Failed to listen to gdb socket");
INFO_LOG_FMT(GDB_STUB, "Waiting for gdb to connect..."); INFO_LOG_FMT(GDB_STUB, "Waiting for gdb to connect...");
sock = accept(tmpsock, client_addr, client_addrlen); s_sock = accept(s_tmpsock, client_addr, client_addrlen);
if (sock < 0) if (s_sock < 0)
ERROR_LOG_FMT(GDB_STUB, "Failed to accept gdb client"); ERROR_LOG_FMT(GDB_STUB, "Failed to accept gdb client");
INFO_LOG_FMT(GDB_STUB, "Client connected."); INFO_LOG_FMT(GDB_STUB, "Client connected.");
#ifdef _WIN32 #ifdef _WIN32
closesocket(s_tmpsock); closesocket(s_tmpsock);
#else #else
close(tmpsock); close(s_tmpsock);
#endif #endif
tmpsock = -1; s_tmpsock = -1;
m_gdbStubUpdateEvent = CoreTiming::RegisterEvent("GDBStubUpdate", GDBStubUpdateCallback); s_update_event = CoreTiming::RegisterEvent("GDBStubUpdate", UpdateCallback);
CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, m_gdbStubUpdateEvent); CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, s_update_event);
hasControl = true; s_has_control = true;
} }
void gdb_deinit() void Deinit()
{ {
if (tmpsock != -1) if (s_tmpsock != -1)
{ {
shutdown(tmpsock, SHUT_RDWR); shutdown(s_tmpsock, SHUT_RDWR);
tmpsock = -1; s_tmpsock = -1;
} }
if (sock != -1) if (s_sock != -1)
{ {
shutdown(sock, SHUT_RDWR); shutdown(s_sock, SHUT_RDWR);
sock = -1; s_sock = -1;
} }
s_socket_context.reset(); s_socket_context.reset();
hasControl = false; s_has_control = false;
} }
bool gdb_active() bool IsActive()
{ {
return tmpsock != -1 || sock != -1; return s_tmpsock != -1 || s_sock != -1;
} }
bool gdb_hasControl() bool HasControl()
{ {
return hasControl; return s_has_control;
} }
void gdb_takeControl() void TakeControl()
{ {
hasControl = true; s_has_control = true;
} }
int gdb_signal(u32 s) void SendSignal(Signal signal)
{ {
if (sock == -1) char bfr[128] = {};
return 1; fmt::format_to(bfr, "T{:02x}{:02x}:{:08x};{:02x}:{:08x};", signal, 64, PC, 1, GPR(1));
SendReply(bfr);
sig = s;
if (send_signal)
{
gdb_handle_signal();
send_signal = 0;
}
return 0;
} }
} // namespace GDBStub

View File

@ -8,42 +8,21 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Core/CoreTiming.h" #include "Core/CoreTiming.h"
typedef enum namespace GDBStub
{ {
GDB_SIGTRAP = 5, enum class Signal
GDB_SIGTERM = 15,
} gdb_signals;
typedef enum
{ {
GDB_BP_TYPE_NONE = 0, Sigtrap = 5,
GDB_BP_TYPE_X, Sigterm = 15,
GDB_BP_TYPE_R, };
GDB_BP_TYPE_W,
GDB_BP_TYPE_A
} gdb_bp_type;
const s64 GDB_UPDATE_CYCLES = 100000; void Init(u32 port);
void InitLocal(const char* socket);
void Deinit();
bool IsActive();
bool HasControl();
void TakeControl();
void GDBStubUpdateCallback(u64 userdata, s64 cycles_late); void ProcessCommands(bool loop_until_continue);
void SendSignal(Signal signal);
void gdb_init(u32 port);
void gdb_init_local(const char* socket);
void gdb_deinit();
bool gdb_active();
bool gdb_hasControl();
void gdb_takeControl();
void gdb_break();
void gdb_handle_exception(bool loopUntilContinue);
int gdb_signal(u32 signal);
int gdb_bp_x(u32 addr);
int gdb_bp_r(u32 addr);
int gdb_bp_w(u32 addr);
int gdb_bp_a(u32 addr);
bool gdb_add_bp(u32 type, u32 addr, u32 len);
void gdb_handle_exception(bool loop_until_continue);
void SendSignal(u32 signal);
} // namespace GDBStub } // namespace GDBStub

View File

@ -293,8 +293,8 @@ void Interpreter::Run()
#endif #endif
INFO_LOG_FMT(POWERPC, "Hit Breakpoint - {:08x}", PC); INFO_LOG_FMT(POWERPC, "Hit Breakpoint - {:08x}", PC);
CPU::Break(); CPU::Break();
if (gdb_active()) if (GDBStub::IsActive())
gdb_takeControl(); GDBStub::TakeControl();
if (PowerPC::breakpoints.IsTempBreakPoint(PC)) if (PowerPC::breakpoints.IsTempBreakPoint(PC))
PowerPC::breakpoints.Remove(PC); PowerPC::breakpoints.Remove(PC);

View File

@ -519,8 +519,8 @@ static void Memcheck(u32 address, u64 var, bool write, size_t size)
CPU::Break(); CPU::Break();
if (gdb_active()) if (GDBStub::IsActive())
gdb_takeControl(); GDBStub::TakeControl();
// Fake a DSI so that all the code that tests for it in order to skip // Fake a DSI so that all the code that tests for it in order to skip
// the rest of the instruction will apply. (This means that // the rest of the instruction will apply. (This means that

View File

@ -614,8 +614,8 @@ void CheckBreakPoints()
if (PowerPC::breakpoints.IsBreakPointBreakOnHit(PC)) if (PowerPC::breakpoints.IsBreakPointBreakOnHit(PC))
{ {
CPU::Break(); CPU::Break();
if (gdb_active()) if (GDBStub::IsActive())
gdb_takeControl(); GDBStub::TakeControl();
} }
if (PowerPC::breakpoints.IsBreakPointLogOnHit(PC)) if (PowerPC::breakpoints.IsBreakPointLogOnHit(PC))
{ {