GDBStub: rework the breakpoint and the control logic

This commit is contained in:
aldelaro5 2021-09-30 23:36:11 -04:00
parent 994847f09c
commit 7d3ea4c3a1
5 changed files with 82 additions and 147 deletions

View File

@ -22,11 +22,14 @@ typedef SSIZE_T ssize_t;
#include <unistd.h> #include <unistd.h>
#endif #endif
#include "Common/Event.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/SocketContext.h" #include "Common/SocketContext.h"
#include "Core/Core.h"
#include "Core/HW/CPU.h" #include "Core/HW/CPU.h"
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
#include "Core/Host.h" #include "Core/Host.h"
#include "Core/PowerPC/BreakPoints.h"
#include "Core/PowerPC/GDBStub.h" #include "Core/PowerPC/GDBStub.h"
#include "Core/PowerPC/Gekko.h" #include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/PPCCache.h" #include "Core/PowerPC/PPCCache.h"
@ -38,13 +41,14 @@ std::optional<Common::SocketContext> s_socket_context;
} // namespace } // namespace
#define GDB_BFR_MAX 10000 #define GDB_BFR_MAX 10000
#define GDB_MAX_BP 10
#define GDB_STUB_START '$' #define GDB_STUB_START '$'
#define GDB_STUB_END '#' #define GDB_STUB_END '#'
#define GDB_STUB_ACK '+' #define GDB_STUB_ACK '+'
#define GDB_STUB_NAK '-' #define GDB_STUB_NAK '-'
static bool hasControl = false;
static int tmpsock = -1; static int tmpsock = -1;
static int sock = -1; static int sock = -1;
@ -57,18 +61,6 @@ static u32 step_break = 0;
static CoreTiming::EventType* m_gdbStubUpdateEvent; static CoreTiming::EventType* m_gdbStubUpdateEvent;
typedef struct
{
u32 active;
u32 addr;
u32 len;
} gdb_bp_t;
static gdb_bp_t bp_x[GDB_MAX_BP];
static gdb_bp_t bp_r[GDB_MAX_BP];
static gdb_bp_t bp_w[GDB_MAX_BP];
static gdb_bp_t bp_a[GDB_MAX_BP];
static const char* CommandBufferAsString() static const char* CommandBufferAsString()
{ {
return reinterpret_cast<const char*>(cmd_bfr); return reinterpret_cast<const char*>(cmd_bfr);
@ -148,91 +140,24 @@ static u8 gdb_calc_chksum()
return c; return c;
} }
static gdb_bp_t* gdb_bp_ptr(u32 type)
{
switch (type)
{
case GDB_BP_TYPE_X:
return bp_x;
case GDB_BP_TYPE_R:
return bp_x;
case GDB_BP_TYPE_W:
return bp_x;
case GDB_BP_TYPE_A:
return bp_x;
default:
return nullptr;
}
}
static gdb_bp_t* gdb_bp_empty_slot(u32 type)
{
gdb_bp_t* p;
u32 i;
p = gdb_bp_ptr(type);
if (p == nullptr)
return nullptr;
for (i = 0; i < GDB_MAX_BP; i++)
{
if (p[i].active == 0)
return &p[i];
}
return nullptr;
}
static gdb_bp_t* gdb_bp_find(u32 type, u32 addr, u32 len)
{
gdb_bp_t* p;
u32 i;
p = gdb_bp_ptr(type);
if (p == nullptr)
return nullptr;
for (i = 0; i < GDB_MAX_BP; i++)
{
if (p[i].active == 1 && p[i].addr == addr && p[i].len == len)
return &p[i];
}
return nullptr;
}
static void gdb_bp_remove(u32 type, u32 addr, u32 len) static void gdb_bp_remove(u32 type, u32 addr, u32 len)
{ {
gdb_bp_t* p; if (type == GDB_BP_TYPE_X)
do
{ {
p = gdb_bp_find(type, addr, len); while (PowerPC::breakpoints.IsAddressBreakPoint(addr))
if (p != nullptr)
{ {
PowerPC::breakpoints.Remove(addr);
DEBUG_LOG_FMT(GDB_STUB, "gdb: removed a breakpoint: {:08x} bytes at {:08x}", len, addr); DEBUG_LOG_FMT(GDB_STUB, "gdb: removed a breakpoint: {:08x} bytes at {:08x}", len, addr);
p->active = 0;
memset(p, 0, sizeof(gdb_bp_t));
} }
} while (p != nullptr);
}
static int gdb_bp_check(u32 addr, u32 type)
{
gdb_bp_t* p;
u32 i;
p = gdb_bp_ptr(type);
if (p == nullptr)
return 0;
for (i = 0; i < GDB_MAX_BP; i++)
{
if (p[i].active == 1 && (addr >= p[i].addr && addr < p[i].addr + p[i].len))
return 1;
} }
else
return 0; {
while (PowerPC::memchecks.GetMemCheck(addr, len) != nullptr)
{
PowerPC::memchecks.Remove(addr);
DEBUG_LOG_FMT(GDB_STUB, "gdb: removed a memcheck: {:08x} bytes at {:08x}", len, addr);
}
}
} }
static void gdb_nak() static void gdb_nak()
@ -268,6 +193,7 @@ static void gdb_read_command()
{ {
CPU::Break(); CPU::Break();
gdb_signal(GDB_SIGTRAP); gdb_signal(GDB_SIGTRAP);
hasControl = true;
return; return;
} }
else if (c != GDB_STUB_START) else if (c != GDB_STUB_START)
@ -639,27 +565,45 @@ void gdb_break()
static void gdb_step() static void gdb_step()
{ {
gdb_break(); Common::Event sync_event;
PowerPC::CoreMode old_mode = PowerPC::GetMode();
PowerPC::SetMode(PowerPC::CoreMode::Interpreter);
PowerPC::breakpoints.ClearAllTemporary();
CPU::StepOpcode(&sync_event);
sync_event.WaitFor(std::chrono::milliseconds(20));
PowerPC::SetMode(old_mode);
send_signal = 1;
} }
static void gdb_continue() static void gdb_continue()
{ {
send_signal = 1; send_signal = 1;
CPU::EnableStepping(false);
Core::CallOnStateChangedCallbacks(Core::State::Running);
} }
bool gdb_add_bp(u32 type, u32 addr, u32 len) bool gdb_add_bp(u32 type, u32 addr, u32 len)
{ {
gdb_bp_t* bp; if (type == GDB_BP_TYPE_X)
bp = gdb_bp_empty_slot(type); {
if (bp == nullptr) PowerPC::breakpoints.Add(addr);
return false; DEBUG_LOG_FMT(GDB_STUB, "gdb: added {} breakpoint: {:08x} bytes at {:08x}", type, len, addr);
}
bp->active = 1; else
bp->addr = addr; {
bp->len = len; TMemCheck new_memcheck;
new_memcheck.start_address = addr;
DEBUG_LOG_FMT(GDB_STUB, "gdb: added {} breakpoint: {:08x} bytes at {:08x}", type, bp->len, new_memcheck.end_address = addr + len - 1;
bp->addr); 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.break_on_hit = true;
new_memcheck.log_on_hit = false;
new_memcheck.is_enabled = true;
PowerPC::memchecks.Add(new_memcheck);
INFO_LOG_FMT(GDB_STUB, "gdb: added {} memcheck: {:08x} bytes at {:08x}", type, len, addr);
}
return true; return true;
} }
@ -798,6 +742,7 @@ void gdb_handle_exception(bool loop_until_continue)
case 'C': case 'C':
case 'c': case 'c':
gdb_continue(); gdb_continue();
hasControl = false;
return; return;
case 'z': case 'z':
gdb_remove_bp(); gdb_remove_bp();
@ -851,10 +796,6 @@ static void gdb_init_generic(int domain, const sockaddr* server_addr, socklen_t
sockaddr* client_addr, socklen_t* client_addrlen) sockaddr* client_addr, socklen_t* client_addrlen)
{ {
s_socket_context.emplace(); s_socket_context.emplace();
memset(bp_x, 0, sizeof bp_x);
memset(bp_r, 0, sizeof bp_r);
memset(bp_w, 0, sizeof bp_w);
memset(bp_a, 0, sizeof bp_a);
tmpsock = socket(domain, SOCK_STREAM, 0); tmpsock = socket(domain, SOCK_STREAM, 0);
if (tmpsock == -1) if (tmpsock == -1)
@ -886,6 +827,7 @@ static void gdb_init_generic(int domain, const sockaddr* server_addr, socklen_t
m_gdbStubUpdateEvent = CoreTiming::RegisterEvent("GDBStubUpdate", GDBStubUpdateCallback); m_gdbStubUpdateEvent = CoreTiming::RegisterEvent("GDBStubUpdate", GDBStubUpdateCallback);
CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, m_gdbStubUpdateEvent); CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, m_gdbStubUpdateEvent);
hasControl = true;
} }
void gdb_deinit() void gdb_deinit()
@ -902,6 +844,7 @@ void gdb_deinit()
} }
s_socket_context.reset(); s_socket_context.reset();
hasControl = false;
} }
bool gdb_active() bool gdb_active()
@ -909,6 +852,16 @@ bool gdb_active()
return tmpsock != -1 || sock != -1; return tmpsock != -1 || sock != -1;
} }
bool gdb_hasControl()
{
return hasControl;
}
void gdb_takeControl()
{
hasControl = true;
}
int gdb_signal(u32 s) int gdb_signal(u32 s)
{ {
if (sock == -1) if (sock == -1)
@ -924,43 +877,3 @@ int gdb_signal(u32 s)
return 0; return 0;
} }
int gdb_bp_x(u32 addr)
{
if (sock == -1)
return 0;
if (step_break)
{
step_break = 0;
DEBUG_LOG_FMT(GDB_STUB, "Step was successful.");
return 1;
}
return gdb_bp_check(addr, GDB_BP_TYPE_X);
}
int gdb_bp_r(u32 addr)
{
if (sock == -1)
return 0;
return gdb_bp_check(addr, GDB_BP_TYPE_R);
}
int gdb_bp_w(u32 addr)
{
if (sock == -1)
return 0;
return gdb_bp_check(addr, GDB_BP_TYPE_W);
}
int gdb_bp_a(u32 addr)
{
if (sock == -1)
return 0;
return gdb_bp_check(addr, GDB_BP_TYPE_A);
}

