On Sat, Nov 30, 2013 at 4:47 AM, Qingping Hou <dave2008713@xxxxxxxxx> wrote: > From: Yicheng Qin <qycqycqycqycqyc@xxxxxxxxx> > > Test cases include: > > * cparser ability > * C function call on user-defined kernel functions > * C function call on native kernel functions > > Signed-off-by: Yicheng Qin <qycqycqycqycqyc@xxxxxxxxx> > Signed-off-by: Qingping Hou <qingping.hou@xxxxxxxxx> > --- > test/ffi/Makefile | 46 ++++++++ > test/ffi/cparser_test.c | 304 > ++++++++++++++++++++++++++++++++++++++++++++++++ > test/ffi/funct.c | 46 ++++++++ > test/ffi/halt.kp | 3 + > test/ffi/kfunct.kp | 8 ++ > test/ffi/ufunct.kp | 24 ++++ > test/run_test.sh | 2 + > 7 files changed, 433 insertions(+) > create mode 100644 test/ffi/Makefile > create mode 100644 test/ffi/cparser_test.c > create mode 100644 test/ffi/funct.c > create mode 100644 test/ffi/halt.kp > create mode 100644 test/ffi/kfunct.kp > create mode 100644 test/ffi/ufunct.kp > > diff --git a/test/ffi/Makefile b/test/ffi/Makefile > new file mode 100644 > index 0000000..cd17ff7 > --- /dev/null > +++ b/test/ffi/Makefile > @@ -0,0 +1,46 @@ > +obj-m += funct.o > + > +all: funct_mod cparser_test > + > +funct_mod: > + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules > + > +INC=../../include > +U_DIR=../../userspace > +INTP=../../interpreter > +U_FFI_DIR=$(U_DIR)/ffi > +CPARSER_FILES=cparser.o ctype.o ffi_type.o > +KTAPC_CFLAGS = -Wall -O2 > + > +cparser.o: $(U_FFI_DIR)/cparser.c $(INC)/* > + $(CC) -DCONFIG_KTAP_FFI -o $@ -c $< > + > +ctype.o: $(U_FFI_DIR)/ctype.c $(INC)/* > + $(CC) -DCONFIG_KTAP_FFI -o $@ -c $< > + > +ffi_type.o: $(INTP)/ffi/ffi_type.c $(INC)/* > + $(CC) -DCONFIG_KTAP_FFI -o $@ -c $< > + > +cparser_test: cparser_test.c $(CPARSER_FILES) $(INC)/* > + $(CC) -DCONFIG_KTAP_FFI -I$(INC) -I$(U_DIR) $(KTAPC_CFLAGS) \ > + -o $@ $< $(CPARSER_FILES) > + > +load: > + insmod funct.ko > + > +unload: > + rmmod funct > + > +test: all > + @echo "------------ test cparser -----------" > + ./cparser_test > + @echo "------------ test ffi module ------------" > + rmmod funct > /dev/null 2>&1 || true > + insmod funct.ko > + ../../ktap ufunct.kp > + ../../ktap kfunct.kp > + rmmod funct.ko > + > +clean: > + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean > + rm -rf cparser_test > diff --git a/test/ffi/cparser_test.c b/test/ffi/cparser_test.c > new file mode 100644 > index 0000000..a4f203a > --- /dev/null > +++ b/test/ffi/cparser_test.c > @@ -0,0 +1,304 @@ > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <assert.h> > + > +#include "ktap_types.h" > +#include "ktap_opcodes.h" > +#include "../../userspace/ktapc.h" > +#include "cparser.h" > + > +void ffi_cparser_init(void); > +void ffi_cparser_free(void); > +int ffi_cdef(char *s); > + > +static cp_csymbol_state *csym_state; > + > +#define cs_nr (csym_state->cs_nr) > +#define cs_arr_size (csym_state->cs_arr_size) > +#define cs_arr (csym_state->cs_arr) > + > + > +#define DO_TEST(name) do { \ > + int ret; \ > + printf("[*] start "#name" test...\n"); \ > + ret = test_##name(); \ > + if (ret) \ > + fprintf(stderr, "[!] "#name" test failed.\n"); \ > + else \ > + printf("[*] "#name" test passed.\n"); \ > +} while (0) > + > +#define assert_csym_arr_type(cs_arr, n, t) do { \ > + csymbol *ncs; \ > + ncs = &cs_arr[n]; \ > + assert(ncs->type == t); \ > +} while (0) > + > +#define assert_arg_type(fcs, n, t) do { \ > + csymbol *ncs; \ > + ncs = &cs_arr[fcs->arg_ids[n]]; \ > + assert(ncs->type == t); \ > +} while (0) > + > + > + > + > +/* mock find_kernel_symbol */ > +unsigned long find_kernel_symbol(const char *symbol) > +{ > + return 0xdeadbeef; > +} > + > +int lookup_csymbol_id_by_name(char *name) > +{ > + int i; > + > + for (i = 0; i < cs_nr; i++) { > + if (!strcmp(name, cs_arr[i].name)) { > + return i; > + } > + } > + > + return -1; > +} > + > +int test_func_sched_clock() > +{ > + int idx; > + csymbol *cs; > + csymbol_func *fcs; > + > + ffi_cdef("unsigned long long sched_clock();"); > + > + csym_state = ctype_get_csym_state(); > + assert(cs_arr); > + > + idx = lookup_csymbol_id_by_name("sched_clock"); > + assert(idx >= 0); > + cs = &cs_arr[idx]; > + assert(cs->type == FFI_FUNC); > + > + fcs = csym_func(cs); > + > + /* check return type */ > + cs = &cs_arr[fcs->ret_id]; > + assert(cs->type == FFI_UINT64); > + > + /* check arguments */ > + assert(fcs->arg_nr == 0); > + > + return 0; > +} > + > +int test_func_funct_module() > +{ > + int idx; > + csymbol *cs; > + csymbol_func *fcs; > + > + ffi_cdef("void funct_void();"); > + ffi_cdef("int funct_int1(unsigned char a, char b, unsigned short c, " > + "short d);"); > + ffi_cdef("long long funct_int2(unsigned int a, int b, " > + "unsigned long c, long d, unsigned long long e, " > + "long long f, long long g);"); > + ffi_cdef("void *funct_pointer1(char *a);"); > + > + csym_state = ctype_get_csym_state(); > + assert(cs_arr); > + > + /* check funct_void function */ > + idx = lookup_csymbol_id_by_name("funct_void"); > + assert(idx >= 0); > + cs = &cs_arr[idx]; > + assert(cs->type == FFI_FUNC); > + fcs = csym_func(cs); > + > + /* check return type */ > + cs = &cs_arr[fcs->ret_id]; > + assert(cs->type == FFI_VOID); > + > + /* check arguments */ > + assert(fcs->arg_nr == 0); > + > + > + > + /* check funct_int1 function */ > + idx = lookup_csymbol_id_by_name("funct_int1"); > + assert(idx >= 0); > + cs = &cs_arr[idx]; > + assert(cs); > + assert(cs->type == FFI_FUNC); > + fcs = csym_func(cs); > + > + /* check return type */ > + cs = &cs_arr[fcs->ret_id]; > + assert(cs->type == FFI_INT32); > + > + /* check arguments */ > + assert(fcs->arg_nr == 4); > + assert_arg_type(fcs, 0, FFI_UINT8); > + assert_arg_type(fcs, 1, FFI_INT8); > + assert_arg_type(fcs, 2, FFI_UINT16); > + assert_arg_type(fcs, 3, FFI_INT16); > + > + > + > + /* check funct_int2 function */ > + idx = lookup_csymbol_id_by_name("funct_int2"); > + assert(idx >= 0); > + cs = &cs_arr[idx]; > + assert(cs); > + assert(cs->type == FFI_FUNC); > + fcs = csym_func(cs); > + > + /* check return type */ > + cs = &cs_arr[fcs->ret_id]; > + assert(cs->type == FFI_INT64); > + > + /* check arguments */ > + assert(fcs->arg_nr == 7); > + assert_arg_type(fcs, 0, FFI_UINT32); > + assert_arg_type(fcs, 1, FFI_INT32); > + assert_arg_type(fcs, 2, FFI_UINT64); > + assert_arg_type(fcs, 3, FFI_INT64); > + assert_arg_type(fcs, 4, FFI_UINT64); > + assert_arg_type(fcs, 5, FFI_INT64); > + assert_arg_type(fcs, 6, FFI_INT64); > + > + > + > + /* check funct_pointer1 function */ > + idx = lookup_csymbol_id_by_name("funct_pointer1"); > + assert(idx >= 0); > + cs = &cs_arr[idx]; > + assert(cs); > + assert(cs->type == FFI_FUNC); > + fcs = csym_func(cs); > + > + /* check return type */ > + cs = &cs_arr[fcs->ret_id]; > + assert(cs->type == FFI_PTR); > + > + /* check arguments */ > + assert(fcs->arg_nr == 1); > + assert_arg_type(fcs, 0, FFI_PTR); > + /*@TODO check pointer dereference type 18.11 2013 (houqp)*/ > + > + return 0; > +} > + > +int test_struct_timespec() > +{ > + int idx; > + csymbol *cs; > + csymbol_struct *stcs; > + > + ffi_cdef("struct timespec { long ts_sec; long ts_nsec; };"); > + > + csym_state = ctype_get_csym_state(); > + assert(cs_arr); > + > + idx = lookup_csymbol_id_by_name("struct timespec"); > + assert(idx >= 0); > + cs = &cs_arr[idx]; > + assert(cs); > + assert(cs->type == FFI_STRUCT); > + > + stcs = csym_struct(cs); > + assert(stcs->memb_nr == 2); > + > + return 0; > +} > + > +int test_func_time_to_tm() > +{ > + int idx; > + csymbol *cs, *arg_cs; > + csymbol_struct *stcs; > + csymbol_func *fcs; > + > + ffi_cdef("typedef long time_t;"); > + ffi_cdef("struct tm { " > + "int tm_sec;" > + "int tm_min;" > + "int tm_hour;" > + "int tm_mday;" > + "int tm_mon;" > + "long tm_year;" > + "int tm_wday;" > + "int tm_yday;" > + "};"); > + ffi_cdef("void time_to_tm(time_t totalsecs, int offset, struct tm > *result);"); > + > + csym_state = ctype_get_csym_state(); > + assert(cs_arr); > + > + idx = lookup_csymbol_id_by_name("struct tm"); > + assert(idx >= 0); > + cs = cp_id_to_csym(idx); > + assert(cs); > + assert(cs->type == FFI_STRUCT); > + > + stcs = csym_struct(cs); > + assert(stcs->memb_nr == 8); > + > + > + idx = lookup_csymbol_id_by_name("time_to_tm"); > + assert(idx >= 0); > + cs = cp_id_to_csym(idx); > + assert(cs); > + assert(cs->type == FFI_FUNC); > + > + fcs = csym_func(cs); > + assert(csymf_arg_nr(fcs) == 3); > + /* check first argument */ > + assert_arg_type(fcs, 0, FFI_INT64); > + > + /* check second argument */ > + assert_arg_type(fcs, 1, FFI_INT32); > + /* check third argument */ > + assert_arg_type(fcs, 2, FFI_PTR); > + arg_cs = cp_csymf_arg(fcs, 2); > + assert(!strcmp(csym_name(arg_cs), "struct tm *")); > + assert(csym_ptr_deref_id(arg_cs) == > + lookup_csymbol_id_by_name("struct tm")); > + > + return 0; > +} > + > +int test_pointer_symbols() > +{ > + csymbol_func *fcs_foo, *fcs_bar; > + > + /* int pointer symbol should be resolved to the same id */ > + ffi_cdef("void foo(int *a);"); > + ffi_cdef("int *bar(void);"); > + > + csym_state = ctype_get_csym_state(); > + assert(cs_arr); > + > + fcs_foo = csym_func(cp_id_to_csym(lookup_csymbol_id_by_name("foo"))); > + fcs_bar = csym_func(cp_id_to_csym(lookup_csymbol_id_by_name("bar"))); > + > + assert(csymf_arg_ids(fcs_foo)[0] == csymf_ret_id(fcs_bar)); > + assert(cp_csymf_arg(fcs_foo, 0) == cp_csymf_ret(fcs_bar)); > + > + return 0; > +} > + > +int main (int argc, char *argv[]) > +{ > + ffi_cparser_init(); > + > + DO_TEST(func_sched_clock); > + DO_TEST(func_funct_module); > + DO_TEST(struct_timespec); > + DO_TEST(func_time_to_tm); > + DO_TEST(pointer_symbols); > + > + ffi_cparser_free(); > + > + return 0; > +} > diff --git a/test/ffi/funct.c b/test/ffi/funct.c > new file mode 100644 > index 0000000..789188f > --- /dev/null > +++ b/test/ffi/funct.c > @@ -0,0 +1,46 @@ > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/init.h> > + > +void funct_void(void) > +{ > +} > +EXPORT_SYMBOL(funct_void); > + > +int funct_int1(unsigned char a, char b, unsigned short c, short d) > +{ > + return a + b + c + d; > +} > +EXPORT_SYMBOL(funct_int1); > + > +long long funct_int2(unsigned int a, int b, unsigned long c, long d, > + unsigned long long e, long long f, long long g) > +{ > + return a + b + c + d + e + f + g; > +} > +EXPORT_SYMBOL(funct_int2); > + > +void *funct_pointer1(char *a) { > + return a; > +} > +EXPORT_SYMBOL(funct_pointer1); > + > + > + > +static int __init funct_init(void) > +{ > + printk("Hello World!\n"); > + return 0; > +} Remove the printk in funct_init and funct_exit, it's unacceptable to bring side effects for system during executing testcases And the kernel module name "funct.ko" is really bad, use "ktap_ffi_test"? Similarly, also change the ktap script name "ufunct.kp" to "ffi_test.kp". > + > +static void __exit funct_exit(void) > +{ > + printk("Goodbye!\n"); > +} > + > + > +MODULE_DESCRIPTION("ktap test module"); > +MODULE_LICENSE("GPL"); > + > +module_init(funct_init); > +module_exit(funct_exit); > diff --git a/test/ffi/halt.kp b/test/ffi/halt.kp > new file mode 100644 > index 0000000..7bfae9a > --- /dev/null > +++ b/test/ffi/halt.kp > @@ -0,0 +1,3 @@ > +cdef("void kernel_halt(void);") > + > +C.kernel_halt() > diff --git a/test/ffi/kfunct.kp b/test/ffi/kfunct.kp > new file mode 100644 > index 0000000..dc6aa74 > --- /dev/null > +++ b/test/ffi/kfunct.kp > @@ -0,0 +1,8 @@ > +cdef("unsigned long long sched_clock();") > + > + > +print("start of user script...") > + > +print("[*] try a registered C function...") > +ret = C.sched_clock() > +print("C function returned, value: ", ret) > diff --git a/test/ffi/ufunct.kp b/test/ffi/ufunct.kp > new file mode 100644 > index 0000000..adbe3fe > --- /dev/null > +++ b/test/ffi/ufunct.kp > @@ -0,0 +1,24 @@ > +cdef("void funct_void();") > +cdef("int funct_int1(unsigned char a, char b, unsigned short c, short d);") > +cdef("long long funct_int2(unsigned int a, int b, unsigned long c, long d, > unsigned long long e, long long f, long long g);") > +cdef("void *funct_pointer1(char *a);") > + > + > +print("start of user script...") > + > +print("[*] try funct_void...") > +ret = C.funct_void() > +print("C function returned, value: ", ret, ", should be nil") > + We could just check the return value by script, not check by eyes. :) if (ret != nil) print("test C.funct_void() failed") And below cases. > +print("[*] try funct_int1...") > +ret = C.funct_int1(1111, 1111, 1111, 1111) > +print("C function returned, value: ", ret, ", should be 2396") > + > +print("[*] try funct_int2...") > +ret = C.funct_int2(90, 7800, 560000, 34000000, 1200000000, 900000000000, > 78000000000000) > +print("C function returned, value: ", ret, ", should be 78901234567890") > + > +print("[*] try funct_pointer1...") > +ret = C.funct_pointer1("") > +print("C function returned, value: ", ret, > + ", should be around 0xffff8800--------") I suggest don't check like this, the result should be accurately. > diff --git a/test/run_test.sh b/test/run_test.sh > index 71e96b6..3375c36 100644 > --- a/test/run_test.sh > +++ b/test/run_test.sh > @@ -51,6 +51,8 @@ sleep 1 > pkill ktap > sleep 1 > > +cd ffi && make test && cd - > + > ##################################################### > rmmod ktapvm > if test $? -ne 0; then > -- How about move sched_clock ffi testcase(kfunct.kp) into samples/ffi/sched_clock.kp ? of course this ffi sample script doesn't seems have much value for use currently, but at least show user how to use ffi.(use your ffi_test.kp for ffi test is enough IMO, and sched_lock testcase doesn't check any return value.) Also please simplify the output of ffi test, when I using `make test`, the ffi test output is bad, I perfer old normal testcase one line output, no gcc compile log, no insmod/rmmod hint. Except above comments, I really like your cparser_test and funct.ko(currently) testcase, it's well designed, and would be very very useful for future ffi functionality verification. Thank you. Jovi