Debugger: Add software breakpoints, fix cleanup

This commit is contained in:
Jeffrey Pfau 2016-09-07 17:49:27 -07:00
parent fff0e9b83c
commit c45d91d311
9 changed files with 128 additions and 22 deletions

View File

@ -92,6 +92,7 @@ Misc:
- 3DS: 3D banner
- FFmpeg: Full support for libavcodec 56+
- Util: PNG utils should support 16-bit when applicable
- Debugger: Add software breakpoint support to gdb
0.4.1: (2016-07-11)
Bugfixes:

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2014 Jeffrey Pfau
/* Copyright (c) 2013-2016 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -37,7 +37,8 @@ static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
return;
}
struct mDebuggerEntryInfo info = {
.address = breakpoint->address
.address = breakpoint->address,
.breakType = BREAKPOINT_HARDWARE
};
mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info);
}
@ -79,6 +80,16 @@ void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
if (debugger->clearSoftwareBreakpoint) {
// Clear the stack backwards in case any overlap
size_t b;
for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
}
}
ARMDebuggerRemoveMemoryShim(debugger);
ARMDebugBreakpointListDeinit(&debugger->breakpoints);
ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
ARMDebugWatchpointListDeinit(&debugger->watchpoints);
@ -103,8 +114,8 @@ static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerE
}
}
}
if (debugger->entered) {
debugger->entered(debugger->d.p, reason, info);
if (debugger->d.p->entered) {
debugger->d.p->entered(debugger->d.p, reason, info);
}
}
@ -124,6 +135,26 @@ bool ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t addr
return true;
}
void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
if (!debugger->clearSoftwareBreakpoint) {
return;
}
struct ARMDebugBreakpoint* breakpoint = NULL;
// Clear the stack backwards in case any overlap
size_t b;
for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
if (breakpoint->address == address) {
break;
}
breakpoint = NULL;
}
debugger->clearSoftwareBreakpoint(debugger, address, breakpoint->sw.mode, breakpoint->sw.opcode);
}
static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);

View File

@ -39,3 +39,4 @@ struct ARMDebugger {
struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void);
bool ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* debugger, uint32_t address, enum ExecutionMode mode);
void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* debugger, uint32_t address);

View File

@ -0,0 +1,22 @@
set(SRC
debugger.c
parser.c
)
set(RENDERER_SRC
renderers/software-bg.c
renderers/software-mode0.c
renderers/software-obj.c
renderers/video-software.c
)
if(USE_CLI_DEBUGGER)
list(APPEND SRC cli-debugger.c)
endif()
if(USE_GDB_STUB)
list(APPEND SRC gdb-stub.c)
endif()
source_group("Debugger source" FILES ${SRC})
add_internal_library(debugger "${SRC}" "")

View File

@ -102,8 +102,8 @@ void mDebuggerRun(struct mDebugger* debugger) {
void mDebuggerEnter(struct mDebugger* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
debugger->state = DEBUGGER_PAUSED;
if (debugger->entered) {
debugger->entered(debugger, reason, info);
if (debugger->platform->entered) {
debugger->platform->entered(debugger->platform, reason, info);
}
}

View File

@ -40,6 +40,11 @@ enum mWatchpointType {
WATCHPOINT_RW = WATCHPOINT_WRITE | WATCHPOINT_READ
};
enum mBreakpointType {
BREAKPOINT_HARDWARE,
BREAKPOINT_SOFTWARE
};
enum mDebuggerEntryReason {
DEBUGGER_ENTER_MANUAL,
DEBUGGER_ENTER_ATTACHED,
@ -48,6 +53,14 @@ enum mDebuggerEntryReason {
DEBUGGER_ENTER_ILLEGAL_OP
};
struct mDebugWatchpoint {
uint32_t address;
enum mWatchpointType type;
};
DECLARE_VECTOR(mDebugBreakpointList, struct mDebugBreakpoint);
DECLARE_VECTOR(mDebugWatchpointList, struct mDebugWatchpoint);
extern const char* ERROR_MISSING_ARGS;
extern const char* ERROR_OVERFLOW;
@ -63,6 +76,7 @@ struct mDebuggerEntryInfo {
struct {
uint32_t opcode;
enum mBreakpointType breakType;
};
};
};

View File

@ -1,10 +1,11 @@
/* Copyright (c) 2013-2014 Jeffrey Pfau
/* Copyright (c) 2013-2016 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gdb-stub.h"
#include "arm/debugger/debugger.h"
#include "arm/isa-inlines.h"
#include "core/core.h"
#include "gba/memory.h"
@ -44,7 +45,11 @@ static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReaso
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
break;
case DEBUGGER_ENTER_BREAKPOINT:
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP); // TODO: Use hwbreak/swbreak if gdb supports it
if (stub->supportsHwbreak && stub->supportsSwbreak && info) {
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "T%02x%cwbreak:;", SIGTRAP, info->breakType == BREAKPOINT_SOFTWARE ? 's' : 'h');
} else {
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02xk", SIGTRAP);
}
break;
case DEBUGGER_ENTER_WATCHPOINT:
if (info) {
@ -385,6 +390,35 @@ static void _readRegister(struct GDBStub* stub, const char* message) {
_sendMessage(stub);
}
static void _processQSupportedCommand(struct GDBStub* stub, const char* message) {
const char* terminator = strrchr(message, '#');
stub->supportsSwbreak = false;
stub->supportsHwbreak = false;
while (message < terminator) {
const char* end = strchr(message, ';');
size_t len;
if (end && end < terminator) {
len = end - message;
} else {
len = end - terminator;
}
if (!strncmp(message, "swbreak+", 8)) {
stub->supportsSwbreak = true;
} else if (!strncmp(message, "hwbreak+", 8)) {
stub->supportsHwbreak = true;
} else if (!strncmp(message, "swbreak-", 8)) {
stub->supportsSwbreak = false;
} else if (!strncmp(message, "hwbreak-", 8)) {
stub->supportsHwbreak = false;
}
if (!end) {
break;
}
message = end + 1;
}
strncpy(stub->outgoing, "swbreak+;hwbreak+", GDB_STUB_MAX_LINE - 4);
}
static void _processQReadCommand(struct GDBStub* stub, const char* message) {
stub->outgoing[0] = '\0';
if (!strncmp("HostInfo#", message, 9)) {
@ -401,6 +435,8 @@ static void _processQReadCommand(struct GDBStub* stub, const char* message) {
strncpy(stub->outgoing, "m1", GDB_STUB_MAX_LINE - 4);
} else if (!strncmp("sThreadInfo#", message, 12)) {
strncpy(stub->outgoing, "l", GDB_STUB_MAX_LINE - 4);
} else if (!strncmp("Supported:", message, 10)) {
_processQSupportedCommand(stub, message + 10);
}
_sendMessage(stub);
}
@ -434,36 +470,31 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
unsigned i = 0;
uint32_t address = _readHex(readAddress, &i);
readAddress += i + 1;
uint32_t kind = _readHex(readAddress, &i); // We don't use this in hardware watchpoints
UNUSED(kind);
uint32_t kind = _readHex(readAddress, &i);
switch (message[0]) {
case '0': // Memory breakpoints are not currently supported
case '0':
ARMDebuggerSetSoftwareBreakpoint(stub->d.platform, address, kind == 2 ? MODE_THUMB : MODE_ARM);
break;
case '1':
stub->d.platform->setBreakpoint(stub->d.platform, address);
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
_sendMessage(stub);
break;
case '2':
stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_WRITE);
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
_sendMessage(stub);
break;
case '3':
stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_READ);
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
_sendMessage(stub);
break;
case '4':
stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_RW);
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
_sendMessage(stub);
break;
default:
stub->outgoing[0] = '\0';
_sendMessage(stub);
break;
return;
}
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
_sendMessage(stub);
}
static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
@ -471,7 +502,9 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
unsigned i = 0;
uint32_t address = _readHex(readAddress, &i);
switch (message[0]) {
case '0': // Memory breakpoints are not currently supported
case '0':
ARMDebuggerClearSoftwareBreakpoint(stub->d.platform, address);
break;
case '1':
stub->d.platform->clearBreakpoint(stub->d.platform, address);
break;

View File

@ -34,6 +34,9 @@ struct GDBStub {
bool shouldBlock;
int untilPoll;
bool supportsSwbreak;
bool supportsHwbreak;
};
void GDBStubCreate(struct GDBStub*);

View File

@ -799,7 +799,8 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) {
case CPU_COMPONENT_DEBUGGER:
if (gba->debugger) {
struct mDebuggerEntryInfo info = {
.address = _ARMPCAddress(cpu)
.address = _ARMPCAddress(cpu),
.breakType = BREAKPOINT_SOFTWARE
};
mDebuggerEnter(gba->debugger->d.p, DEBUGGER_ENTER_BREAKPOINT, &info);
}