mirror of
https://github.com/ringtailsoftware/uvm32.git
synced 2026-06-05 22:43:39 +00:00
Test metering, prove that system can resume after a hang error with a range of meter sizes
This commit is contained in:
parent
9d9e7542fc
commit
ac1975a820
14 changed files with 222 additions and 53 deletions
|
|
@ -171,6 +171,7 @@ int main(int argc, char *argv[]) {
|
||||||
printf("UVM32_EVT_ERR '%s' (%d)\n", evt.data.err.errstr, (int)evt.data.err.errcode);
|
printf("UVM32_EVT_ERR '%s' (%d)\n", evt.data.err.errstr, (int)evt.data.err.errcode);
|
||||||
if (evt.data.err.errcode == UVM32_ERR_HUNG) {
|
if (evt.data.err.errcode == UVM32_ERR_HUNG) {
|
||||||
printf("VM may have hung, increase max_instrs_per_run\n");
|
printf("VM may have hung, increase max_instrs_per_run\n");
|
||||||
|
uvm32_clearError(&vmst); // allow to continue
|
||||||
} else {
|
} else {
|
||||||
isrunning = false;
|
isrunning = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
TESTS = example_1 stackoverflow custom_syscall
|
TESTS = \
|
||||||
|
basic_syscalls \
|
||||||
|
stackoverflow \
|
||||||
|
custom_syscall \
|
||||||
|
syscall_args \
|
||||||
|
meter
|
||||||
|
|
||||||
RUNCMD = $(foreach TEST,${TESTS},make -C ${TEST} &&)
|
RUNCMD = $(foreach TEST,${TESTS},make -C ${TEST} &&)
|
||||||
CLEANCMD = $(foreach TEST,${TESTS},make -C ${TEST} clean &&)
|
CLEANCMD = $(foreach TEST,${TESTS},make -C ${TEST} clean &&)
|
||||||
|
|
||||||
|
|
|
||||||
10
test/basic_syscalls/rom/rom.c
Normal file
10
test/basic_syscalls/rom/rom.c
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#include "uvm32_target.h"
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
println("Hello world");
|
||||||
|
print("Hello world");
|
||||||
|
printdec(42);
|
||||||
|
printhex(0xDEADBEEF);
|
||||||
|
putc('G');
|
||||||
|
}
|
||||||
|
|
||||||
56
test/basic_syscalls/test/tests.c
Normal file
56
test/basic_syscalls/test/tests.c
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include "unity.h"
|
||||||
|
#include "uvm32.h"
|
||||||
|
#include "../common/uvm32_common_custom.h"
|
||||||
|
|
||||||
|
#include "rom-header.h"
|
||||||
|
|
||||||
|
static uvm32_state_t vmst;
|
||||||
|
static uvm32_evt_t evt;
|
||||||
|
|
||||||
|
void setUp(void) {
|
||||||
|
// runs before each test
|
||||||
|
uvm32_init(&vmst);
|
||||||
|
uvm32_load(&vmst, rom_bin, rom_bin_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_syscalls(void) {
|
||||||
|
// check for println syscall
|
||||||
|
uvm32_run(&vmst, &evt, 1000);
|
||||||
|
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
|
||||||
|
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTLN);
|
||||||
|
TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG0), "Hello world"));
|
||||||
|
|
||||||
|
// check for print syscall
|
||||||
|
uvm32_run(&vmst, &evt, 1000);
|
||||||
|
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
|
||||||
|
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINT);
|
||||||
|
TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG0), "Hello world"));
|
||||||
|
|
||||||
|
// check for printdec syscall
|
||||||
|
uvm32_run(&vmst, &evt, 1000);
|
||||||
|
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
|
||||||
|
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTDEC);
|
||||||
|
TEST_ASSERT_EQUAL(42, uvm32_getval(&vmst, &evt, ARG0));
|
||||||
|
|
||||||
|
// check for printhex syscall
|
||||||
|
uvm32_run(&vmst, &evt, 1000);
|
||||||
|
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
|
||||||
|
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTHEX);
|
||||||
|
TEST_ASSERT_EQUAL(0xDEADBEEF, uvm32_getval(&vmst, &evt, ARG0));
|
||||||
|
|
||||||
|
// check for putc syscall
|
||||||
|
uvm32_run(&vmst, &evt, 1000);
|
||||||
|
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
|
||||||
|
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PUTC);
|
||||||
|
TEST_ASSERT_EQUAL('G', uvm32_getval(&vmst, &evt, ARG0));
|
||||||
|
|
||||||
|
// run vm to completion
|
||||||
|
uvm32_run(&vmst, &evt, 10000);
|
||||||
|
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
#include "uvm32_target.h"
|
|
||||||
|
|
||||||
void main(void) {
|
|
||||||
println("Hello world");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
#include <string.h>
|
|
||||||
#include "unity.h"
|
|
||||||
#include "uvm32.h"
|
|
||||||
#include "../common/uvm32_common_custom.h"
|
|
||||||
|
|
||||||
#include "rom-header.h"
|
|
||||||
|
|
||||||
static uvm32_state_t vmst;
|
|
||||||
static uvm32_evt_t evt;
|
|
||||||
|
|
||||||
void setUp(void) {
|
|
||||||
// runs before each test
|
|
||||||
uvm32_init(&vmst);
|
|
||||||
uvm32_load(&vmst, rom_bin, rom_bin_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void tearDown(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_helloworld1(void) {
|
|
||||||
// run the vm
|
|
||||||
uvm32_run(&vmst, &evt, 10000);
|
|
||||||
// check for println syscall
|
|
||||||
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
|
|
||||||
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTLN);
|
|
||||||
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
|
|
||||||
TEST_ASSERT_EQUAL(0, strcmp(str, "Hello world"));
|
|
||||||
// run vm to completion
|
|
||||||
uvm32_run(&vmst, &evt, 10000);
|
|
||||||
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
33
test/meter/Makefile
Normal file
33
test/meter/Makefile
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
C_COMPILER=gcc
|
||||||
|
|
||||||
|
UNITY_ROOT=../unity
|
||||||
|
|
||||||
|
CFLAGS=-std=c99
|
||||||
|
CFLAGS += -Wall
|
||||||
|
CFLAGS += -Werror
|
||||||
|
CFLAGS += -DUVM32_MEMORY_SIZE=16384
|
||||||
|
|
||||||
|
SUITE_NAME=tests
|
||||||
|
|
||||||
|
TARGET_BASE1=test1
|
||||||
|
TARGET1 = $(TARGET_BASE1)
|
||||||
|
SRC_FILES1=$(UNITY_ROOT)/src/unity.c test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c ../../uvm32/uvm32.c
|
||||||
|
INC_DIRS=-I$(UNITY_ROOT)/src -I../../uvm32/ -I../../common -Irom
|
||||||
|
|
||||||
|
.PHONY: rom
|
||||||
|
|
||||||
|
default: $(SRC_FILES1) rom
|
||||||
|
@$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) rom/rom-header.c -o $(TARGET1)
|
||||||
|
@ ./$(TARGET1)
|
||||||
|
|
||||||
|
rom:
|
||||||
|
@(cd rom && make)
|
||||||
|
|
||||||
|
test/test_runners/${SUITE_NAME}_Runner.c: test/${SUITE_NAME}.c
|
||||||
|
@mkdir -p test/test_runners
|
||||||
|
@ruby $(UNITY_ROOT)/auto/generate_test_runner.rb test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(TARGET1) test/test_runners
|
||||||
|
(cd rom && make clean)
|
||||||
|
|
||||||
14
test/meter/rom/Makefile
Normal file
14
test/meter/rom/Makefile
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
TOPDIR=../../../
|
||||||
|
PROJECT:=$(shell basename ${PWD})
|
||||||
|
SRCS=${PROJECT}.c ${TOPDIR}/apps/crt0.S
|
||||||
|
OPT=-O0 # don't let optimiser remove stall loop
|
||||||
|
all: all_common
|
||||||
|
@# Convert ROM to C file and header
|
||||||
|
@xxd -i ${PROJECT}.bin > ${PROJECT}-header.c
|
||||||
|
@echo "extern unsigned char ${PROJECT}_bin[]; extern int ${PROJECT}_bin_len;" > ${PROJECT}-header.h
|
||||||
|
|
||||||
|
test: test_common
|
||||||
|
clean: clean_common
|
||||||
|
rm -f ${PROJECT}-header.h ${PROJECT}-header.c
|
||||||
|
|
||||||
|
include ${TOPDIR}/apps/makefile.common
|
||||||
15
test/meter/rom/rom.c
Normal file
15
test/meter/rom/rom.c
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#include "uvm32_target.h"
|
||||||
|
|
||||||
|
void stall(void) {
|
||||||
|
volatile uint32_t i;
|
||||||
|
for (i=0;i<100;i++) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
for (int i=0;i<100;i++) {
|
||||||
|
stall();
|
||||||
|
printdec(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
58
test/meter/test/tests.c
Normal file
58
test/meter/test/tests.c
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include "unity.h"
|
||||||
|
#include "uvm32.h"
|
||||||
|
#include "../common/uvm32_common_custom.h"
|
||||||
|
|
||||||
|
#include "rom-header.h"
|
||||||
|
|
||||||
|
static uvm32_state_t vmst;
|
||||||
|
static uvm32_evt_t evt;
|
||||||
|
|
||||||
|
void setUp(void) {
|
||||||
|
// runs before each test
|
||||||
|
uvm32_init(&vmst);
|
||||||
|
uvm32_load(&vmst, rom_bin, rom_bin_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// run program until printdec() reaches 100
|
||||||
|
// every time it hangs, resume
|
||||||
|
// run in batches of num_instr
|
||||||
|
void metered_run(uint32_t num_instr) {
|
||||||
|
uint32_t expected = 0;
|
||||||
|
|
||||||
|
while(expected < 100) {
|
||||||
|
uvm32_run(&vmst, &evt, num_instr);
|
||||||
|
switch(evt.typ) {
|
||||||
|
case UVM32_EVT_SYSCALL: {
|
||||||
|
TEST_ASSERT_EQUAL(UVM32_SYSCALL_PRINTDEC, evt.data.syscall.code);
|
||||||
|
uint32_t val = uvm32_getval(&vmst, &evt, ARG0);
|
||||||
|
TEST_ASSERT_EQUAL(val, expected);
|
||||||
|
expected++;
|
||||||
|
} break;
|
||||||
|
case UVM32_EVT_ERR:
|
||||||
|
TEST_ASSERT_EQUAL(evt.data.err.errcode, UVM32_ERR_HUNG);
|
||||||
|
uvm32_clearError(&vmst); // clear the hung error so it can continue
|
||||||
|
break;
|
||||||
|
case UVM32_EVT_END:
|
||||||
|
TEST_ASSERT_EQUAL(0, 1); // trigger an assert, we didn't get to 100000 yet
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// run vm to completion
|
||||||
|
uvm32_run(&vmst, &evt, 10000);
|
||||||
|
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_meter(void) {
|
||||||
|
// test many different meter values, including 0 (which should run 1 instruction, as 0 is going to hang)
|
||||||
|
for (uint32_t i=0;i<1000;i++) {
|
||||||
|
uvm32_init(&vmst);
|
||||||
|
uvm32_load(&vmst, rom_bin, rom_bin_len);
|
||||||
|
metered_run(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -138,27 +138,33 @@ void uvm32_clearError(uvm32_state_t *vmst) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) {
|
uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) {
|
||||||
uint32_t num_instr = 0;
|
const uint32_t min_instrs = 1;
|
||||||
|
uint32_t orig_instr_meter = instr_meter;
|
||||||
|
|
||||||
|
if (instr_meter < min_instrs) {
|
||||||
|
instr_meter = min_instrs;
|
||||||
|
}
|
||||||
|
|
||||||
if (vmst->stack_canary != UVM32_NULL && *vmst->stack_canary != STACK_CANARY_VALUE) {
|
if (vmst->stack_canary != UVM32_NULL && *vmst->stack_canary != STACK_CANARY_VALUE) {
|
||||||
setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE);
|
setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE);
|
||||||
setup_err_evt(vmst, evt);
|
setup_err_evt(vmst, evt);
|
||||||
return num_instr;
|
return orig_instr_meter - instr_meter;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vmst->status != UVM32_STATUS_PAUSED) {
|
if (vmst->status != UVM32_STATUS_PAUSED) {
|
||||||
setStatusErr(vmst, UVM32_ERR_NOTREADY);
|
setStatusErr(vmst, UVM32_ERR_NOTREADY);
|
||||||
setup_err_evt(vmst, evt);
|
setup_err_evt(vmst, evt);
|
||||||
return num_instr;
|
return orig_instr_meter - instr_meter;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(vmst, UVM32_STATUS_RUNNING);
|
setStatus(vmst, UVM32_STATUS_RUNNING);
|
||||||
|
|
||||||
// run CPU until no longer in running state
|
// run CPU until no longer in running state
|
||||||
while(vmst->status == UVM32_STATUS_RUNNING) {
|
while(vmst->status == UVM32_STATUS_RUNNING && instr_meter > 0) {
|
||||||
uint64_t elapsedUs = 1;
|
uint64_t elapsedUs = 1;
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
ret = MiniRV32IMAStep(vmst, &vmst->core, vmst->memory, 0, elapsedUs, 1);
|
ret = MiniRV32IMAStep(vmst, &vmst->core, vmst->memory, 0, elapsedUs, 1);
|
||||||
|
instr_meter--;
|
||||||
|
|
||||||
switch(ret) {
|
switch(ret) {
|
||||||
case 0: // ok
|
case 0: // ok
|
||||||
|
|
@ -213,28 +219,25 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
num_instr++;
|
if (vmst->status == UVM32_STATUS_RUNNING && instr_meter == 0) {
|
||||||
|
// no syscall occurred, so we've hung
|
||||||
// check instruction meter, in case of hang/infinite loop
|
|
||||||
if (instr_meter-- == 0) {
|
|
||||||
setStatusErr(vmst, UVM32_ERR_HUNG);
|
setStatusErr(vmst, UVM32_ERR_HUNG);
|
||||||
setup_err_evt(vmst, evt);
|
setup_err_evt(vmst, evt);
|
||||||
return num_instr;
|
return orig_instr_meter - instr_meter;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (vmst->status == UVM32_STATUS_ENDED) {
|
if (vmst->status == UVM32_STATUS_ENDED) {
|
||||||
evt->typ = UVM32_EVT_END;
|
evt->typ = UVM32_EVT_END;
|
||||||
return num_instr;
|
return orig_instr_meter - instr_meter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// an event is ready
|
// an event is ready
|
||||||
if (vmst->status == UVM32_STATUS_PAUSED) {
|
if (vmst->status == UVM32_STATUS_PAUSED) {
|
||||||
// send back the built up event
|
// send back the built up event
|
||||||
UVM32_MEMCPY(evt, &vmst->ioevt, sizeof(uvm32_evt_t));
|
UVM32_MEMCPY(evt, &vmst->ioevt, sizeof(uvm32_evt_t));
|
||||||
return num_instr;
|
return orig_instr_meter - instr_meter;
|
||||||
} else {
|
} else {
|
||||||
if (vmst->status == UVM32_STATUS_ERROR) {
|
if (vmst->status == UVM32_STATUS_ERROR) {
|
||||||
setup_err_evt(vmst, evt);
|
setup_err_evt(vmst, evt);
|
||||||
|
|
@ -242,7 +245,7 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
|
||||||
setStatusErr(vmst, UVM32_ERR_INTERNAL_STATE);
|
setStatusErr(vmst, UVM32_ERR_INTERNAL_STATE);
|
||||||
setup_err_evt(vmst, evt);
|
setup_err_evt(vmst, evt);
|
||||||
}
|
}
|
||||||
return num_instr;
|
return orig_instr_meter - instr_meter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,3 +304,14 @@ uvm32_evt_syscall_buf_t uvm32_getbuf(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm3
|
||||||
return scb;
|
return scb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uvm32_evt_syscall_buf_t uvm32_getbuf_fixed(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uint32_t len) {
|
||||||
|
uvm32_evt_syscall_buf_t scb;
|
||||||
|
if (!get_safeptr(vmst, uvm32_getval(vmst, evt, argPtr), len, &scb)) {
|
||||||
|
setStatusErr(vmst, UVM32_ERR_MEM_RD);
|
||||||
|
garbage = 0;
|
||||||
|
scb.ptr = (uint8_t *)&garbage;
|
||||||
|
scb.len = 0;
|
||||||
|
}
|
||||||
|
return scb;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ uint32_t uvm32_getval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t);
|
||||||
const char *uvm32_getcstr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t);
|
const char *uvm32_getcstr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t);
|
||||||
void uvm32_setval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t, uint32_t val);
|
void uvm32_setval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t, uint32_t val);
|
||||||
uvm32_evt_syscall_buf_t uvm32_getbuf(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uvm32_arg_t argLen);
|
uvm32_evt_syscall_buf_t uvm32_getbuf(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uvm32_arg_t argLen);
|
||||||
|
uvm32_evt_syscall_buf_t uvm32_getbuf_fixed(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uint32_t len);
|
||||||
void uvm32_clearError(uvm32_state_t *vmst);
|
void uvm32_clearError(uvm32_state_t *vmst);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue