diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 1537272571..b9a3b60d1d 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -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. // SetState must be called on the host thread, so we defer it for later. 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_UpdateMainFrame(); Host_Message(HostMessageID::WMUserCreate); @@ -390,9 +391,9 @@ static void CpuThread(const std::optional& savestate_path, bool del if (_CoreParameter.bFastmem) EMM::UninstallExceptionHandler(); - if (gdb_active()) + if (GDBStub::IsActive()) { - gdb_deinit(); + GDBStub::Deinit(); INFO_LOG_FMT(GDB_STUB, "Killed by CPU shutdown"); return; } @@ -657,7 +658,7 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi } INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stopping GDB ...")); - gdb_deinit(); + GDBStub::Deinit(); INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GDB stopped.")); } diff --git a/Source/Core/Core/HW/CPU.cpp b/Source/Core/Core/HW/CPU.cpp index d64c1e5c90..766eb01f7b 100644 --- a/Source/Core/Core/HW/CPU.cpp +++ b/Source/Core/Core/HW/CPU.cpp @@ -97,6 +97,7 @@ void Run() s_state_cpu_cvar.wait(state_lock, [] { return !s_state_paused_and_locked; }); ExecutePendingJobs(state_lock); + Common::Event gdb_step_sync_event; switch (s_state) { case State::Running: @@ -130,16 +131,24 @@ void Run() case State::Stepping: // 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); state_lock.unlock(); - if (gdb_active() && gdb_hasControl()) + if (GDBStub::IsActive() && GDBStub::HasControl()) { - gdb_signal(GDB_SIGTRAP); - gdb_handle_exception(true); + GDBStub::SendSignal(GDBStub::Signal::Sigtrap); + GDBStub::ProcessCommands(true); // 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_sync = &gdb_step_sync_event; + } } state_lock.lock(); return s_state_cpu_step_instruction || !IsStepping(); @@ -293,6 +302,12 @@ void Break() RunAdjacentSystems(false); } +void Continue() +{ + CPU::EnableStepping(false); + Core::CallOnStateChangedCallbacks(Core::State::Running); +} + bool PauseAndLock(bool do_lock, bool unpause_on_unlock, bool control_adjacent) { // NOTE: This is protected by s_stepping_lock. diff --git a/Source/Core/Core/HW/CPU.h b/Source/Core/Core/HW/CPU.h index c797583c6a..d69206c64b 100644 --- a/Source/Core/Core/HW/CPU.h +++ b/Source/Core/Core/HW/CPU.h @@ -53,6 +53,9 @@ void EnableStepping(bool stepping); // should not be used by the Host. void Break(); +// This should only be called from the CPU thread +void Continue(); + // Shorthand for GetState() == State::Stepping. // WARNING: State::PowerDown will return false, not just State::Running. bool IsStepping(); diff --git a/Source/Core/Core/PowerPC/GDBStub.cpp b/Source/Core/Core/PowerPC/GDBStub.cpp index 7e947fccf2..bbf61f8111 100644 --- a/Source/Core/Core/PowerPC/GDBStub.cpp +++ b/Source/Core/Core/PowerPC/GDBStub.cpp @@ -3,6 +3,7 @@ // Originally written by Sven Peter for anergistic. +#include #include #include #include @@ -35,10 +36,9 @@ typedef SSIZE_T ssize_t; #include "Core/PowerPC/PPCCache.h" #include "Core/PowerPC/PowerPC.h" -namespace +namespace GDBStub { std::optional s_socket_context; -} // namespace #define GDB_BFR_MAX 10000 @@ -47,27 +47,35 @@ std::optional s_socket_context; #define GDB_STUB_ACK '+' #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; -static int sock = -1; +const s64 GDB_UPDATE_CYCLES = 100000; -static u8 cmd_bfr[GDB_BFR_MAX]; -static u32 cmd_len; +static bool s_has_control = false; -static u32 sig = 0; -static u32 send_signal = 0; -static u32 step_break = 0; +static int s_tmpsock = -1; +static int s_sock = -1; -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() { - return reinterpret_cast(cmd_bfr); + return reinterpret_cast(s_cmd_bfr); } // private helpers -static u8 hex2char(u8 hex) +static u8 Hex2char(u8 hex) { if (hex >= '0' && hex <= '9') return hex - '0'; @@ -80,7 +88,7 @@ static u8 hex2char(u8 hex) return 0; } -static u8 nibble2hex(u8 n) +static u8 Nibble2hex(u8 n) { n &= 0xf; if (n < 0xa) @@ -89,49 +97,50 @@ static u8 nibble2hex(u8 n) 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) { const u8 tmp = *src++; - *dst++ = nibble2hex(tmp >> 4); - *dst++ = nibble2hex(tmp); + *dst++ = Nibble2hex(tmp >> 4); + *dst++ = Nibble2hex(tmp); } } -static void hex2mem(u8* dst, u8* src, u32 len) +static void Hex2mem(u8* dst, u8* src, u32 len) { while (len-- > 0) { - *dst++ = (hex2char(*src) << 4) | hex2char(*(src + 1)); + *dst++ = (Hex2char(*src) << 4) | Hex2char(*(src + 1)); src += 2; } } -void GDBStubUpdateCallback(u64 userdata, s64 cycles_late) +static void UpdateCallback(u64 userdata, s64 cycles_late) { - gdb_handle_exception(false); - CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, m_gdbStubUpdateEvent); + ProcessCommands(false); + if (IsActive()) + CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, s_update_event); } -static u8 gdb_read_byte() +static u8 ReadByte() { 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) { ERROR_LOG_FMT(GDB_STUB, "recv failed : {}", res); - gdb_deinit(); + Deinit(); } return c; } -static u8 gdb_calc_chksum() +static u8 CalculateChecksum() { - u32 len = cmd_len; - u8* ptr = cmd_bfr; + u32 len = s_cmd_len; + u8* ptr = s_cmd_bfr; u8 c = 0; while (len-- > 0) @@ -140,9 +149,9 @@ static u8 gdb_calc_chksum() 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)) { @@ -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 ssize_t res = send(sock, &nak, 1, 0); + const ssize_t res = send(s_sock, &nak, 1, 0); if (res != 1) ERROR_LOG_FMT(GDB_STUB, "send failed"); } -static void gdb_ack() +static void 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) ERROR_LOG_FMT(GDB_STUB, "send failed"); } -static void gdb_read_command() +static void ReadCommand() { - cmd_len = 0; - memset(cmd_bfr, 0, sizeof cmd_bfr); + s_cmd_len = 0; + memset(s_cmd_bfr, 0, sizeof s_cmd_bfr); - u8 c = gdb_read_byte(); + u8 c = ReadByte(); if (c == '+') { // ignore ack @@ -192,8 +201,8 @@ static void gdb_read_command() else if (c == 0x03) { CPU::Break(); - gdb_signal(GDB_SIGTRAP); - hasControl = true; + SendSignal(Signal::Sigtrap); + s_has_control = true; return; } else if (c != GDB_STUB_START) @@ -202,146 +211,138 @@ static void gdb_read_command() return; } - while ((c = gdb_read_byte()) != GDB_STUB_END) + while ((c = ReadByte()) != GDB_STUB_END) { - cmd_bfr[cmd_len++] = c; - if (cmd_len == sizeof cmd_bfr) + s_cmd_bfr[s_cmd_len++] = c; + if (s_cmd_len == sizeof s_cmd_bfr) { ERROR_LOG_FMT(GDB_STUB, "gdb: cmd_bfr overflow"); - gdb_nak(); + Nack(); return; } } - u8 chk_read = hex2char(gdb_read_byte()) << 4; - chk_read |= hex2char(gdb_read_byte()); + u8 chk_read = Hex2char(ReadByte()) << 4; + chk_read |= Hex2char(ReadByte()); - const u8 chk_calc = gdb_calc_chksum(); + const u8 chk_calc = CalculateChecksum(); if (chk_calc != chk_read) { ERROR_LOG_FMT(GDB_STUB, "gdb: invalid checksum: calculated {:02x} and read {:02x} for ${}# (length: {})", - chk_calc, chk_read, CommandBufferAsString(), cmd_len); - cmd_len = 0; + chk_calc, chk_read, CommandBufferAsString(), s_cmd_len); + s_cmd_len = 0; - gdb_nak(); + Nack(); return; } DEBUG_LOG_FMT(GDB_STUB, "gdb: read command {} with a length of {}: {}", - static_cast(cmd_bfr[0]), cmd_len, CommandBufferAsString()); - gdb_ack(); + static_cast(s_cmd_bfr[0]), s_cmd_len, CommandBufferAsString()); + Ack(); } -static int gdb_data_available() +static bool IsDataAvailable() { struct timeval t; fd_set _fds, *fds = &_fds; FD_ZERO(fds); - FD_SET(sock, fds); + FD_SET(s_sock, fds); t.tv_sec = 0; 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"); - return 0; + return false; } - if (FD_ISSET(sock, fds)) - return 1; - return 0; + if (FD_ISSET(s_sock, fds)) + return true; + return false; } -static void gdb_reply(const char* reply) +static void SendReply(const char* reply) { - if (!gdb_active()) + if (!IsActive()) return; - memset(cmd_bfr, 0, sizeof cmd_bfr); + memset(s_cmd_bfr, 0, sizeof s_cmd_bfr); - cmd_len = (u32)strlen(reply); - if (cmd_len + 4 > sizeof cmd_bfr) + s_cmd_len = (u32)strlen(reply); + if (s_cmd_len + 4 > sizeof s_cmd_bfr) 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++; - const u8 chk = gdb_calc_chksum(); - cmd_len--; - cmd_bfr[0] = GDB_STUB_START; - cmd_bfr[cmd_len + 1] = GDB_STUB_END; - cmd_bfr[cmd_len + 2] = nibble2hex(chk >> 4); - cmd_bfr[cmd_len + 3] = nibble2hex(chk); + s_cmd_len++; + const u8 chk = CalculateChecksum(); + s_cmd_len--; + s_cmd_bfr[0] = GDB_STUB_START; + s_cmd_bfr[s_cmd_len + 1] = GDB_STUB_END; + s_cmd_bfr[s_cmd_len + 2] = Nibble2hex(chk >> 4); + 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; - u32 left = cmd_len + 4; + const char* ptr = (const char*)s_cmd_bfr; + u32 left = s_cmd_len + 4; while (left > 0) { - const int n = send(sock, ptr, left, 0); + const int n = send(s_sock, ptr, left, 0); if (n < 0) { ERROR_LOG_FMT(GDB_STUB, "gdb: send failed"); - return gdb_deinit(); + return Deinit(); } left -= n; ptr += n; } } -static void gdb_handle_query() +static void HandleQuery() { 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 || - memcmp(cmd_bfr, "Hc0", 3) == 0 || memcmp(cmd_bfr, "Hc1", 3) == 0) - return gdb_reply("OK"); - gdb_reply("E01"); + if (memcmp(s_cmd_bfr, "Hg0", 3) == 0 || memcmp(s_cmd_bfr, "Hc-1", 4) == 0 || + memcmp(s_cmd_bfr, "Hc0", 3) == 0 || memcmp(s_cmd_bfr, "Hc1", 3) == 0) + return SendReply("OK"); + SendReply("E01"); } -static void gdb_handle_thread_alive() +static void HandleIsThreadAlive() { - if (memcmp(cmd_bfr, "T0", 2) == 0 || memcmp(cmd_bfr, "T1", 4) == 0 || - memcmp(cmd_bfr, "T-1", 3) == 0) - return gdb_reply("OK"); - gdb_reply("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); + if (memcmp(s_cmd_bfr, "T0", 2) == 0 || memcmp(s_cmd_bfr, "T1", 4) == 0 || + memcmp(s_cmd_bfr, "T-1", 3) == 0) + return SendReply("OK"); + SendReply("E01"); } static void wbe32hex(u8* p, u32 v) { u32 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) { u32 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) @@ -350,7 +351,7 @@ static u32 re32hex(u8* p) u32 res = 0; for (i = 0; i < 8; i++) - res = (res << 4) | hex2char(p[i]); + res = (res << 4) | Hex2char(p[i]); return res; } @@ -361,22 +362,22 @@ static u64 re64hex(u8* p) u64 res = 0; for (i = 0; i < 16; i++) - res = (res << 4) | hex2char(p[i]); + res = (res << 4) | Hex2char(p[i]); return res; } -static void gdb_read_register() +static void ReadRegister() { static u8 reply[64]; u32 id; memset(reply, 0, sizeof reply); - id = hex2char(cmd_bfr[1]); - if (cmd_bfr[2] != '\0') + id = Hex2char(s_cmd_bfr[1]); + if (s_cmd_bfr[2] != '\0') { id <<= 4; - id |= hex2char(cmd_bfr[2]); + id |= Hex2char(s_cmd_bfr[2]); } if (id < 32) @@ -416,15 +417,15 @@ static void gdb_read_register() wbe32hex(reply, FPSCR.Hex); break; default: - return gdb_reply("E01"); + return SendReply("E01"); break; } } - gdb_reply((char*)reply); + SendReply((char*)reply); } -static void gdb_read_registers() +static void ReadRegisters() { static u8 bfr[GDB_BFR_MAX - 4]; u8* bufptr = bfr; @@ -438,13 +439,13 @@ static void gdb_read_registers() } bufptr += 32 * 8; - gdb_reply((char*)bfr); + SendReply((char*)bfr); } -static void gdb_write_registers() +static void WriteRegisters() { u32 i; - u8* bufptr = cmd_bfr; + u8* bufptr = s_cmd_bfr; for (i = 0; i < 32; i++) { @@ -452,21 +453,21 @@ static void gdb_write_registers() } bufptr += 32 * 8; - gdb_reply("OK"); + SendReply("OK"); } -static void gdb_write_register() +static void WriteRegister() { u32 id; - u8* bufptr = cmd_bfr + 3; + u8* bufptr = s_cmd_bfr + 3; - id = hex2char(cmd_bfr[1]); - if (cmd_bfr[2] != '=') + id = Hex2char(s_cmd_bfr[1]); + if (s_cmd_bfr[2] != '=') { ++bufptr; id <<= 4; - id |= hex2char(cmd_bfr[2]); + id |= Hex2char(s_cmd_bfr[2]); } if (id < 32) @@ -506,15 +507,15 @@ static void gdb_write_register() FPSCR.Hex = re32hex(bufptr); break; default: - return gdb_reply("E01"); + return SendReply("E01"); break; } } - gdb_reply("OK"); + SendReply("OK"); } -static void gdb_read_mem() +static void ReadMemory() { static u8 reply[GDB_BFR_MAX - 4]; u32 addr, len; @@ -522,70 +523,57 @@ static void gdb_read_mem() i = 1; addr = 0; - while (cmd_bfr[i] != ',') - addr = (addr << 4) | hex2char(cmd_bfr[i++]); + while (s_cmd_bfr[i] != ',') + addr = (addr << 4) | Hex2char(s_cmd_bfr[i++]); i++; len = 0; - while (i < cmd_len) - len = (len << 4) | hex2char(cmd_bfr[i++]); + while (i < s_cmd_len) + len = (len << 4) | Hex2char(s_cmd_bfr[i++]); DEBUG_LOG_FMT(GDB_STUB, "gdb: read memory: {:08x} bytes from {:08x}", len, addr); if (len * 2 > sizeof reply) - gdb_reply("E01"); + SendReply("E01"); u8* data = Memory::GetPointer(addr); if (!data) - return gdb_reply("E0"); - mem2hex(reply, data, len); + return SendReply("E0"); + Mem2hex(reply, data, len); reply[len * 2] = '\0'; - gdb_reply((char*)reply); + SendReply((char*)reply); } -static void gdb_write_mem() +static void WriteMemory() { u32 addr, len; u32 i; i = 1; addr = 0; - while (cmd_bfr[i] != ',') - addr = (addr << 4) | hex2char(cmd_bfr[i++]); + while (s_cmd_bfr[i] != ',') + addr = (addr << 4) | Hex2char(s_cmd_bfr[i++]); i++; len = 0; - while (cmd_bfr[i] != ':') - len = (len << 4) | hex2char(cmd_bfr[i++]); + while (s_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); u8* dst = Memory::GetPointer(addr); if (!dst) - return gdb_reply("E00"); - hex2mem(dst, cmd_bfr + i + 1, len); - gdb_reply("OK"); + return SendReply("E00"); + Hex2mem(dst, s_cmd_bfr + i + 1, len); + SendReply("OK"); } -// forces a break on next instruction check -void gdb_break() +static void Step() { - step_break = 1; - send_signal = 1; + CPU::EnableStepping(true); + Core::CallOnStateChangedCallbacks(Core::State::Paused); } -static void gdb_step() +static bool AddBreakpoint(BreakpointType type, u32 addr, u32 len) { - send_signal = 1; -} - -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) + if (type == BreakpointType::ExecuteHard || type == BreakpointType::ExecuteSoft) { PowerPC::breakpoints.Add(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.end_address = addr + 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_write = (type == GDB_BP_TYPE_W || type == GDB_BP_TYPE_A); + new_memcheck.is_break_on_read = + (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.log_on_hit = false; new_memcheck.is_enabled = true; @@ -607,161 +597,127 @@ bool gdb_add_bp(u32 type, u32 addr, u32 len) return true; } -static void _gdb_add_bp() +static void HandleAddBreakpoint() { u32 type; u32 i, addr = 0, len = 0; - type = hex2char(cmd_bfr[1]); - switch (type) - { - 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"); - } + type = Hex2char(s_cmd_bfr[1]); + if (type > 4) + return SendReply("E01"); i = 3; - while (cmd_bfr[i] != ',') - addr = addr << 4 | hex2char(cmd_bfr[i++]); + while (s_cmd_bfr[i] != ',') + addr = addr << 4 | Hex2char(s_cmd_bfr[i++]); i++; - while (i < cmd_len) - len = len << 4 | hex2char(cmd_bfr[i++]); + while (i < s_cmd_len) + len = len << 4 | Hex2char(s_cmd_bfr[i++]); - if (!gdb_add_bp(type, addr, len)) - return gdb_reply("E02"); - gdb_reply("OK"); + if (!AddBreakpoint(static_cast(type), addr, len)) + return SendReply("E02"); + SendReply("OK"); } -static void gdb_remove_bp() +static void HandleRemoveBreakpoint() { u32 type, addr, len, i; - type = hex2char(cmd_bfr[1]); - switch (type) - { - 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"); - } + type = Hex2char(s_cmd_bfr[1]); + if (type >= 4) + return SendReply("E01"); addr = 0; len = 0; i = 3; - while (cmd_bfr[i] != ',') - addr = (addr << 4) | hex2char(cmd_bfr[i++]); + while (s_cmd_bfr[i] != ',') + addr = (addr << 4) | Hex2char(s_cmd_bfr[i++]); i++; - while (i < cmd_len) - len = (len << 4) | hex2char(cmd_bfr[i++]); + while (i < s_cmd_len) + len = (len << 4) | Hex2char(s_cmd_bfr[i++]); - gdb_bp_remove(type, addr, len); - gdb_reply("OK"); + RemoveBreakpoint(static_cast(type), addr, len); + SendReply("OK"); } - -void gdb_handle_exception(bool loop_until_continue) +void ProcessCommands(bool loop_until_continue) { - while (gdb_active()) + while (IsActive()) { if (CPU::GetState() == CPU::State::PowerDown) { - gdb_deinit(); + Deinit(); INFO_LOG_FMT(GDB_STUB, "killed by power down"); return; } - if (!gdb_data_available()) + if (!IsDataAvailable()) { if (loop_until_continue) continue; else return; } - gdb_read_command(); + ReadCommand(); // No more commands - if (cmd_len == 0) + if (s_cmd_len == 0) continue; - switch (cmd_bfr[0]) + switch (s_cmd_bfr[0]) { case 'q': - gdb_handle_query(); + HandleQuery(); break; case 'H': - gdb_handle_set_thread(); + HandleSetThread(); break; case 'T': - gdb_handle_thread_alive(); + HandleIsThreadAlive(); break; case '?': - gdb_handle_signal(); + SendSignal(Signal::Sigterm); break; case 'k': - gdb_deinit(); + Deinit(); INFO_LOG_FMT(GDB_STUB, "killed by gdb"); return; case 'g': - gdb_read_registers(); + ReadRegisters(); break; case 'G': - gdb_write_registers(); + WriteRegisters(); break; case 'p': - gdb_read_register(); + ReadRegister(); break; case 'P': - gdb_write_register(); + WriteRegister(); break; case 'm': - gdb_read_mem(); + ReadMemory(); break; case 'M': - gdb_write_mem(); + WriteMemory(); PowerPC::ppcState.iCache.Reset(); Host_UpdateDisasmDialog(); break; case 's': - gdb_step(); return; case 'C': case 'c': - gdb_continue(); - hasControl = false; + CPU::Continue(); + s_has_control = false; return; case 'z': - gdb_remove_bp(); + HandleRemoveBreakpoint(); break; case 'Z': - _gdb_add_bp(); + HandleAddBreakpoint(); break; default: - gdb_reply(""); + SendReply(""); break; } } @@ -769,11 +725,11 @@ void gdb_handle_exception(bool loop_until_continue) // exported functions -static void gdb_init_generic(int domain, const sockaddr* server_addr, socklen_t server_addrlen, - sockaddr* client_addr, socklen_t* client_addrlen); +static void InitGeneric(int domain, const sockaddr* server_addr, socklen_t server_addrlen, + sockaddr* client_addr, socklen_t* client_addrlen); #ifndef _WIN32 -void gdb_init_local(const char* socket) +void InitLocal(const char* socket) { unlink(socket); @@ -781,11 +737,11 @@ void gdb_init_local(const char* socket) addr.sun_family = AF_UNIX; 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 -void gdb_init(u32 port) +void Init(u32 port) { sockaddr_in saddr_server = {}; sockaddr_in saddr_client; @@ -796,94 +752,86 @@ void gdb_init(u32 port) socklen_t client_addrlen = sizeof(saddr_client); - gdb_init_generic(PF_INET, (const sockaddr*)&saddr_server, sizeof(saddr_server), - (sockaddr*)&saddr_client, &client_addrlen); + InitGeneric(PF_INET, (const sockaddr*)&saddr_server, sizeof(saddr_server), + (sockaddr*)&saddr_client, &client_addrlen); 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, - sockaddr* client_addr, socklen_t* client_addrlen) +static void InitGeneric(int domain, const sockaddr* server_addr, socklen_t server_addrlen, + sockaddr* client_addr, socklen_t* client_addrlen) { s_socket_context.emplace(); - tmpsock = socket(domain, SOCK_STREAM, 0); - if (tmpsock == -1) + s_tmpsock = socket(domain, SOCK_STREAM, 0); + if (s_tmpsock == -1) ERROR_LOG_FMT(GDB_STUB, "Failed to create gdb socket"); 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"); - 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"); - if (listen(tmpsock, 1) < 0) + if (listen(s_tmpsock, 1) < 0) ERROR_LOG_FMT(GDB_STUB, "Failed to listen to gdb socket"); INFO_LOG_FMT(GDB_STUB, "Waiting for gdb to connect..."); - sock = accept(tmpsock, client_addr, client_addrlen); - if (sock < 0) + s_sock = accept(s_tmpsock, client_addr, client_addrlen); + if (s_sock < 0) ERROR_LOG_FMT(GDB_STUB, "Failed to accept gdb client"); INFO_LOG_FMT(GDB_STUB, "Client connected."); #ifdef _WIN32 closesocket(s_tmpsock); #else - close(tmpsock); + close(s_tmpsock); #endif - tmpsock = -1; + s_tmpsock = -1; - m_gdbStubUpdateEvent = CoreTiming::RegisterEvent("GDBStubUpdate", GDBStubUpdateCallback); - CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, m_gdbStubUpdateEvent); - hasControl = true; + s_update_event = CoreTiming::RegisterEvent("GDBStubUpdate", UpdateCallback); + CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, s_update_event); + s_has_control = true; } -void gdb_deinit() +void Deinit() { - if (tmpsock != -1) + if (s_tmpsock != -1) { - shutdown(tmpsock, SHUT_RDWR); - tmpsock = -1; + shutdown(s_tmpsock, SHUT_RDWR); + s_tmpsock = -1; } - if (sock != -1) + if (s_sock != -1) { - shutdown(sock, SHUT_RDWR); - sock = -1; + shutdown(s_sock, SHUT_RDWR); + s_sock = -1; } 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) - return 1; - - sig = s; - - if (send_signal) - { - gdb_handle_signal(); - send_signal = 0; - } - - return 0; + char bfr[128] = {}; + fmt::format_to(bfr, "T{:02x}{:02x}:{:08x};{:02x}:{:08x};", signal, 64, PC, 1, GPR(1)); + SendReply(bfr); } +} // namespace GDBStub diff --git a/Source/Core/Core/PowerPC/GDBStub.h b/Source/Core/Core/PowerPC/GDBStub.h index 56bfa35d14..d7580dbf8c 100644 --- a/Source/Core/Core/PowerPC/GDBStub.h +++ b/Source/Core/Core/PowerPC/GDBStub.h @@ -8,42 +8,21 @@ #include "Common/CommonTypes.h" #include "Core/CoreTiming.h" -typedef enum +namespace GDBStub { - GDB_SIGTRAP = 5, - GDB_SIGTERM = 15, -} gdb_signals; - -typedef enum +enum class Signal { - GDB_BP_TYPE_NONE = 0, - GDB_BP_TYPE_X, - GDB_BP_TYPE_R, - GDB_BP_TYPE_W, - GDB_BP_TYPE_A -} gdb_bp_type; + Sigtrap = 5, + Sigterm = 15, +}; -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 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); +void ProcessCommands(bool loop_until_continue); +void SendSignal(Signal signal); } // namespace GDBStub diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp index 453097f016..ac48988080 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp @@ -293,8 +293,8 @@ void Interpreter::Run() #endif INFO_LOG_FMT(POWERPC, "Hit Breakpoint - {:08x}", PC); CPU::Break(); - if (gdb_active()) - gdb_takeControl(); + if (GDBStub::IsActive()) + GDBStub::TakeControl(); if (PowerPC::breakpoints.IsTempBreakPoint(PC)) PowerPC::breakpoints.Remove(PC); diff --git a/Source/Core/Core/PowerPC/MMU.cpp b/Source/Core/Core/PowerPC/MMU.cpp index a027720a4f..8742868bc5 100644 --- a/Source/Core/Core/PowerPC/MMU.cpp +++ b/Source/Core/Core/PowerPC/MMU.cpp @@ -519,8 +519,8 @@ static void Memcheck(u32 address, u64 var, bool write, size_t size) CPU::Break(); - if (gdb_active()) - gdb_takeControl(); + if (GDBStub::IsActive()) + GDBStub::TakeControl(); // 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 diff --git a/Source/Core/Core/PowerPC/PowerPC.cpp b/Source/Core/Core/PowerPC/PowerPC.cpp index d124643560..40cdfdac53 100644 --- a/Source/Core/Core/PowerPC/PowerPC.cpp +++ b/Source/Core/Core/PowerPC/PowerPC.cpp @@ -614,8 +614,8 @@ void CheckBreakPoints() if (PowerPC::breakpoints.IsBreakPointBreakOnHit(PC)) { CPU::Break(); - if (gdb_active()) - gdb_takeControl(); + if (GDBStub::IsActive()) + GDBStub::TakeControl(); } if (PowerPC::breakpoints.IsBreakPointLogOnHit(PC)) {