[ktap] Re: [PATCHv3 8/8] Add ffi test cases and script

  • From: Jovi Zhangwei <jovi.zhangwei@xxxxxxxxx>
  • To: Qingping Hou <dave2008713@xxxxxxxxx>
  • Date: Sat, 30 Nov 2013 17:15:13 +0800

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

Other related posts: