mirror of https://github.com/mgba-emu/mgba.git
Debugger: Add software breakpoints, fix cleanup
This commit is contained in:
parent
fff0e9b83c
commit
c45d91d311
1
CHANGES
1
CHANGES
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}" "")
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -34,6 +34,9 @@ struct GDBStub {
|
|||
|
||||
bool shouldBlock;
|
||||
int untilPoll;
|
||||
|
||||
bool supportsSwbreak;
|
||||
bool supportsHwbreak;
|
||||
};
|
||||
|
||||
void GDBStubCreate(struct GDBStub*);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue