Added backtrace command to debugger

This commit is contained in:
Lior Halphon 2016-08-09 22:48:53 +03:00
parent a5670b6643
commit 806d0775a4
4 changed files with 61 additions and 6 deletions

View File

@ -1166,12 +1166,29 @@ static bool mbc(GB_gameboy_t *gb, char *arguments)
return true; return true;
} }
static bool backtrace(GB_gameboy_t *gb, char *arguments)
{
if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: backtrace\n");
return true;
}
GB_log(gb, " 1. %s\n", value_to_string(gb, gb->pc, true));
for (unsigned int i = gb->backtrace_size; i--;) {
GB_log(gb, "%3d. %s\n", gb->backtrace_size - i + 1, debugger_value_to_string(gb, (value_t){true, gb->backtrace_returns[i].bank, gb->backtrace_returns[i].addr}, true));
}
return true;
}
static bool help(GB_gameboy_t *gb, char *arguments); static bool help(GB_gameboy_t *gb, char *arguments);
static const debugger_command_t commands[] = { static const debugger_command_t commands[] = {
{"continue", 1, cont, "Continue running until next stop"}, {"continue", 1, cont, "Continue running until next stop"},
{"next", 1, next, "Run the next instruction, skipping over function calls"}, {"next", 1, next, "Run the next instruction, skipping over function calls"},
{"step", 1, step, "Run the next instruction, stepping into function calls"}, {"step", 1, step, "Run the next instruction, stepping into function calls"},
{"finish", 1, finish, "Run until the current function returns"}, {"finish", 1, finish, "Run until the current function returns"},
{"backtrace", 2, backtrace, "Display the current call stack"},
{"bt", 2, backtrace, NULL},
{"sld", 3, stack_leak_detection, "Run until the current function returns, or a stack leak is detected (Experimental)"}, {"sld", 3, stack_leak_detection, "Run until the current function returns, or a stack leak is detected (Experimental)"},
{"registers", 1, registers, "Print values of processor registers and other important registers"}, {"registers", 1, registers, "Print values of processor registers and other important registers"},
{"cartridge", 2, mbc, "Displays information about the MBC and cartridge"}, {"cartridge", 2, mbc, "Displays information about the MBC and cartridge"},
@ -1185,6 +1202,7 @@ static const debugger_command_t commands[] = {
{"eval", 2, print, NULL}, {"eval", 2, print, NULL},
{"examine", 2, examine, "Examine values at address"}, {"examine", 2, examine, "Examine values at address"},
{"x", 1, examine, NULL}, {"x", 1, examine, NULL},
{"help", 1, help, "List available commands"}, {"help", 1, help, "List available commands"},
}; };
@ -1215,7 +1233,7 @@ static const debugger_command_t *find_command(const char *string)
return NULL; return NULL;
} }
void GB_debugger_call_hook(GB_gameboy_t *gb) void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr)
{ {
/* Called just after the CPU calls a function/enters an interrupt/etc... */ /* Called just after the CPU calls a function/enters an interrupt/etc... */
@ -1230,6 +1248,23 @@ void GB_debugger_call_hook(GB_gameboy_t *gb)
} }
} }
if (gb->backtrace_size < sizeof(gb->backtrace_sps) / sizeof(gb->backtrace_sps[0])) {
while (gb->backtrace_size) {
if (gb->backtrace_sps[gb->backtrace_size - 1] < gb->registers[GB_REGISTER_SP]) {
gb->backtrace_size--;
}
else {
break;
}
}
gb->backtrace_sps[gb->backtrace_size] = gb->registers[GB_REGISTER_SP];
gb->backtrace_returns[gb->backtrace_size].bank = bank_for_addr(gb, call_addr);
gb->backtrace_returns[gb->backtrace_size].addr = call_addr;
gb->backtrace_size++;
}
gb->debug_call_depth++; gb->debug_call_depth++;
} }
@ -1253,6 +1288,15 @@ void GB_debugger_ret_hook(GB_gameboy_t *gb)
} }
} }
} }
while (gb->backtrace_size) {
if (gb->backtrace_sps[gb->backtrace_size - 1] <= gb->registers[GB_REGISTER_SP]) {
gb->backtrace_size--;
}
else {
break;
}
}
} }
static bool _GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, value_t addr, uint8_t value) static bool _GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, value_t addr, uint8_t value)

