Started gdb stub support.

This commit is contained in:
Matthew Parlane 2013-01-06 23:28:27 +13:00
parent 4d6056f146
commit b7fd1225ba
11 changed files with 1071 additions and 2 deletions

View file

@ -222,6 +222,12 @@ if(FASTLOG)
add_definitions(-DDEBUGFAST) add_definitions(-DDEBUGFAST)
endif() endif()
option(GDBSTUB "Enable gdb stub for remote debugging." OFF)
if(GDBSTUB)
add_definitions(-DUSE_GDBSTUB)
endif(GDBSTUB)
# For now GLES and EGL are tied to each other. # For now GLES and EGL are tied to each other.
# Enabling GLES also disables the OpenGL plugin. # Enabling GLES also disables the OpenGL plugin.
option(USE_GLES "Enables GLES, disables OGL" OFF) option(USE_GLES "Enables GLES, disables OGL" OFF)

View file

@ -44,6 +44,7 @@ enum LOG_TYPE {
DVDINTERFACE, DVDINTERFACE,
DYNA_REC, DYNA_REC,
EXPANSIONINTERFACE, EXPANSIONINTERFACE,
GDB_STUB,
POWERPC, POWERPC,
GPFIFO, GPFIFO,
OSHLE, OSHLE,

View file

@ -57,6 +57,7 @@ LogManager::LogManager()
m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface"); m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface");
m_Log[LogTypes::GPFIFO] = new LogContainer("GP", "GPFifo"); m_Log[LogTypes::GPFIFO] = new LogContainer("GP", "GPFifo");
m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt"); m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt");
m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub");
m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt"); m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt");
m_Log[LogTypes::POWERPC] = new LogContainer("PowerPC", "IBM CPU"); m_Log[LogTypes::POWERPC] = new LogContainer("PowerPC", "IBM CPU");
m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE"); m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE");

View file

@ -222,5 +222,9 @@ if(OPROFILE_FOUND)
set(LIBS ${LIBS} opagent bfd) set(LIBS ${LIBS} opagent bfd)
endif(OPROFILE_FOUND) endif(OPROFILE_FOUND)
if(GDBSTUB)
set(SRCS ${SRCS} Src/PowerPC/GDBStub.cpp)
endif(GDBSTUB)
add_library(core STATIC ${SRCS}) add_library(core STATIC ${SRCS})
target_link_libraries(core ${LIBS}) target_link_libraries(core ${LIBS})

View file

