Compare commits
3 commits
ebece52aec
...
504d270947
Author | SHA1 | Date | |
---|---|---|---|
504d270947 | |||
2675a647b0 | |||
4aa536e1ef |
17 changed files with 389 additions and 57 deletions
2
nemu/.gitignore
vendored
2
nemu/.gitignore
vendored
|
@ -7,6 +7,8 @@ build/
|
|||
.config
|
||||
.config.old
|
||||
.envrc
|
||||
.metals/
|
||||
.vscode/
|
||||
compile_commands.json
|
||||
|
||||
### C ###
|
||||
|
|
|
@ -48,6 +48,7 @@ CFLAGS_BUILD += $(if $(CONFIG_CC_ASAN),-fsanitize=address,)
|
|||
CFLAGS_TRACE += -DITRACE_COND=$(if $(CONFIG_ITRACE_COND),$(call remove_quote,$(CONFIG_ITRACE_COND)),true)
|
||||
CFLAGS += $(CFLAGS_BUILD) $(CFLAGS_TRACE) -D__GUEST_ISA__=$(GUEST_ISA)
|
||||
LDFLAGS += $(CFLAGS_BUILD)
|
||||
INC_PATH += $(NEMU_HOME)/src/isa/$(GUEST_ISA)/local-include
|
||||
|
||||
# Include rules for menuconfig
|
||||
include $(NEMU_HOME)/scripts/config.mk
|
||||
|
@ -60,8 +61,6 @@ else
|
|||
include $(NEMU_HOME)/scripts/native.mk
|
||||
endif
|
||||
|
||||
.PHONY: test
|
||||
|
||||
include $(NEMU_HOME)/tests/Makefile
|
||||
all-tests: TEST_OBJS = $(filter-out $(OBJ_DIR)/src/nemu-main.o, $(OBJS))
|
||||
all-tests: CFLAGS += $(shell pkg-config --cflags check)
|
||||
|
@ -70,3 +69,5 @@ all-tests: $(TEST_SRCS:%.c=$(OBJ_DIR)/%)
|
|||
|
||||
test: all-tests
|
||||
@$(OBJ_DIR)/tests/expr_test
|
||||
|
||||
.PHONY: test
|
74
nemu/configs/rv32_defconfig
Normal file
74
nemu/configs/rv32_defconfig
Normal file
|
@ -0,0 +1,74 @@
|
|||
#
|
||||
# Automatically generated file; DO NOT EDIT.
|
||||
# NEMU Configuration Menu
|
||||
#
|
||||
# CONFIG_ISA_x86 is not set
|
||||
# CONFIG_ISA_mips32 is not set
|
||||
CONFIG_ISA_riscv=y
|
||||
# CONFIG_ISA_loongarch32r is not set
|
||||
CONFIG_ISA="riscv32"
|
||||
|
||||
#
|
||||
# ISA-dependent Options for riscv
|
||||
#
|
||||
# CONFIG_RV64 is not set
|
||||
# CONFIG_RVE is not set
|
||||
# end of ISA-dependent Options for riscv
|
||||
|
||||
CONFIG_ENGINE_INTERPRETER=y
|
||||
CONFIG_ENGINE="interpreter"
|
||||
CONFIG_MODE_SYSTEM=y
|
||||
CONFIG_TARGET_NATIVE_ELF=y
|
||||
# CONFIG_TARGET_SHARE is not set
|
||||
# CONFIG_TARGET_AM is not set
|
||||
|
||||
#
|
||||
# Build Options
|
||||
#
|
||||
CONFIG_CC_GCC=y
|
||||
# CONFIG_CC_GPP is not set
|
||||
# CONFIG_CC_CLANG is not set
|
||||
CONFIG_CC="gcc"
|
||||
# CONFIG_CC_O0 is not set
|
||||
# CONFIG_CC_O1 is not set
|
||||
CONFIG_CC_O2=y
|
||||
# CONFIG_CC_O3 is not set
|
||||
CONFIG_CC_OPT="-O2"
|
||||
# CONFIG_CC_LTO is not set
|
||||
# CONFIG_CC_DEBUG is not set
|
||||
CONFIG_CC_ASAN=y
|
||||
# end of Build Options
|
||||
|
||||
#
|
||||
# Testing and Debugging
|
||||
#
|
||||
CONFIG_TRACE=y
|
||||
CONFIG_TRACE_START=0
|
||||
CONFIG_TRACE_END=10000
|
||||
CONFIG_ITRACE=y
|
||||
CONFIG_ITRACE_COND="true"
|
||||
# CONFIG_DIFFTEST is not set
|
||||
CONFIG_DIFFTEST_REF_PATH="none"
|
||||
CONFIG_DIFFTEST_REF_NAME="none"
|
||||
# end of Testing and Debugging
|
||||
|
||||
#
|
||||
# Memory Configuration
|
||||
#
|
||||
CONFIG_MBASE=0x80000000
|
||||
CONFIG_MSIZE=0x8000000
|
||||
CONFIG_PC_RESET_OFFSET=0
|
||||
# CONFIG_PMEM_MALLOC is not set
|
||||
CONFIG_PMEM_GARRAY=y
|
||||
CONFIG_MEM_RANDOM=y
|
||||
# end of Memory Configuration
|
||||
|
||||
# CONFIG_DEVICE is not set
|
||||
|
||||
#
|
||||
# Miscellaneous
|
||||
#
|
||||
CONFIG_TIMER_GETTIMEOFDAY=y
|
||||
# CONFIG_TIMER_CLOCK_GETTIME is not set
|
||||
CONFIG_RT_CHECK=y
|
||||
# end of Miscellaneous
|
|
@ -50,6 +50,21 @@
|
|||
libllvm
|
||||
];
|
||||
|
||||
configurePhase = ''
|
||||
echo NEMU_HOME=$NEMU_HOME
|
||||
echo pwd=$(pwd)
|
||||
mkdir -p $(pwd)/kconfig
|
||||
WORK_DIR=$(pwd) obj=$(pwd)/kconfig make --trace -e -f scripts/config.mk WORK_DIR=$(pwd) obj=$(pwd)/kconfig rv32_defconfig
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
BUILD_DIR=$out make install
|
||||
'';
|
||||
|
||||
checkPhase = ''
|
||||
BUILD_DIR=$out make test
|
||||
'';
|
||||
|
||||
NEMU_HOME = src;
|
||||
|
||||
meta = with lib; {
|
||||
|
|
|
@ -21,7 +21,15 @@
|
|||
#include <utils.h>
|
||||
|
||||
#define Log(format, ...) \
|
||||
_Log(ANSI_FMT("[%s:%d %s] " format, ANSI_FG_BLUE) "\n", \
|
||||
_Log(ANSI_FMT("[INFO] %s:%d %s() ", ANSI_FG_BLUE) format "\n", \
|
||||
__FILE__, __LINE__, __func__, ## __VA_ARGS__)
|
||||
|
||||
#define Warning(format, ...) \
|
||||
_Log(ANSI_FMT("[WARNING] %s:%d %s() ", ANSI_FG_YELLOW) format "\n", \
|
||||
__FILE__, __LINE__, __func__, ## __VA_ARGS__)
|
||||
|
||||
#define Error(format, ...) \
|
||||
_Log(ANSI_FMT("[ERROR] %s:%d %s() ", ANSI_FG_RED) format "\n", \
|
||||
__FILE__, __LINE__, __func__, ## __VA_ARGS__)
|
||||
|
||||
#define Assert(cond, format, ...) \
|
||||
|
|
|
@ -10,7 +10,7 @@ endif
|
|||
WORK_DIR = $(shell pwd)
|
||||
BUILD_DIR = $(WORK_DIR)/build
|
||||
|
||||
INC_PATH := $(WORK_DIR)/include $(INC_PATH)
|
||||
INC_PATH := $(WORK_DIR)/include $(BUILD_DIR)/include $(INC_PATH)
|
||||
OBJ_DIR = $(BUILD_DIR)/obj-$(NAME)$(SO)
|
||||
BINARY = $(BUILD_DIR)/$(NAME)$(SO)
|
||||
|
||||
|
@ -42,13 +42,13 @@ $(OBJ_DIR)/%.o: %.cc
|
|||
|
||||
$(OBJ_DIR)/%.tag.c: %.y
|
||||
@echo + YACC $<
|
||||
@mkdir -p $(dir $@)
|
||||
@$(YACC) $(YFLAGS) --header=$(<:.y=.h) -o $@ $<
|
||||
@mkdir -p $(dir $@) $(BUILD_DIR)/include
|
||||
@$(YACC) $(YFLAGS) --header=$(BUILD_DIR)/include/$(notdir $(<:.y=.h)) -o $@ $<
|
||||
|
||||
$(OBJ_DIR)/%.yy.c: %.l $(OBJ_DIR)/%.tag.c
|
||||
@echo + LEX $<
|
||||
@mkdir -p $(dir $@)
|
||||
@$(LEX) $(LFLAGS) --header=$(<:.l=_lex.h) -o $@ $<
|
||||
@mkdir -p $(dir $@) $(BUILD_DIR)/include
|
||||
@$(LEX) $(LFLAGS) --header=$(BUILD_DIR)/include/$(notdir $(<:.l=_lex.h)) -o $@ $<
|
||||
|
||||
$(OBJ_DIR)/%.tag.o: $(OBJ_DIR)/%.tag.c
|
||||
@echo + CC $<
|
||||
|
@ -67,7 +67,7 @@ $(OBJ_DIR)/%.yy.o: $(OBJ_DIR)/%.yy.c
|
|||
|
||||
# Some convenient rules
|
||||
|
||||
.PHONY: app clean
|
||||
.PHONY: app install clean
|
||||
|
||||
app: $(BINARY)
|
||||
|
||||
|
@ -75,5 +75,9 @@ $(BINARY):: $(OBJS) $(ARCHIVES)
|
|||
@echo + LD $@
|
||||
@$(LD) -o $@ $(OBJS) $(LDFLAGS) $(ARCHIVES) $(LIBS)
|
||||
|
||||
install: $(BINARY)
|
||||
@mkdir -p $(PREFIX)/bin
|
||||
@cp $(BINARY) $(PREFIX)/bin/
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILD_DIR)
|
||||
|
|
|
@ -31,6 +31,7 @@ static uint64_t g_timer = 0; // unit: us
|
|||
static bool g_print_step = false;
|
||||
|
||||
void device_update();
|
||||
bool wp_eval_all();
|
||||
|
||||
static void trace_and_difftest(Decode *_this, vaddr_t dnpc) {
|
||||
#ifdef CONFIG_ITRACE_COND
|
||||
|
@ -77,6 +78,10 @@ static void execute(uint64_t n) {
|
|||
exec_once(&s, cpu.pc);
|
||||
g_nr_guest_inst ++;
|
||||
trace_and_difftest(&s, cpu.pc);
|
||||
if (wp_eval_all()) {
|
||||
puts(s.logbuf);
|
||||
break;
|
||||
}
|
||||
if (nemu_state.state != NEMU_RUNNING) break;
|
||||
IFDEF(CONFIG_DEVICE, device_update());
|
||||
}
|
||||
|
|
|
@ -36,5 +36,16 @@ void isa_reg_display() {
|
|||
}
|
||||
|
||||
word_t isa_reg_str2val(const char *s, bool *success) {
|
||||
assert(s);
|
||||
int i;
|
||||
for (i = 0; i < 32 && strcmp(s, regs[i]) != 0; i++)
|
||||
;
|
||||
|
||||
if (i == 32) {
|
||||
*success = false;
|
||||
return 0;
|
||||
}
|
||||
*success = true;
|
||||
|
||||
return gpr(i);
|
||||
}
|
||||
|
|
2
nemu/src/monitor/sdb/.gitignore
vendored
2
nemu/src/monitor/sdb/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
addrexp_lex.h
|
||||
addrexp.h
|
|
@ -1,5 +1,8 @@
|
|||
%{
|
||||
#include <isa.h>
|
||||
#include <addrexp.h>
|
||||
static bool success = false;
|
||||
void yyerror(word_t *result, const char *err);
|
||||
%}
|
||||
%option noyywrap
|
||||
|
||||
|
@ -7,7 +10,15 @@
|
|||
|
||||
0[xX][0-9a-fA-F]+ { yylval = strtoul(yytext, NULL, 16); return HEX_NUMBER; }
|
||||
[0-9]+ { yylval = strtoul(yytext, NULL, 10); return NUMBER; }
|
||||
[+\-*/()] { return *yytext; }
|
||||
$[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); }
|
||||
. { printf("Unexpected character: %s\n", yytext); return YYerror; }
|
||||
%%
|
||||
|
|
|
@ -1,16 +1,25 @@
|
|||
%{
|
||||
%code requires {
|
||||
#include <common.h>
|
||||
#include <memory/vaddr.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
extern int yylex(void);
|
||||
void yyerror(uint32_t *result, const char *err) {
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
}
|
||||
%{
|
||||
#include <common.h>
|
||||
#include <isa.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
void yyerror(word_t *result, const char *err) {
|
||||
Error("%s", err);
|
||||
}
|
||||
%}
|
||||
|
||||
%token NUMBER HEX_NUMBER
|
||||
%token REGISTER
|
||||
%locations
|
||||
%start input
|
||||
%define api.value.type { uint32_t }
|
||||
%define api.value.type { word_t }
|
||||
%parse-param { uint32_t *result }
|
||||
%left '-' '+'
|
||||
%left '*' '/'
|
||||
|
@ -22,6 +31,12 @@ input
|
|||
|
||||
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; }
|
||||
|
@ -33,10 +48,12 @@ expression
|
|||
$$ = $1 / $3;
|
||||
}
|
||||
| '-' number { $$ = -$2; }
|
||||
| '*' expression { $$ = vaddr_read($2, WORD_BYTES); }
|
||||
| '(' expression ')' { $$ = $2; }
|
||||
|
||||
number
|
||||
: NUMBER
|
||||
: REGISTER
|
||||
| NUMBER
|
||||
| HEX_NUMBER
|
||||
|
||||
%%
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
SRCS-y += src/monitor/sdb/addrexp.tag.c src/monitor/sdb/addrexp.yy.c
|
||||
INC_PATH += src/monitor/sdb
|
||||
LFLAGS += -DYY_NO_UNPUT -DYY_NO_INPUT
|
||||
|
|
|
@ -16,22 +16,24 @@
|
|||
#include "sdb.h"
|
||||
#include "common.h"
|
||||
#include "sys/types.h"
|
||||
#include <addrexp.h>
|
||||
#include <addrexp_lex.h>
|
||||
#include <cpu/cpu.h>
|
||||
#include <errno.h>
|
||||
#include <isa.h>
|
||||
#include <memory/paddr.h>
|
||||
#include <memory/vaddr.h>
|
||||
#include <readline/history.h>
|
||||
#include <readline/readline.h>
|
||||
#include <stdint.h>
|
||||
#include <addrexp.h>
|
||||
#include <addrexp_lex.h>
|
||||
|
||||
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);
|
||||
|
@ -53,8 +55,10 @@ static struct CommandTable {
|
|||
{"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)},
|
||||
|
@ -125,21 +129,17 @@ static word_t parse_uint(const char *arg, bool *success) {
|
|||
}
|
||||
}
|
||||
|
||||
static paddr_t parse_expr(const char *arg, bool *success) {
|
||||
word_t parse_expr(const char *arg, bool *success) {
|
||||
if (arg == NULL) {
|
||||
puts("Invalid expr argument.");
|
||||
*success = false;
|
||||
return 0;
|
||||
} else {
|
||||
// bool res = false;
|
||||
// FIXME: We cannot use `parse_uint` here, it accept `-1234` as input
|
||||
// paddr_t addr = parse_uint(arg, &res);
|
||||
// *success = res;
|
||||
paddr_t addr;
|
||||
word_t res;
|
||||
yy_scan_string(arg);
|
||||
*success = !yyparse(&addr);
|
||||
*success = !yyparse(&res);
|
||||
yylex_destroy();
|
||||
return addr;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,6 +148,22 @@ static int cmd_c(char *args) {
|
|||
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: <expr>]\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_q(char *args) {
|
||||
nemu_state.state = NEMU_QUIT;
|
||||
return -1;
|
||||
|
@ -185,6 +201,12 @@ static int cmd_info_w(char *args) {
|
|||
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;
|
||||
|
@ -193,15 +215,15 @@ static int cmd_x(char *args) {
|
|||
goto wrong_usage;
|
||||
// No deliminter here, just pass all the remain argument to `parse_expr()`
|
||||
arg = strtok(NULL, "");
|
||||
word_t addr = parse_expr(arg, &res);
|
||||
word_t start_addr = parse_expr(arg, &res);
|
||||
if (!res)
|
||||
goto wrong_usage;
|
||||
addr = addr & ~(WORD_BYTES - 1);
|
||||
for (paddr_t paddr = addr; paddr < addr + n; paddr += WORD_BYTES) {
|
||||
word_t value = paddr_read(addr, WORD_BYTES);
|
||||
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",
|
||||
paddr, value);
|
||||
vaddr, value);
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -18,4 +18,8 @@
|
|||
|
||||
#include <common.h>
|
||||
|
||||
word_t parse_expr(const char *arg, bool *success);
|
||||
int wp_add(char * expr);
|
||||
int wp_remove_by_number(int number);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,36 +1,38 @@
|
|||
/***************************************************************************************
|
||||
* 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.
|
||||
***************************************************************************************/
|
||||
* 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 <stdio.h>
|
||||
|
||||
#define NR_WP 32
|
||||
|
||||
typedef struct watchpoint {
|
||||
int NO;
|
||||
struct watchpoint *next;
|
||||
|
||||
/* TODO: Add more members if necessary */
|
||||
|
||||
word_t val;
|
||||
char *expr;
|
||||
} WP;
|
||||
|
||||
static WP wp_pool[NR_WP] = {};
|
||||
static WP *head = NULL, *free_ = NULL;
|
||||
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 ++) {
|
||||
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]);
|
||||
}
|
||||
|
@ -39,5 +41,110 @@ void init_wp_pool() {
|
|||
free_ = wp_pool;
|
||||
}
|
||||
|
||||
/* TODO: Implement the functionality of watchpoint */
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
TEST_SRCS += tests/expr_test.c
|
||||
YACC = bison
|
||||
|
||||
$(OBJ_DIR)/%: %.c $(TEST_OBJS) app
|
||||
@mkdir -p $(dir $@)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "macro.h"
|
||||
#include "sys/types.h"
|
||||
#include "unistd.h"
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <check.h>
|
||||
#include <math.h>
|
||||
|
@ -9,6 +10,8 @@
|
|||
#include <time.h>
|
||||
#include <addrexp.h>
|
||||
#include <addrexp_lex.h>
|
||||
#include <isa.h>
|
||||
#include <reg.h>
|
||||
|
||||
char buf[65536] = {}, ref_buf[65536] = {};
|
||||
static char code_buf[65536 + 128] = {}; // a little larger than `buf`
|
||||
|
@ -147,6 +150,12 @@ struct {
|
|||
{"-0x1", 0xFFFFFFFFU},
|
||||
{"0--1", 0x1},
|
||||
{"0--0x1", 0x1},
|
||||
}, reg_exprs[] = {
|
||||
{"$ra", 0x1},
|
||||
{"0x2 + 4*-$a7", 0xFFFFFFBEU},
|
||||
{"0x1831/$gp + 13", 2077U},
|
||||
{"$$0 == 123", 0},
|
||||
{"$$0 == 0", 1},
|
||||
};
|
||||
START_TEST(test_expr_negative_operand) {
|
||||
yy_scan_string(exprs[_i].expr);
|
||||
|
@ -160,6 +169,47 @@ START_TEST(test_expr_negative_operand) {
|
|||
}
|
||||
END_TEST
|
||||
|
||||
extern const char *regs[];
|
||||
START_TEST(test_expr_plain_register) {
|
||||
int i, j, result;
|
||||
char buf[30] = {};
|
||||
uint32_t value;
|
||||
// NOTE: need to fix this if want to support more arch
|
||||
buf[0] = '$';
|
||||
for (i = 0; i < 32; i++) {
|
||||
ck_assert(strncpy(buf + 1, regs[i], 10) != NULL);
|
||||
gpr(i) = i;
|
||||
yy_scan_string(buf);
|
||||
result = yyparse(&value);
|
||||
yylex_destroy();
|
||||
ck_assert_msg(result == 0, "expr = %s\n", buf);
|
||||
|
||||
ck_assert(value == i);
|
||||
for (j = 1; j < 10; j++) {
|
||||
buf[j] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_expr_register) {
|
||||
int i;
|
||||
uint32_t value;
|
||||
for (i = 0; i < 32; i++) {
|
||||
gpr(i) = i;
|
||||
}
|
||||
|
||||
yy_scan_string(reg_exprs[_i].expr);
|
||||
ck_assert(!yyparse(&value));
|
||||
yylex_destroy();
|
||||
|
||||
ck_assert_msg(value == reg_exprs[_i].reference,
|
||||
"\n\texpr = %s\n\t(addr = %u) != (reference = %u)\n", reg_exprs[_i].expr,
|
||||
value, reg_exprs[_i].reference);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *expr_suite(void) {
|
||||
Suite *s;
|
||||
TCase *tc_core;
|
||||
|
@ -170,6 +220,9 @@ Suite *expr_suite(void) {
|
|||
tcase_add_loop_test(tc_core, test_expr_random_100, 0, 20);
|
||||
tcase_add_loop_test(tc_core, test_expr_negative_operand, 0,
|
||||
sizeof(exprs) / sizeof(exprs[0]));
|
||||
tcase_add_loop_test(tc_core, test_expr_register, 0,
|
||||
sizeof(reg_exprs) / sizeof(reg_exprs[0]));
|
||||
tcase_add_test(tc_core, test_expr_plain_register);
|
||||
suite_add_tcase(s, tc_core);
|
||||
|
||||
return s;
|
||||
|
|
Loading…
Reference in a new issue