From 806d0775a4a61a6c9c9a5897f481973569f57011 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Tue, 9 Aug 2016 22:48:53 +0300 Subject: [PATCH] Added backtrace command to debugger --- Core/debugger.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- Core/debugger.h | 2 +- Core/gb.h | 10 +++++++++- Core/z80_cpu.c | 9 ++++++--- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/Core/debugger.c b/Core/debugger.c index cb7e7ea0..1f7efce1 100644 --- a/Core/debugger.c +++ b/Core/debugger.c @@ -1166,12 +1166,29 @@ static bool mbc(GB_gameboy_t *gb, char *arguments) 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 const debugger_command_t commands[] = { {"continue", 1, cont, "Continue running until next stop"}, {"next", 1, next, "Run the next instruction, skipping over function calls"}, {"step", 1, step, "Run the next instruction, stepping into function calls"}, {"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)"}, {"registers", 1, registers, "Print values of processor registers and other important registers"}, {"cartridge", 2, mbc, "Displays information about the MBC and cartridge"}, @@ -1185,6 +1202,7 @@ static const debugger_command_t commands[] = { {"eval", 2, print, NULL}, {"examine", 2, examine, "Examine values at address"}, {"x", 1, examine, NULL}, + {"help", 1, help, "List available commands"}, }; @@ -1215,7 +1233,7 @@ static const debugger_command_t *find_command(const char *string) 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... */ @@ -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++; } @@ -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) diff --git a/Core/debugger.h b/Core/debugger.h index 7e751855..4e7808f8 100644 --- a/Core/debugger.h +++ b/Core/debugger.h @@ -4,7 +4,7 @@ void GB_debugger_run(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_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); diff --git a/Core/gb.h b/Core/gb.h index ae461bc4..4c3fe93c 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -385,12 +385,20 @@ typedef struct GB_gameboy_s { uint16_t n_breakpoints; struct GB_breakpoint_s *breakpoints; - /* SLD */ + /* SLD (Todo: merge with backtrace) */ bool stack_leak_detection; int debug_call_depth; uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */ 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 */ uint16_t n_watchpoints; struct GB_watchpoint_s *watchpoints; diff --git a/Core/z80_cpu.c b/Core/z80_cpu.c index 1d490a38..21b08381 100644 --- a/Core/z80_cpu.c +++ b/Core/z80_cpu.c @@ -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) { + uint16_t call_addr = gb->pc; gb->pc++; if (condition_code(gb, opcode)) { 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->pc = addr; - GB_debugger_call_hook(gb); + GB_debugger_call_hook(gb, call_addr); } else { 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) { + uint16_t call_addr = gb->pc; GB_advance_cycles(gb, 8); gb->registers[GB_REGISTER_SP] -= 2; 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_advance_cycles(gb, 4); 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) @@ -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) { + uint16_t call_addr = gb->pc; gb->pc++; GB_advance_cycles(gb, 4); 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_advance_cycles(gb, 4); 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)