GDBStub: Refactor the whole code
This commit is contained in:
parent
94a0f416eb
commit
1b92f81379
|
@ -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."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue