From 7f0d1ba75ad71794a27f4a57541fdc7cb5b41c4e Mon Sep 17 00:00:00 2001 From: xinyangli Date: Thu, 25 Apr 2024 16:54:27 +0800 Subject: [PATCH] feat(nemu): gdb remote debug --- nemu/Kconfig | 4 +- nemu/default.nix | 2 + nemu/include/cpu/cpu.h | 10 + nemu/include/cpu/decode.h | 120 +++++--- nemu/include/isa.h | 4 + nemu/scripts/build.mk | 2 +- nemu/src/cpu/cpu-exec.c | 106 +++++--- nemu/src/engine/interpreter/init.c | 8 +- nemu/src/filelist.mk | 4 +- nemu/src/isa/riscv32/include/isa-def.h | 1 + nemu/src/isa/riscv32/inst.c | 256 +++++++++++++----- nemu/src/isa/riscv32/reg.c | 46 +++- nemu/src/memory/paddr.c | 45 +-- nemu/src/monitor/filelist.mk | 3 + nemu/src/monitor/gdbstub.cc | 130 +++++++++ nemu/src/monitor/monitor.c | 96 ++++--- nemu/src/monitor/sdb/addrexp.l | 24 -- nemu/src/monitor/sdb/addrexp.y | 60 ---- nemu/src/monitor/sdb/filelist.mk | 2 - nemu/src/monitor/sdb/sdb.c | 361 ------------------------- nemu/src/monitor/sdb/sdb.h | 25 -- nemu/src/monitor/sdb/watchpoint.c | 150 ---------- 22 files changed, 613 insertions(+), 846 deletions(-) create mode 100644 nemu/src/monitor/filelist.mk create mode 100644 nemu/src/monitor/gdbstub.cc delete mode 100644 nemu/src/monitor/sdb/addrexp.l delete mode 100644 nemu/src/monitor/sdb/addrexp.y delete mode 100644 nemu/src/monitor/sdb/filelist.mk delete mode 100644 nemu/src/monitor/sdb/sdb.c delete mode 100644 nemu/src/monitor/sdb/sdb.h delete mode 100644 nemu/src/monitor/sdb/watchpoint.c diff --git a/nemu/Kconfig b/nemu/Kconfig index 5816143..8c91522 100644 --- a/nemu/Kconfig +++ b/nemu/Kconfig @@ -180,7 +180,7 @@ config ITRACE_BUFFER default 10 config MTRACE - depends on TRACE + depends on TRACE && LOG_TRACE bool "Enable memory tracing" default n @@ -198,7 +198,7 @@ config MTRACE_RANGE_MAX default 10 config FTRACE - depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER + depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER && LOG_TRACE bool "Enable function tracing" default n diff --git a/nemu/default.nix b/nemu/default.nix index 052b4a7..e6ffb2b 100644 --- a/nemu/default.nix +++ b/nemu/default.nix @@ -3,6 +3,7 @@ stdenv, am-kernels, dtc, + mini-gdbstub, defconfig ? "alldefconfig", }: @@ -23,6 +24,7 @@ stdenv.mkDerivation rec { buildInputs = with pkgs; [ readline libllvm + mini-gdbstub ]; checkInputs = [ diff --git a/nemu/include/cpu/cpu.h b/nemu/include/cpu/cpu.h index 2be0d77..c9bb700 100644 --- a/nemu/include/cpu/cpu.h +++ b/nemu/include/cpu/cpu.h @@ -17,8 +17,18 @@ #define __CPU_CPU_H__ #include +#include + +#include +#include + +typedef struct { + size_t addr; + bp_type_t type; +} breakpoint_t; void cpu_exec(uint64_t n); +breakpoint_t *cpu_exec_with_bp(uint64_t n, breakpoint_t *bp, size_t len); void set_nemu_state(int state, vaddr_t pc, int halt_ret); void invalid_inst(vaddr_t thispc); diff --git a/nemu/include/cpu/decode.h b/nemu/include/cpu/decode.h index a17c888..81946bf 100644 --- a/nemu/include/cpu/decode.h +++ b/nemu/include/cpu/decode.h @@ -16,39 +16,62 @@ #ifndef __CPU_DECODE_H__ #define __CPU_DECODE_H__ +#include "types.h" #include +typedef enum { INST_NORMAL, INST_MEM_WRITE, INST_MEM_READ } inst_type_t; + +typedef union { + paddr_t rmem; + paddr_t wmem; +} inst_type_op; + typedef struct Decode { vaddr_t pc; vaddr_t snpc; // static next pc vaddr_t dnpc; // dynamic next pc + inst_type_t inst_type; + inst_type_op inst_value; ISADecodeInfo isa; } Decode; // --- pattern matching mechanism --- -__attribute__((always_inline)) -static inline void pattern_decode(const char *str, int len, - uint64_t *key, uint64_t *mask, uint64_t *shift) { +__attribute__((always_inline)) static inline void +pattern_decode(const char *str, int len, uint64_t *key, uint64_t *mask, + uint64_t *shift) { uint64_t __key = 0, __mask = 0, __shift = 0; -#define macro(i) \ - if ((i) >= len) goto finish; \ - else { \ - char c = str[i]; \ - if (c != ' ') { \ - Assert(c == '0' || c == '1' || c == '?', \ - "invalid character '%c' in pattern string", c); \ - __key = (__key << 1) | (c == '1' ? 1 : 0); \ - __mask = (__mask << 1) | (c == '?' ? 0 : 1); \ - __shift = (c == '?' ? __shift + 1 : 0); \ - } \ +#define macro(i) \ + if ((i) >= len) \ + goto finish; \ + else { \ + char c = str[i]; \ + if (c != ' ') { \ + Assert(c == '0' || c == '1' || c == '?', \ + "invalid character '%c' in pattern string", c); \ + __key = (__key << 1) | (c == '1' ? 1 : 0); \ + __mask = (__mask << 1) | (c == '?' ? 0 : 1); \ + __shift = (c == '?' ? __shift + 1 : 0); \ + } \ } -#define macro2(i) macro(i); macro((i) + 1) -#define macro4(i) macro2(i); macro2((i) + 2) -#define macro8(i) macro4(i); macro4((i) + 4) -#define macro16(i) macro8(i); macro8((i) + 8) -#define macro32(i) macro16(i); macro16((i) + 16) -#define macro64(i) macro32(i); macro32((i) + 32) +#define macro2(i) \ + macro(i); \ + macro((i) + 1) +#define macro4(i) \ + macro2(i); \ + macro2((i) + 2) +#define macro8(i) \ + macro4(i); \ + macro4((i) + 4) +#define macro16(i) \ + macro8(i); \ + macro8((i) + 8) +#define macro32(i) \ + macro16(i); \ + macro16((i) + 16) +#define macro64(i) \ + macro32(i); \ + macro32((i) + 32) macro64(0); panic("pattern too long"); #undef macro @@ -58,21 +81,24 @@ finish: *shift = __shift; } -__attribute__((always_inline)) -static inline void pattern_decode_hex(const char *str, int len, - uint64_t *key, uint64_t *mask, uint64_t *shift) { +__attribute__((always_inline)) static inline void +pattern_decode_hex(const char *str, int len, uint64_t *key, uint64_t *mask, + uint64_t *shift) { uint64_t __key = 0, __mask = 0, __shift = 0; -#define macro(i) \ - if ((i) >= len) goto finish; \ - else { \ - char c = str[i]; \ - if (c != ' ') { \ - Assert((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || c == '?', \ - "invalid character '%c' in pattern string", c); \ - __key = (__key << 4) | (c == '?' ? 0 : (c >= '0' && c <= '9') ? c - '0' : c - 'a' + 10); \ - __mask = (__mask << 4) | (c == '?' ? 0 : 0xf); \ - __shift = (c == '?' ? __shift + 4 : 0); \ - } \ +#define macro(i) \ + if ((i) >= len) \ + goto finish; \ + else { \ + char c = str[i]; \ + if (c != ' ') { \ + Assert((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || c == '?', \ + "invalid character '%c' in pattern string", c); \ + __key = (__key << 4) | (c == '?' ? 0 \ + : (c >= '0' && c <= '9') ? c - '0' \ + : c - 'a' + 10); \ + __mask = (__mask << 4) | (c == '?' ? 0 : 0xf); \ + __shift = (c == '?' ? __shift + 4 : 0); \ + } \ } macro16(0); @@ -84,18 +110,22 @@ finish: *shift = __shift; } - // --- pattern matching wrappers for decode --- -#define INSTPAT(pattern, ...) do { \ - uint64_t key, mask, shift; \ - pattern_decode(pattern, STRLEN(pattern), &key, &mask, &shift); \ - if ((((uint64_t)INSTPAT_INST(s) >> shift) & mask) == key) { \ - INSTPAT_MATCH(s, ##__VA_ARGS__); \ - goto *(__instpat_end); \ - } \ -} while (0) +#define INSTPAT(pattern, ...) \ + do { \ + uint64_t key, mask, shift; \ + pattern_decode(pattern, STRLEN(pattern), &key, &mask, &shift); \ + if ((((uint64_t)INSTPAT_INST(s) >> shift) & mask) == key) { \ + INSTPAT_MATCH(s, ##__VA_ARGS__); \ + goto *(__instpat_end); \ + } \ + } while (0) -#define INSTPAT_START(name) { const void ** __instpat_end = &&concat(__instpat_end_, name); -#define INSTPAT_END(name) concat(__instpat_end_, name): ; } +#define INSTPAT_START(name) \ + { \ + const void **__instpat_end = &&concat(__instpat_end_, name); +#define INSTPAT_END(name) \ + concat(__instpat_end_, name) :; \ + } #endif diff --git a/nemu/include/isa.h b/nemu/include/isa.h index 5040349..e860538 100644 --- a/nemu/include/isa.h +++ b/nemu/include/isa.h @@ -17,6 +17,7 @@ #define __ISA_H__ // Located at src/isa/$(GUEST_ISA)/include/isa-def.h +#include #include // The macro `__GUEST_ISA__` is defined in $(CFLAGS). @@ -30,8 +31,11 @@ void init_isa(); // reg extern CPU_state cpu; +extern arch_info_t isa_arch_info; void isa_reg_display(); word_t isa_reg_str2val(const char *name, bool *success); +int isa_read_reg(void *args, int regno, size_t *reg_value); +int isa_write_reg(void *args, int regno, size_t data); // exec struct Decode; diff --git a/nemu/scripts/build.mk b/nemu/scripts/build.mk index a3b71d2..f3bfe15 100644 --- a/nemu/scripts/build.mk +++ b/nemu/scripts/build.mk @@ -22,7 +22,7 @@ CXX := g++ endif LD := $(CXX) INCLUDES = $(addprefix -I, $(INC_PATH)) -CFLAGS := -O2 -MMD -Wall -Werror $(INCLUDES) $(CFLAGS) +CFLAGS := -O2 -MMD -Wall $(INCLUDES) $(CFLAGS) LDFLAGS := -O2 $(LDFLAGS) OBJS = $(SRCS:%.c=$(OBJ_DIR)/%.o) $(CXXSRC:%.cc=$(OBJ_DIR)/%.o) diff --git a/nemu/src/cpu/cpu-exec.c b/nemu/src/cpu/cpu-exec.c index 097c02f..f4be925 100644 --- a/nemu/src/cpu/cpu-exec.c +++ b/nemu/src/cpu/cpu-exec.c @@ -13,11 +13,12 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ -#include +#include "gdbstub.h" #include #include #include #include +#include /* The assembly code of instructions executed is only output to the screen * when the number of instructions executed is less than this value. @@ -34,13 +35,16 @@ IFDEF(CONFIG_ITRACE, extern char logbuf[CONFIG_ITRACE_BUFFER][128]); IFDEF(CONFIG_ITRACE, extern int logbuf_rear); void device_update(); -bool wp_eval_all(); static void trace_and_difftest(Decode *_this, vaddr_t dnpc) { #ifdef CONFIG_ITRACE_COND - if (ITRACE_COND) { log_write("%s\n", logbuf[logbuf_rear]); } + if (ITRACE_COND) { + log_write("%s\n", logbuf[logbuf_rear]); + } #endif - if (g_print_step) { IFDEF(CONFIG_ITRACE, puts(logbuf[logbuf_rear])); } + if (g_print_step) { + IFDEF(CONFIG_ITRACE, puts(logbuf[logbuf_rear])); + } IFDEF(CONFIG_DIFFTEST, difftest_step(_this->pc, dnpc)); } @@ -56,12 +60,13 @@ static void exec_once(Decode *s, vaddr_t pc) { int ilen = s->snpc - s->pc; int i; uint8_t *inst = (uint8_t *)&s->isa.inst.val; - for (i = ilen - 1; i >= 0; i --) { + for (i = ilen - 1; i >= 0; i--) { p += snprintf(p, 4, " %02x", inst[i]); } int ilen_max = MUXDEF(CONFIG_ISA_x86, 8, 4); int space_len = ilen_max - ilen; - if (space_len < 0) space_len = 0; + if (space_len < 0) + space_len = 0; space_len = space_len * 3 + 1; memset(p, ' ', space_len); p += space_len; @@ -69,7 +74,8 @@ static void exec_once(Decode *s, vaddr_t pc) { #ifndef CONFIG_ISA_loongarch32r void disassemble(char *str, int size, uint64_t pc, uint8_t *code, int nbyte); disassemble(p, logbuf[logbuf_rear] + sizeof(logbuf[logbuf_rear]) - p, - MUXDEF(CONFIG_ISA_x86, s->snpc, s->pc), (uint8_t *)&s->isa.inst.val, ilen); + MUXDEF(CONFIG_ISA_x86, s->snpc, s->pc), + (uint8_t *)&s->isa.inst.val, ilen); #else p[0] = '\0'; // the upstream llvm does not support loongarch32r #endif @@ -78,15 +84,12 @@ static void exec_once(Decode *s, vaddr_t pc) { static void execute(uint64_t n) { Decode s; - for (;n > 0; n --) { + for (; n > 0; n--) { exec_once(&s, cpu.pc); - g_nr_guest_inst ++; + g_nr_guest_inst++; trace_and_difftest(&s, cpu.pc); - if (wp_eval_all()) { - IFDEF(CONFIG_ITRACE, puts(logbuf[logbuf_rear])); + if (nemu_state.state != NEMU_RUNNING) break; - } - if (nemu_state.state != NEMU_RUNNING) break; IFDEF(CONFIG_DEVICE, device_update()); } } @@ -96,8 +99,12 @@ static void statistic() { #define NUMBERIC_FMT MUXDEF(CONFIG_TARGET_AM, "%", "%'") PRIu64 Log("host time spent = " NUMBERIC_FMT " us", g_timer); Log("total guest instructions = " NUMBERIC_FMT, g_nr_guest_inst); - if (g_timer > 0) Log("simulation frequency = " NUMBERIC_FMT " inst/s", g_nr_guest_inst * 1000000 / g_timer); - else Log("Finish running in less than 1 us and can not calculate the simulation frequency"); + if (g_timer > 0) + Log("simulation frequency = " NUMBERIC_FMT " inst/s", + g_nr_guest_inst * 1000000 / g_timer); + else + Log("Finish running in less than 1 us and can not calculate the simulation " + "frequency"); } void assert_fail_msg() { @@ -109,10 +116,13 @@ void assert_fail_msg() { void cpu_exec(uint64_t n) { g_print_step = (n < MAX_INST_TO_PRINT); switch (nemu_state.state) { - case NEMU_END: case NEMU_ABORT: - printf("Program execution has ended. To restart the program, exit NEMU and run again.\n"); - return; - default: nemu_state.state = NEMU_RUNNING; + case NEMU_END: + case NEMU_ABORT: + printf("Program execution has ended. To restart the program, exit NEMU and " + "run again.\n"); + return; + default: + nemu_state.state = NEMU_RUNNING; } uint64_t timer_start = get_time(); @@ -123,18 +133,52 @@ void cpu_exec(uint64_t n) { g_timer += timer_end - timer_start; switch (nemu_state.state) { - case NEMU_RUNNING: nemu_state.state = NEMU_STOP; break; + case NEMU_RUNNING: + nemu_state.state = NEMU_STOP; + break; - case NEMU_END: case NEMU_ABORT: { - Log("nemu: %s at pc = " FMT_WORD, - (nemu_state.state == NEMU_ABORT ? ANSI_FMT("ABORT", ANSI_FG_RED) : - (nemu_state.halt_ret == 0 ? ANSI_FMT("HIT GOOD TRAP", ANSI_FG_GREEN) : - ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))), - nemu_state.halt_pc); - if(nemu_state.halt_ret != 0) { - IFDEF(CONFIG_ITRACE, log_itrace_print()); - } - } // fall through - case NEMU_QUIT: statistic(); + case NEMU_END: + case NEMU_ABORT: { + Log("nemu: %s at pc = " FMT_WORD, + (nemu_state.state == NEMU_ABORT + ? ANSI_FMT("ABORT", ANSI_FG_RED) + : (nemu_state.halt_ret == 0 + ? ANSI_FMT("HIT GOOD TRAP", ANSI_FG_GREEN) + : ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))), + nemu_state.halt_pc); + if (nemu_state.halt_ret != 0) { + IFDEF(CONFIG_ITRACE, log_itrace_print()); + } + } // fall through + case NEMU_QUIT: + statistic(); } } + +breakpoint_t *cpu_exec_with_bp(uint64_t n, breakpoint_t *bp, size_t len) { + static Decode s; + nemu_state.state = NEMU_RUNNING; + do { + exec_once(&s, cpu.pc); + g_nr_guest_inst++; + for (int i = 0; i < len; i++) { + size_t addr = bp[i].addr; + bp_type_t bptype = bp[i].type; + if (bptype == BP_SOFTWARE && cpu.pc == addr) { + return bp + i; + } + bool is_write = (bptype == BP_WRITE) || (bptype == BP_ACCESS); + bool is_read = (bptype == BP_READ) || (bptype == BP_ACCESS); + if (s.inst_type == INST_MEM_READ && s.inst_value.rmem == addr && + is_write) { + return bp + i; + } else if (s.inst_type == INST_MEM_WRITE && s.inst_value.wmem == addr && + is_read) { + return bp + i; + } + } + if (nemu_state.state != NEMU_RUNNING) + return NULL; + } while (--n); + return NULL; +} diff --git a/nemu/src/engine/interpreter/init.c b/nemu/src/engine/interpreter/init.c index a517c3e..fed4923 100644 --- a/nemu/src/engine/interpreter/init.c +++ b/nemu/src/engine/interpreter/init.c @@ -13,7 +13,9 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ +#include "debug.h" #include +#include void sdb_mainloop(); @@ -22,6 +24,10 @@ void engine_start() { cpu_exec(-1); #else /* Receive commands from user. */ - sdb_mainloop(); + int nemu_gdbstub_run(); + if (nemu_gdbstub_run()) { + Error("gdbstub exited abnormally"); + exit(1); + } #endif } diff --git a/nemu/src/filelist.mk b/nemu/src/filelist.mk index 03e05df..4ae2376 100644 --- a/nemu/src/filelist.mk +++ b/nemu/src/filelist.mk @@ -14,12 +14,12 @@ #**************************************************************************************/ SRCS-y += src/nemu-main.c -DIRS-y += src/cpu src/monitor src/utils +DIRS-y += src/cpu src/utils DIRS-$(CONFIG_MODE_SYSTEM) += src/memory DIRS-BLACKLIST-$(CONFIG_TARGET_AM) += src/monitor/sdb SHARE = $(if $(CONFIG_TARGET_SHARE),1,0) -LIBS += $(if $(CONFIG_TARGET_NATIVE_ELF),-lreadline -ldl -pie,) +LIBS += $(if $(CONFIG_TARGET_NATIVE_ELF),-lgdbstub -lreadline -ldl -pie,) ifdef mainargs ASFLAGS += -DBIN_PATH=\"$(mainargs)\" diff --git a/nemu/src/isa/riscv32/include/isa-def.h b/nemu/src/isa/riscv32/include/isa-def.h index dd0105c..524cb4e 100644 --- a/nemu/src/isa/riscv32/include/isa-def.h +++ b/nemu/src/isa/riscv32/include/isa-def.h @@ -20,6 +20,7 @@ typedef struct { word_t gpr[MUXDEF(CONFIG_RVE, 16, 32)]; + // word_t csr[MUXDEF(CONFIG_RVE, )] vaddr_t pc; } MUXDEF(CONFIG_RV64, riscv64_CPU_state, riscv32_CPU_state); diff --git a/nemu/src/isa/riscv32/inst.c b/nemu/src/isa/riscv32/inst.c index 1c41c63..dc65f47 100644 --- a/nemu/src/isa/riscv32/inst.c +++ b/nemu/src/isa/riscv32/inst.c @@ -13,45 +13,91 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ -#include #include "local-include/reg.h" #include "macro.h" +#include "types.h" +#include #include -#include #include +#include #include #include #define R(i) gpr(i) -#define Mr vaddr_read -#define Mw vaddr_write enum { - TYPE_R, TYPE_I, TYPE_I_SHIFT, TYPE_U, TYPE_S, TYPE_B, TYPE_J, + TYPE_R, + TYPE_I, + TYPE_I_SHIFT, + TYPE_U, + TYPE_S, + TYPE_B, + TYPE_J, TYPE_N, // none }; -#define src1R() do { *src1 = R(rs1); } while (0) -#define src2R() do { *src2 = R(rs2); } while (0) -#define immI() do { *imm = SEXT(BITS(i, 31, 20), 12); } while(0) -#define immU() do { *imm = SEXT(BITS(i, 31, 12), 20) << 12; } while(0) -#define immS() do { *imm = SEXT(BITS(i, 31, 25), 7) << 5 | BITS(i, 11, 7); } while(0) -#define immB() do { *imm = SEXT(BITS(i, 31, 31), 1) << 12 | BITS(i, 30, 25) << 5 | BITS(i, 11, 8) << 1 | BITS(i, 7, 7) << 11; } while(0) -#define immJ() do { *imm = SEXT(BITS(i, 31, 31), 1) << 20 | BITS(i, 30, 21) << 1 | BITS(i, 20, 20) << 11 | BITS(i, 19, 12) << 12; } while(0) +#define src1R() \ + do { \ + *src1 = R(rs1); \ + } while (0) +#define src2R() \ + do { \ + *src2 = R(rs2); \ + } while (0) +#define immI() \ + do { \ + *imm = SEXT(BITS(i, 31, 20), 12); \ + } while (0) +#define immU() \ + do { \ + *imm = SEXT(BITS(i, 31, 12), 20) << 12; \ + } while (0) +#define immS() \ + do { \ + *imm = SEXT(BITS(i, 31, 25), 7) << 5 | BITS(i, 11, 7); \ + } while (0) +#define immB() \ + do { \ + *imm = SEXT(BITS(i, 31, 31), 1) << 12 | BITS(i, 30, 25) << 5 | \ + BITS(i, 11, 8) << 1 | BITS(i, 7, 7) << 11; \ + } while (0) +#define immJ() \ + do { \ + *imm = SEXT(BITS(i, 31, 31), 1) << 20 | BITS(i, 30, 21) << 1 | \ + BITS(i, 20, 20) << 11 | BITS(i, 19, 12) << 12; \ + } while (0) static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, word_t *imm, int type) { uint32_t i = s->isa.inst.val; int rs1 = BITS(i, 19, 15); int rs2 = BITS(i, 24, 20); - *rd = BITS(i, 11, 7); + *rd = BITS(i, 11, 7); switch (type) { - case TYPE_R: src1R(); src2R(); break; - case TYPE_I: src1R(); immI(); break; - case TYPE_U: immU(); break; - case TYPE_J: immJ(); break; - case TYPE_S: src1R(); src2R(); immS(); break; - case TYPE_B: src1R(); src2R(); immB(); break; + case TYPE_R: + src1R(); + src2R(); + break; + case TYPE_I: + src1R(); + immI(); + break; + case TYPE_U: + immU(); + break; + case TYPE_J: + immJ(); + break; + case TYPE_S: + src1R(); + src2R(); + immS(); + break; + case TYPE_B: + src1R(); + src2R(); + immB(); + break; } } @@ -62,11 +108,23 @@ static void do_branch(Decode *s, bool condition, word_t offset) { } } +static inline word_t Mr(Decode *s, vaddr_t addr, int len) { + s->inst_type = INST_MEM_READ; + s->inst_value.rmem = addr & ~0x3; + return vaddr_read(addr, len); +} + +static inline void Mw(Decode *s, vaddr_t addr, int len, word_t data) { + vaddr_write(addr, len, data); + s->inst_type = INST_MEM_WRITE; + s->inst_value.wmem = addr & ~0x3; +} + #ifdef CONFIG_FTRACE static void ftrace_jalr(Decode *s, int rd, vaddr_t dst) { uint32_t i = s->isa.inst.val; int rs1 = BITS(i, 19, 15); - if(rs1 == 1 && rd == 0) { + if (rs1 == 1 && rd == 0) { ftrace_return(s->pc, dst); } else { ftrace_call(s->pc, dst); @@ -80,70 +138,118 @@ static int decode_exec(Decode *s) { s->dnpc = s->snpc; #define INSTPAT_INST(s) ((s)->isa.inst.val) -#define INSTPAT_MATCH(s, name, type, ... /* execute body */ ) { \ - decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \ - __VA_ARGS__ ; \ -} +#define INSTPAT_MATCH(s, name, type, ... /* execute body */) \ + { \ + decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \ + __VA_ARGS__; \ + } INSTPAT_START(); - INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui , U, R(rd) = imm); - INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + imm); + INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui, U, R(rd) = imm); + INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc, U, + R(rd) = s->pc + imm); - INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, do { - s->dnpc = s->pc + imm; R(rd) = s->pc + 4; - IFDEF(CONFIG_FTRACE, ftrace_call(s->pc, s->pc + imm)); } while(0)); - INSTPAT("??????? ????? ????? ??? ????? 11001 11", jalr , I, do { - s->dnpc = src1 + imm; R(rd) = s->pc + 4; - IFDEF(CONFIG_FTRACE, ftrace_jalr(s, rd, src1 + imm)); } while(0)); - INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq , B, do_branch(s, src1 == src2, imm)); - INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne , B, do_branch(s, src1 != src2, imm)); - INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt , B, do_branch(s, (sword_t)src1 < (sword_t)src2, imm)); - INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge , B, do_branch(s, (sword_t)src1 >= (sword_t)src2, imm)); - INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu , B, do_branch(s, src1 < src2, imm)); - INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu , B, do_branch(s, src1 >= src2, imm)); + INSTPAT( + "??????? ????? ????? ??? ????? 11011 11", jal, J, do { + s->dnpc = s->pc + imm; + R(rd) = s->pc + 4; + IFDEF(CONFIG_FTRACE, ftrace_call(s->pc, s->pc + imm)); + } while (0)); + INSTPAT( + "??????? ????? ????? ??? ????? 11001 11", jalr, I, do { + s->dnpc = src1 + imm; + R(rd) = s->pc + 4; + IFDEF(CONFIG_FTRACE, ftrace_jalr(s, rd, src1 + imm)); + } while (0)); + INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq, B, + do_branch(s, src1 == src2, imm)); + INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne, B, + do_branch(s, src1 != src2, imm)); + INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt, B, + do_branch(s, (sword_t)src1 < (sword_t)src2, imm)); + INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge, B, + do_branch(s, (sword_t)src1 >= (sword_t)src2, imm)); + INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu, B, + do_branch(s, src1 < src2, imm)); + INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu, B, + do_branch(s, src1 >= src2, imm)); - INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb , I, R(rd) = SEXT(Mr(src1 + imm, 1), 8)); - INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh , I, R(rd) = SEXT(Mr(src1 + imm, 2), 16)); - INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw , I, R(rd) = SEXT(Mr(src1 + imm, 4), 32)); - INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu , I, R(rd) = Mr(src1 + imm, 1)); - INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu , I, R(rd) = Mr(src1 + imm, 2)); - INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb , S, Mw(src1 + imm, 1, src2)); - INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh , S, Mw(src1 + imm, 2, src2)); - INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw , S, Mw(src1 + imm, 4, src2)); + INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb, I, + R(rd) = SEXT(Mr(s, src1 + imm, 1), 8)); + INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh, I, + R(rd) = SEXT(Mr(s, src1 + imm, 2), 16)); + INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw, I, + R(rd) = SEXT(Mr(s, src1 + imm, 4), 32)); + INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu, I, + R(rd) = Mr(s, src1 + imm, 1)); + INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu, I, + R(rd) = Mr(s, src1 + imm, 2)); + INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb, S, + Mw(s, src1 + imm, 1, src2)); + INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh, S, + Mw(s, src1 + imm, 2, src2)); + INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw, S, + Mw(s, src1 + imm, 4, src2)); - INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi , I, R(rd) = src1 + imm); - INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti , I, R(rd) = (sword_t)src1 < (sword_t)imm ? 1 : 0); - INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu , I, R(rd) = src1 < imm ? 1 : 0); - INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori , I, R(rd) = src1 ^ imm); - INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori , I, R(rd) = src1 | imm); - INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi , I, R(rd) = src1 & imm); - INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli , I, R(rd) = src1 << imm); - INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli , I, R(rd) = src1 >> imm); - INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai , I, R(rd) = (sword_t)src1 >> (imm & 0x01F)); - INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add , R, R(rd) = src1 + src2); - INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub , R, R(rd) = src1 - src2); - INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll , R, R(rd) = src1 << src2); - INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt , R, R(rd) = (sword_t)src1 < (sword_t)src2 ? 1 : 0); - INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu , R, R(rd) = src1 < src2 ? 1 : 0); - INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor , R, R(rd) = src1 ^ src2); - INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl , R, R(rd) = src1 >> src2); - INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra , R, R(rd) = (sword_t)src1 >> (src2 & 0x01F)); - INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or , R, R(rd) = src1 | src2); - INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and , R, R(rd) = src1 & src2); + INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi, I, + R(rd) = src1 + imm); + INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti, I, + R(rd) = (sword_t)src1 < (sword_t)imm ? 1 : 0); + INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu, I, + R(rd) = src1 < imm ? 1 : 0); + INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori, I, + R(rd) = src1 ^ imm); + INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori, I, R(rd) = src1 | imm); + INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi, I, + R(rd) = src1 & imm); + INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli, I, + R(rd) = src1 << imm); + INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli, I, + R(rd) = src1 >> imm); + INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai, I, + R(rd) = (sword_t)src1 >> (imm & 0x01F)); + INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add, R, + R(rd) = src1 + src2); + INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub, R, + R(rd) = src1 - src2); + INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll, R, + R(rd) = src1 << src2); + INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt, R, + R(rd) = (sword_t)src1 < (sword_t)src2 ? 1 : 0); + INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu, R, + R(rd) = src1 < src2 ? 1 : 0); + INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor, R, + R(rd) = src1 ^ src2); + INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl, R, + R(rd) = src1 >> src2); + INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra, R, + R(rd) = (sword_t)src1 >> (src2 & 0x01F)); + INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or, R, R(rd) = src1 | src2); + INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and, R, + R(rd) = src1 & src2); - INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0 + INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak, N, + NEMUTRAP(s->pc, R(10))); // R(10) is $a0 // "M" - INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul , R, R(rd) = src1 * src2); - INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh , R, R(rd) = (int64_t)(sword_t)src1 * (sword_t)src2 >> 32); - INSTPAT("0000001 ????? ????? 010 ????? 01100 11", mulhsu , R, R(rd) = (int64_t)(sword_t)src1 * (uint64_t)src2 >> 32); - INSTPAT("0000001 ????? ????? 011 ????? 01100 11", mulhu , R, R(rd) = (uint64_t)src1 * (uint64_t)src2 >> 32); - INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div , R, R(rd) = (sword_t)src1 / (sword_t)src2); - INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu , R, R(rd) = src1 / src2); - INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem , R, R(rd) = (sword_t)src1 % (sword_t)src2); - INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu , R, R(rd) = src1 % src2); + INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul, R, + R(rd) = src1 * src2); + INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh, R, + R(rd) = (int64_t)(sword_t)src1 * (sword_t)src2 >> 32); + INSTPAT("0000001 ????? ????? 010 ????? 01100 11", mulhsu, R, + R(rd) = (int64_t)(sword_t)src1 * (uint64_t)src2 >> 32); + INSTPAT("0000001 ????? ????? 011 ????? 01100 11", mulhu, R, + R(rd) = (uint64_t)src1 * (uint64_t)src2 >> 32); + INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div, R, + R(rd) = (sword_t)src1 / (sword_t)src2); + INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu, R, + R(rd) = src1 / src2); + INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem, R, + R(rd) = (sword_t)src1 % (sword_t)src2); + INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu, R, + R(rd) = src1 % src2); - INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv , N, INV(s->pc)); + INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv, N, INV(s->pc)); INSTPAT_END(); R(0) = 0; // reset $zero to 0 diff --git a/nemu/src/isa/riscv32/reg.c b/nemu/src/isa/riscv32/reg.c index 4630df9..52e6ba0 100644 --- a/nemu/src/isa/riscv32/reg.c +++ b/nemu/src/isa/riscv32/reg.c @@ -13,20 +13,20 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ -#include #include "local-include/reg.h" +#include "gdbstub.h" #include "macro.h" +#include +#include -const char *regs[] = { - "$0", "ra", "sp", "gp", "tp", "t0", "t1", "t2", - "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", - "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", - "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6" -}; +const char *regs[] = {"$0", "ra", "sp", "gp", "tp", "t0", "t1", "t2", + "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", + "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"}; void isa_reg_display() { int colomn_per_row = 4; - for(int i = 0; i < ARRLEN(regs); i++) { + for (int i = 0; i < ARRLEN(regs); i++) { printf("\e[1;34m%3s\e[0m: " FMT_PADDR, reg_name(i), gpr(i)); if (i % colomn_per_row == 3) putchar('\n'); @@ -49,3 +49,33 @@ word_t isa_reg_str2val(const char *s, bool *success) { return gpr(i); } + +int isa_read_reg(void *args, int regno, size_t *reg_value) { + if (regno > 32) { + return EFAULT; + } + + if (regno == 32) { + *reg_value = cpu.pc; + } else { + *reg_value = cpu.gpr[regno]; + } + return 0; +} + +int isa_write_reg(void *args, int regno, size_t data) { + if (regno > 32) { + return EFAULT; + } + + if (regno == 32) { + cpu.pc = data; + } else { + cpu.gpr[regno] = data; + } + return 0; +} + +arch_info_t isa_arch_info = {.reg_num = 33, + .reg_byte = MUXDEF(CONFIG_RV64, 8, 4), + .target_desc = TARGET_RV32}; diff --git a/nemu/src/memory/paddr.c b/nemu/src/memory/paddr.c index 437debd..10ee66c 100644 --- a/nemu/src/memory/paddr.c +++ b/nemu/src/memory/paddr.c @@ -15,12 +15,12 @@ #include "common.h" #include "debug.h" -#include -#include #include #include +#include +#include -#if defined(CONFIG_PMEM_MALLOC) +#if defined(CONFIG_PMEM_MALLOC) static uint8_t *pmem = NULL; #else // CONFIG_PMEM_GARRAY static uint8_t pmem[CONFIG_MSIZE] PG_ALIGN = {}; @@ -31,7 +31,7 @@ static word_t mtrace_end[CONFIG_MTRACE_RANGE_MAX] = {0}; static int range_count = 0; #endif -uint8_t* guest_to_host(paddr_t paddr) { return pmem + paddr - CONFIG_MBASE; } +uint8_t *guest_to_host(paddr_t paddr) { return pmem + paddr - CONFIG_MBASE; } paddr_t host_to_guest(uint8_t *haddr) { return haddr - pmem + CONFIG_MBASE; } static word_t pmem_read(paddr_t addr, int len) { @@ -44,22 +44,24 @@ static void pmem_write(paddr_t addr, int len, word_t data) { } static void out_of_bound(paddr_t addr) { - panic("address = " FMT_PADDR " is out of bound of pmem [" FMT_PADDR ", " FMT_PADDR "] at pc = " FMT_WORD, - addr, PMEM_LEFT, PMEM_RIGHT, cpu.pc); + panic("address = " FMT_PADDR " is out of bound of pmem [" FMT_PADDR + ", " FMT_PADDR "] at pc = " FMT_WORD, + addr, PMEM_LEFT, PMEM_RIGHT, cpu.pc); } #ifdef CONFIG_MTRACE static void mtrace_print(char type, word_t addr, int len, word_t data) { for (int i = 0; i < range_count; i++) - if (addr <= mtrace_end[i] && addr >= mtrace_start[i] ) { - Trace("Mem %c " FMT_PADDR "%d D " FMT_PADDR, type, addr, len, data); + if (addr <= mtrace_end[i] && addr >= mtrace_start[i]) { + Trace("PC=" FMT_PADDR " Mem %c" FMT_PADDR " %d D " FMT_PADDR, cpu.pc, + type, addr, len, data); break; } } #endif void init_mem() { -#if defined(CONFIG_PMEM_MALLOC) +#if defined(CONFIG_PMEM_MALLOC) pmem = malloc(CONFIG_MSIZE); assert(pmem); #endif @@ -67,15 +69,17 @@ void init_mem() { char range[sizeof(CONFIG_MTRACE_RANGE)] = CONFIG_MTRACE_RANGE; char *saveptr, *ptr; ptr = strtok_r(range, ",", &saveptr); - for (range_count = 0; range_count < CONFIG_MTRACE_RANGE_MAX; ) { + for (range_count = 0; range_count < CONFIG_MTRACE_RANGE_MAX;) { word_t start, end; - Assert(sscanf(ptr, FMT_PADDR "-" FMT_PADDR, &start, &end) == 2, "Config option MTRACE_RANGE has wrong format"); + Assert(sscanf(ptr, FMT_PADDR "-" FMT_PADDR, &start, &end) == 2, + "Config option MTRACE_RANGE has wrong format"); mtrace_start[range_count] = start; mtrace_end[range_count] = end; range_count++; ptr = strtok_r(NULL, ",", &saveptr); - if (!ptr) break; + if (!ptr) + break; } Trace("MTRACE ranges: "); for (int i = 0; i < range_count; i++) { @@ -83,24 +87,31 @@ void init_mem() { } #endif IFDEF(CONFIG_MEM_RANDOM, memset(pmem, rand(), CONFIG_MSIZE)); - Log("physical memory area [" FMT_PADDR ", " FMT_PADDR "]", PMEM_LEFT, PMEM_RIGHT); + Log("physical memory area [" FMT_PADDR ", " FMT_PADDR "]", PMEM_LEFT, + PMEM_RIGHT); } word_t paddr_read(paddr_t addr, int len) { word_t result = 0; - if (likely(in_pmem(addr))) { result = pmem_read(addr, len); goto mtrace;} - IFDEF(CONFIG_DEVICE, result = mmio_read(addr, len); goto mtrace) + if (likely(in_pmem(addr))) { + result = pmem_read(addr, len); + goto mtrace; + } + IFDEF(CONFIG_DEVICE, result = mmio_read(addr, len); goto mtrace;) out_of_bound(addr); mtrace: IFDEF(CONFIG_MTRACE, mtrace_print('R', addr, len, result)); - + return result; } void paddr_write(paddr_t addr, int len, word_t data) { IFDEF(CONFIG_MTRACE, mtrace_print('W', addr, len, data)); - if (likely(in_pmem(addr))) { pmem_write(addr, len, data); return; } + if (likely(in_pmem(addr))) { + pmem_write(addr, len, data); + return; + } IFDEF(CONFIG_DEVICE, mmio_write(addr, len, data); return); out_of_bound(addr); } diff --git a/nemu/src/monitor/filelist.mk b/nemu/src/monitor/filelist.mk new file mode 100644 index 0000000..4094bee --- /dev/null +++ b/nemu/src/monitor/filelist.mk @@ -0,0 +1,3 @@ +DIRS-y += src/monitor/monitor.c + +CXXSRC += src/monitor/gdbstub.cc diff --git a/nemu/src/monitor/gdbstub.cc b/nemu/src/monitor/gdbstub.cc new file mode 100644 index 0000000..40aa594 --- /dev/null +++ b/nemu/src/monitor/gdbstub.cc @@ -0,0 +1,130 @@ +#include "utils.h" +#include + +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +} + +typedef struct { + std::vector *bp; + bool halt; +} DbgState; + +int nemu_read_mem(void *args, size_t addr, size_t len, void *val) { + if (!in_pmem(addr)) + return EINVAL; + memcpy(val, guest_to_host(addr), len); + return 0; +} + +int nemu_write_mem(void *args, size_t addr, size_t len, void *val) { + if (!in_pmem(addr)) + return EINVAL; + memcpy(guest_to_host(addr), val, len); + return 0; +} + +static void nemu_is_stopped(gdb_action_t *act, breakpoint_t *stopped_at) { + switch (nemu_state.state) { + case NEMU_RUNNING: + nemu_state.state = NEMU_STOP; + switch (stopped_at->type) { + case BP_SOFTWARE: + act->reason = gdb_action_t::ACT_BREAKPOINT; + break; + case BP_ACCESS: + act->reason = gdb_action_t::ACT_WATCH; + break; + case BP_WRITE: + act->reason = gdb_action_t::ACT_WWATCH; + break; + case BP_READ: + act->reason = gdb_action_t::ACT_RWATCH; + break; + } + act->data = stopped_at->addr; + break; + + default: + act->reason = gdb_action_t::ACT_SHUTDOWN; + act->data = nemu_state.halt_ret; + } +} + +void nemu_cont(void *args, gdb_action_t *res) { + DbgState *dbg_state = (DbgState *)args; + breakpoint_t *stopped_at = + cpu_exec_with_bp(-1, dbg_state->bp->data(), dbg_state->bp->size()); + nemu_is_stopped(res, stopped_at); +} + +void nemu_stepi(void *args, gdb_action_t *res) { + DbgState *dbg_state = (DbgState *)args; + breakpoint_t *stopped_at = + cpu_exec_with_bp(1, dbg_state->bp->data(), dbg_state->bp->size()); + nemu_is_stopped(res, stopped_at); +} + +bool nemu_set_bp(void *args, size_t addr, bp_type_t type) { + DbgState *dbg_state = (DbgState *)args; + for (const auto &bp : *dbg_state->bp) { + if (bp.addr == addr && bp.type == type) { + return true; + } + } + dbg_state->bp->push_back({.addr = addr, .type = type}); + return true; +} + +bool nemu_del_bp(void *args, size_t addr, bp_type_t type) { + DbgState *dbg_state = (DbgState *)args; + for (auto it = dbg_state->bp->begin(); it != dbg_state->bp->end(); it++) { + if (it->addr == addr && it->type == type) { + std::swap(*it, *dbg_state->bp->rbegin()); + dbg_state->bp->pop_back(); + return true; + } + } + return false; +} + +void nemu_on_interrupt(void *args) { + // fputs("Not implemented", stderr); +} + +static struct target_ops nemu_gdbstub_ops = {.cont = nemu_cont, + .stepi = nemu_stepi, + .read_reg = isa_read_reg, + .write_reg = isa_write_reg, + .read_mem = nemu_read_mem, + .write_mem = nemu_write_mem, + .set_bp = nemu_set_bp, + .del_bp = nemu_del_bp, + .on_interrupt = NULL}; +static DbgState dbg; +extern "C" { +static gdbstub_t gdbstub_priv; +#define SOCKET_ADDR "127.0.0.1:1234" +int nemu_gdbstub_init() { + dbg.bp = new std::vector(); + assert(dbg.bp); + if (!gdbstub_init(&gdbstub_priv, &nemu_gdbstub_ops, + (arch_info_t)isa_arch_info, SOCKET_ADDR)) { + return EINVAL; + } + return 0; +} +int nemu_gdbstub_run() { + puts("Waiting for gdb connection at " SOCKET_ADDR); + bool success = gdbstub_run(&gdbstub_priv, &dbg); + gdbstub_close(&gdbstub_priv); + return !success; +} +} diff --git a/nemu/src/monitor/monitor.c b/nemu/src/monitor/monitor.c index 0154208..9f49786 100644 --- a/nemu/src/monitor/monitor.c +++ b/nemu/src/monitor/monitor.c @@ -22,24 +22,25 @@ void init_log(const char *log_file); void init_mem(); void init_difftest(char *ref_so_file, long img_size, int port); void init_device(); -void init_sdb(); void init_disasm(const char *triple); +int nemu_gdbstub_init(); static void welcome() { - Log("Trace: %s", MUXDEF(CONFIG_TRACE, ANSI_FMT("ON", ANSI_FG_GREEN), ANSI_FMT("OFF", ANSI_FG_RED))); - IFDEF(CONFIG_TRACE, Log("If trace is enabled, a log file will be generated " - "to record the trace. This may lead to a large log file. " - "If it is not necessary, you can disable it in menuconfig")); + Log("Trace: %s", MUXDEF(CONFIG_TRACE, ANSI_FMT("ON", ANSI_FG_GREEN), + ANSI_FMT("OFF", ANSI_FG_RED))); + IFDEF(CONFIG_TRACE, + Log("If trace is enabled, a log file will be generated " + "to record the trace. This may lead to a large log file. " + "If it is not necessary, you can disable it in menuconfig")); Log("Build time: %s, %s", __TIME__, __DATE__); - printf("Welcome to %s-NEMU!\n", ANSI_FMT(str(__GUEST_ISA__), ANSI_FG_YELLOW ANSI_BG_RED)); + printf("Welcome to %s-NEMU!\n", + ANSI_FMT(str(__GUEST_ISA__), ANSI_FG_YELLOW ANSI_BG_RED)); printf("For help, type \"help\"\n"); } #ifndef CONFIG_TARGET_AM #include -void sdb_set_batch_mode(); - static char *log_file = NULL; static char *elf_file = NULL; static char *diff_so_file = NULL; @@ -70,32 +71,41 @@ static long load_img() { static int parse_args(int argc, char *argv[]) { const struct option table[] = { - {"batch" , no_argument , NULL, 'b'}, - {"log" , required_argument, NULL, 'l'}, - {"diff" , required_argument, NULL, 'd'}, - {"port" , required_argument, NULL, 'p'}, - {"elf" , required_argument, NULL, 'f'}, - {"help" , no_argument , NULL, 'h'}, - {0 , 0 , NULL, 0 }, + {"batch", no_argument, NULL, 'b'}, + {"log", required_argument, NULL, 'l'}, + {"diff", required_argument, NULL, 'd'}, + {"port", required_argument, NULL, 'p'}, + {"elf", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {0, 0, NULL, 0}, }; int o; - while ( (o = getopt_long(argc, argv, "-bhl:d:p:", table, NULL)) != -1) { + while ((o = getopt_long(argc, argv, "-bhl:d:p:", table, NULL)) != -1) { switch (o) { - case 'b': sdb_set_batch_mode(); break; - case 'p': sscanf(optarg, "%d", &difftest_port); break; - case 'l': log_file = optarg; break; - case 'd': diff_so_file = optarg; break; - case 'f': elf_file = optarg; break; - case 1: img_file = optarg; return 0; - default: - printf("Usage: %s [OPTION...] IMAGE [args]\n\n", argv[0]); - printf("\t-b,--batch run with batch mode\n"); - printf("\t-l,--log=FILE output log to FILE\n"); - printf("\t-d,--diff=REF_SO run DiffTest with reference REF_SO\n"); - printf("\t-p,--port=PORT run DiffTest with port PORT\n"); - printf("\t-f,--elf=FILE elf file with debug info\n"); - printf("\n"); - exit(0); + case 'p': + sscanf(optarg, "%d", &difftest_port); + break; + case 'l': + log_file = optarg; + break; + case 'd': + diff_so_file = optarg; + break; + case 'f': + elf_file = optarg; + break; + case 1: + img_file = optarg; + return 0; + default: + printf("Usage: %s [OPTION...] IMAGE [args]\n\n", argv[0]); + printf("\t-b,--batch run with batch mode\n"); + printf("\t-l,--log=FILE output log to FILE\n"); + printf("\t-d,--diff=REF_SO run DiffTest with reference REF_SO\n"); + printf("\t-p,--port=PORT run DiffTest with port PORT\n"); + printf("\t-f,--elf=FILE elf file with debug info\n"); + printf("\n"); + exit(0); } } return 0; @@ -128,11 +138,14 @@ void init_monitor(int argc, char *argv[]) { /* Initialize differential testing. */ init_difftest(diff_so_file, img_size, difftest_port); - /* Initialize the simple debugger. */ - init_sdb(); + /* Initialize debugger */ + if (nemu_gdbstub_init()) { + Error("Failed to init"); + exit(1); + } // printf("elf_file: %s\n", elf_file); - if(elf_file != NULL) { + if (elf_file != NULL) { #ifdef CONFIG_FTRACE void init_elf(const char *path); init_elf(elf_file); @@ -142,14 +155,13 @@ void init_monitor(int argc, char *argv[]) { } #ifndef CONFIG_ISA_loongarch32r - IFDEF(CONFIG_ITRACE, init_disasm( - MUXDEF(CONFIG_ISA_x86, "i686", - MUXDEF(CONFIG_ISA_mips32, "mipsel", - MUXDEF(CONFIG_ISA_riscv, - MUXDEF(CONFIG_RV64, "riscv64", - "riscv32"), - "bad"))) "-pc-linux-gnu" - )); + IFDEF(CONFIG_ITRACE, + init_disasm( + MUXDEF(CONFIG_ISA_x86, "i686", + MUXDEF(CONFIG_ISA_mips32, "mipsel", + MUXDEF(CONFIG_ISA_riscv, + MUXDEF(CONFIG_RV64, "riscv64", "riscv32"), + "bad"))) "-pc-linux-gnu")); #endif /* Display welcome message. */ diff --git a/nemu/src/monitor/sdb/addrexp.l b/nemu/src/monitor/sdb/addrexp.l deleted file mode 100644 index d814ee3..0000000 --- a/nemu/src/monitor/sdb/addrexp.l +++ /dev/null @@ -1,24 +0,0 @@ -%{ - #include - #include - static bool success = false; - void yyerror(word_t *result, const char *err); -%} -%option noyywrap - -%% - -0[xX][0-9a-fA-F]+ { yylval = strtoul(yytext, NULL, 16); return HEX_NUMBER; } -[0-9]+ { yylval = strtoul(yytext, NULL, 10); return NUMBER; } -$[asgprt$][0-9pa][0-9]? { - yylval = isa_reg_str2val(yytext + 1, &success); - if(!success) { - yyerror(NULL, "Failed to convert reg to value"); - return YYerror; - } - return REGISTER; -} -[+\-*/<=()] { return *yytext; } -[ \t] { } -. { printf("Unexpected character: %s\n", yytext); return YYerror; } -%% diff --git a/nemu/src/monitor/sdb/addrexp.y b/nemu/src/monitor/sdb/addrexp.y deleted file mode 100644 index 4094e0b..0000000 --- a/nemu/src/monitor/sdb/addrexp.y +++ /dev/null @@ -1,60 +0,0 @@ -%code requires { - #include - #include - #include - #include - extern int yylex(void); -} -%{ - #include - #include - #include - #include - #include - void yyerror(word_t *result, const char *err) { - Error("%s", err); - } -%} - -%token NUMBER HEX_NUMBER -%token REGISTER -%locations -%start input -%define api.value.type { word_t } -%parse-param { uint32_t *result } -%left '-' '+' -%left '*' '/' - -%% -input - : expression { *result = $1; } - ; - -expression - : number { $$ = $1; } - | expression '>' '=' expression { $$ = ($1 >= $4); } - | expression '<' '=' expression { $$ = ($1 <= $4); } - | expression '=' '=' expression { $$ = ($1 == $4); } - | expression '!' '=' expression { $$ = ($1 == $4); } - | expression '>' expression { $$ = ($1 > $3); } - | expression '<' expression { $$ = ($1 < $3); } - | expression '+' expression { $$ = $1 + $3; } - | expression '-' expression { $$ = $1 - $3; } - | expression '*' expression { $$ = $1 * $3; } - | expression '/' expression { - if($3 == 0) { - fprintf(stderr, "Error: divide by zero at %u / %u\n", $1, $3); - YYABORT; - }; - $$ = $1 / $3; - } - | '-' number { $$ = -$2; } - | '*' expression { $$ = vaddr_read($2, WORD_BYTES); } - | '(' expression ')' { $$ = $2; } - -number - : REGISTER - | NUMBER - | HEX_NUMBER - -%% diff --git a/nemu/src/monitor/sdb/filelist.mk b/nemu/src/monitor/sdb/filelist.mk deleted file mode 100644 index f477a4f..0000000 --- a/nemu/src/monitor/sdb/filelist.mk +++ /dev/null @@ -1,2 +0,0 @@ -SRCS-y += src/monitor/sdb/addrexp.tag.c src/monitor/sdb/addrexp.yy.c -LFLAGS += -DYY_NO_UNPUT -DYY_NO_INPUT diff --git a/nemu/src/monitor/sdb/sdb.c b/nemu/src/monitor/sdb/sdb.c deleted file mode 100644 index 3b56172..0000000 --- a/nemu/src/monitor/sdb/sdb.c +++ /dev/null @@ -1,361 +0,0 @@ -/*************************************************************************************** - * Copyright (c) 2014-2022 Zihao Yu, Nanjing University - * - * NEMU is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan - *PSL v2. You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY - *KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO - *NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * - * See the Mulan PSL v2 for more details. - ***************************************************************************************/ - -#include "sdb.h" -#include "common.h" -#include "sys/types.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int is_batch_mode = false; - -// command handlers -static int cmd_help(char *args); -static int cmd_c(char *args); -static int cmd_p(char *args); -static int cmd_q(char *args); -static int cmd_w(char *args); -static int cmd_x(char *args); -static int cmd_si(char *args); -static int cmd_info(char *args); -static int cmd_info_r(char *args); -static int cmd_info_w(char *args); - -static struct CommandTable { - const char *name; - const char *description; - int (*handler)(char *); - struct CommandTable *subcommand; - int nr_subcommand; -} cmd_info_table[] = - { - {"r", "List all registers and their contents", cmd_info_r, NULL, 0}, - {"w", "Status of specified watchpoints", cmd_info_w, NULL, 0}, -}, - cmd_table[] = { - {"help", "Display information about all supported commands", cmd_help, - NULL, 0}, - {"c", "Continue the execution of the program", cmd_c, NULL, 0}, - {"p", "Print expression result", cmd_p, NULL, 0}, - {"q", "Exit NEMU", cmd_q, NULL, 0}, - {"x", "Examine content of physical memory address", cmd_x, NULL, 0}, - {"w", "Break when expression is changed", cmd_w, NULL, 0}, - {"si", "Execute next [n] program line", cmd_si, NULL, 0}, - {"info", "Print information of registers or watchpoints", cmd_info, - cmd_info_table, ARRLEN(cmd_info_table)}, -}; - -#define NR_CMD ARRLEN(cmd_table) - -void init_regex(); -void init_wp_pool(); - -/* We use the `readline' library to provide more flexibility to read from stdin. - */ -static char *rl_gets() { - static char *line_read = NULL; - - if (line_read) { - free(line_read); - line_read = NULL; - } - - line_read = readline("\e[1;34m(nemu)\e[0m "); - - if (line_read && *line_read) { - add_history(line_read); - } - - return line_read; -} - -/* Extract Integer from a string. Can handle hex, binary and decimal numbers. - * Print error if meet any error. - * Return `UINTMAX_MAX` if the string is invalid or number exceed the limit of - * uint. - */ -static word_t parse_uint(const char *arg, bool *success) { - if (arg == NULL) { - puts("Invalid uint argument."); - *success = false; - return 0; - } - int base = 10; - int token_length = strnlen(arg, 34); - if (token_length > 2) { - if (arg[0] == '0' && (arg[1] == 'b' || arg[1] == 'B')) { - base = 2; - arg = arg + 2; - } else if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) { - base = 16; - arg = arg + 2; - } - } - char *endptr; - uintmax_t n = strtoumax(arg, &endptr, base); - if (errno == ERANGE || n > WORD_T_MAX) { - printf("%s exceed the limit of uint\n", arg); - *success = false; - return 0; - } else if (arg == endptr) { - puts("Invalid uint argument."); - *success = false; - return 0; - } else if (n > WORD_T_MAX) { - *success = false; - return WORD_T_MAX; - } else { - *success = true; - return n; - } -} - -word_t parse_expr(const char *arg, bool *success) { - if (arg == NULL) { - puts("Invalid expr argument."); - *success = false; - return 0; - } else { - word_t res; - yy_scan_string(arg); - *success = !yyparse(&res); - yylex_destroy(); - return res; - } -} - -static int cmd_c(char *args) { - cpu_exec(-1); - return 0; -} - -static int cmd_p(char *args) { - char *arg = strtok(NULL, ""); - bool res = false; - - word_t result = parse_expr(arg, &res); - if (!res) - goto wrong_usage; - printf("%s: %u\n", arg, result); - return 0; - -wrong_usage: - printf("Invalid argument for command p: %s\n", arg); - printf("Usage: p [EXPR: ]\n"); - return 0; -} - -static int cmd_q(char *args) { - nemu_state.state = NEMU_QUIT; - return -1; -} - -/* Single stepping - * : execute step - */ -static int cmd_si(char *args) { - char *arg = strtok(NULL, " "); - if (arg == NULL) { - cpu_exec(1); - } else { - bool res = false; - word_t n = parse_uint(arg, &res); - if (!res) - goto wrong_usage; - cpu_exec(n); - } - return 0; - -wrong_usage: - printf("Invalid argument for command si: %s\n", args); - printf("Usage: si [N: uint]\n"); - return 0; -} - -static int cmd_info_r(char *args) { - isa_reg_display(); - return 0; -} - -static int cmd_info_w(char *args) { - printf("Not implemented"); - return 0; -} - -static int cmd_w(char *args) { - char *expr = strtok(NULL, ""); - wp_add(expr); - return 0; -} - -static int cmd_x(char *args) { - char *arg = strtok(NULL, " "); - bool res = false; - word_t n = parse_uint(arg, &res); - if (!res) - goto wrong_usage; - // No deliminter here, just pass all the remain argument to `parse_expr()` - arg = strtok(NULL, ""); - word_t start_addr = parse_expr(arg, &res); - if (!res) - goto wrong_usage; - start_addr = start_addr & ~(WORD_BYTES - 1); - for (vaddr_t vaddr = start_addr; vaddr < start_addr + n; vaddr += WORD_BYTES) { - word_t value = vaddr_read(vaddr, WORD_BYTES); - printf("\e[1;34m" FMT_PADDR "\e[0m" - " " FMT_WORD "\n", - vaddr, value); - } - return 0; - -wrong_usage: - printf("Invalid argument for command x: %s\n", arg); - printf("Usage: x [N: uint] [EXPR: ]\n"); - return 0; -} - -static int cmd_info(char *args) { - char *arg = strtok(NULL, " "); - int i; - if (arg == NULL) { - goto wrong_usage; - return 0; - } - for (i = 0; i < ARRLEN(cmd_info_table); i++) { - if (strcmp(arg, cmd_info_table[i].name) == 0) { - cmd_info_table[i].handler(args); - return 0; - } - } - -wrong_usage: - printf("Invalid argument for command info: %s\n", args); - printf("Usage: info [r | w]\n"); - return 0; -} - -static int cmd_help_print(char *args, struct CommandTable *cur_cmd_table, - int cur_nr_cmd) { - int i; - char *arg = strtok(NULL, " "); - if (arg == NULL) { - return -1; - } else { - for (i = 0; i < cur_nr_cmd; i++) { - if (strcmp(arg, cur_cmd_table[i].name) == 0) { - printf("%s ", cur_cmd_table[i].name); - if (cmd_help_print(arg, cur_cmd_table[i].subcommand, - cur_cmd_table[i].nr_subcommand) == -1) { - printf("-- %s\n", cur_cmd_table[i].description); - } - return 0; - } - } - return -1; - } -} - -static int cmd_help(char *args) { - /* extract the first argument */ - char *arg = strtok(NULL, " "); - int i; - - if (arg == NULL) { - /* no argument given */ - for (i = 0; i < NR_CMD; i++) { - printf("%s -- %s\n", cmd_table[i].name, cmd_table[i].description); - } - } else { - for (i = 0; i < NR_CMD; i++) { - if (strcmp(arg, cmd_table[i].name) == 0) { - printf("%s ", cmd_table[i].name); - if (cmd_help_print(args, cmd_table[i].subcommand, - cmd_table[i].nr_subcommand) == -1) { - printf("-- %s\n", cmd_table[i].description); - // Print available subcommands - for (int j = 0; j < cmd_table[i].nr_subcommand; j++) { - struct CommandTable *sub_cmd_table = cmd_table[i].subcommand; - printf(" > %s -- %s\n", sub_cmd_table[j].name, - sub_cmd_table[j].description); - } - } - return 0; - } - } - printf("Unknown command '%s'\n", arg); - } - return 0; -} - -void sdb_set_batch_mode() { is_batch_mode = true; } - -void sdb_mainloop() { - if (is_batch_mode) { - cmd_c(NULL); - return; - } - - for (char *str; (str = rl_gets()) != NULL;) { - char *str_end = str + strlen(str); - - /* extract the first token as the command */ - char *cmd = strtok(str, " "); - if (cmd == NULL) { - continue; - } - - /* treat the remaining string as the arguments, - * which may need further parsing - */ - char *args = cmd + strlen(cmd) + 1; - if (args >= str_end) { - args = NULL; - } - -#ifdef CONFIG_DEVICE - extern void sdl_clear_event_queue(); - sdl_clear_event_queue(); -#endif - - int i; - for (i = 0; i < NR_CMD; i++) { - if (strcmp(cmd, cmd_table[i].name) == 0) { - if (cmd_table[i].handler(args) < 0) { - return; - } - break; - } - } - - if (i == NR_CMD) { - printf("Unknown command '%s'\n", cmd); - } - } -} - -void init_sdb() { - // /* Compile the regular expressions. */ - // init_regex(); - - /* Initialize the watchpoint pool. */ - init_wp_pool(); -} diff --git a/nemu/src/monitor/sdb/sdb.h b/nemu/src/monitor/sdb/sdb.h deleted file mode 100644 index 883f1b6..0000000 --- a/nemu/src/monitor/sdb/sdb.h +++ /dev/null @@ -1,25 +0,0 @@ -/*************************************************************************************** -* Copyright (c) 2014-2022 Zihao Yu, Nanjing University -* -* NEMU is licensed under Mulan PSL v2. -* You can use this software according to the terms and conditions of the Mulan PSL v2. -* You may obtain a copy of Mulan PSL v2 at: -* http://license.coscl.org.cn/MulanPSL2 -* -* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -* -* See the Mulan PSL v2 for more details. -***************************************************************************************/ - -#ifndef __SDB_H__ -#define __SDB_H__ - -#include - -word_t parse_expr(const char *arg, bool *success); -int wp_add(char * expr); -int wp_remove_by_number(int number); - -#endif diff --git a/nemu/src/monitor/sdb/watchpoint.c b/nemu/src/monitor/sdb/watchpoint.c deleted file mode 100644 index aa48d78..0000000 --- a/nemu/src/monitor/sdb/watchpoint.c +++ /dev/null @@ -1,150 +0,0 @@ -/*************************************************************************************** - * Copyright (c) 2014-2022 Zihao Yu, Nanjing University - * - * NEMU is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan - *PSL v2. You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY - *KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO - *NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * - * See the Mulan PSL v2 for more details. - ***************************************************************************************/ - -#include "sdb.h" -#include -#include - -#define NR_WP 32 - -typedef struct watchpoint { - int NO; - struct watchpoint *next; - word_t val; - char *expr; -} WP; - -static WP wp_pool[NR_WP] = {}; -static WP *head = NULL, *tail = NULL, *free_ = NULL; -static int wp_count = 0; - -void init_wp_pool() { - int i; - for (i = 0; i < NR_WP; i++) { - wp_pool[i].NO = i; - wp_pool[i].next = (i == NR_WP - 1 ? NULL : &wp_pool[i + 1]); - } - - head = NULL; - free_ = wp_pool; -} - -static WP *wp_new() { - if (free_ == NULL) { - Error("wp_pool: Watchpoint pool not initialized or is full."); - return NULL; - } - - WP *ret = free_; - free_ = free_->next; - - ret->NO = 0; - ret->next = NULL; - return ret; -} - -static void wp_delete(WP *wp) { - Assert(wp, "Failed to delete watchpoint from pool."); - wp->next = free_; - free_ = wp; -} - -int wp_add(char * expr) { - WP *wp = wp_new(); - if (wp == NULL) { - Error("watchpoint: Failed to add watchpoint, pool is full."); - goto failed_create; - } - - wp->NO = wp_count++; - if (tail == NULL) { - head = wp; - tail = wp; - } else { - tail->next = wp; - tail = wp; - } - - bool success = false; - wp->val = parse_expr(expr, &success); - if (!success) { - Error("Failed to parse given expression `%s`", expr); - goto failed_create; - } - - int len = strlen(expr); - wp->expr = malloc((len + 1) * sizeof(char)); - if (wp->expr == NULL) { - Error("Failed to allocate memory for expression"); - goto failed_create; - } - strncpy(wp->expr, expr, len + 1); - wp->expr[len] = '\0'; - return 0; - -failed_create: - wp_delete(wp); - return 1; -} - -int wp_remove_by_number(int number) { - WP *target_prev; - // Find previous node of target number - for (target_prev = head; target_prev != NULL && target_prev->next->NO != number; target_prev = target_prev->next) ; - if (target_prev == NULL) { - Error("Watchpoint not found, you can check current watchpoints with `info w`"); - return 1; - } - WP *target = target_prev->next; - target_prev->next = target->next; - if (target == head) { - head = target->next; - } else if (target == tail) { - tail = target_prev; - } - wp_delete(target); - return 0; -} - -static bool wp_check_change(WP* wp) { - bool success = false; - word_t result; - - result = parse_expr(wp->expr, &success); - if (!success) { - panic("Failed to evaluate expression `%s`", wp->expr); - } - if (result != wp->val) { - wp->val = result; - return true; - } - return false; -} - -/* - Check if watchpoint value changed after execution -*/ -bool wp_eval_all() { - WP *wp; - bool value_change = false; - for (wp = head; wp != NULL; wp = wp->next) { - int prev_val = wp->val; - if (wp_check_change(wp)) { - printf("Watchpoint %d: %s\n %u -> %u\n", wp->NO, wp->expr, prev_val, wp->val); - value_change = true; - } - } - return value_change; -}