View File

@ -4,7 +4,7 @@
void GB_debugger_run(GB_gameboy_t *gb); void GB_debugger_run(GB_gameboy_t *gb);
void GB_debugger_handle_async_commands(GB_gameboy_t *gb); void GB_debugger_handle_async_commands(GB_gameboy_t *gb);
void GB_debugger_call_hook(GB_gameboy_t *gb); void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr);
void GB_debugger_ret_hook(GB_gameboy_t *gb); void GB_debugger_ret_hook(GB_gameboy_t *gb);
void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value); void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr); void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr);

View File

@ -385,12 +385,20 @@ typedef struct GB_gameboy_s {
uint16_t n_breakpoints; uint16_t n_breakpoints;
struct GB_breakpoint_s *breakpoints; struct GB_breakpoint_s *breakpoints;
/* SLD */ /* SLD (Todo: merge with backtrace) */
bool stack_leak_detection; bool stack_leak_detection;
int debug_call_depth; int debug_call_depth;
uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */ uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */
uint16_t addr_for_call_depth[0x200]; uint16_t addr_for_call_depth[0x200];
/* Backtrace */
unsigned int backtrace_size;
uint16_t backtrace_sps[0x200];
struct {
uint16_t bank;
uint16_t addr;
} backtrace_returns[0x200];
/* Watchpoints */ /* Watchpoints */
uint16_t n_watchpoints; uint16_t n_watchpoints;
struct GB_watchpoint_s *watchpoints; struct GB_watchpoint_s *watchpoints;

View File

@ -789,6 +789,7 @@ static void jp_a16(GB_gameboy_t *gb, uint8_t opcode)
static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode) static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode)
{ {
uint16_t call_addr = gb->pc;
gb->pc++; gb->pc++;
if (condition_code(gb, opcode)) { if (condition_code(gb, opcode)) {
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
@ -803,7 +804,7 @@ static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode)
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
gb->pc = addr; gb->pc = addr;
GB_debugger_call_hook(gb); GB_debugger_call_hook(gb, call_addr);
} }
else { else {
GB_advance_cycles(gb, 12); GB_advance_cycles(gb, 12);
@ -973,6 +974,7 @@ static void cp_a_d8(GB_gameboy_t *gb, uint8_t opcode)
static void rst(GB_gameboy_t *gb, uint8_t opcode) static void rst(GB_gameboy_t *gb, uint8_t opcode)
{ {
uint16_t call_addr = gb->pc;
GB_advance_cycles(gb, 8); GB_advance_cycles(gb, 8);
gb->registers[GB_REGISTER_SP] -= 2; gb->registers[GB_REGISTER_SP] -= 2;
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc + 1) >> 8); GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc + 1) >> 8);
@ -980,7 +982,7 @@ static void rst(GB_gameboy_t *gb, uint8_t opcode)
GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc + 1) & 0xFF); GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc + 1) & 0xFF);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
gb->pc = opcode ^ 0xC7; gb->pc = opcode ^ 0xC7;
GB_debugger_call_hook(gb); GB_debugger_call_hook(gb, call_addr);
} }
static void ret(GB_gameboy_t *gb, uint8_t opcode) static void ret(GB_gameboy_t *gb, uint8_t opcode)
@ -1002,6 +1004,7 @@ static void reti(GB_gameboy_t *gb, uint8_t opcode)
static void call_a16(GB_gameboy_t *gb, uint8_t opcode) static void call_a16(GB_gameboy_t *gb, uint8_t opcode)
{ {
uint16_t call_addr = gb->pc;
gb->pc++; gb->pc++;
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
gb->registers[GB_REGISTER_SP] -= 2; gb->registers[GB_REGISTER_SP] -= 2;
@ -1014,7 +1017,7 @@ static void call_a16(GB_gameboy_t *gb, uint8_t opcode)
GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc + 2) & 0xFF); GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc + 2) & 0xFF);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
gb->pc = addr; gb->pc = addr;
GB_debugger_call_hook(gb); GB_debugger_call_hook(gb, call_addr);
} }
static void ld_da8_a(GB_gameboy_t *gb, uint8_t opcode) static void ld_da8_a(GB_gameboy_t *gb, uint8_t opcode)