@ -157,6 +157,7 @@ void SConfig::SaveSettings()
ini.Set("General", "RecursiveGCMPaths", m_RecursiveISOFolder); ini.Set("General", "RecursiveGCMPaths", m_RecursiveISOFolder);
ini.Set("General", "NANDRoot", m_NANDPath); ini.Set("General", "NANDRoot", m_NANDPath);
ini.Set("General", "WirelessMac", m_WirelessMac); ini.Set("General", "WirelessMac", m_WirelessMac);
ini.Set("General", "GDBPort", m_LocalCoreStartupParameter.iGDBPort);
// Interface // Interface
ini.Set("Interface", "ConfirmStop", m_LocalCoreStartupParameter.bConfirmStop); ini.Set("Interface", "ConfirmStop", m_LocalCoreStartupParameter.bConfirmStop);
@ -270,6 +271,9 @@ void SConfig::LoadSettings()
{ {
ini.Get("General", "LastFilename", &m_LastFilename); ini.Get("General", "LastFilename", &m_LastFilename);
ini.Get("General", "ShowLag", &m_ShowLag, false); ini.Get("General", "ShowLag", &m_ShowLag, false);
#ifdef USE_GDBSTUB
ini.Get("General", "GDBPort", &(m_LocalCoreStartupParameter.iGDBPort), -1);
#endif
m_ISOFolder.clear(); m_ISOFolder.clear();
int numGCMPaths; int numGCMPaths;

View file

@ -55,6 +55,9 @@
#include "PowerPC/PowerPC.h" #include "PowerPC/PowerPC.h"
#include "PowerPC/JitCommon/JitBase.h" #include "PowerPC/JitCommon/JitBase.h"
#ifdef USE_GDBSTUB
#include "PowerPC/GDBStub.h"
#endif
#include "DSPEmulator.h" #include "DSPEmulator.h"
#include "ConfigManager.h" #include "ConfigManager.h"
@ -101,6 +104,9 @@ std::string g_stateFileName;
std::thread g_EmuThread; std::thread g_EmuThread;
static std::thread g_cpu_thread; static std::thread g_cpu_thread;
#ifdef USE_GDBSTUB
static std::thread g_gdb_thread;
#endif
static bool g_requestRefreshInfo = false; static bool g_requestRefreshInfo = false;
static int g_pauseAndLockDepth = 0; static int g_pauseAndLockDepth = 0;
@ -448,6 +454,20 @@ void EmuThread()
else else
cpuThreadFunc = CpuThread; cpuThreadFunc = CpuThread;
#ifdef USE_GDBSTUB
if(_CoreParameter.iGDBPort > 0)
{
INFO_LOG(GDB_STUB, "Trying to start the GDB Stub listening on port %d.", _CoreParameter.iGDBPort);
Core::SetState(Core::CORE_PAUSE);
gdb_init(_CoreParameter.iGDBPort);
g_gdb_thread = std::thread(gdb_thread);
//gdb_signal(SIGTRAP);
gdb_add_bp(GDB_BP_TYPE_X, 0x80004050, 4);
}
#endif
// ENTER THE VIDEO THREAD LOOP // ENTER THE VIDEO THREAD LOOP
if (_CoreParameter.bCPUThread) if (_CoreParameter.bCPUThread)
{ {
@ -491,6 +511,14 @@ void EmuThread()
g_cpu_thread.join(); g_cpu_thread.join();
INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str()); INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str());
#ifdef USE_GDBSTUB
// Wait for g_gdb_thread to exit
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping GDB thread ...").c_str());
gdb_deinit();
g_gdb_thread.join();
INFO_LOG(CONSOLE, "%s", StopMessage(true, "GDB thread stopped.").c_str());
#endif
VolumeHandler::EjectVolume(); VolumeHandler::EjectVolume();
FileMon::Close(); FileMon::Close();

View file

@ -68,6 +68,9 @@ SCoreStartupParameter::SCoreStartupParameter()
void SCoreStartupParameter::LoadDefaults() void SCoreStartupParameter::LoadDefaults()
{ {
bEnableDebugging = false; bEnableDebugging = false;
#ifdef USE_GDBSTUB
iGDBPort = -1;
#endif
iCPUCore = 1; iCPUCore = 1;
bCPUThread = false; bCPUThread = false;
bSkipIdle = false; bSkipIdle = false;

View file

@ -71,6 +71,9 @@ struct SCoreStartupParameter
// Settings // Settings
bool bEnableDebugging; bool bEnableDebugging;
#ifdef USE_GDBSTUB
int iGDBPort;
#endif
bool bAutomaticStart; bool bAutomaticStart;
bool bBootToPause; bool bBootToPause;

View file

@ -0,0 +1,942 @@
// Copyright (C) 2010 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
#include "GDBStub.h"
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef _WIN32
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <iphlpapi.h>
#else
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#include <stdarg.h>
#define GDB_BFR_MAX 10000
#define GDB_MAX_BP 10
#define GDB_STUB_START '$'
#define GDB_STUB_END '#'
#define GDB_STUB_ACK '+'
#define GDB_STUB_NAK '-'
static int tmpsock = -1;
static int sock = -1;
static struct sockaddr_in saddr_server, saddr_client;
static u8 cmd_bfr[GDB_BFR_MAX];
static u32 cmd_len;
static u32 sig = 0;
static u32 send_signal = 0;
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];
// private helpers
static u8 hex2char(u8 hex)
{
if (hex >= '0' && hex <= '9')
return hex - '0';
else if (hex >= 'a' && hex <= 'f')
return hex - 'a' + 0xa;
else if (hex >= 'A' && hex <= 'F')
return hex - 'A' + 0xa;
ERROR_LOG(GDB_STUB, "Invalid nibble: %c (%02x)\n", hex, hex);
return 0;
}
static u8 nibble2hex(u8 n)
{
n &= 0xf;
if (n < 0xa)
return '0' + n;
else
return 'A' + n - 0xa;
}
static void mem2hex(u8 *dst, u8 *src, u32 len)
{
u8 tmp;
while (len-- > 0) {
tmp = *src++;
*dst++ = nibble2hex(tmp>>4);
*dst++ = nibble2hex(tmp);
}
}
static void hex2mem(u8 *dst, u8 *src, u32 len)
{
while (len-- > 0) {
*dst = hex2char(*src++) << 4;
*dst++ |= hex2char(*src++);
}
}
static u8 gdb_read_byte()
{
ssize_t res;
u8 c = '+';
res = recv(sock, &c, 1, MSG_WAITALL);
if (res != 1)
{
ERROR_LOG(GDB_STUB, "recv failed : %ld", res);
gdb_deinit();
}
return c;
}
static u8 gdb_calc_chksum()
{
u32 len = cmd_len;
u8 *ptr = cmd_bfr;
u8 c = 0;
while(len-- > 0)
c += *ptr++;
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 NULL;
}
}
static gdb_bp_t *gdb_bp_empty_slot(u32 type)
{
gdb_bp_t *p;
u32 i;
p = gdb_bp_ptr(type);
if (p == NULL)
return NULL;
for (i = 0; i < GDB_MAX_BP; i++) {
if (p[i].active == 0)
return &p[i];
}
return NULL;
}
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 == NULL)
return NULL;
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 NULL;
}
static void gdb_bp_remove(u32 type, u32 addr, u32 len)
{
gdb_bp_t *p;
do {
p = gdb_bp_find(type, addr, len);
if (p != NULL) {
DEBUG_LOG(GDB_STUB, "gdb: removed a breakpoint: %08x bytes at %08x\n", len, addr);
p->active = 0;
memset(p, 0, sizeof p);
}
} while (p != NULL);
}
static int gdb_bp_check(u32 addr, u32 type)
{
gdb_bp_t *p;
u32 i;
p = gdb_bp_ptr(type);
if (p == NULL)
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;
}
return 0;
}
static void gdb_nak()
{
const char nak = GDB_STUB_NAK;
ssize_t res;
res = send(sock, &nak, 1, 0);
if (res != 1)
ERROR_LOG(GDB_STUB, "send failed");
}
static void gdb_ack()
{
const char ack = GDB_STUB_ACK;
ssize_t res;
res = send(sock, &ack, 1, 0);
if (res != 1)
ERROR_LOG(GDB_STUB, "send failed");
}
static void gdb_read_command()
{
u8 c;
u8 chk_read, chk_calc;
cmd_len = 0;
memset(cmd_bfr, 0, sizeof cmd_bfr);
c = gdb_read_byte();
if (c == '+')
{
//ignore ack
return;
}
else if (c == 0x03)
{
CCPU::Break();
gdb_signal(SIGTRAP);
return;
}
else if (c != GDB_STUB_START) {
DEBUG_LOG(GDB_STUB, "gdb: read invalid byte %02x\n", c);
return;
}
while ((c = gdb_read_byte()) != GDB_STUB_END) {
cmd_bfr[cmd_len++] = c;
if (cmd_len == sizeof cmd_bfr)
ERROR_LOG(GDB_STUB, "gdb: cmd_bfr overflow\n");
}
chk_read = hex2char(gdb_read_byte()) << 4;
chk_read |= hex2char(gdb_read_byte());
chk_calc = gdb_calc_chksum();
if (chk_calc != chk_read) {
ERROR_LOG(GDB_STUB, "gdb: invalid checksum: calculated %02x and read %02x for $%s# (length: %d)\n", chk_calc, chk_read, cmd_bfr, cmd_len);
cmd_len = 0;
gdb_nak();
}
DEBUG_LOG(GDB_STUB, "gdb: read command %c with a length of %d: %s\n", cmd_bfr[0], cmd_len, cmd_bfr);
}
static int gdb_data_available() {
struct timeval t;
fd_set _fds, *fds = &_fds;
FD_ZERO(fds);
FD_SET(sock, fds);
t.tv_sec = 0;
t.tv_usec = 20;
if (select(sock + 1, fds, NULL, NULL, &t) < 0)
{
ERROR_LOG(GDB_STUB, "select failed");
return 0;
}
if (FD_ISSET(sock, fds))
return 1;
return 0;
}
static void gdb_reply(const char *reply)
{
u8 chk;
u32 left;
u8 *ptr;
int n;
memset(cmd_bfr, 0, sizeof cmd_bfr);
cmd_len = strlen(reply);
if (cmd_len + 4 > sizeof cmd_bfr)
ERROR_LOG(GDB_STUB, "cmd_bfr overflow in gdb_reply");
memcpy(cmd_bfr + 1, reply, cmd_len);
cmd_len++;
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);
DEBUG_LOG(GDB_STUB, "gdb: reply (len: %d): %s\n", cmd_len, cmd_bfr);
ptr = cmd_bfr;
left = cmd_len + 4;
while (left > 0) {
n = send(sock, ptr, left, 0);
if (n < 0)
ERROR_LOG(GDB_STUB, "gdb: send failed");
left -= n;
ptr += n;
}
}
static void gdb_handle_query()
{
gdb_ack();
DEBUG_LOG(GDB_STUB, "gdb: query '%s'\n", cmd_bfr+1);
if (!strcmp((const char *)(cmd_bfr+1), "TStatus"))
{
return gdb_reply("T0");
}
gdb_reply("");
}
static void gdb_handle_set_thread()
{
gdb_ack();
if (memcmp(cmd_bfr, "Hg0", 3) == 0 ||
memcmp(cmd_bfr, "Hc-1", 4) == 0 ||
memcmp(cmd_bfr, "Hc0", 4) == 0)
return gdb_reply("OK");
gdb_reply("E01");
}
static void gdb_handle_signal()
{
char bfr[128];
gdb_ack();
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)
{
u32 i;
for (i = 0; i < 8; 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));
}
static void gdb_read_register()
{
static u8 reply[64];
u32 id;
memset(reply, 0, sizeof reply);
id = hex2char(cmd_bfr[1]) << 4;
id |= hex2char(cmd_bfr[2]);
gdb_ack();
switch (id) {
case 0 ... 31:
wbe32hex(reply, GPR(id));
break;
case 32 ... 63:
wbe64hex(reply, riPS0(id-32));
break;
case 64:
wbe32hex(reply, PC);
break;
case 65:
wbe32hex(reply, MSR);
break;
case 66:
wbe32hex(reply, GetCR());
break;
case 67:
wbe32hex(reply, LR);
break;
case 68:
wbe32hex(reply, CTR);
break;
case 69:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_XER]);
break;
case 70:
wbe32hex(reply, 0x0BADC0DE);
break;
case 71:
wbe32hex(reply, FPSCR.Hex);
break;
default:
return gdb_reply("E01");
break;
}
gdb_reply((char *)reply);
}
static void gdb_read_registers()
{
static u8 bfr[GDB_BFR_MAX - 4];
u8 * bufptr = bfr;
u32 i;
gdb_ack();
memset(bfr, 0, sizeof bfr);
for (i = 0; i < 32; i++) {
wbe32hex(bufptr + i*4, GPR(i));
}
bufptr += 32 * 4;
for (i = 0; i < 32; i++) {
wbe64hex(bufptr + i*4, riPS0(i));
}
bufptr += 64 * 4;
wbe32hex(bufptr, PC); bufptr += 4;
wbe32hex(bufptr, MSR); bufptr += 4;
wbe32hex(bufptr, GetCR()); bufptr += 4;
wbe32hex(bufptr, LR); bufptr += 4;
wbe32hex(bufptr, CTR); bufptr += 4;
wbe32hex(bufptr, PowerPC::ppcState.spr[SPR_XER]); bufptr += 4;
// MQ register not used.
wbe32hex(bufptr, 0x0BADC0DE); bufptr += 4;
gdb_reply((char *)bfr);
}
static u32 re32hex(u8 *p)
{
u32 i;
u32 res = 0;
for (i = 0; i < 8; i++)
res = (res << 4) | hex2char(p[i]);
return res;
}
static u64 re64hex(u8 *p)
{
u32 i;
u64 res = 0;
for (i = 0; i < 16; i++)
res = (res << 4) | hex2char(p[i]);
return res;
}
static void gdb_write_registers()
{
gdb_ack();
u32 i;
u8 * bufptr = cmd_bfr;
for (i = 0; i < 32; i++) {
GPR(i) = re32hex(bufptr + i*4);
}
bufptr += 32 * 4;
for (i = 0; i < 32; i++) {
riPS0(i) = re64hex(bufptr + i*4);
}
bufptr += 64 * 4;
PC = re32hex(bufptr); bufptr += 4;
MSR = re32hex(bufptr); bufptr += 4;
SetCR(re32hex(bufptr)); bufptr += 4;
LR = re32hex(bufptr); bufptr += 4;
CTR = re32hex(bufptr); bufptr += 4;
PowerPC::ppcState.spr[SPR_XER] = re32hex(bufptr); bufptr += 4;
// MQ register not used.
(void)re32hex(bufptr); bufptr += 4;
gdb_reply("OK");
}
static void gdb_write_register()
{
u32 id;
gdb_ack();
id = hex2char(cmd_bfr[1]) << 4;
id |= hex2char(cmd_bfr[2]);
u8 * bufptr = cmd_bfr + 4;
switch (id) {
case 0 ... 31:
GPR(id) = re32hex(bufptr);
break;
case 32 ... 63:
riPS0(id-32) = re64hex(bufptr);
break;
case 64:
PC = re32hex(bufptr);
break;
case 65:
MSR = re32hex(bufptr);
break;
case 66:
SetCR(re32hex(bufptr));
break;
case 67:
LR = re32hex(bufptr);
break;
case 68:
CTR = re32hex(bufptr);
break;
case 69:
PowerPC::ppcState.spr[SPR_XER] = re32hex(bufptr);
break;
case 70:
// do nothing, we dont have MQ
break;
case 71:
FPSCR.Hex = re32hex(bufptr);
break;
default:
return gdb_reply("E01");
break;
}
gdb_reply("OK");
}
static void gdb_read_mem()
{
static u8 reply[GDB_BFR_MAX - 4];
u32 addr, len;
u32 i;
gdb_ack();
i = 1;
addr = 0;
while (cmd_bfr[i] != ',')
addr = (addr << 4) | hex2char(cmd_bfr[i++]);
i++;
len = 0;
while (i < cmd_len)
len = (len << 4) | hex2char(cmd_bfr[i++]);
DEBUG_LOG(GDB_STUB, "gdb: read memory: %08x bytes from %08x\n", len, addr);
if (len*2 > sizeof reply)
gdb_reply("E01");
mem2hex(reply, Memory::GetPointer(addr), len);
reply[len*2] = '\0';
gdb_reply((char *)reply);
}
static void gdb_write_mem()
{
u32 addr, len;
u32 i;
gdb_ack();
i = 1;
addr = 0;
while (cmd_bfr[i] != ',')
addr = (addr << 4) | hex2char(cmd_bfr[i++]);
i++;
len = 0;
while (cmd_bfr[i] != ':')
len = (len << 4) | hex2char(cmd_bfr[i++]);
DEBUG_LOG(GDB_STUB, "gdb: write memory: %08x bytes to %08x\n", len, addr);
hex2mem(Memory::GetPointer(addr), cmd_bfr + i, len);
gdb_reply("OK");
}
static void gdb_continue()
{
gdb_ack();
CCPU::EnableStepping(false);
m_GdbWaitEvent.Set();
send_signal = 1;
}
bool gdb_add_bp(u32 type, u32 addr, u32 len)
{
gdb_bp_t *bp;
bp = gdb_bp_empty_slot(type);
if (bp == NULL)
return false;
bp->active = 1;
bp->addr = addr;
bp->len = len;
DEBUG_LOG(GDB_STUB, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, bp->len, bp->addr);
return true;
}
static void _gdb_add_bp()
{
u32 type;
u32 i, addr = 0, len = 0;
gdb_ack();
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");
}
i = 3;
while (cmd_bfr[i] != ',')
addr = addr << 4 | hex2char(cmd_bfr[i++]);
i++;
while (i < cmd_len)
len = len << 4 | hex2char(cmd_bfr[i++]);
if (!gdb_add_bp(type, addr, len))
return gdb_reply("E02");
gdb_reply("OK");
}
static void gdb_remove_bp()
{
u32 type, addr, len, i;
gdb_ack();
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");
}
addr = 0;
len = 0;
i = 3;
while (cmd_bfr[i] != ',')
addr = (addr << 4) | hex2char(cmd_bfr[i++]);
i++;
while (i < cmd_len)
len = (len << 4) | hex2char(cmd_bfr[i++]);
gdb_bp_remove(type, addr, len);
gdb_reply("OK");
}
static void gdb_parse_command()
{
if (cmd_len == 0)
return;
switch(cmd_bfr[0]) {
case 'q':
gdb_handle_query();
break;
case 'H':
gdb_handle_set_thread();
break;
case '?':
gdb_handle_signal();
break;
case 'k':
gdb_ack();
gdb_deinit();
INFO_LOG(GDB_STUB, "killed by gdb");
break;
case 'g':
gdb_read_registers();
break;
case 'G':
gdb_write_registers();
break;
case 'p':
gdb_read_register();
break;
case 'P':
gdb_write_register();
break;
case 'm':
gdb_read_mem();
break;
case 'M':
gdb_write_mem();
break;
case 's':
case 'C':
case 'c':
gdb_continue();
break;
case 'v':
gdb_ack();
gdb_reply("");
break;
case 'z':
gdb_remove_bp();
break;
case 'Z':
_gdb_add_bp();
break;
default:
gdb_ack();
gdb_reply("");
break;
}
}
#ifdef _WIN32
WSADATA InitData;
#endif
// exported functions
void gdb_init(u32 port)
{
socklen_t len;
int on;
#ifdef _WIN32
WSAStartup(MAKEWORD(2,2), &InitData);
#endif
m_GdbWaitEvent.Reset();
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(AF_INET, SOCK_STREAM, 0);
if (tmpsock == -1)
ERROR_LOG(GDB_STUB, "Failed to create gdb socket");
on = 1;
if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) < 0)
ERROR_LOG(GDB_STUB, "Failed to setsockopt");
memset(&saddr_server, 0, sizeof saddr_server);
saddr_server.sin_family = AF_INET;
saddr_server.sin_port = htons(port);
saddr_server.sin_addr.s_addr = INADDR_ANY;
if (bind(tmpsock, (struct sockaddr *)&saddr_server, sizeof saddr_server) < 0)
ERROR_LOG(GDB_STUB, "Failed to bind gdb socket");
if (listen(tmpsock, 1) < 0)
ERROR_LOG(GDB_STUB, "Failed to listen to gdb socket");
INFO_LOG(GDB_STUB, "Waiting for gdb to connect...\n");
sock = accept(tmpsock, (struct sockaddr *)&saddr_client, &len);
if (sock < 0)
ERROR_LOG(GDB_STUB, "Failed to accept gdb client");
INFO_LOG(GDB_STUB, "Client connected.\n");
saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr);
/*if (((saddr_client.sin_addr.s_addr >> 24) & 0xff) != 127 ||
* ((saddr_client.sin_addr.s_addr >> 16) & 0xff) != 0 ||
* ((saddr_client.sin_addr.s_addr >> 8) & 0xff) != 0 ||
* ((saddr_client.sin_addr.s_addr >> 0) & 0xff) != 1)
* ERROR_LOG(GDB_STUB, "gdb: incoming connection not from localhost");
*/
close(tmpsock);
tmpsock = -1;
}
void gdb_deinit()
{
if (tmpsock != -1)
{
close(tmpsock);
tmpsock = -1;
}
if (sock != -1)
{
close(sock);
sock = -1;
}
m_GdbWaitEvent.Set();
m_GdbWaitEvent.Reset();
#ifdef _WIN32
WSACleanup();
#endif
}
bool gdb_active()
{
return tmpsock != -1 || sock != -1;
}
void gdb_thread()
{
while(sock != -1)
{
gdb_handle_events();
}
}
void gdb_handle_events()
{
if (sock == -1)
return;
while (sock != -1 && gdb_data_available()) {
gdb_read_command();
gdb_parse_command();
}
}
int gdb_signal(u32 s)
{
if (sock == -1)
return 1;
sig = s;
if (send_signal) {
gdb_handle_signal();
send_signal = 0;
}
return 0;
}
int gdb_bp_x(u32 addr)
{
if (sock == -1)
return 0;
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

@ -0,0 +1,61 @@
// Copyright (C) 2010 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
#ifndef GDB_H__
#define GDB_H__
#include <signal.h>
#include "Common.h"
#include "Thread.h"
#include "PowerPC.h"
#include "../HW/CPU.h"
#include "../HW/Memmap.h"
#ifdef _WIN32
#define SIGTRAP 5
#define SIGTERM 15
#define MSG_WAITALL 8
#endif
typedef enum {
GDB_BP_TYPE_NONE = 0,
GDB_BP_TYPE_X,
GDB_BP_TYPE_R,
GDB_BP_TYPE_W,
GDB_BP_TYPE_A
} gdb_bp_type;
void gdb_init(u32 port);
void gdb_deinit();
bool gdb_active();
void gdb_thread();
void gdb_handle_events();
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);
static Common::Event m_GdbWaitEvent;
#endif

View file

@ -27,6 +27,10 @@
#include "../../IPC_HLE/WII_IPC_HLE.h" #include "../../IPC_HLE/WII_IPC_HLE.h"
#include "Atomic.h" #include "Atomic.h"
#ifdef USE_GDBSTUB
#include "../GDBStub.h"
#endif
namespace { namespace {
u32 last_pc; u32 last_pc;
@ -99,7 +103,18 @@ void Trace( UGeckoInstruction &instCode )
int Interpreter::SingleStepInner(void) int Interpreter::SingleStepInner(void)
{ {
static UGeckoInstruction instCode; static UGeckoInstruction instCode;
#ifdef USE_GDBSTUB
if (gdb_active() && gdb_bp_x(PC)) {
Host_UpdateDisasmDialog();
gdb_signal(SIGTRAP);
gdb_handle_events();
m_GdbWaitEvent.Wait();
}
#endif
NPC = PC + sizeof(UGeckoInstruction); NPC = PC + sizeof(UGeckoInstruction);
instCode.hex = Memory::Read_Opcode(PC); instCode.hex = Memory::Read_Opcode(PC);
@ -218,7 +233,8 @@ void Interpreter::Run()
if (PCVec.size() > ShowSteps) if (PCVec.size() > ShowSteps)
PCVec.erase(PCVec.begin()); PCVec.erase(PCVec.begin());
#endif #endif
//2: check for breakpoint //2: check for breakpoint
if (PowerPC::breakpoints.IsAddressBreakPoint(PC)) if (PowerPC::breakpoints.IsAddressBreakPoint(PC))
{ {