View File

@ -35,6 +35,8 @@ void gdb_init(u32 port);
void gdb_init_local(const char* socket); void gdb_init_local(const char* socket);
void gdb_deinit(); void gdb_deinit();
bool gdb_active(); bool gdb_active();
bool gdb_hasControl();
void gdb_takeControl();
void gdb_break(); void gdb_break();
void gdb_handle_exception(bool loopUntilContinue); void gdb_handle_exception(bool loopUntilContinue);

View File

@ -296,6 +296,9 @@ 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();
#ifdef USE_GDBSTUB
gdb_takeControl();
#endif
if (PowerPC::breakpoints.IsTempBreakPoint(PC)) if (PowerPC::breakpoints.IsTempBreakPoint(PC))
PowerPC::breakpoints.Remove(PC); PowerPC::breakpoints.Remove(PC);

View File

@ -24,6 +24,10 @@
#include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoBackendBase.h"
#ifdef USE_GDBSTUB
#include "Core/PowerPC/GDBStub.h"
#endif
namespace PowerPC namespace PowerPC
{ {
// EFB RE // EFB RE
@ -518,6 +522,10 @@ static void Memcheck(u32 address, u64 var, bool write, size_t size)
CPU::Break(); CPU::Break();
#ifdef USE_GDBSTUB
gdb_takeControl();
#endif
// 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
// watchpoints will stop the emulator before the offending load/store, // watchpoints will stop the emulator before the offending load/store,

View File

@ -30,6 +30,10 @@
#include "Core/PowerPC/MMU.h" #include "Core/PowerPC/MMU.h"
#include "Core/PowerPC/PPCSymbolDB.h" #include "Core/PowerPC/PPCSymbolDB.h"
#ifdef USE_GDBSTUB
#include "Core/PowerPC/GDBStub.h"
#endif
namespace PowerPC namespace PowerPC
{ {
// STATE_TO_SAVE // STATE_TO_SAVE
@ -611,7 +615,12 @@ void CheckBreakPoints()
return; return;
if (PowerPC::breakpoints.IsBreakPointBreakOnHit(PC)) if (PowerPC::breakpoints.IsBreakPointBreakOnHit(PC))
{
CPU::Break(); CPU::Break();
#ifdef USE_GDBSTUB
gdb_takeControl();
#endif
}
if (PowerPC::breakpoints.IsBreakPointLogOnHit(PC)) if (PowerPC::breakpoints.IsBreakPointLogOnHit(PC))
{ {
NOTICE_LOG_FMT(MEMMAP, NOTICE_LOG_FMT(MEMMAP,