diff --git a/Makefile b/Makefile index f08d403c..087986c5 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ GDBSTUB_LIB := $(GDBSTUB_OUT)/libgdbstub.a $(GDBSTUB_LIB): mini-gdbstub/Makefile $(MAKE) -C $(dir $<) O=$(dir $@) $(OUT)/emulate.o: $(GDBSTUB_LIB) -OBJS_EXT += gdbstub.o +OBJS_EXT += gdbstub.o breakpoint.o CFLAGS += -D ENABLE_GDBSTUB -D'GDBSTUB_COMM="$(GDBSTUB_COMM)"' LDFLAGS += $(GDBSTUB_LIB) gdbstub-test: $(BIN) diff --git a/breakpoint.c b/breakpoint.c new file mode 100644 index 00000000..81b609eb --- /dev/null +++ b/breakpoint.c @@ -0,0 +1,60 @@ +#include "breakpoint.h" +#include + +static inline int cmp(const void *arg0, const void *arg1) +{ + riscv_word_t *a = (riscv_word_t *) arg0, *b = (riscv_word_t *) arg1; + return (*a < *b) ? _CMP_LESS : (*a > *b) ? _CMP_GREATER : _CMP_EQUAL; +} + +breakpoint_map_t breakpoint_map_new() +{ + return map_init(riscv_word_t, breakpoint_t, cmp); +} + +bool breakpoint_map_insert(breakpoint_map_t map, riscv_word_t addr) +{ + breakpoint_t bp = (breakpoint_t){.addr = addr, .orig_insn = 0}; + map_iter_t it; + map_find(map, &it, &addr); + /* We don't expect to set breakpoint at duplicate address */ + if (!map_at_end(map, &it)) + return false; + return map_insert(map, &addr, &bp); +} + +static bool breakpoint_map_find_it(breakpoint_map_t map, + riscv_word_t addr, + map_iter_t *it) +{ + map_find(map, it, &addr); + if (map_at_end(map, it)) { + return false; + } + + return true; +} + +breakpoint_t *breakpoint_map_find(breakpoint_map_t map, riscv_word_t addr) +{ + map_iter_t it; + if (!breakpoint_map_find_it(map, addr, &it)) + return NULL; + + return (breakpoint_t *) map_iter_value(&it, breakpoint_t *); +} + +bool breakpoint_map_del(breakpoint_map_t map, riscv_word_t addr) +{ + map_iter_t it; + if (!breakpoint_map_find_it(map, addr, &it)) + return false; + + map_erase(map, &it); + return true; +} + +void breakpoint_map_destroy(breakpoint_map_t map) +{ + map_delete(map); +} diff --git a/breakpoint.h b/breakpoint.h new file mode 100644 index 00000000..19820447 --- /dev/null +++ b/breakpoint.h @@ -0,0 +1,17 @@ +#pragma once + +#include "map.h" +#include "riscv.h" + +typedef struct { + riscv_word_t addr; + uint32_t orig_insn; +} breakpoint_t; + +typedef map_t breakpoint_map_t; + +breakpoint_map_t breakpoint_map_new(); +bool breakpoint_map_insert(breakpoint_map_t map, riscv_word_t addr); +breakpoint_t *breakpoint_map_find(breakpoint_map_t map, riscv_word_t addr); +bool breakpoint_map_del(breakpoint_map_t map, riscv_word_t addr); +void breakpoint_map_destroy(breakpoint_map_t map); diff --git a/emulate.c b/emulate.c index 52d28514..71b08eac 100644 --- a/emulate.c +++ b/emulate.c @@ -1688,10 +1688,13 @@ void rv_debug(struct riscv_t *rv) GDBSTUB_COMM)) { return; } + rv->breakpoint_map = breakpoint_map_new(); if (!gdbstub_run(&rv->gdbstub, (void *) rv)) { return; } + + breakpoint_map_destroy(rv->breakpoint_map); gdbstub_close(&rv->gdbstub); } diff --git a/gdbstub.c b/gdbstub.c index 1c4c27a5..8419fb0e 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1,6 +1,7 @@ #include #include "mini-gdbstub/include/gdbstub.h" #include "riscv_private.h" +#include "breakpoint.h" static size_t rv_read_reg(void *args, int regno) { @@ -31,7 +32,7 @@ static gdb_action_t rv_cont(void *args) const uint32_t cycles_per_step = 1; for (; !rv_has_halted(rv);) { - if (rv->breakpoint_specified && (rv_get_pc(rv) == rv->breakpoint_addr)) { + if (breakpoint_map_find(rv->breakpoint_map, rv_get_pc(rv)) != NULL) { break; } rv_step(rv, cycles_per_step); @@ -50,12 +51,10 @@ static gdb_action_t rv_stepi(void *args) static bool rv_set_bp(void *args, size_t addr, bp_type_t type) { struct riscv_t *rv = (struct riscv_t *) args; - if (type != BP_SOFTWARE || rv->breakpoint_specified) + if (type != BP_SOFTWARE) return false; - rv->breakpoint_specified = true; - rv->breakpoint_addr = addr; - return true;; + return breakpoint_map_insert(rv->breakpoint_map, addr); } static bool rv_del_bp(void *args, size_t addr, bp_type_t type) @@ -64,11 +63,7 @@ static bool rv_del_bp(void *args, size_t addr, bp_type_t type) if (type != BP_SOFTWARE) return false; /* When there is no matched breakpoint, no further action is taken */ - if (!rv->breakpoint_specified || addr != rv->breakpoint_addr) - return true; - - rv->breakpoint_specified = false; - rv->breakpoint_addr = 0; + breakpoint_map_del(rv->breakpoint_map, addr); return true; } diff --git a/mini-gdbstub b/mini-gdbstub index 895ac336..742bcd45 160000 --- a/mini-gdbstub +++ b/mini-gdbstub @@ -1 +1 @@ -Subproject commit 895ac336deeb06938717e3fde64914372dd3ca89 +Subproject commit 742bcd45b1b4f3dbd488be8f9f7b3a3b9053b910 diff --git a/riscv_private.h b/riscv_private.h index de84b8a3..de24abe8 100644 --- a/riscv_private.h +++ b/riscv_private.h @@ -2,6 +2,7 @@ #include #ifdef ENABLE_GDBSTUB +#include "breakpoint.h" #include "mini-gdbstub/include/gdbstub.h" #endif #include "riscv.h" @@ -146,8 +147,7 @@ struct riscv_t { gdbstub_t gdbstub; /* GDB instruction breakpoint */ - bool breakpoint_specified; - riscv_word_t breakpoint_addr; + breakpoint_map_t breakpoint_map; #endif #ifdef ENABLE_RV32F diff --git a/tests/gdbstub.sh b/tests/gdbstub.sh index 337aee60..c5287beb 100755 --- a/tests/gdbstub.sh +++ b/tests/gdbstub.sh @@ -7,24 +7,36 @@ PID=$! if ps -p $PID > /dev/null then tmpfile=/tmp/rv32emu-gdbstub.$PID - riscv32-unknown-elf-gdb --batch \ - -ex "file build/puzzle.elf" \ - -ex "target remote :1234" \ - -ex "break *0x10700" \ - -ex "continue" \ - -ex "print \$pc" \ - -ex "del 1" \ - -ex "stepi" \ - -ex "stepi" \ - -ex "continue" > ${tmpfile} + breakpoint_arr=(0x10700 0x10800 0x10900) + GDB_COMMANDS="riscv32-unknown-elf-gdb --batch " + GDB_COMMANDS+="-ex 'file build/puzzle.elf' " + GDB_COMMANDS+="-ex 'target remote :1234' " + for t in ${breakpoint_arr[@]}; do + GDB_COMMANDS+="-ex 'break *$t' " + done + for i in {1..3}; do + GDB_COMMANDS+="-ex 'continue' " + GDB_COMMANDS+="-ex 'print \$pc' " + done + for i in {1..3}; do + GDB_COMMANDS+="-ex 'del $i' " + done + GDB_COMMANDS+="-ex 'stepi' " + GDB_COMMANDS+="-ex 'stepi' " + GDB_COMMANDS+="-ex 'continue' " + + eval ${GDB_COMMANDS} > ${tmpfile} # check if we stop at the breakpoint - expected=$(grep -rw "Breakpoint 1 at" ${tmpfile} | awk {'print $4'}) - ans=$(grep -r "$1 =" ${tmpfile} | awk {'print $5'}) - if [ "$expected" != "$ans" ]; then - # Fail - exit 1 - fi + for i in {1..3} + do + expected=$(grep -rw "Breakpoint ${i} at" ${tmpfile} | awk {'print $4'}) + ans=$(grep -r "\$${i} =" ${tmpfile} | awk {'print $5'}) + if [ "$expected" != "$ans" ]; then + # Fail + exit 1 + fi + done # Pass and wait exit 0 wait $PID