ktap-0.4
authorLi xin <lixin.fnst@cn.fujitsu.com>
Fri, 17 Oct 2014 12:06:28 +0000 (15:06 +0300)
committerSimon Horman <horms@verge.net.au>
Fri, 5 Dec 2014 00:21:03 +0000 (09:21 +0900)
118 files changed:
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/ktap/Kconfig [new file with mode: 0644]
drivers/staging/ktap/Makefile [new file with mode: 0644]
drivers/staging/ktap/README.md [new file with mode: 0644]
drivers/staging/ktap/RELEASES.txt [new file with mode: 0644]
drivers/staging/ktap/doc/tutorial.md [new file with mode: 0644]
drivers/staging/ktap/include/ktap_ffi.h [new file with mode: 0644]
drivers/staging/ktap/include/ktap_opcodes.h [new file with mode: 0644]
drivers/staging/ktap/include/ktap_types.h [new file with mode: 0644]
drivers/staging/ktap/runtime/ffi/call_x86_64.S [new file with mode: 0644]
drivers/staging/ktap/runtime/ffi/cdata.c [new file with mode: 0644]
drivers/staging/ktap/runtime/ffi/ffi_call.c [new file with mode: 0644]
drivers/staging/ktap/runtime/ffi/ffi_symbol.c [new file with mode: 0644]
drivers/staging/ktap/runtime/ffi/ffi_type.c [new file with mode: 0644]
drivers/staging/ktap/runtime/ffi/ffi_util.c [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_amalg.c [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_load.c [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_load.h [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_obj.c [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_obj.h [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_opcode.c [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_str.c [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_str.h [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_tab.c [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_tab.h [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_transport.c [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_transport.h [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_vm.c [new file with mode: 0644]
drivers/staging/ktap/runtime/kp_vm.h [new file with mode: 0644]
drivers/staging/ktap/runtime/ktap.c [new file with mode: 0644]
drivers/staging/ktap/runtime/ktap.h [new file with mode: 0644]
drivers/staging/ktap/runtime/lib_ansi.c [new file with mode: 0644]
drivers/staging/ktap/runtime/lib_base.c [new file with mode: 0644]
drivers/staging/ktap/runtime/lib_ffi.c [new file with mode: 0644]
drivers/staging/ktap/runtime/lib_kdebug.c [new file with mode: 0644]
drivers/staging/ktap/runtime/lib_timer.c [new file with mode: 0644]
drivers/staging/ktap/samples/ansi/ansi_color_demo.kp [new file with mode: 0644]
drivers/staging/ktap/samples/basic/backtrace.kp [new file with mode: 0644]
drivers/staging/ktap/samples/basic/event_trigger.kp [new file with mode: 0644]
drivers/staging/ktap/samples/basic/event_trigger_ftrace.kp [new file with mode: 0644]
drivers/staging/ktap/samples/basic/ftrace.kp [new file with mode: 0644]
drivers/staging/ktap/samples/basic/function_time.kp [new file with mode: 0644]
drivers/staging/ktap/samples/basic/kretprobe.kp [new file with mode: 0644]
drivers/staging/ktap/samples/ffi/ffi_kmalloc.kp [new file with mode: 0644]
drivers/staging/ktap/samples/ffi/printk.kp [new file with mode: 0644]
drivers/staging/ktap/samples/ffi/sched_clock.kp [new file with mode: 0644]
drivers/staging/ktap/samples/game/tetris.kp [new file with mode: 0644]
drivers/staging/ktap/samples/helloworld.kp [new file with mode: 0644]
drivers/staging/ktap/samples/interrupt/hardirq_time.kp [new file with mode: 0644]
drivers/staging/ktap/samples/interrupt/softirq_time.kp [new file with mode: 0644]
drivers/staging/ktap/samples/io/kprobes-do-sys-open.kp [new file with mode: 0644]
drivers/staging/ktap/samples/io/traceio.kp [new file with mode: 0644]
drivers/staging/ktap/samples/mem/kmalloc-top.kp [new file with mode: 0644]
drivers/staging/ktap/samples/mem/kmem.kp [new file with mode: 0644]
drivers/staging/ktap/samples/profiling/function_profiler.kp [new file with mode: 0644]
drivers/staging/ktap/samples/profiling/stack_profile.kp [new file with mode: 0644]
drivers/staging/ktap/samples/schedule/sched_transition.kp [new file with mode: 0644]
drivers/staging/ktap/samples/schedule/schedtimes.kp [new file with mode: 0644]
drivers/staging/ktap/samples/syscalls/errinfo.kp [new file with mode: 0644]
drivers/staging/ktap/samples/syscalls/execve.kp [new file with mode: 0644]
drivers/staging/ktap/samples/syscalls/opensnoop.kp [new file with mode: 0644]
drivers/staging/ktap/samples/syscalls/sctop.kp [new file with mode: 0644]
drivers/staging/ktap/samples/syscalls/syscalls.kp [new file with mode: 0644]
drivers/staging/ktap/samples/syscalls/syscalls_count.kp [new file with mode: 0644]
drivers/staging/ktap/samples/syscalls/syscalls_count_by_proc.kp [new file with mode: 0644]
drivers/staging/ktap/samples/syscalls/syslatl.kp [new file with mode: 0644]
drivers/staging/ktap/samples/syscalls/syslist.kp [new file with mode: 0644]
drivers/staging/ktap/samples/tracepoints/eventcount.kp [new file with mode: 0644]
drivers/staging/ktap/samples/tracepoints/eventcount_by_proc.kp [new file with mode: 0644]
drivers/staging/ktap/samples/tracepoints/tracepoints.kp [new file with mode: 0644]
drivers/staging/ktap/samples/userspace/gcc_unwind.kp [new file with mode: 0644]
drivers/staging/ktap/samples/userspace/glibc_func_hist.kp [new file with mode: 0644]
drivers/staging/ktap/samples/userspace/glibc_sdt.kp [new file with mode: 0644]
drivers/staging/ktap/samples/userspace/glibc_trace.kp [new file with mode: 0644]
drivers/staging/ktap/samples/userspace/malloc_free.kp [new file with mode: 0644]
drivers/staging/ktap/samples/userspace/malloc_size_hist.kp [new file with mode: 0644]
drivers/staging/ktap/test/arg.kp [new file with mode: 0644]
drivers/staging/ktap/test/arithmetic.kp [new file with mode: 0644]
drivers/staging/ktap/test/benchmark/sembench.c [new file with mode: 0644]
drivers/staging/ktap/test/benchmark/test.sh [new file with mode: 0644]
drivers/staging/ktap/test/concat.kp [new file with mode: 0644]
drivers/staging/ktap/test/count.kp [new file with mode: 0644]
drivers/staging/ktap/test/ffi/.gitignore [new file with mode: 0644]
drivers/staging/ktap/test/ffi/Makefile [new file with mode: 0644]
drivers/staging/ktap/test/ffi/cparser_test.c [new file with mode: 0644]
drivers/staging/ktap/test/ffi/ffi_test.kp [new file with mode: 0644]
drivers/staging/ktap/test/ffi/ktap_ffi_test.c [new file with mode: 0644]
drivers/staging/ktap/test/fibonacci.kp [new file with mode: 0644]
drivers/staging/ktap/test/function.kp [new file with mode: 0644]
drivers/staging/ktap/test/if.kp [new file with mode: 0644]
drivers/staging/ktap/test/kprobe.kp [new file with mode: 0644]
drivers/staging/ktap/test/kretprobe.kp [new file with mode: 0644]
drivers/staging/ktap/test/ksym.kp [new file with mode: 0644]
drivers/staging/ktap/test/len.kp [new file with mode: 0644]
drivers/staging/ktap/test/looping.kp [new file with mode: 0644]
drivers/staging/ktap/test/pairs.kp [new file with mode: 0644]
drivers/staging/ktap/test/ptable.kp [new file with mode: 0644]
drivers/staging/ktap/test/run_test.sh [new file with mode: 0644]
drivers/staging/ktap/test/stack_overflow.kp [new file with mode: 0644]
drivers/staging/ktap/test/table.kp [new file with mode: 0644]
drivers/staging/ktap/test/timer.kp [new file with mode: 0644]
drivers/staging/ktap/test/tracepoint.kp [new file with mode: 0644]
drivers/staging/ktap/test/zerodivide.kp [new file with mode: 0644]
drivers/staging/ktap/userspace/code.c [new file with mode: 0644]
drivers/staging/ktap/userspace/cparser.h [new file with mode: 0644]
drivers/staging/ktap/userspace/dump.c [new file with mode: 0644]
drivers/staging/ktap/userspace/eventdef.c [new file with mode: 0644]
drivers/staging/ktap/userspace/ffi/cparser.c [new file with mode: 0644]
drivers/staging/ktap/userspace/ffi/ctype.c [new file with mode: 0644]
drivers/staging/ktap/userspace/ktapc.h [new file with mode: 0644]
drivers/staging/ktap/userspace/ktapio.c [new file with mode: 0644]
drivers/staging/ktap/userspace/lex.c [new file with mode: 0644]
drivers/staging/ktap/userspace/main.c [new file with mode: 0644]
drivers/staging/ktap/userspace/parser.c [new file with mode: 0644]
drivers/staging/ktap/userspace/symbol.c [new file with mode: 0644]
drivers/staging/ktap/userspace/symbol.h [new file with mode: 0644]
drivers/staging/ktap/userspace/util.c [new file with mode: 0644]

index 6c7a9b1..7ab94ac 100644 (file)
@@ -148,4 +148,6 @@ source "drivers/staging/dgap/Kconfig"
 
 source "drivers/staging/lttng/Kconfig"
 
+source "drivers/staging/ktap/Kconfig"
+
 endif # STAGING
index 4ec98c9..a2b53b9 100644 (file)
@@ -66,3 +66,4 @@ obj-$(CONFIG_DGNC)                    += dgnc/
 obj-$(CONFIG_DGAP)                     += dgap/
 obj-$(CONFIG_MTD_SPINAND_MT29F)        += mt29f_spinand/
 obj-$(CONFIG_LTTNG)            += lttng/
+obj-$(CONFIG_KTAP)              += ktap/
diff --git a/drivers/staging/ktap/Kconfig b/drivers/staging/ktap/Kconfig
new file mode 100644 (file)
index 0000000..6c6cf63
--- /dev/null
@@ -0,0 +1,32 @@
+config KTAP
+       tristate "a programable dynamic tracing tool for Linux"
+       depends on PERF_EVENTS && EVENT_TRACING
+       default n
+       help
+         ktap is a new script-based dynamic tracing tool for Linux,
+         it uses a scripting language and lets users trace the
+         Linux kernel dynamically. ktap is designed to give
+         operational insights with interoperability that allow
+         users to tune, troubleshoot and extend kernel and application.
+         It's similar with Linux Systemtap and Solaris Dtrace.
+
+         ktap have different design principles from Linux mainstream
+         dynamic tracing language in that it's based on bytecode,
+         so it doesn't depend upon GCC, doesn't require compiling
+         kernel module for each script, safe to use in production
+         environment, fulfilling the embedded ecosystem's tracing needs.
+
+         See ktap tutorial for more information:
+             http://www.ktap.org/doc/tutorial.html
+
+config KTAP_FFI
+       tristate "FFI support for ktap"
+       depends on KTAP
+       depends on X86_64
+       default n
+       help
+         This option brings FFI support to ktap. With FFI enabled ktap,
+         users can call into native kernel C function directly in ktap
+         script. Except for a new cdef keyword, this option also adds
+         a ffi module which exports helper functions like ffi.new and
+         ffi.sizeof.
diff --git a/drivers/staging/ktap/Makefile b/drivers/staging/ktap/Makefile
new file mode 100644 (file)
index 0000000..f565438
--- /dev/null
@@ -0,0 +1,203 @@
+
+#
+# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds)
+# (this will also disable resolve resolving symbols in DSO functionality)
+#
+# Define FFI if you want to compile ktap with FFI support. By default This
+# toggle is off.
+#
+# Define amalg to enable amalgamation build, This compiles the ktapvm as
+# one huge C file and allows GCC to generate faster and shorter code. Alas,
+# this requires lots of memory during the build.
+# Recommend to use amalgmation build as default.
+amalg = 1
+NO_LIBELF = 1
+# Do not instrument the tracer itself:
+ifdef CONFIG_FUNCTION_TRACER
+ORIG_CFLAGS := $(KBUILD_CFLAGS)
+KBUILD_CFLAGS = $(subst -pg,,$(ORIG_CFLAGS))
+endif
+
+all: mod ktap
+
+INC = include
+RUNTIME = runtime
+
+FFIDIR = $(RUNTIME)/ffi
+KTAP_LIBS = -lpthread
+
+LIB_OBJS += $(RUNTIME)/lib_base.o $(RUNTIME)/lib_kdebug.o $(RUNTIME)/lib_timer.o \
+               $(RUNTIME)/lib_ansi.o
+
+ifndef amalg
+ifdef FFI
+FFI_OBJS += $(FFIDIR)/ffi_call.o $(FFIDIR)/ffi_type.o $(FFIDIR)/ffi_symbol.o \
+    $(FFIDIR)/cdata.o $(FFIDIR)/ffi_util.o
+RUNTIME_OBJS += $(FFI_OBJS)
+LIB_OBJS += $(RUNTIME)/lib_ffi.o
+endif
+RUNTIME_OBJS += $(RUNTIME)/ktap.o $(RUNTIME)/kp_load.o $(RUNTIME)/kp_obj.o \
+               $(RUNTIME)/kp_str.o $(RUNTIME)/kp_tab.o $(RUNTIME)/kp_vm.o \
+               $(RUNTIME)/kp_opcode.o $(RUNTIME)/kp_transport.o \
+               $(LIB_OBJS)
+else
+RUNTIME_OBJS += $(RUNTIME)/kp_amalg.o
+endif
+
+ifdef FFI
+ifeq ($(KBUILD_MODULES), 1)
+ifdef CONFIG_X86_64
+# call_x86_64.o is compiled from call_x86_64.S
+RUNTIME_OBJS += $(FFIDIR)/call_x86_64.o
+else
+$(error ktap FFI only supports x86_64 for now!)
+endif
+endif
+
+
+ccflags-y      += -DCONFIG_KTAP_FFI
+endif
+
+obj-m          += ktapvm.o
+ktapvm-y       := $(RUNTIME_OBJS)
+
+KVERSION ?= $(shell uname -r)
+KERNEL_SRC ?= /lib/modules/$(KVERSION)/build
+PWD := $(shell pwd)
+mod:
+       $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules
+
+modules_install:
+       $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules_install
+
+KTAPC_CFLAGS = -Wall -O2
+
+
+# try-cc
+# Usage: option = $(call try-cc, source-to-build, cc-options, msg)
+ifneq ($(V),1)
+TRY_CC_OUTPUT= > /dev/null 2>&1
+endif
+TRY_CC_MSG=echo "    CHK $(3)" 1>&2;
+
+try-cc = $(shell sh -c                                                 \
+         'TMP="/tmp/.$$$$";                                            \
+          $(TRY_CC_MSG)                                                        \
+          echo "$(1)" |                                                        \
+          $(CC) -x c - $(2) -o "$$TMP" $(TRY_CC_OUTPUT) && echo y;     \
+          rm -f "$$TMP"')
+
+
+define SOURCE_LIBELF
+#include <libelf.h>
+
+int main(void)
+{
+        Elf *elf = elf_begin(0, ELF_C_READ, 0);
+        return (long)elf;
+}
+endef
+
+FLAGS_LIBELF = -lelf
+
+ifdef NO_LIBELF
+       KTAPC_CFLAGS += -DNO_LIBELF
+else
+ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y)
+    $(warning No libelf found, disables symbol resolving, please install elfutils-libelf-devel/libelf-dev);
+    NO_LIBELF := 1
+    KTAPC_CFLAGS += -DNO_LIBELF
+else
+    KTAP_LIBS += -lelf
+endif
+endif
+
+UDIR = userspace
+
+$(UDIR)/lex.o: $(UDIR)/lex.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/parser.o: $(UDIR)/parser.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/code.o: $(UDIR)/code.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/dump.o: $(UDIR)/dump.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/main.o: $(UDIR)/main.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/util.o: $(UDIR)/util.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/ktapio.o: $(UDIR)/ktapio.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/eventdef.o: $(UDIR)/eventdef.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/kp_opcode.o: $(RUNTIME)/kp_opcode.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/kp_tab.o: $(RUNTIME)/kp_tab.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/kp_str.o: $(RUNTIME)/kp_str.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/kp_obj.o: $(RUNTIME)/kp_obj.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+ifndef NO_LIBELF
+$(UDIR)/symbol.o: $(UDIR)/symbol.c
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+endif
+ifdef FFI
+KTAPC_CFLAGS += -DCONFIG_KTAP_FFI
+$(UDIR)/ffi_type.o: $(RUNTIME)/ffi/ffi_type.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/ffi/cparser.o: $(UDIR)/ffi/cparser.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/ffi/ctype.o: $(UDIR)/ffi/ctype.c $(INC)/*
+       $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+endif
+
+
+KTAPOBJS =
+KTAPOBJS += $(UDIR)/lex.o
+KTAPOBJS += $(UDIR)/parser.o
+KTAPOBJS += $(UDIR)/code.o
+KTAPOBJS += $(UDIR)/dump.o
+KTAPOBJS += $(UDIR)/main.o
+KTAPOBJS += $(UDIR)/util.o
+KTAPOBJS += $(UDIR)/ktapio.o
+KTAPOBJS += $(UDIR)/eventdef.o
+KTAPOBJS += $(UDIR)/kp_opcode.o
+KTAPOBJS += $(UDIR)/kp_tab.o
+KTAPOBJS += $(UDIR)/kp_str.o
+KTAPOBJS += $(UDIR)/kp_obj.o
+ifndef NO_LIBELF
+KTAPOBJS += $(UDIR)/symbol.o
+endif
+ifdef FFI
+KTAPOBJS += $(UDIR)/ffi_type.o
+KTAPOBJS += $(UDIR)/ffi/cparser.o
+KTAPOBJS += $(UDIR)/ffi/ctype.o
+endif
+
+ktap: $(KTAPOBJS)
+       $(QUIET_LINK)$(CC) $(KTAPC_CFLAGS) -o $@ $(KTAPOBJS) $(KTAP_LIBS)
+
+KMISC := /lib/modules/$(KVERSION)/ktapvm/
+
+install: mod ktap
+       install -d $(KMISC)
+       install -m 644 -c *.ko /lib/modules/$(KVERSION)/ktapvm/
+       /sbin/depmod -a
+
+load:
+       insmod ktapvm.ko
+
+unload:
+       rmmod ktapvm
+
+test: FORCE
+       cd test; sh ./run_test.sh; cd -
+
+clean:
+       $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
+       $(RM) ktap
+
+PHONY += FORCE
+FORCE:
+
diff --git a/drivers/staging/ktap/README.md b/drivers/staging/ktap/README.md
new file mode 100644 (file)
index 0000000..82deb4b
--- /dev/null
@@ -0,0 +1,167 @@
+# ktap
+
+A New Scripting Dynamic Tracing Tool For Linux
+[www.ktap.org][homepage]
+
+ktap is a new scripting dynamic tracing tool for Linux,
+it uses a scripting language and lets users trace the Linux kernel dynamically.
+ktap is designed to give operational insights with interoperability
+that allows users to tune, troubleshoot and extend kernel and application.
+It's similar with Linux Systemtap and Solaris Dtrace.
+
+ktap have different design principles from Linux mainstream dynamic tracing
+language in that it's based on bytecode, so it doesn't depend upon GCC,
+doesn't require compiling kernel module for each script, safe to use in
+production environment, fulfilling the embedded ecosystem's tracing needs.
+
+More information can be found at [ktap homepage][homepage].
+
+[homepage]: http://www.ktap.org
+
+## Highlights
+
+  * simple but powerful scripting language
+  * register based interpreter (heavily optimized) in Linux kernel
+  * small and lightweight (6KLOC of interpreter)
+  * not depend on gcc for each script running
+  * easy to use in embedded environment without debugging info
+  * support for tracepoint, kprobe, uprobe, function trace, timer, and more
+  * supported in x86, arm, ppc, mips
+  * safety in sandbox
+
+## Building & Running
+
+1. Clone ktap from github
+
+        $ git clone http://github.com/ktap/ktap.git
+
+2. Compiling ktap
+
+        $ cd ktap
+        $ make       #generate ktapvm kernel module and ktap binary
+
+3. Load ktapvm kernel module(make sure debugfs mounted)
+
+        $ make load  #need to be root or have sudo access
+
+4. Running ktap
+
+        $ ./ktap samples/helloworld.kp
+
+
+## Examples
+
+1. simplest one-liner command to enable all tracepoints
+
+        ktap -e "trace *:* { print(argevent) }"
+
+2. syscall tracing on target process
+
+        ktap -e "trace syscalls:* { print(argevent) }" -- ls
+
+3. ftrace(kernel newer than 3.3, and must compiled with CONFIG_FUNCTION_TRACER)
+
+        ktap -e "trace ftrace:function { print(argevent) }"
+
+        ktap -e "trace ftrace:function /ip==mutex*/ { print(argevent) }"
+
+4. simple syscall tracing
+
+        trace syscalls:* {
+                print(cpu(), pid(), execname(), argevent)
+        }
+
+5. syscall tracing in histogram style
+
+        s = {}
+
+        trace syscalls:sys_enter_* {
+                s[argname] += 1
+        }
+
+        trace_end {
+                histogram(s)
+        }
+
+6. kprobe tracing
+
+        trace probe:do_sys_open dfd=%di fname=%dx flags=%cx mode=+4($stack) {
+                print("entry:", execname(), argevent)
+        }
+
+        trace probe:do_sys_open%return fd=$retval {
+                print("exit:", execname(), argevent)
+        }
+
+7. uprobe tracing
+
+        trace probe:/lib/libc.so.6:malloc {
+                print("entry:", execname(), argevent)
+        }
+
+        trace probe:/lib/libc.so.6:malloc%return {
+                print("exit:", execname(), argevent)
+        }
+
+8. stapsdt tracing (userspace static marker)
+
+        trace sdt:/lib64/libc.so.6:lll_futex_wake {
+                print("lll_futex_wake", execname(), argevent)
+        }
+
+        or:
+
+        #trace all static mark in libc
+        trace sdt:/lib64/libc.so.6:* {
+                print(execname(), argevent)
+        }
+
+9. timer
+
+        tick-1ms {
+                printf("time fired on one cpu\n");
+        }
+
+        profile-2s {
+                printf("time fired on every cpu\n");
+        }
+
+10. FFI (Call kernel function from ktap script, need compile with FFI=1)
+
+        cdef[[
+                int printk(char *fmt, ...);
+        ]]
+
+        C.printk("This message is called from ktap ffi\n")
+
+More examples can be found at [samples][samples_dir] directory.
+
+[samples_dir]: https://github.com/ktap/ktap/tree/master/samples
+
+## Mailing list
+
+ktap@freelists.org
+You can subscribe to ktap mailing list at link (subscribe before posting):
+http://www.freelists.org/list/ktap
+
+
+## Copyright and License
+
+ktap is licensed under GPL v2
+
+Copyright (C) 2012-2013, Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+All rights reserved.
+
+
+## Contribution
+
+ktap is still under active development, so contributions are welcome.
+You are encouraged to report bugs, provide feedback, send feature request,
+or hack on it.
+
+
+## See More
+
+More info can be found at [documentation][tutorial]
+[tutorial]: http://www.ktap.org/doc/tutorial.html
+
diff --git a/drivers/staging/ktap/RELEASES.txt b/drivers/staging/ktap/RELEASES.txt
new file mode 100644 (file)
index 0000000..bdc47dd
--- /dev/null
@@ -0,0 +1,155 @@
+Version 0.4 (Dec 9 2013)
+-------------------------
+= Highlight changes from v0.3
+
+   * kernel symbol read (syntax: `symbol_name`)
+
+   * parse symbol on uprobe (need libelf link)
+        trace probe:/lib64/libc.so.6:malloc {}
+        trace probe:/lib64/libc.so.6:malloc%return {}
+        trace probe:/lib64/libc.so.6:* {} # trace all function in glibc
+
+   * support static marker(SDT)
+        trace sdt:/lib64/libc.so.6:setjmp {}
+        trace sdt:/lib64/libc.so.6:* {} # trace all sdt in glibc
+
+   * support kprobe wildcard
+        trace probe:vfs* {}
+
+   * support run multiple ktap instances concurrently
+
+   * add command option for list available events and symbols
+        -le [glob]     : list pre-defined events in system
+        -lf DSO        : list available functions from DSO
+        -lm DSO        : list available sdt notes from DSO
+
+   * better annotation for output of argname
+
+   * basic FFI support (depend on CONFIG_KTAP_FFI)
+        FFI will allow call kernel function from ktap script
+
+        cdef [[ int printk(char *fmt, ...); ]]
+        C.printk("this is ffi printk from ktap\n")
+
+        (currently only support basic C types, structure support is ongoing)
+
+   * New sample scripts
+        userspace/malloc_size_hist.kp
+        userspace/malloc_free.kp
+        userspace/gcc_unwind.kp
+        userspace/glibc_sdt.kp        #trace all static marker in glibc
+        userspace/glibc_trace.kp      #trace all functions in glibc
+        userspace/glibc_func_hist.kp  #show glibc functions in histogram
+        syscalls/syslatl.kp           #syscall latency linear aggregation
+        syscalls/syslist.kp           #syscall latency as a list with counts
+        syscalls/opensnoop.kp         #trace open() syscalls and print basic details
+        ffi/ffi_kmalloc.kp
+        ffi/printk.kp
+        ffi/sched_clock.kp
+
+   * use amalgamation build as default
+        x86_64 build: ktap binary size is 98K, ktapvm.ko size is 983K
+
+   * Big cleanups and lots of bugfix
+
+
+Version 0.3 (Oct 29 2013)
+-------------------------
+= Highlight changes from v0.2
+
+   * Homepage released: www.ktap.org
+
+   * Tutorial: http://www.ktap.org/doc/tutorial.html
+
+   * Wiki: https://github.com/ktap/ktap/wiki
+
+   * simple new tracing block syntax
+       trace EVENTDEF { action }
+       trace_end { action }
+
+   * New event tracing keywords: argevent, argname, arg1..arg9
+       trace "syscalls:*" function () {
+               print(argevent)
+       }
+
+   * New timer block syntax
+       tick-N { action }
+       profile-N { action }
+
+   * Basic aggregation support
+       It's similar with systemtap, use "<<<" operator
+       support aggregate function: count, sum, avg, max, min
+
+   * Introduce new "+=" operator
+
+   * Introduce sort_paris for table sort iteration
+
+   * New sample scripts
+       helloworld.kp
+       syscalls/sctop.kp
+       profiling/stack_profile.kp
+       io/traceio.kp
+       mem/kmalloc-top.kp
+       syscalls/errinfo.kp
+       schedule/schedtimes.kp
+       game/tetris.kp
+
+   * ansi library for sending ANSI escape sequences
+
+
+   * statistics of ktapvm
+
+   * Big cleanups and lots of bugfix
+
+Version 0.2 (Jul 31 2013)
+-------------------------
+
+= Script highlight changes from v0.1
+
+   * new tracing block syntax
+       trace EVENTDEF function (e) { BODY }
+       trace_end function (e) { BODY }
+
+   * support trace filter
+       trace 'sched:sched_switch /prev_comm == foo || next_comm == foo/
+
+   * support kprobe/kretprobe
+       trace "probe:do_sys_open dfd=%di filename=%dx flags=%cx mode=+4($stack)"
+       trace "probe:do_sys_open%return fd=$retval"
+
+   * support uprobe/uretprobe
+       trace "probe:/lib/libc.so.6:0x000773c0"
+       trace "probe:/lib/libc.so.6:0x000773c0%return"
+
+   * support function tracing
+       trace "ftrace:function /ip == mutex*/"
+
+   * support oneline scripting
+       ktap -e 'trace "syscalls:*" function (e) { print(e) }'
+
+   * specific pid or cpu to tracing
+       ktap -C cpu *.kp
+       ktap -p pid *.kp
+
+   * more sample scripts
+
+   * support calling print_backtrace() in any context
+
+   * support calling exit() in any context
+
+= Backend highlight changes from v0.1
+
+   * unified perf callback mechanism
+   * use ring buffer transport instead of relayfs
+   * reentrant in ktap tracing
+   * performance boost(use percpu data in many case)
+   * safe table/string manipulation
+   * safe ktap exit
+   * big code cleanups
+   * fixed a lot of bugs, more stable than v0.1
+
+Version 0.1 (May 21 2013)
+-------------------------
+
+    https://lwn.net/Articles/551253/
+
diff --git a/drivers/staging/ktap/doc/tutorial.md b/drivers/staging/ktap/doc/tutorial.md
new file mode 100644 (file)
index 0000000..8edce6d
--- /dev/null
@@ -0,0 +1,691 @@
+% The ktap Tutorial
+
+# Introduction
+
+ktap is a new script-based dynamic tracing tool for linux
+http://www.ktap.org
+
+ktap is a new script-based dynamic tracing tool for Linux,
+it uses a scripting language and lets users trace the Linux kernel dynamically.
+ktap is designed to give operational insights with interoperability
+that allows users to tune, troubleshoot and extend kernel and application.
+It's similar with Linux Systemtap and Solaris Dtrace.
+
+ktap have different design principles from Linux mainstream dynamic tracing
+language in that it's based on bytecode, so it doesn't depend upon GCC,
+doesn't require compiling kernel module for each script, safe to use in
+production environment, fulfilling the embedded ecosystem's tracing needs.
+
+Highlights features:
+
+* simple but powerful scripting language
+* register based interpreter (heavily optimized) in Linux kernel
+* small and lightweight
+* not depend on gcc for each script running
+* easy to use in embedded environment without debugging info
+* support for tracepoint, kprobe, uprobe, function trace, timer, and more
+* supported in x86, arm, ppc, mips
+* safety in sandbox
+
+
+# Getting started
+
+Requirements
+
+* Linux 3.1 or later(Need some kernel patches for kernel earlier than 3.1)
+* CONFIG_EVENT_TRACING enabled
+* CONFIG_PERF_EVENTS enabled
+* CONFIG_DEBUG_FS enabled
+     make sure debugfs mounted before insmod ktapvm
+     mount debugfs: mount -t debugfs none /sys/kernel/debug/
+* libelf (optional)
+     Install elfutils-libelf-devel on RHEL-based distros, or libelf-dev on
+     Debian-based distros.
+     Use `make NO_LIBELF=1` to build without libelf support.
+     libelf is required for resolving symbols to addresses in DSO, and for sdt.
+
+Note that those configuration is always enabled in Linux distribution,
+like REHL, Fedora, Ubuntu, etc.
+
+1. Clone ktap from github
+
+        $ git clone http://github.com/ktap/ktap.git
+
+2. Compiling ktap
+
+        $ cd ktap
+        $ make       #generate ktapvm kernel module and ktap binary
+
+3. Load ktapvm kernel module(make sure debugfs mounted)
+
+        $ make load  #need to be root or have sudo access
+
+4. Running ktap
+
+        $ ./ktap scripts/helloworld.kp
+
+
+# Language basics
+
+## Syntax basics
+
+ktap's syntax is design on the mind of C language syntax friendly,
+to make it easy scripting by kernel developer.
+
+1. Variable declaration
+The biggest syntax differences with C is that ktap is a dynamic typed
+language, so you won't need add any variable type declaration, just
+use the variable.
+
+2. function
+All functions in ktap should use keyword "function" declaration
+
+3. comments
+The comments of ktap is starting from '#', long comments doesn't support now.
+
+4. others
+Don't need place any ';' at the ending of statement in ktap.
+ktap use free syntax style, so you can choose to use the ';' or not.
+
+ktap use nil as NULL, the result of any number operate on nil is nil.
+
+ktap don't have array structure, also don't have any pointer operation.
+
+## Control structures
+
+ktap if/else is same as C language.
+
+There have two method of for-loop in ktap:
+
+    for (i = init, limit, step) { body }
+
+this is same as below in C:
+
+    for (i = init; i < limit; i += step) { body }
+
+The next for-loop method is:
+
+    for (k, v in pairs(t)) { body }   # looping all elements of table
+
+Note that ktap don't have "continue" keyword, but C does.
+
+## Date structures
+
+Associative array is heavily used in ktap, it's also called by table.
+
+table declaration:
+
+    t = {}
+
+how to use table:
+
+    t[1] = 1
+    t[1] = "xxx"
+    t["key"] = 10
+    t["key"] = "value"
+
+    for (k, v in pairs(t)) { body }   # looping all elements of table
+
+
+# Built in functions and librarys
+
+## Built in functions
+
+**print (...)**
+Receives any number of arguments, and prints their values,
+print is not intended for formatted output, but only as a
+quick way to show a value, typically for debugging.
+For formatted output, use printf.
+
+**printf (fmt, ...)**
+Similar with C printf, use for format string output.
+
+**pairs (t)**
+Returns three values: the next function, the table t, and nil,
+so that the construction
+for (k,v in pairs(t)) { body }
+will iterate over all key-value pairs of table t.
+
+**len (t) /len (s)**
+If the argument is string, return length of string,
+if the argument is table, return counts of table pairs.
+
+**in_interrupt ()**
+checking is context is interrupt context
+
+**exit ()**
+quit ktap executing, similar with exit syscall
+
+**pid ()**
+return current process pid
+
+**tid ()**
+return current thread id
+
+**uid ()**
+return current process uid
+
+**execname ()**
+return current process exec name string
+
+**cpu ()**
+return current cpu id
+
+**arch ()**
+return machine architecture, like x86, arm, etc.
+
+**kernel_v ()**
+return Linux kernel version string, like 3.9, etc.
+
+**user_string (addr)**
+Receive userspace address, read string from userspace, return string.
+
+**histogram (t)**
+Receive table, output table histogram to user.
+
+**curr_task_info (offset, fetch_bytes)**
+fetch value in field offset of task_struct structure, argument fetch_bytes
+could be 4 or 8, if fetch_bytes is not given, default is 4.
+
+user may need to get field offset by gdb, for example:
+gdb vmlinux
+(gdb)p &(((struct task_struct *)0).prio)
+
+**print_backtrace ()**
+print current task stack info
+
+
+## Librarys
+
+### Kdebug Library
+
+**kdebug.probe_by_id (eventdef_info, eventfun)**
+
+This function is underly representation of high level tracing primitive.
+Note that eventdef_info is just a userspace memory pointer refer to real
+eventdef_info structure, the structure defintion is:
+
+        struct ktap_eventdef_info {
+            int nr; /* the number to id */
+            int *id_arr; /* id array */
+            char *filter;
+        };
+
+Those id is read from /sys/kernel/debug/tracing/events/$SYS/$EVENT/id
+
+The second argument in above examples is a function:
+function eventfun () { action }
+
+
+**kdebug.probe_end (endfunc)**
+
+This function is used for invoking a function when tracing end, it will wait
+until user press CTRL+C to stop tracing, then ktap will call endfunc function,
+user could show tracing results in that function, or do other things.
+
+User don't have to use kdebug library directly, use trace/trace_end keyword.
+
+### Timer Library
+
+
+
+# Linux tracing basics
+
+tracepoints, probe, timer
+filters
+above explaintion
+Ring buffer
+
+# Tracing semantics in ktap
+
+## Tracing block
+
+**trace EVENTDEF /FILTER/ { ACTION }**
+
+This is the basic tracing block for ktap, you need to use a specific EVENTDEF
+string, and own event function.
+
+There have four type of EVENTDEF, tracepoint, kprobe, uprobe, sdt.
+
+- tracepoint:
+
+       EventDef               Description
+       --------------------   -------------------------------
+       syscalls:*             trace all syscalls events
+       syscalls:sys_enter_*   trace all syscalls entry events
+       kmem:*                 trace all kmem related events
+       sched:*                trace all sched related events
+       sched:sched_switch     trace sched_switch tracepoint
+       \*:\*                  trace all tracepoints in system
+
+       All tracepoint events are based on:
+                 /sys/kernel/debug/tracing/events/$SYS/$EVENT
+
+- ftrace(kernel newer than 3.3, and must compiled with CONFIG_FUNCTION_TRACER)
+
+       EventDef               Description
+       --------------------   -------------------------------
+       ftrace:function        trace kernel functions based on ftrace
+
+       User need to use filter (/ip==*/) to trace specfic functions.
+       Function must be listed in /sys/kernel/debug/tracing/available_filter_functions
+
+> ***Note*** of function event
+>
+> perf support ftrace:function tracepoint since Linux 3.3(see below commit),
+> ktap is based on perf callback, so it means kernel must be newer than 3.3
+> then can use this feature.
+>
+>     commit ced39002f5ea736b716ae233fb68b26d59783912
+>     Author: Jiri Olsa <jolsa@redhat.com>
+>     Date:   Wed Feb 15 15:51:52 2012 +0100
+>
+>     ftrace, perf: Add support to use function tracepoint in perf
+>
+
+- kprobe:
+
+       EventDef               Description
+       --------------------   -----------------------------------
+       probe:schedule         trace schedule function
+       probe:schedule%return  trace schedule function return
+       probe:SyS_write        trace SyS_write function
+       probe:vfs*             trace wildcards vfs related function
+
+       kprobe functions must be listed in /proc/kallsyms
+- uprobe:
+
+       EventDef                               Description
+       ------------------------------------   ---------------------------
+       probe:/lib64/libc.so.6:malloc          trace malloc function
+       probe:/lib64/libc.so.6:malloc%return   trace malloc function return
+       probe:/lib64/libc.so.6:free            trace free function
+       probe:/lib64/libc.so.6:0x82000         trace function with file offset 0x82000
+       probe:/lib64/libc.so.6:*               trace all libc function
+
+       symbol resolving need libelf support
+
+- sdt:
+
+       EventDef                               Description
+       ------------------------------------   --------------------------
+       sdt:/libc64/libc.so.6:lll_futex_wake   trace stapsdt lll_futex_wake
+       sdt:/libc64/libc.so.6:*                trace all static markers in libc
+
+       sdt resolving need libelf support
+
+
+**trace_end { ACTION }**
+
+## Tracing built-in variables
+
+**argevent**
+event object, you can print it by: print(argevent), it will print events
+into human readable string, the result is mostly same as each entry of
+/sys/kernel/debug/tracing/trace
+
+**argname**
+event name, each event have a name associated with it.
+
+**arg1..9**
+get argument 1..9 of event object.
+
+> ***Note*** of arg offset
+>
+> The arg offset(1..9) is determined by event format shown in debugfs.
+>
+>     #cat /sys/kernel/debug/tracing/events/sched/sched_switch/format
+>     name: sched_switch
+>     ID: 268
+>     format:
+>         field:char prev_comm[32];         <- arg1
+>         field:pid_t prev_pid;             <- arg2
+>         field:int prev_prio;              <- arg3
+>         field:long prev_state;            <- arg4
+>         field:char next_comm[32];         <- arg5
+>         field:pid_t next_pid;             <- arg6
+>         field:int next_prio;              <- arg7
+>
+> As shown, tracepoint event sched:sched_switch have 7 arguments, from arg1 to
+> arg7.
+>
+> Need to note that arg1 of syscall event is syscall number, not first argument
+> of syscall function. Use arg2 as first argument of syscall function.
+> For example:
+>
+>     SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
+>                                         <arg2>             <arg3>       <arg4>
+>
+> This is similar with kprobe and uprobe, the arg1 of kprobe/uprobe event
+> always is _probe_ip, not the first argument given by user, for example:
+>
+>     # ktap -e 'trace probe:/lib64/libc.so.6:malloc size=%di'
+>
+>     # cat /sys/kernel/debug/tracing/events/ktap_uprobes_3796/malloc/format
+>         field:unsigned long __probe_ip;   <- arg1
+>         field:u64 size;                   <- arg2
+
+
+## Timer syntax
+
+**tick-Ns        { ACTION }**
+**tick-Nsec      { ACTION }**
+**tick-Nms       { ACTION }**
+**tick-Nmsec     { ACTION }**
+**tick-Nus       { ACTION }**
+**tick-Nusec     { ACTION }**
+
+**profile-Ns     { ACTION }**
+**profile-Nsec   { ACTION }**
+**profile-Nms    { ACTION }**
+**profile-Nmsec  { ACTION }**
+**profile-Nus    { ACTION }**
+**profile-Nusec  { ACTION }**
+
+architecture overview picture reference(pnp format)
+one-liners
+simple event tracing
+
+# Advanced tracing pattern
+
+Aggregation/Histogram
+thread local
+flame graph
+
+# Overhead/Performance
+
+ktap have more fast boot time thant Systemtap(try the helloword script)
+ktap have little memory usage than Systemtap
+and some scripts show that ktap have a little overhead than Systemtap
+(we choosed two scripts to compare, function profile, stack profile.
+this is not means all scripts in Systemtap have big overhead than ktap)
+
+
+# FAQ
+
+**Q: Why use bytecode design?**
+A: Using bytecode would be a clean and lightweight solution,
+   you don't need gcc toolchain to compile every scripts, all you
+   need is a ktapvm kernel modules and userspace tool called ktap.
+   Since its language virtual machine design, it have great portability,
+   suppose you are working at a multi-arch cluster, if you want to run
+   a tracing script on each board, you won't need cross-compile tracing
+   script onto all board, what you really need to do is use ktap tool
+   to run script just in time.
+
+   Bytecode based design also will make executing more safer, than native code
+   generation.
+
+   Reality already showing that SystemTap is not widely used in embedded Linux,
+   caused by problem of SystemTap's architecture design choice, it's a natural
+   design for Redhat and IBM, because Redhat/IBM is focusing on server area,
+   not embedded area.
+
+**Q: What's the differences with SystemTap and Dtrace?**
+A: For SystemTap, the answer is already mentioned at above question,
+   SystemTap use translator design, for trade-off on performance with usability,
+   based on GCC, that's what ktap want to solve.
+
+   For Dtrace, one common design with Dtrace is also use bytecode, so basically
+   Dtrace and ktap is on the same road. There have some projects aim to porting
+   Dtrace from Solaris to Linux, but the process is still on the road, Dtrace
+   is rooted in Solaris, and there have many huge differences between Solaris
+   tracing infrastructure with Linux's.
+
+   Dtrace is based on D language, a language subset of C, it's a restricted
+   language, like without for-looping, for safty use in production system.
+   It seems that Dtrace for Linux only support x86 architecture, not work on
+   powerpc and arm/mips, obviously it's not suit for embedded Linux currently.
+
+   Dtrace use ctf as input for debuginfo handing, compare with vmlinux for
+   SystemTap.
+
+   On the license part, Dtrace is released as CDDL, which is incompatible with
+   GPL(this is why it's impossible to upstream Dtrace into mainline).
+
+**Q: Why use dynamically typed language? but not statically typed language?**
+A: It's hard to say which one is more better than other, dynamically typed
+   language bring efficiency and fast prototype production, but loosing type
+   check at compiling phase, and easy to make mistake in runtime, also it's
+   need many runtime checking, In contrast, statically typed language win on
+   programing safety, and performance. Statically language would suit for
+   interoperate with kernel, as kernel is wrote mainly in C, Need to note that
+   SystemTap and Dtrace both is statically language.
+
+   ktap choose dynamically typed language as initial implementation.
+
+**Q: Why we need ktap for event tracing? There already have a built-in ftrace**
+A: This also is a common question for all dynamic tracing tool, not only ktap.
+   ktap provide more flexibility than built-in tracing infrastructure. Suppose
+   you need print a global variable when tracepoint hit, or you want print
+   backtrace, even more, you want to store some info into associative array, and
+   display it in histogram style when tracing end, in these case, some of them
+   ftrace can take it, some of them ftrace can not.
+   Overall, ktap provide you with great flexibility to scripting your own trace
+   need.
+
+**Q: How about the performance? Is ktap slow?**
+A: ktap is not slow, the bytecode is very high-level, based on lua, the language
+   virtual machine is register-based(compare with stack-based), with little
+   instruction, the table data structure is heavily optimized in ktapvm.
+   ktap use per-cpu allocation in many place, without global locking scheme,
+   it's very fast when executing tracepoint callback.
+   Performance benchmark showing that the overhead of ktap running is nearly
+   10%(store event name into associative array), compare with full speed
+   running without any tracepoint enabled.
+
+   ktap will optimize overhead all the time, hopefully the overhead will
+   decrease to little than 5%, even more.
+
+**Q: Why not porting a high level language implementation into kernel directly?
+   Like python/JVM?**
+A: I take serious on the size of vm and memory footprint. Python vm is large,
+   it's not suit to embed into kernel, and python have some functionality
+   which we don't need.
+
+   The bytecode of other high level language is also big, ktap only have 32
+   bytecodes, python/java/erlang have nearly two hundred bytecodes.
+   There also have some problems when porting those language into kernel,
+   userspace programming have many differences with kernel programming,
+   like float numbers, handle sleeping code carefully in kernel, deadloop is
+   not allowed in kernel, multi-thread management, etc.., so it's impossible
+   to porting language implementation into kernel with little adaption work.
+
+**Q: What's the status of ktap now?**
+A: Basically it works on x86-32, x86-64, powerpc, arm, it also could work for
+   other hardware architecture, but not proven yet(I don't have enough hardware
+   to test)
+   If you found some bug, fix it on you own programming skill, or report to me.
+
+**Q: How to hack ktap? I want to write some extensions onto ktap.**
+A: welcome hacking.
+   You can write your own library to fulfill your specific need,
+   you can write any script as you want.
+
+**Q: What's the plan of ktap? any roadmap?**
+A: the current plan is deliver stable ktapvm kernel modules, more ktap script,
+   and bugfix.
+
+
+# References
+
+* [Linux Performance Analysis and Tools][LPAT]
+* [Dtrace Blog][dtraceblog]
+* [Dtrace User Guide][dug]
+* [LWN: ktap -- yet another kernel tracer][lwn1]
+* [LWN: Ktap almost gets into 3.13][lwn2]
+* [staging: ktap: add to the kernel tree][ktap_commit]
+* [ktap introduction in LinuxCon Japan 2013][lcj](content is out of date)
+* [ktap Examples by Brendan Gregg][KEBG]
+
+[LPAT]: http://www.brendangregg.com/Slides/SCaLE_Linux_Performance2013.pdf
+[dtraceblog]: http://dtrace.org/blogs/
+[dug]: http://docs.huihoo.com/opensolaris/dtrace-user-guide/html/index.html
+[lwn1]: http://lwn.net/Articles/551314/
+[lwn2]: http://lwn.net/Articles/572788/
+[ktap_commit]: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c63a164271f81220ff4966d41218a9101f3d0ec4
+[lcj]: http://events.linuxfoundation.org/sites/events/files/lcjpcojp13_zhangwei.pdf
+[KEBG]: http://www.brendangregg.com/ktap.html
+
+# History
+
+* ktap was invented at 2002
+* First RFC sent to LKML at 2012.12.31
+* The code was released in github at 2013.01.18
+* ktap released v0.1 at 2013.05.21
+* ktap released v0.2 at 2013.07.31
+* ktap released v0.3 at 2013.10.29
+
+For more release info, please look at RELEASES.txt in project root directory.
+
+# Examples
+
+1. simplest one-liner command to enable all tracepoints
+
+        ktap -e "trace *:* { print(argevent) }"
+
+2. syscall tracing on target process
+
+        ktap -e "trace syscalls:* { print(argevent) }" -- ls
+
+3. ftrace(kernel newer than 3.3, and must compiled with CONFIG_FUNCTION_TRACER)
+
+        ktap -e "trace ftrace:function { print(argevent) }"
+
+        ktap -e "trace ftrace:function /ip==mutex*/ { print(argevent) }"
+
+4. simple syscall tracing
+
+        trace syscalls:* {
+                print(cpu(), pid(), execname(), argevent)
+        }
+
+5. syscall tracing in histogram style
+
+        s = {}
+
+        trace syscalls:sys_enter_* {
+                s[argname] += 1
+        }
+
+        trace_end {
+                histogram(s)
+        }
+
+6. kprobe tracing
+
+        trace probe:do_sys_open dfd=%di fname=%dx flags=%cx mode=+4($stack) {
+                print("entry:", execname(), argevent)
+        }
+
+        trace probe:do_sys_open%return fd=$retval {
+                print("exit:", execname(), argevent)
+        }
+
+7. uprobe tracing
+
+        trace probe:/lib/libc.so.6:malloc {
+                print("entry:", execname(), argevent)
+        }
+
+        trace probe:/lib/libc.so.6:malloc%return {
+                print("exit:", execname(), argevent)
+        }
+
+8. stapsdt tracing (userspace static marker)
+
+        trace sdt:/lib64/libc.so.6:lll_futex_wake {
+                print("lll_futex_wake", execname(), argevent)
+        }
+
+        or:
+
+        #trace all static mark in libc
+        trace sdt:/lib64/libc.so.6:* {
+                print(execname(), argevent)
+        }
+
+9. timer
+
+        tick-1ms {
+                printf("time fired on one cpu\n");
+        }
+
+        profile-2s {
+                printf("time fired on every cpu\n");
+        }
+
+10. FFI (Call kernel function from ktap script, need compile with FFI=1)
+
+        cdef[[
+                int printk(char *fmt, ...);
+        ]]
+
+        C.printk("This message is called from ktap ffi\n")
+
+More examples can be found at [samples][samples_dir] directory.
+
+[samples_dir]: https://github.com/ktap/ktap/tree/master/samples
+
+# Appendix
+
+Here is the complete syntax of ktap in extended BNF.
+(based on lua syntax: http://www.lua.org/manual/5.1/manual.html#5.1)
+
+        chunk ::= {stat [';']} [laststat [';']
+
+        block ::= chunk
+
+        stat ::=  varlist '=' explist |
+                 functioncall |
+                 { block } |
+                 while exp { block } |
+                 repeat block until exp |
+                 if exp { block {elseif exp { block }} [else block] } |
+                 for Name '=' exp ',' exp [',' exp] { block } |
+                 for namelist in explist { block } |
+                 function funcname funcbody |
+                 local function Name funcbody |
+                 local namelist ['=' explist]
+
+        laststat ::= return [explist] | break
+
+        funcname ::= Name {'.' Name} [':' Name]
+
+        varlist ::= var {',' var}
+
+        var ::=  Name | prefixexp '[' exp ']'| prefixexp '.' Name
+
+        namelist ::= Name {',' Name}
+
+        explist ::= {exp ',' exp
+
+        exp ::=  nil | false | true | Number | String | '...' | function |
+                 prefixexp | tableconstructor | exp binop exp | unop exp
+
+        prefixexp ::= var | functioncall | '(' exp ')'
+
+        functioncall ::=  prefixexp args | prefixexp ':' Name args
+
+        args ::=  '(' [explist] ')' | tableconstructor | String
+
+        function ::= function funcbody
+
+        funcbody ::= '(' [parlist] ')' { block }
+
+        parlist ::= namelist [',' '...'] | '...'
+
+        tableconstructor ::= '{' [fieldlist] '}'
+
+        fieldlist ::= field {fieldsep field} [fieldsep]
+
+        field ::= '[' exp ']' '=' exp | Name '=' exp | exp
+
+        fieldsep ::= ',' | ';'
+
+        binop ::= '+' | '-' | '*' | '/' | '^' | '%' | '..' |
+                  '<' | '<=' | '>' | '>=' | '==' | '!=' |
+                  and | or
+
+        unop ::= '-'
+
diff --git a/drivers/staging/ktap/include/ktap_ffi.h b/drivers/staging/ktap/include/ktap_ffi.h
new file mode 100644 (file)
index 0000000..c8167f0
--- /dev/null
@@ -0,0 +1,180 @@
+#ifndef __KTAP_FFI_H__
+#define __KTAP_FFI_H__
+
+#ifdef CONFIG_KTAP_FFI
+
+#include "../include/ktap_types.h"
+
+/*
+ * Types design in FFI module
+ *
+ * ktap_cdata is an instance of csymbol, so it's a combination of csymbol
+ * and it's actual data in memory.
+ *
+ * csymbol structs are globally unique and readonly type that represent C
+ * types.  For non scalar C types like struct and function, helper structs are
+ * used to store detailed information. See csymbol_func and csymbol_struct for
+ * more information.
+ */
+
+typedef enum {
+       /* 0 - 4 */
+       FFI_VOID,
+       FFI_UINT8,
+       FFI_INT8,
+       FFI_UINT16,
+       FFI_INT16,
+       /* 5 - 9 */
+       FFI_UINT32,
+       FFI_INT32,
+       FFI_UINT64,
+       FFI_INT64,
+       FFI_PTR,
+       /* 10 - 12 */
+       FFI_FUNC,
+       FFI_STRUCT,
+       FFI_UNKNOWN,
+} ffi_type;
+#define NUM_FFI_TYPE ((int)FFI_UNKNOWN)
+
+
+/* following struct and macros are added for C typedef
+ * size and alignment calculation */
+typedef struct {
+       size_t size;
+       size_t align;
+       const char *name;
+} ffi_mode;
+extern const ffi_mode const ffi_type_modes[];
+
+#define ffi_type_size(t) (ffi_type_modes[t].size)
+#define ffi_type_align(t) (ffi_type_modes[t].align)
+#define ffi_type_name(t) (ffi_type_modes[t].name)
+
+
+/* start of csymbol definition */
+#define CSYM_NAME_MAX_LEN 64
+
+typedef struct csymbol_func {
+       void *addr;             /* function address */
+       csymbol_id ret_id;      /* function return type */
+       int arg_nr;             /* number of arguments */
+       csymbol_id *arg_ids;    /* function argument types */
+       unsigned has_var_arg;   /* is this a var arg function? */
+} csymbol_func;
+
+typedef struct struct_member {
+       char name[CSYM_NAME_MAX_LEN];   /* name for this struct member */
+       csymbol_id id;                  /* type for this struct member */
+} struct_member;
+
+typedef struct csymbol_struct {
+       int memb_nr;                    /* number of members */
+       struct_member *members;         /* array for each member definition */
+       size_t size;                    /* bytes used to store struct */
+       /* alignment of the struct, 0 indicates uninitialization */
+       size_t align;
+} csymbol_struct;
+
+
+/* wrapper struct for all C symbols */
+typedef struct csymbol {
+       char name[CSYM_NAME_MAX_LEN];   /* name for this symbol */
+       ffi_type type;                  /* type for this symbol  */
+       /* following members are used only for non scalar C types */
+       union {
+               csymbol_id p;           /* pointer type */
+               csymbol_func f;         /* C function type */
+               csymbol_struct st;      /* struct type */
+               csymbol_id td;          /* typedef type */
+       } u;
+} csymbol;
+
+/* lookup csymbol address by it's id */
+inline csymbol *ffi_get_csym_by_id(ktap_state *ks, csymbol_id id);
+#define id_to_csym(ks, id) (ffi_get_csym_by_id(ks, id))
+
+/* helper macros for struct csymbol */
+#define csym_type(cs) ((cs)->type)
+#define csym_name(cs) ((cs)->name)
+
+/*
+ * helper macros for pointer symbol
+ */
+#define csym_ptr_deref_id(cs) ((cs)->u.p)
+#define csym_set_ptr_deref_id(cs, id) ((cs)->u.p = (id))
+/* following macro gets csymbol address */
+#define csym_ptr_deref(ks, cs) (id_to_csym(ks, csym_ptr_deref_id(cs)))
+
+/*
+ * helper macros for function symbol
+ * csym_* accepts csymbol type
+ * csymf_* accepts csymbol_func type
+ */
+#define csymf_addr(csf) ((csf)->addr)
+#define csymf_ret_id(csf) ((csf)->ret_id)
+#define csymf_arg_nr(csf) ((csf)->arg_nr)
+#define csymf_arg_ids(csf) ((csf)->arg_ids)
+/* get csymbol id for the nth argument */
+#define csymf_arg_id(csf, n) ((csf)->arg_ids[n])
+#define csym_func(cs) (&((cs)->u.f))
+#define csym_func_addr(cs) (csymf_addr(csym_func(cs)))
+#define csym_func_arg_ids(cs) (csymf_arg_ids(csym_func(cs)))
+/* following macros get csymbol address */
+#define csymf_ret(ks, csf) (id_to_csym(ks, csymf_ret_id(csf)))
+/* get csymbol address for the nth argument */
+#define csymf_arg(ks, csf, n) (id_to_csym(ks, csymf_arg_id(csf, n)))
+#define csym_func_arg(ks, cs, n) (csymf_arg(ks, csym_func(cs), n))
+
+/*
+ * helper macors for struct symbol
+ * csym_* accepts csymbol type
+ * csymst_* accepts csymbol_struct type
+ */
+#define csymst_mb_nr(csst) ((csst)->memb_nr)
+#define csym_struct(cs) (&(cs)->u.st)
+#define csym_struct_mb(cs) (csymst_mb(ks, csym_struct(cs), n))
+/* following macro gets csymbol address for the nth struct member */
+#define csymst_mb(ks, csst, n) (id_to_csym(ks, (csst)->members[n].id))
+
+
+/*
+ * helper macros for ktap_cdata type
+ */
+#define cd_csym_id(cd) ((cd)->id)
+#define cd_set_csym_id(cd, id) (cd_csym_id(cd) = (id))
+#define cd_csym(ks, cd) (id_to_csym(ks, cd_csym_id(cd)))
+#define cd_type(ks, cd) (cd_csym(ks, cd)->type)
+
+#define cd_int(cd) ((cd)->u.i)
+#define cd_ptr(cd) ((cd)->u.p)
+#define cd_struct(cd) ((cd)->u.st)
+
+
+#ifdef __KERNEL__
+size_t csym_size(ktap_state *ks, csymbol *sym);
+size_t csym_align(ktap_state *ks, csymbol *sym);
+size_t csym_struct_offset(ktap_state *ks, csymbol_struct *csst, int idx);
+void init_csym_struct(ktap_state *ks, csymbol_struct *csst);
+
+void kp_ffi_free_symbol(ktap_state *ks);
+csymbol_id ffi_get_csym_id(ktap_state *ks, char *name);
+
+ktap_cdata *kp_cdata_new(ktap_state *ks);
+void kp_cdata_dump(ktap_state *ks, ktap_cdata *cd);
+ktap_cdata *kp_cdata_new_ptr(ktap_state *ks, void *addr, csymbol_id id);
+ktap_cdata *kp_cdata_new_struct(ktap_state *ks, void *val, csymbol_id id);
+
+int kp_ffi_call(ktap_state *ks, csymbol_func *cf);
+#endif /* for __KERNEL__ */
+
+#else
+
+static void __maybe_unused kp_ffi_free_symbol(ktap_state *ks)
+{
+       return;
+}
+
+#endif /* CONFIG_KTAP_FFI */
+
+#endif /* __KTAP_FFI_H__ */
diff --git a/drivers/staging/ktap/include/ktap_opcodes.h b/drivers/staging/ktap/include/ktap_opcodes.h
new file mode 100644 (file)
index 0000000..fd9f955
--- /dev/null
@@ -0,0 +1,239 @@
+#ifndef __KTAP_BYTECODE_H__
+#define __KTAP_BYTECODE_H__
+
+
+/* opcode is copied from lua initially */
+
+typedef enum {
+/*----------------------------------------------------------------------
+ * name            args    description
+ * ------------------------------------------------------------------------*/
+OP_MOVE,/*      A B     R(A) := R(B)                                    */
+OP_LOADK,/*     A Bx    R(A) := Kst(Bx)                                 */
+OP_LOADKX,/*    A       R(A) := Kst(extra arg)                          */
+OP_LOADBOOL,/*  A B C   R(A) := (Bool)B; if (C) pc++                    */
+OP_LOADNIL,/*   A B     R(A), R(A+1), ..., R(A+B) := nil                */
+OP_GETUPVAL,/*  A B     R(A) := UpValue[B]                              */
+
+OP_GETTABUP,/*  A B C   R(A) := UpValue[B][RK(C)]                       */
+OP_GETTABLE,/*  A B C   R(A) := R(B)[RK(C)]                             */
+
+OP_SETTABUP,/*  A B C   UpValue[A][RK(B)] := RK(C)                      */
+OP_SETTABUP_INCR,/*  A B C   UpValue[A][RK(B)] += RK(C)                 */
+OP_SETTABUP_AGGR,/*  A B C   UpValue[A][RK(B)] <<< RK(C)                */
+OP_SETUPVAL,/*  A B     UpValue[B] := R(A)                              */
+OP_SETTABLE,/*  A B C   R(A)[RK(B)] := RK(C)                            */
+OP_SETTABLE_INCR,/*  A B C   R(A)[RK(B)] += RK(C)                       */
+OP_SETTABLE_AGGR,/*  A B C   R(A)[RK(B)] <<< RK(C)                      */
+
+OP_NEWTABLE,/*  A B C   R(A) := {} (size = B,C)                         */
+
+OP_SELF,/*      A B C   R(A+1) := R(B); R(A) := R(B)[RK(C)]             */
+
+OP_ADD,/*       A B C   R(A) := RK(B) + RK(C)                           */
+OP_SUB,/*       A B C   R(A) := RK(B) - RK(C)                           */
+OP_MUL,/*       A B C   R(A) := RK(B) * RK(C)                           */
+OP_DIV,/*       A B C   R(A) := RK(B) / RK(C)                           */
+OP_MOD,/*       A B C   R(A) := RK(B) % RK(C)                           */
+OP_POW,/*       A B C   R(A) := RK(B) ^ RK(C)                           */
+OP_UNM,/*       A B     R(A) := -R(B)                                   */
+OP_NOT,/*       A B     R(A) := not R(B)                                */
+OP_LEN,/*       A B     R(A) := length of R(B)                          */
+
+OP_CONCAT,/*    A B C   R(A) := R(B).. ... ..R(C)                       */
+
+OP_JMP,/*       A sBx   pc+=sBx; if (A) close all upvalues >= R(A) + 1  */
+OP_EQ,/*        A B C   if ((RK(B) == RK(C)) != A) then pc++            */
+OP_LT,/*        A B C   if ((RK(B) <  RK(C)) != A) then pc++            */
+OP_LE,/*        A B C   if ((RK(B) <= RK(C)) != A) then pc++            */
+
+OP_TEST,/*      A C     if not (R(A) <=> C) then pc++                   */
+OP_TESTSET,/*   A B C   if (R(B) <=> C) then R(A) := R(B) else pc++     */
+
+OP_CALL,/*      A B C   R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
+OP_TAILCALL,/*  A B C   return R(A)(R(A+1), ... ,R(A+B-1))              */
+OP_RETURN,/*    A B     return R(A), ... ,R(A+B-2)      (see note)      */
+
+OP_FORLOOP,/*   A sBx   R(A)+=R(A+2);
+                        if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
+OP_FORPREP,/*   A sBx   R(A)-=R(A+2); pc+=sBx                           */
+
+OP_TFORCALL,/*  A C     R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));  */
+OP_TFORLOOP,/*  A sBx   if R(A+1) != nil then { R(A)=R(A+1); pc += sBx }*/
+
+OP_SETLIST,/*   A B C   R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B        */
+
+OP_CLOSURE,/*   A Bx    R(A) := closure(KPROTO[Bx])                     */
+
+OP_VARARG,/*    A B     R(A), R(A+1), ..., R(A+B-2) = vararg            */
+
+OP_EXTRAARG,/*   Ax      extra (larger) argument for previous opcode     */
+
+OP_EVENT,/*  A B C   R(A) := R(B)[C]                             */
+
+OP_EVENTNAME, /* A     R(A) = event_name() */
+
+OP_EVENTARG,/* A B     R(A) := event_arg(B)*/
+
+OP_LOAD_GLOBAL,/*  A B C   R(A) := R(B)[C]                             */
+
+OP_EXIT,
+
+} OpCode;
+
+
+#define NUM_OPCODES     ((int)OP_LOAD_GLOBAL + 1)
+
+
+enum OpMode {iABC, iABx, iAsBx, iAx};  /* basic instruction format */
+
+
+/*
+ * ** size and position of opcode arguments.
+ * */
+#define SIZE_C          9
+#define SIZE_B          9
+#define SIZE_Bx         (SIZE_C + SIZE_B)
+#define SIZE_A          8
+#define SIZE_Ax         (SIZE_C + SIZE_B + SIZE_A)
+
+#define SIZE_OP         6
+
+#define POS_OP          0
+#define POS_A           (POS_OP + SIZE_OP)
+#define POS_C           (POS_A + SIZE_A)
+#define POS_B           (POS_C + SIZE_C)
+#define POS_Bx          POS_C
+#define POS_Ax          POS_A
+
+
+
+/*
+ * ** limits for opcode arguments.
+ * ** we use (signed) int to manipulate most arguments,
+ * ** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
+ * */
+#define MAXARG_Bx        ((1<<SIZE_Bx)-1)
+#define MAXARG_sBx        (MAXARG_Bx>>1)         /* `sBx' is signed */
+
+#define MAXARG_Ax       ((1<<SIZE_Ax)-1)
+
+#define MAXARG_A        ((1<<SIZE_A)-1)
+#define MAXARG_B        ((1<<SIZE_B)-1)
+#define MAXARG_C        ((1<<SIZE_C)-1)
+
+
+/* creates a mask with `n' 1 bits at position `p' */
+#define MASK1(n,p)      ((~((~(ktap_instruction)0)<<(n)))<<(p))
+
+/* creates a mask with `n' 0 bits at position `p' */
+#define MASK0(n,p)      (~MASK1(n,p))
+
+/*
+ * ** the following macros help to manipulate instructions
+ * */
+
+#define GET_OPCODE(i)   ((OpCode)((i)>>POS_OP) & MASK1(SIZE_OP,0))
+#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
+                ((((ktap_instruction)o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
+
+#define getarg(i,pos,size)      ((int)((i)>>pos) & MASK1(size,0))
+#define setarg(i,v,pos,size)    ((i) = (((i)&MASK0(size,pos)) | \
+                ((((ktap_instruction)v)<<pos)&MASK1(size,pos))))
+
+#define GETARG_A(i)     getarg(i, POS_A, SIZE_A)
+#define SETARG_A(i,v)   setarg(i, v, POS_A, SIZE_A)
+
+#define GETARG_B(i)     getarg(i, POS_B, SIZE_B)
+#define SETARG_B(i,v)   setarg(i, v, POS_B, SIZE_B)
+
+#define GETARG_C(i)     getarg(i, POS_C, SIZE_C)
+#define SETARG_C(i,v)   setarg(i, v, POS_C, SIZE_C)
+
+#define GETARG_Bx(i)    getarg(i, POS_Bx, SIZE_Bx)
+#define SETARG_Bx(i,v)  setarg(i, v, POS_Bx, SIZE_Bx)
+
+#define GETARG_Ax(i)    getarg(i, POS_Ax, SIZE_Ax)
+#define SETARG_Ax(i,v)  setarg(i, v, POS_Ax, SIZE_Ax)
+
+#define GETARG_sBx(i)   (GETARG_Bx(i)-MAXARG_sBx)
+#define SETARG_sBx(i,b) SETARG_Bx((i), (unsigned int)(b)+MAXARG_sBx)
+
+#define CREATE_ABC(o,a,b,c)     (((ktap_instruction)(o))<<POS_OP) \
+                        | (((ktap_instruction)(a))<<POS_A) \
+                        | (((ktap_instruction)(b))<<POS_B) \
+                        | (((ktap_instruction)(c))<<POS_C)
+
+#define CREATE_ABx(o,a,bc)      (((ktap_instruction)(o))<<POS_OP) \
+                        | (((ktap_instruction)(a))<<POS_A) \
+                        | (((ktap_instruction)(bc))<<POS_Bx)
+
+#define CREATE_Ax(o,a)          (((ktap_instruction)(o))<<POS_OP) \
+                        | (((ktap_instruction)(a))<<POS_Ax)
+
+
+
+/*
+ * ** Macros to operate RK indices
+ * */
+
+/* this bit 1 means constant (0 means register) */
+#define BITRK           (1 << (SIZE_B - 1))
+
+/* test whether value is a constant */
+#define ISK(x)          ((x) & BITRK)
+
+/* gets the index of the constant */
+#define INDEXK(r)       ((int)(r) & ~BITRK)
+
+#define MAXINDEXRK      (BITRK - 1)
+
+/* code a constant index as a RK value */
+#define RKASK(x)        ((x) | BITRK)
+
+
+/*
+ * ** invalid register that fits in 8 bits
+ * */
+#define NO_REG          MAXARG_A
+
+
+/*
+ * ** R(x) - register
+ * ** Kst(x) - constant (in constant table)
+ * ** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
+ * */
+
+
+
+/*
+ * ** masks for instruction properties. The format is:
+ * ** bits 0-1: op mode
+ * ** bits 2-3: C arg mode
+ * ** bits 4-5: B arg mode
+ * ** bit 6: instruction set register A
+ * ** bit 7: operator is a test (next instruction must be a jump)
+ * */
+
+enum OpArgMask {
+  OpArgN,  /* argument is not used */
+  OpArgU,  /* argument is used */
+  OpArgR,  /* argument is a register or a jump offset */
+  OpArgK   /* argument is a constant or register/constant */
+};
+
+extern const u8 ktap_opmodes[NUM_OPCODES];
+
+#define getOpMode(m)    ((enum OpMode)ktap_opmodes[m] & 3)
+#define getBMode(m)     ((enum OpArgMask)(ktap_opmodes[m] >> 4) & 3)
+#define getCMode(m)     ((enum OpArgMask)(ktap_opmodes[m] >> 2) & 3)
+#define testAMode(m)    (ktap_opmodes[m] & (1 << 6))
+#define testTMode(m)    (ktap_opmodes[m] & (1 << 7))
+
+
+/* number of list items to accumulate before a SETLIST instruction */
+#define LFIELDS_PER_FLUSH       50
+
+extern const char *const ktap_opnames[NUM_OPCODES + 1];
+
+#endif /* __KTAP_BYTECODE_H__ */
diff --git a/drivers/staging/ktap/include/ktap_types.h b/drivers/staging/ktap/include/ktap_types.h
new file mode 100644 (file)
index 0000000..7388a34
--- /dev/null
@@ -0,0 +1,609 @@
+#ifndef __KTAP_TYPES_H__
+#define __KTAP_TYPES_H__
+
+#ifdef __KERNEL__
+#include <linux/perf_event.h>
+#else
+typedef char u8;
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#endif
+
+/*
+ * The first argument type of kdebug.probe_by_id()
+ * The value is a userspace memory pointer.
+ */
+typedef struct ktap_eventdef_info {
+       int nr;
+       int *id_arr;
+       char *filter;
+} ktap_eventdef_info;
+
+typedef struct ktap_parm {
+       char *trunk; /* __user */
+       int trunk_len;
+       int argc;
+       char **argv; /* __user */
+       int verbose;
+       int trace_pid;
+       int workload;
+       int trace_cpu;
+       int print_timestamp;
+       int quiet;
+} ktap_parm;
+
+/*
+ * Ioctls that can be done on a ktap fd:
+ * todo: use _IO macro in include/uapi/asm-generic/ioctl.h
+ */
+#define KTAP_CMD_IOC_VERSION           ('$' + 0)
+#define KTAP_CMD_IOC_RUN               ('$' + 1)
+#define KTAP_CMD_IOC_EXIT              ('$' + 3)
+
+#define KTAP_ENV       "_ENV"
+
+#define KTAP_VERSION_MAJOR       "0"
+#define KTAP_VERSION_MINOR       "4"
+
+#define KTAP_VERSION    "ktap " KTAP_VERSION_MAJOR "." KTAP_VERSION_MINOR
+#define KTAP_AUTHOR    "Jovi Zhangwei <jovi.zhangwei@gmail.com>"
+#define KTAP_COPYRIGHT  KTAP_VERSION "  Copyright (C) 2012-2013, " KTAP_AUTHOR
+
+#define MYINT(s)        (s[0] - '0')
+#define VERSION         (MYINT(KTAP_VERSION_MAJOR) * 16 + MYINT(KTAP_VERSION_MINOR))
+#define FORMAT          0 /* this is the official format */
+
+#define KTAP_SIGNATURE  "\033ktap"
+
+/* data to catch conversion errors */
+#define KTAPC_TAIL      "\x19\x93\r\n\x1a\n"
+
+/* size in bytes of header of binary files */
+#define KTAPC_HEADERSIZE       (sizeof(KTAP_SIGNATURE) - sizeof(char) + 2 + \
+                                6 + sizeof(KTAPC_TAIL) - sizeof(char))
+
+typedef long ktap_number;
+#define kp_number2int(i, n)    ((i) = (int)(n))
+
+typedef int ktap_instruction;
+
+typedef union ktap_gcobject ktap_gcobject;
+
+#define CommonHeader ktap_gcobject *next; u8 tt;
+
+typedef union ktap_string {
+       int dummy;  /* ensures maximum alignment for strings */
+       struct {
+               CommonHeader;
+               u8 extra;  /* reserved words for short strings; "has hash" for longs */
+               unsigned int hash;
+               size_t len;  /* number of characters in string */
+       } tsv;
+       /* short string is stored here, just after tsv */
+} ktap_string;
+
+
+struct ktap_state;
+typedef int (*ktap_cfunction) (struct ktap_state *ks);
+
+typedef struct ktap_value {
+       union {
+               ktap_gcobject *gc;    /* collectable objects */
+               void *p;         /* light userdata */
+               int b;           /* booleans */
+               ktap_cfunction f; /* light C functions */
+               ktap_number n;         /* numbers */
+       } val;
+       int type;
+} ktap_value;
+
+typedef ktap_value * StkId;
+
+
+/*
+ * Description of an upvalue for function prototypes
+ */
+typedef struct ktap_upvaldesc {
+       ktap_string *name;  /* upvalue name (for debug information) */
+       u8 instack;  /* whether it is in stack */
+       u8 idx;  /* index of upvalue (in stack or in outer function's list) */
+} ktap_upvaldesc;
+
+/*
+ * Description of a local variable for function prototypes
+ * (used for debug information)
+ */
+typedef struct ktap_locvar {
+       ktap_string *varname;
+       int startpc;  /* first point where variable is active */
+       int endpc;    /* first point where variable is dead */
+} ktap_locvar;
+
+
+typedef struct ktap_upval {
+       CommonHeader;
+       ktap_value *v;  /* points to stack or to its own value */
+       union {
+               ktap_value value;  /* the value (when closed) */
+               struct {  /* double linked list (when open) */
+                       struct ktap_upval *prev;
+                       struct ktap_upval *next;
+               } l;
+       } u;
+} ktap_upval;
+
+
+#define KTAP_MAX_STACK_ENTRIES 100
+
+typedef struct ktap_btrace {
+       CommonHeader;
+       unsigned int nr_entries;
+       /* entries stored in here, after nr_entries */
+} ktap_btrace;
+
+typedef struct ktap_closure {
+       CommonHeader;
+       u8 nupvalues;
+       struct ktap_proto *p;
+       struct ktap_upval *upvals[1];  /* list of upvalues */
+       ktap_gcobject *gclist;
+} ktap_closure;
+
+typedef struct ktap_proto {
+       CommonHeader;
+       ktap_value *k;  /* constants used by the function */
+       ktap_instruction *code;
+       struct ktap_proto **p;  /* functions defined inside the function */
+       int *lineinfo;  /* map from opcodes to source lines (debug information) */
+       struct ktap_locvar *locvars;  /* information about local variables (debug information) */
+       struct ktap_upvaldesc *upvalues;  /* upvalue information */
+       ktap_closure *cache;  /* last created closure with this prototype */
+       ktap_string  *source;  /* used for debug information */
+       int sizeupvalues;  /* size of 'upvalues' */
+       int sizek;  /* size of `k' */
+       int sizecode;
+       int sizelineinfo;
+       int sizep;  /* size of `p' */
+       int sizelocvars;
+       int linedefined;
+       int lastlinedefined;
+       u8 numparams;  /* number of fixed parameters */
+       u8 is_vararg;
+       u8 maxstacksize;  /* maximum stack used by this function */
+} ktap_proto;
+
+
+/*
+ * information about a call
+ */
+typedef struct ktap_callinfo {
+       StkId func;  /* function index in the stack */
+       StkId top;  /* top for this function */
+       struct ktap_callinfo *prev, *next;  /* dynamic call link */
+       short nresults;  /* expected number of results from this function */
+       u8 callstatus;
+       int extra;
+       union {
+               struct {  /* only for ktap functions */
+                       StkId base;  /* base for this function */
+                       const unsigned int *savedpc;
+               } l;
+               struct {  /* only for C functions */
+                       int ctx;  /* context info. in case of yields */
+                       u8 status;
+               } c;
+       } u;
+} ktap_callinfo;
+
+
+/*
+ * ktap_tab
+ */
+typedef struct ktap_tkey {
+       struct ktap_tnode *next;  /* for chaining */
+       ktap_value tvk;
+} ktap_tkey;
+
+
+typedef struct ktap_tnode {
+       ktap_value i_val;
+       ktap_tkey i_key;
+} ktap_tnode;
+
+
+typedef struct ktap_stat_data {
+       int count;
+       int sum;
+       int min, max;
+} ktap_stat_data;
+
+
+typedef struct ktap_tab {
+       CommonHeader;
+#ifdef __KERNEL__
+       arch_spinlock_t lock;
+#endif
+       u8 flags;  /* 1<<p means tagmethod(p) is not present */
+       u8 lsizenode;  /* log2 of size of `node' array */
+       int sizearray;  /* size of `array' array */
+       ktap_value *array;  /* array part */
+       ktap_tnode *node;
+       ktap_tnode *lastfree;  /* any free position is before this position */
+
+       int with_stats;  /* for aggregation table: ptable */
+       ktap_stat_data *sd_arr;
+       ktap_stat_data *sd_rec;
+
+       ktap_tnode *sorted;  /* sorted table, with linked node list */
+       ktap_tnode *sort_head;
+
+       ktap_gcobject *gclist;
+} ktap_tab;
+
+#define lmod(s,size)   ((int)((s) & ((size)-1)))
+
+/* parallel table */
+typedef struct ktap_ptab {
+       CommonHeader;
+       ktap_tab **tbl; /* percpu table */
+       ktap_tab *agg;
+} ktap_ptab;
+
+typedef struct ktap_stringtable {
+       ktap_gcobject **hash;
+       int nuse;
+       int size;
+} ktap_stringtable;
+
+#ifdef CONFIG_KTAP_FFI
+typedef int csymbol_id;
+typedef struct csymbol csymbol;
+
+/* global ffi state maintained in each ktap vm instance */
+typedef struct ffi_state {
+       ktap_tab *ctable;
+       int csym_nr;
+       csymbol *csym_arr;
+} ffi_state;
+
+/* instance of csymbol */
+typedef struct ktap_cdata {
+       CommonHeader;
+       csymbol_id id;
+       union {
+               uint64_t i;
+               void *p;        /* pointer address */
+               void *st;       /* struct member data */
+       } u;
+} ktap_cdata;
+#endif
+
+typedef struct ktap_stats {
+       int mem_allocated;
+       int nr_mem_allocate;
+       int nr_mem_free;
+       int events_hits;
+       int events_missed;
+} ktap_stats;
+
+#define KTAP_STATS(ks) this_cpu_ptr(G(ks)->stats)
+
+enum {
+       KTAP_PERCPU_DATA_STATE,
+       KTAP_PERCPU_DATA_STACK,
+       KTAP_PERCPU_DATA_BUFFER,
+       KTAP_PERCPU_DATA_BUFFER2,
+       KTAP_PERCPU_DATA_BTRACE,
+
+       KTAP_PERCPU_DATA_MAX
+};
+
+typedef struct ktap_global_state {
+       ktap_stringtable strt;  /* hash table for strings */
+       ktap_value registry;
+       unsigned int seed; /* randonized seed for hashes */
+
+       ktap_gcobject *allgc; /* list of all collectable objects */
+
+       ktap_upval uvhead; /* head of double-linked list of all open upvalues */
+
+       struct ktap_state *mainthread;
+#ifdef __KERNEL__
+       /* global percpu data(like stack) */
+       void __percpu *pcpu_data[KTAP_PERCPU_DATA_MAX][PERF_NR_CONTEXTS];
+
+       int __percpu *recursion_context[PERF_NR_CONTEXTS];
+
+       arch_spinlock_t str_lock; /* string opertion lock */
+
+       ktap_parm *parm;
+       pid_t trace_pid;
+       struct task_struct *trace_task;
+       cpumask_var_t cpumask;
+       struct ring_buffer *buffer;
+       struct dentry *trace_pipe_dentry;
+       int nr_builtin_cfunction;
+       ktap_value *cfunction_tbl;
+       struct task_struct *task;
+       int trace_enabled;
+       struct list_head timers;
+       struct list_head probe_events_head;
+       int exit;
+       int wait_user;
+       ktap_closure *trace_end_closure;
+       struct ktap_stats __percpu *stats;
+       struct kmem_cache *pevent_cache;
+#ifdef CONFIG_KTAP_FFI
+       ffi_state  ffis;
+#endif
+#endif
+       int error;
+} ktap_global_state;
+
+typedef struct ktap_state {
+       CommonHeader;
+       ktap_global_state *g;
+       int stop;
+       StkId top;
+       ktap_callinfo *ci;
+       const unsigned long *oldpc;
+       StkId stack_last;
+       StkId stack;
+       ktap_gcobject *openupval;
+       ktap_callinfo baseci;
+
+       /* list of temp collectable objects, free when thread exit */
+       ktap_gcobject *gclist;
+
+#ifdef __KERNEL__
+       struct ktap_event *current_event;
+#endif
+} ktap_state;
+
+
+typedef struct gcheader {
+       CommonHeader;
+} gcheader;
+
+/*
+ * Union of all collectable objects
+ */
+union ktap_gcobject {
+       gcheader gch;  /* common header */
+       union ktap_string ts;
+       struct ktap_closure cl;
+       struct ktap_tab h;
+       struct ktap_ptab ph;
+       struct ktap_proto p;
+       struct ktap_upval uv;
+       struct ktap_state th;  /* thread */
+       struct ktap_btrace bt;  /* backtrace object */
+#ifdef CONFIG_KTAP_FFI
+       struct ktap_cdata cd;
+#endif
+};
+
+#define gch(o)                 (&(o)->gch)
+
+/* macros to convert a GCObject into a specific value */
+#define rawgco2ts(o)           (&((o)->ts))
+
+#define gco2ts(o)              (&rawgco2ts(o)->tsv)
+#define gco2uv(o)              (&((o)->uv))
+#define obj2gco(v)             ((ktap_gcobject *)(v))
+#define check_exp(c, e)                (e)
+
+
+/* predefined values in the registry */
+#define KTAP_RIDX_MAINTHREAD     1
+#define KTAP_RIDX_GLOBALS        2
+#define KTAP_RIDX_LAST           KTAP_RIDX_GLOBALS
+
+#define KTAP_TNONE             (-1)
+
+#define KTAP_TNIL              0
+#define KTAP_TBOOLEAN          1
+#define KTAP_TLIGHTUSERDATA    2
+#define KTAP_TNUMBER           3
+#define KTAP_TSTRING           4
+#define KTAP_TSHRSTR           (KTAP_TSTRING | (0 << 4))  /* short strings */
+#define KTAP_TLNGSTR           (KTAP_TSTRING | (1 << 4))  /* long strings */
+#define KTAP_TTABLE            5
+#define KTAP_TFUNCTION         6
+#define KTAP_TCLOSURE          (KTAP_TFUNCTION | (0 << 4))  /* closure */
+#define KTAP_TCFUNCTION                (KTAP_TFUNCTION | (1 << 4))  /* light C function */
+#define KTAP_TTHREAD           7
+#define KTAP_TPROTO            8
+#define KTAP_TUPVAL            9
+#define KTAP_TEVENT            10
+#define KTAP_TBTRACE           11
+#define KTAP_TPTABLE           12
+#define KTAP_TSTATDATA         13
+#define KTAP_TCDATA            14
+/*
+ * type number is ok so far, but it may collide later between
+ * 16+ and | (1 << 4), so be careful on this.
+ */
+
+#define ttype(o)               ((o->type) & 0x3F)
+#define settype(obj, t)                ((obj)->type = (t))
+
+/* raw type tag of a TValue */
+#define rttype(o)              ((o)->type)
+
+/* tag with no variants (bits 0-3) */
+#define novariant(x)           ((x) & 0x0F)
+
+/* type tag of a TValue with no variants (bits 0-3) */
+#define ttypenv(o)             (novariant(rttype(o)))
+
+#define val_(o)                        ((o)->val)
+#define gcvalue(o)             (val_(o).gc)
+
+#define bvalue(o)              (val_(o).b)
+#define nvalue(o)              (val_(o).n)
+#define hvalue(o)              (&val_(o).gc->h)
+#define phvalue(o)             (&val_(o).gc->ph)
+#define clvalue(o)             (&val_(o).gc->cl)
+
+#define getstr(ts)             (const char *)((ts) + 1)
+#define eqshrstr(a, b)         ((a) == (b))
+#define rawtsvalue(o)          (&val_(o).gc->ts)
+#define svalue(o)              getstr(rawtsvalue(o))
+
+#define pvalue(o)              (&val_(o).p)
+#define sdvalue(o)             ((ktap_stat_data *)val_(o).p)
+#define fvalue(o)              (val_(o).f)
+#define evalue(o)              (val_(o).p)
+#define btvalue(o)             (&val_(o).gc->bt)
+#define cdvalue(o)             (&val_(o).gc->cd)
+
+#define is_nil(o)              ((o)->type == KTAP_TNIL)
+#define is_boolean(o)          ((o)->type == KTAP_TBOOLEAN)
+#define is_false(o)            (is_nil(o) || (is_boolean(o) && bvalue(o) == 0))
+#define is_shrstring(o)                ((o)->type == KTAP_TSHRSTR)
+#define is_string(o)           (((o)->type & 0x0F) == KTAP_TSTRING)
+#define is_number(o)           ((o)->type == KTAP_TNUMBER)
+#define is_table(o)            ((o)->type == KTAP_TTABLE)
+#define is_ptable(o)           ((o)->type == KTAP_TPTABLE)
+#define is_statdata(o)         ((o)->type == KTAP_TSTATDATA)
+#define is_event(o)            ((o)->type == KTAP_TEVENT)
+#define is_btrace(o)           ((o)->type == KTAP_TBTRACE)
+#define is_needclone(o)                is_btrace(o)
+#ifdef CONFIG_KTAP_FFI
+#define is_cdata(o)            ((o)->type == KTAP_TCDATA)
+#endif
+
+
+#define set_nil(obj) \
+       { ktap_value *io = (obj); io->val.n = 0; settype(io, KTAP_TNIL); }
+
+#define set_boolean(obj, x) \
+       { ktap_value *io = (obj); io->val.b = (x); settype(io, KTAP_TBOOLEAN); }
+
+#define set_number(obj, x) \
+       { ktap_value *io = (obj); io->val.n = (x); settype(io, KTAP_TNUMBER); }
+
+#define set_statdata(obj, x) \
+       { ktap_value *io = (obj); \
+         io->val.p = (x); settype(io, KTAP_TSTATDATA); }
+
+#define set_string(obj, x) \
+       { ktap_value *io = (obj); \
+         ktap_string *x_ = (x); \
+         io->val.gc = (ktap_gcobject *)x_; settype(io, x_->tsv.tt); }
+
+#define set_closure(obj, x) \
+       { ktap_value *io = (obj); \
+         io->val.gc = (ktap_gcobject *)x; settype(io, KTAP_TCLOSURE); }
+
+#define set_cfunction(obj, x) \
+       { ktap_value *io = (obj); val_(io).f = (x); settype(io, KTAP_TCFUNCTION); }
+
+#define set_table(obj, x) \
+       { ktap_value *io = (obj); \
+         val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TTABLE); }
+
+#define set_ptable(obj, x) \
+       { ktap_value *io = (obj); \
+         val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TPTABLE); }
+
+#define set_thread(obj, x) \
+       { ktap_value *io = (obj); \
+         val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TTHREAD); }
+
+#define set_event(obj, x) \
+       { ktap_value *io = (obj); val_(io).p = (x); settype(io, KTAP_TEVENT); }
+
+#define set_btrace(obj, x) \
+       { ktap_value *io = (obj); \
+         val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TBTRACE); }
+
+#ifdef CONFIG_KTAP_FFI
+#define set_cdata(obj, x) \
+       { ktap_value *io=(obj); \
+         val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TCDATA); }
+#endif
+
+#define set_obj(obj1, obj2) \
+        { const ktap_value *io2 = (obj2); ktap_value *io1 = (obj1); \
+          io1->val = io2->val; io1->type = io2->type; }
+
+#define rawequalobj(t1, t2) \
+       (((t1)->type == (t2)->type) && kp_equalobjv(NULL, t1, t2))
+
+#define incr_top(ks) {ks->top++;}
+
+#define NUMADD(a, b)    ((a) + (b))
+#define NUMSUB(a, b)    ((a) - (b))
+#define NUMMUL(a, b)    ((a) * (b))
+#define NUMDIV(a, b)    ((a) / (b))
+#define NUMUNM(a)       (-(a))
+#define NUMEQ(a, b)     ((a) == (b))
+#define NUMLT(a, b)     ((a) < (b))
+#define NUMLE(a, b)     ((a) <= (b))
+#define NUMISNAN(a)     (!NUMEQ((a), (a)))
+
+/* todo: floor and pow in kernel */
+#define NUMMOD(a, b)    ((a) % (b))
+#define NUMPOW(a, b)    (pow(a, b))
+
+#define ktap_assert(s)
+
+#define kp_realloc(ks, v, osize, nsize, t) \
+       ((v) = (t *)kp_reallocv(ks, v, osize * sizeof(t), nsize * sizeof(t)))
+
+#define kp_error(ks, args...) \
+       do { \
+               kp_printf(ks, "error: "args);   \
+               G(ks)->error = 1; \
+               kp_exit(ks);    \
+       } while(0)
+
+#ifdef __KERNEL__
+#define G(ks)   (ks->g)
+
+void kp_printf(ktap_state *ks, const char *fmt, ...);
+extern void __kp_puts(ktap_state *ks, const char *str);
+extern void __kp_bputs(ktap_state *ks, const char *str);
+
+#define kp_puts(ks, str) ({                                            \
+       static const char *trace_printk_fmt                             \
+               __attribute__((section("__trace_printk_fmt"))) =        \
+               __builtin_constant_p(str) ? str : NULL;                 \
+                                                                       \
+       if (__builtin_constant_p(str))                                  \
+               __kp_bputs(ks, trace_printk_fmt);               \
+       else                                                            \
+               __kp_puts(ks, str);             \
+})
+
+#else
+/*
+ * this is used for ktapc tstring operation, tstring need G(ks)->strt
+ * and G(ks)->seed, so ktapc need to init those field
+ */
+#define G(ks)   (&dummy_global_state)
+extern ktap_global_state dummy_global_state;
+
+#define kp_printf(ks, args...)                 printf(args)
+#define kp_puts(ks, str)                       printf("%s", str)
+#define kp_exit(ks)                            exit(EXIT_FAILURE)
+
+#endif
+
+#define __maybe_unused __attribute__((unused))
+
+/*
+ * KTAP_QL describes how error messages quote program elements.
+ * CHANGE it if you want a different appearance.
+ */
+#define KTAP_QL(x)      "'" x "'"
+#define KTAP_QS         KTAP_QL("%s")
+
+#define STRINGIFY(type) #type
+
+#endif /* __KTAP_TYPES_H__ */
+
diff --git a/drivers/staging/ktap/runtime/ffi/call_x86_64.S b/drivers/staging/ktap/runtime/ffi/call_x86_64.S
new file mode 100644 (file)
index 0000000..29b2915
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * call_x86_64.S - assembly code to call C function and handle return value
+ *
+ * This file is part of ktap by Jovi Zhangwei
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifdef __x86_64
+
+       .file "call_x86_64.S"
+       .text
+
+/*     ffi_call_assem_x86_64(void *stack, void *temp_stack,
+ *             void *rvalue, void *func_addr, ffi_type rftype)
+ *     @stack: base address of register values and new stack
+ *     @temp_stack: stack to store temporary values
+ *     @func_addr: Function address
+ *     @rvalue: where to put return value
+ *     @rftype: FFI type of return value
+ */
+       .align 2
+       .globl  ffi_call_assem_x86_64
+       .type   ffi_call_assem_x86_64,@function
+
+ffi_call_assem_x86_64:
+       movq    (%rsp), %rax    /* save return address */
+       /* move stuffs to temp memory region(void *temp_stack) */
+       movq    %rcx, (%rsi)    /* save pointer to return value */
+       movq    %r8, 8(%rsi)    /* save return_ffi_type */
+       movq    %rbp, 16(%rsi)  /* save %rbp */
+       movq    %rax, 24(%rsi)  /* save return address */
+       movq    %rsp, 32(%rsi)  /* save %rsp */
+       movq    %rsi, %rbp      /* point %rbp to temp memory region */
+
+       movq    %rdx, %r11      /* move function address to %r11 */
+
+       movq    %rdi, %r10      /* set %r10 point to register region */
+       movq    (%r10), %rdi    /* load registers */
+       movq    8(%r10), %rsi
+       movq    16(%r10), %rdx
+       movq    24(%r10), %rcx
+       movq    32(%r10), %r8
+       movq    40(%r10), %r9
+       xorq    %rax, %rax
+
+       leaq    48(%r10), %rsp
+
+       callq   *%r11
+
+       movq    32(%rbp), %rsp  /* restore %rsp */
+       movq    24(%rbp), %rcx  /* restore return address */
+       movq    %rcx, (%rsp)
+
+       movq    (%rbp), %rcx    /* get pointer to return value */
+       movq    8(%rbp), %r8    /* get return_ffi_type */
+       movq    16(%rbp), %rbp  /* restore rbp */
+
+       leaq    .Lreturn_table(%rip), %r11      /* start address of return_table */
+       movslq  (%r11, %r8, 8), %r11    /* fetch target address from table */
+       jmpq    *%r11                   /* jump according to value in table */
+
+       .align 8
+.Lreturn_table:
+       .quad   .Lreturn_void           /* FFI_VOID */
+       .quad   .Lreturn_uint8          /* FFI_UINT8 */
+       .quad   .Lreturn_int8           /* FFI_INT8 */
+       .quad   .Lreturn_uint16         /* FFI_UINT16 */
+       .quad   .Lreturn_int16          /* FFI_INT16 */
+       .quad   .Lreturn_uint32         /* FFI_UINT32 */
+       .quad   .Lreturn_int32          /* FFI_INT32 */
+       .quad   .Lreturn_uint64         /* FFI_UINT64 */
+       .quad   .Lreturn_int64          /* FFI_INT64 */
+       .quad   .Lreturn_ptr            /* FFI_PTR */
+       .quad   .Lreturn_func           /* FFI_FUNC */
+       .quad   .Lreturn_struct         /* FFI_STRUCT */
+       .quad   .Lreturn_unknown        /* FFI_UNKNOWN */
+
+       .align 8
+.Lreturn_void:
+.Lreturn_func:
+.Lreturn_unknown:
+       retq
+       .align 8
+.Lreturn_uint8:
+       movzbq  %al, %rax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_int8:
+       movsbq  %al, %rax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_uint16:
+       movzwq  %ax, %rax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_int16:
+       movswq  %ax, %rax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_uint32:
+       movl    %eax, %eax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_int32:
+       movslq  %eax, %rax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_uint64:
+.Lreturn_int64:
+.Lreturn_ptr:
+       movq    %rax, (%rcx)
+       retq
+/* Struct type indicates that struct is put into at most two registers,
+ * and 16 bytes space is always available
+ */
+       .align 8
+.Lreturn_struct:
+       movq    %rax, (%rcx)
+       movq    %rdx, 8(%rcx)
+       retq
+
+#endif /* end for __x86_64 */
diff --git a/drivers/staging/ktap/runtime/ffi/cdata.c b/drivers/staging/ktap/runtime/ffi/cdata.c
new file mode 100644 (file)
index 0000000..aaea559
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * cdata.c - support functions for ktap_cdata
+ *
+ * This file is part of ktap by Jovi Zhangwei
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include "../../include/ktap_types.h"
+#include "../../include/ktap_ffi.h"
+#include "../kp_obj.h"
+
+ktap_cdata *kp_cdata_new(ktap_state *ks)
+{
+       ktap_cdata *cd;
+
+       cd = &kp_newobject(ks, KTAP_TCDATA, sizeof(ktap_cdata), NULL)->cd;
+
+       return cd;
+}
+
+ktap_cdata *kp_cdata_new_ptr(ktap_state *ks, void *addr, csymbol_id id)
+{
+       ktap_cdata *cd;
+
+       cd = kp_cdata_new(ks);
+       cd_set_csym_id(cd, id);
+       cd_ptr(cd) = addr;
+
+       return cd;
+}
+
+ktap_cdata *kp_cdata_new_struct(ktap_state *ks, void *val, csymbol_id id)
+{
+       ktap_cdata *cd;
+
+       cd = kp_cdata_new(ks);
+       cd_set_csym_id(cd, id);
+       cd_struct(cd) = val;
+
+       return cd;
+}
+
+void kp_cdata_dump(ktap_state *ks, ktap_cdata *cd)
+{
+       switch (cd_type(ks, cd)) {
+       case FFI_PTR:
+               kp_printf(ks, "pointer(%p)", cd_ptr(cd));
+               break;
+       default:
+               kp_printf(ks, "unsupported cdata type %d!\n", cd_type(ks, cd));
+       }
+}
diff --git a/drivers/staging/ktap/runtime/ffi/ffi_call.c b/drivers/staging/ktap/runtime/ffi/ffi_call.c
new file mode 100644 (file)
index 0000000..7bd845a
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * ffi_call.c - foreign function calling library support for ktap
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include "../../include/ktap_types.h"
+#include "../../include/ktap_ffi.h"
+#include "../ktap.h"
+#include "../kp_vm.h"
+#include "../kp_obj.h"
+
+static int ffi_type_check(ktap_state *ks, csymbol_func *csf, int idx)
+{
+       StkId arg;
+       csymbol *cs;
+       ffi_type type;
+
+       if (idx >= csymf_arg_nr(csf))
+               return 0;
+       arg = kp_arg(ks, idx + 1);
+       cs = csymf_arg(ks, csf, idx);
+       type = csym_type(cs);
+
+       if (type == FFI_FUNC)
+               goto error;
+
+       switch (ttypenv(arg)) {
+       case KTAP_TLIGHTUSERDATA:
+               if (type != FFI_PTR) goto error;
+               break;
+       case KTAP_TBOOLEAN:
+       case KTAP_TNUMBER:
+               if (type != FFI_UINT8 && type != FFI_INT8
+               && type != FFI_UINT16 && type != FFI_INT16
+               && type != FFI_UINT32 && type != FFI_INT32
+               && type != FFI_UINT64 && type != FFI_INT64)
+                       goto error;
+               break;
+       case KTAP_TSTRING:
+               if (type != FFI_PTR && type != FFI_UINT8 && type != FFI_INT8)
+                       goto error;
+               break;
+       case KTAP_TCDATA:
+               if (cs != cd_csym(ks, cdvalue(arg)))
+                       goto error;
+               break;
+       default:
+               goto error;
+       }
+       return 0;
+
+ error:
+       kp_error(ks, "Error: Cannot convert to csymbol %s for arg %d\n",
+                       csym_name(cs), idx);
+       return -1;
+}
+
+static csymbol *ffi_get_arg_csym(ktap_state *ks, csymbol_func *csf, int idx)
+{
+       StkId arg;
+       csymbol *cs;
+
+       if (idx < csymf_arg_nr(csf))
+               return csymf_arg(ks, csf, idx);
+
+       arg = kp_arg(ks, idx + 1);
+       cs = id_to_csym(ks, ffi_get_csym_id(ks, "void *"));
+       switch (ttypenv(arg)) {
+       case KTAP_TLIGHTUSERDATA:
+       case KTAP_TBOOLEAN:
+       case KTAP_TNUMBER:
+       case KTAP_TSTRING:
+               return cs;
+       case KTAP_TCDATA:
+               return cd_csym(ks, cdvalue(arg));
+       default:
+               kp_error(ks, "Error: Cannot get type for arg %d\n", idx);
+               return cs;
+       }
+}
+
+static void ffi_unpack(ktap_state *ks, csymbol_func *csf, int idx,
+               char *dst, int align)
+{
+       StkId arg = kp_arg(ks, idx + 1);
+       csymbol *cs = ffi_get_arg_csym(ks, csf, idx);
+       ffi_type type = csym_type(cs);
+       size_t size = csym_size(ks, cs);
+       void *p;
+       struct ktap_cdata *cd;
+
+       /* initialize the destination section */
+       memset(dst, 0, ALIGN(size, align));
+
+       switch (ttypenv(arg)) {
+       case KTAP_TBOOLEAN:
+               memcpy(dst, &bvalue(arg), sizeof(bool));
+               return;
+       case KTAP_TLIGHTUSERDATA:
+               memcpy(dst, pvalue(arg), size);
+               return;
+       case KTAP_TNUMBER:
+               memcpy(dst, &nvalue(arg), size < sizeof(ktap_number) ?
+                               size : sizeof(ktap_number));
+               return;
+       case KTAP_TSTRING:
+               p = &rawtsvalue(arg)->tsv + 1;
+               memcpy(dst, &p, size);
+               return;
+       }
+
+       cd = cdvalue(arg);
+       switch (type) {
+       case FFI_VOID:
+               kp_error(ks, "Error: Cannot copy data from void type\n");
+               return;
+       case FFI_UINT8:
+       case FFI_INT8:
+       case FFI_UINT16:
+       case FFI_INT16:
+       case FFI_UINT32:
+       case FFI_INT32:
+       case FFI_UINT64:
+       case FFI_INT64:
+               memcpy(dst, &cd_int(cd), size);
+               return;
+       case FFI_PTR:
+               memcpy(dst, &cd_ptr(cd), size);
+               return;
+       case FFI_STRUCT:
+               memcpy(dst, cd_struct(cd), size);
+               return;
+       case FFI_FUNC:
+       case FFI_UNKNOWN:
+               kp_error(ks, "Error: internal error for csymbol %s\n",
+                               csym_name(cs));
+               return;
+       }
+}
+
+#ifdef __x86_64
+
+enum arg_status {
+       IN_REGISTER,
+       IN_MEMORY,
+       IN_STACK,
+};
+
+#define ALIGN_STACK(v, a) ((void *)(ALIGN(((uint64_t)v), a)))
+#define STACK_ALIGNMENT 8
+#define REDZONE_SIZE 128
+#define GPR_SIZE (sizeof(void *))
+#define MAX_GPR 6
+#define MAX_GPR_SIZE (MAX_GPR * GPR_SIZE)
+#define NEWSTACK_SIZE 512
+
+#define ffi_call(ks, cf, rvalue) ffi_call_x86_64(ks, cf, rvalue)
+
+extern void ffi_call_assem_x86_64(void *stack, void *temp_stack,
+                                       void *func_addr, void *rvalue, ffi_type rtype);
+
+static void ffi_call_x86_64(ktap_state *ks, csymbol_func *csf, void *rvalue)
+{
+       int i;
+       int gpr_nr;
+       int arg_bytes; /* total bytes needed for exceeded args in stack */
+       int mem_bytes; /* total bytes needed for memory storage */
+       char *stack, *stack_p, *gpr_p, *arg_p, *mem_p, *tmp_p;
+       int arg_nr;
+       csymbol *rsym;
+       ffi_type rtype;
+       size_t rsize;
+       bool ret_in_memory;
+       /* New stack to call C function */
+       char space[NEWSTACK_SIZE];
+
+       arg_nr = kp_arg_nr(ks);
+       rsym = csymf_ret(ks, csf);
+       rtype = csym_type(rsym);
+       rsize = csym_size(ks, rsym);
+       ret_in_memory = false;
+       if (rtype == FFI_STRUCT) {
+               if (rsize > 16) {
+                       rvalue = kp_malloc(ks, rsize);
+                       rtype = FFI_VOID;
+                       ret_in_memory = true;
+               } else {
+                       /* much easier to always copy 16 bytes from registers */
+                       rvalue = kp_malloc(ks, 16);
+               }
+       }
+
+       gpr_nr = 0;
+       arg_bytes = mem_bytes = 0;
+       if (ret_in_memory)
+               gpr_nr++;
+       /* calculate bytes needed for stack */
+       for (i = 0; i < arg_nr; i++) {
+               csymbol *cs = ffi_get_arg_csym(ks, csf, i);
+               size_t size = csym_size(ks, cs);
+               size_t align = csym_align(ks, cs);
+               enum arg_status st = IN_REGISTER;
+               int n_gpr_nr = 0;
+               if (size > 32) {
+                       st = IN_MEMORY;
+                       n_gpr_nr = 1;
+               } else if (size > 16)
+                       st = IN_STACK;
+               else
+                       n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE;
+
+               if (gpr_nr + n_gpr_nr > MAX_GPR) {
+                       if (st == IN_MEMORY)
+                               arg_bytes += GPR_SIZE;
+                       else
+                               st = IN_STACK;
+               } else
+                       gpr_nr += n_gpr_nr;
+               if (st == IN_STACK) {
+                       arg_bytes = ALIGN(arg_bytes, align);
+                       arg_bytes += size;
+                       arg_bytes = ALIGN(arg_bytes, STACK_ALIGNMENT);
+               }
+               if (st == IN_MEMORY) {
+                       mem_bytes = ALIGN(mem_bytes, align);
+                       mem_bytes += size;
+                       mem_bytes = ALIGN(mem_bytes, STACK_ALIGNMENT);
+               }
+       }
+
+       /* apply space to fake stack for C function call */
+       if (16 + REDZONE_SIZE + MAX_GPR_SIZE + arg_bytes +
+                       mem_bytes + 6 * 8 >= NEWSTACK_SIZE) {
+               kp_error(ks, "Unable to handle that many arguments by now\n");
+               return;
+       }
+       stack = space;
+       /* 128 bytes below %rsp is red zone */
+       /* stack should be 16-bytes aligned */
+       stack_p = ALIGN_STACK(stack + REDZONE_SIZE, 16);
+       /* save general purpose registers here */
+       gpr_p = stack_p;
+       memset(gpr_p, 0, MAX_GPR_SIZE);
+       /* save arguments in stack here */
+       arg_p = gpr_p + MAX_GPR_SIZE;
+       /* save arguments in memory here */
+       mem_p = arg_p + arg_bytes;
+       /* set additional space as temporary space */
+       tmp_p = mem_p + mem_bytes;
+
+       /* copy arguments here */
+       gpr_nr = 0;
+       if (ret_in_memory) {
+               memcpy(gpr_p, &rvalue, GPR_SIZE);
+               gpr_p += GPR_SIZE;
+               gpr_nr++;
+       }
+       for (i = 0; i < arg_nr; i++) {
+               csymbol *cs = ffi_get_arg_csym(ks, csf, i);
+               size_t size = csym_size(ks, cs);
+               size_t align = csym_align(ks, cs);
+               enum arg_status st = IN_REGISTER;
+               int n_gpr_nr = 0;
+               if (size > 32) {
+                       st = IN_MEMORY;
+                       n_gpr_nr = 1;
+               } else if (size > 16)
+                       st = IN_STACK;
+               else
+                       n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE;
+
+               if (st == IN_MEMORY)
+                       mem_p = ALIGN_STACK(mem_p, align);
+               /* Tricky way about storing it above mem_p. It won't overflow
+                * because temp region can be temporarily used if necesseary. */
+               ffi_unpack(ks, csf, i, mem_p, GPR_SIZE);
+               if (gpr_nr + n_gpr_nr > MAX_GPR) {
+                       if (st == IN_MEMORY) {
+                               memcpy(arg_p, &mem_p, GPR_SIZE);
+                               arg_p += GPR_SIZE;
+                       } else
+                               st = IN_STACK;
+               } else {
+                       memcpy(gpr_p, mem_p, n_gpr_nr * GPR_SIZE);
+                       gpr_p += n_gpr_nr * GPR_SIZE;
+                       gpr_nr += n_gpr_nr;
+               }
+               if (st == IN_STACK) {
+                       arg_p = ALIGN_STACK(arg_p, align);
+                       memcpy(arg_p, mem_p, size);
+                       arg_p += size;
+                       arg_p = ALIGN_STACK(arg_p, STACK_ALIGNMENT);
+               }
+               if (st == IN_MEMORY) {
+                       mem_p += size;
+                       mem_p = ALIGN_STACK(mem_p, STACK_ALIGNMENT);
+               }
+       }
+
+       kp_verbose_printf(ks, "Stack location: %p -redzone- %p -general purpose "
+                       "register used- %p -zero- %p -stack for argument- %p"
+                       " -memory for argument- %p -temp stack-\n",
+                       stack, stack_p, gpr_p, stack_p + MAX_GPR_SIZE,
+                       arg_p, mem_p);
+       kp_verbose_printf(ks, "GPR number: %d; arg in stack: %d; "
+                       "arg in mem: %d\n",
+                       gpr_nr, arg_bytes, mem_bytes);
+       kp_verbose_printf(ks, "Return: address %p type %d\n", rvalue, rtype);
+       kp_verbose_printf(ks, "Number of register used: %d\n", gpr_nr);
+       kp_verbose_printf(ks, "Start FFI call on %p\n", csf->addr);
+       ffi_call_assem_x86_64(stack_p, tmp_p, csf->addr, rvalue, rtype);
+}
+
+#else /* non-supported platform */
+
+#define ffi_call(ks, cf, rvalue) ffi_call_unsupported(ks, cf, rvalue)
+
+static void ffi_call_unsupported(ktap_state *ks,
+               csymbol_func *csf, void *rvalue)
+{
+       kp_error(ks, "unsupported architecture.\n");
+}
+
+#endif /* end for platform-specific setting */
+
+
+static int ffi_set_return(ktap_state *ks, void *rvalue, csymbol_id ret_id)
+{
+       ktap_cdata *cd;
+       ffi_type type = csym_type(id_to_csym(ks, ret_id));
+
+       /* push return value to ktap stack */
+       switch (type) {
+       case FFI_VOID:
+               return 0;
+       case FFI_UINT8:
+       case FFI_INT8:
+       case FFI_UINT16:
+       case FFI_INT16:
+       case FFI_UINT32:
+       case FFI_INT32:
+       case FFI_UINT64:
+       case FFI_INT64:
+               set_number(ks->top, (ktap_number)rvalue);
+               break;
+       case FFI_PTR:
+               cd = kp_cdata_new_ptr(ks, rvalue, ret_id);
+               set_cdata(ks->top, cd);
+               break;
+       case FFI_STRUCT:
+               cd = kp_cdata_new_struct(ks, rvalue, ret_id);
+               set_cdata(ks->top, cd);
+               break;
+       case FFI_FUNC:
+       case FFI_UNKNOWN:
+               kp_error(ks, "Error: Have not support ffi_type %s\n",
+                               ffi_type_name(type));
+               return 0;
+       }
+       incr_top(ks);
+       return 1;
+}
+
+/*
+ * Call C into function
+ * First argument should be function symbol address, argument types
+ * and return type.
+ * Left arguments should be arguments for calling the C function.
+ * Types between Ktap and C are converted automatically.
+ * Only support x86_64 function call by now
+ */
+int kp_ffi_call(ktap_state *ks, csymbol_func *csf)
+{
+       int i;
+       int expected_arg_nr, arg_nr;
+       ktap_closure *cl;
+       void *rvalue;
+
+       expected_arg_nr = csymf_arg_nr(csf);
+       arg_nr = kp_arg_nr(ks);
+
+       /* check stack status for C call */
+       if (!csf->has_var_arg && expected_arg_nr != arg_nr) {
+               kp_error(ks, "wrong argument number %d, which should be %d\n",
+                               arg_nr, expected_arg_nr);
+               goto out;
+       }
+       if (csf->has_var_arg && expected_arg_nr > arg_nr) {
+               kp_error(ks, "argument number %d, which should be bigger than %d\n",
+                               arg_nr, expected_arg_nr);
+               goto out;
+       }
+
+       /* maybe useful later, leave it here first */
+       cl = clvalue(kp_arg(ks, arg_nr + 1));
+
+       /* check the argument types */
+       for (i = 0; i < arg_nr; i++) {
+               if (ffi_type_check(ks, csf, i) < 0)
+                       goto out;
+       }
+
+       /* platform-specific calling workflow */
+       ffi_call(ks, csf, &rvalue);
+       kp_verbose_printf(ks, "Finish FFI call\n");
+
+out:
+       return ffi_set_return(ks, rvalue, csymf_ret_id(csf));
+}
diff --git a/drivers/staging/ktap/runtime/ffi/ffi_symbol.c b/drivers/staging/ktap/runtime/ffi/ffi_symbol.c
new file mode 100644 (file)
index 0000000..0534c37
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * ffi_symbol.c - ktapvm kernel module ffi symbol submodule
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include "../../include/ktap_types.h"
+#include "../../include/ktap_ffi.h"
+#include "../ktap.h"
+#include "../kp_vm.h"
+#include "../kp_obj.h"
+#include "../kp_str.h"
+#include "../kp_tab.h"
+
+void setup_kp_ffi_symbol_table(ktap_state *ks);
+
+
+static inline csymbol *get_csym_arr(ktap_state *ks)
+{
+       return G(ks)->ffis.csym_arr;
+}
+
+static inline int get_csym_nr(ktap_state *ks)
+{
+       return G(ks)->ffis.csym_nr;
+}
+
+static inline void set_csym_arr(ktap_state *ks, csymbol *csym)
+{
+       G(ks)->ffis.csym_arr = csym;
+}
+
+static inline void set_csym_nr(ktap_state *ks, int nr)
+{
+       G(ks)->ffis.csym_nr = nr;
+}
+
+
+static inline ktap_tab *get_ffi_ctable(ktap_state *ks)
+{
+       return G(ks)->ffis.ctable;
+}
+
+static void setup_ffi_ctable(ktap_state *ks)
+{
+       ktap_value ffi_lib_name, ffi_mt;
+       ktap_tab *registry;
+       const ktap_value *gt;
+
+       gt = kp_tab_getint(hvalue(&G(ks)->registry), KTAP_RIDX_GLOBALS);
+
+       G(ks)->ffis.ctable = kp_tab_new(ks);
+
+       /* insert ffi C table to global table */
+       set_table(&ffi_mt, get_ffi_ctable(ks));
+       set_string(&ffi_lib_name, kp_tstring_new(ks, "C"));
+       registry = hvalue(gt);
+       kp_tab_setvalue(ks, registry, &ffi_lib_name, &ffi_mt);
+}
+
+void ffi_set_csym_arr(ktap_state *ks, int cs_nr, csymbol *new_arr)
+{
+       set_csym_nr(ks, cs_nr);
+       set_csym_arr(ks, new_arr);
+
+       if (!new_arr)
+               return;
+
+       setup_kp_ffi_symbol_table(ks);
+}
+
+inline csymbol *ffi_get_csym_by_id(ktap_state *ks, int id)
+{
+       return &(get_csym_arr(ks)[id]);
+}
+
+csymbol_id ffi_get_csym_id(ktap_state *ks, char *name)
+{
+       int i;
+
+       for (i = 0; i < get_csym_nr(ks); i++) {
+               if (!strcmp(name, csym_name(ffi_get_csym_by_id(ks, i)))) {
+                       return i;
+               }
+       }
+
+       kp_error(ks, "Cannot find csymbol with name %s\n", name);
+       return 0;
+}
+
+static void add_ffi_func_to_ctable(ktap_state *ks, csymbol_id id)
+{
+       ktap_value func_name, fv;
+       ktap_cdata *cd;
+       csymbol *cs;
+
+       /* push cdata to ctable */
+       set_cdata(&fv, kp_newobject(ks, KTAP_TCDATA, sizeof(ktap_cdata), NULL));
+       cd = cdvalue(&fv);
+       cd_set_csym_id(cd, id);
+
+       cs = id_to_csym(ks, id);
+       set_string(&func_name, kp_tstring_new(ks, csym_name(cs)));
+       kp_tab_setvalue(ks, get_ffi_ctable(ks), &func_name, &fv);
+}
+
+void setup_kp_ffi_symbol_table(ktap_state *ks)
+{
+       int i;
+       csymbol *cs;
+
+       setup_ffi_ctable(ks);
+
+       /* push all functions to ctable */
+       for (i = 0; i < get_csym_nr(ks); i++) {
+               cs = &get_csym_arr(ks)[i];
+               switch (cs->type) {
+               case FFI_FUNC:
+                       kp_verbose_printf(ks, "[%d] loading C function %s\n",
+                                       i, csym_name(cs));
+                       add_ffi_func_to_ctable(ks, i);
+                       kp_verbose_printf(ks, "%s loaded\n", csym_name(cs));
+                       break;
+               case FFI_STRUCT:
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+void kp_ffi_free_symbol(ktap_state *ks)
+{
+       int i;
+       csymbol_id *arg_ids;
+       csymbol *cs;
+
+       if (!get_csym_arr(ks))
+               return;
+
+       for (i = 0; i < get_csym_nr(ks); i++) {
+               cs = &get_csym_arr(ks)[i];
+               switch (csym_type(cs)) {
+               case FFI_FUNC:
+                       arg_ids = csym_func_arg_ids(cs);
+                       if (arg_ids)
+                               kp_free(ks, arg_ids);
+                       break;
+               case FFI_STRUCT:
+                       /*@TODO finish this  20.11 2013 (houqp)*/
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       kp_free(ks, get_csym_arr(ks));
+}
diff --git a/drivers/staging/ktap/runtime/ffi/ffi_type.c b/drivers/staging/ktap/runtime/ffi/ffi_type.c
new file mode 100644 (file)
index 0000000..6bb8316
--- /dev/null
@@ -0,0 +1,51 @@
+#include "../../include/ktap_ffi.h"
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <stdint.h>
+#include <stddef.h>
+#endif
+
+#define CTYPE_MODE_HELPER(name, type)  \
+struct _##name##_align {               \
+       type t1;                        \
+       char c;                         \
+       type t2;                        \
+};
+
+#define CTYPE_MODE(name)                               \
+{                                                      \
+       offsetof(struct _##name##_align, c),            \
+       offsetof(struct _##name##_align, t2) -          \
+               offsetof(struct _##name##_align, c),    \
+       #name                                   \
+}
+
+#define CTYPE_MODE_NAME(name) _##name##_mode
+
+/* ffi_ctype_mode should be corresponded to ffi_ctype */
+CTYPE_MODE_HELPER(uint8, uint8_t);
+CTYPE_MODE_HELPER(int8, int8_t);
+CTYPE_MODE_HELPER(uint16, uint16_t);
+CTYPE_MODE_HELPER(int16, int16_t);
+CTYPE_MODE_HELPER(uint32, uint32_t);
+CTYPE_MODE_HELPER(int32, int32_t);
+CTYPE_MODE_HELPER(uint64, uint64_t);
+CTYPE_MODE_HELPER(int64, int64_t);
+CTYPE_MODE_HELPER(pointer, void*);
+
+const ffi_mode ffi_type_modes[NUM_FFI_TYPE+1] = {
+       {0, 1, "void"},
+       CTYPE_MODE(uint8),
+       CTYPE_MODE(int8),
+       CTYPE_MODE(uint16),
+       CTYPE_MODE(int16),
+       CTYPE_MODE(uint32),
+       CTYPE_MODE(int32),
+       CTYPE_MODE(uint64),
+       CTYPE_MODE(int64),
+       CTYPE_MODE(pointer),
+       {0, 1, "function"},
+       {0, 1, "struct"},
+       {0, 1, "unknown"},
+};
diff --git a/drivers/staging/ktap/runtime/ffi/ffi_util.c b/drivers/staging/ktap/runtime/ffi/ffi_util.c
new file mode 100644 (file)
index 0000000..6b0376d
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * ffi_util.c - utility function for ffi module
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include "../../include/ktap_types.h"
+#include "../../include/ktap_ffi.h"
+#include "../ktap.h"
+
+size_t csym_size(ktap_state *ks, csymbol *cs)
+{
+       ffi_type type = csym_type(cs);
+       switch(type) {
+       case FFI_STRUCT:
+               if (csym_struct(cs)->align == 0)
+                       init_csym_struct(ks, csym_struct(cs));
+               return csym_struct(cs)->size;
+       default:
+               return ffi_type_size(type);
+       }
+}
+
+size_t csym_align(ktap_state *ks, csymbol *cs)
+{
+       ffi_type type = csym_type(cs);
+       switch(type) {
+       case FFI_STRUCT:
+               if (csym_struct(cs)->align == 0)
+                       init_csym_struct(ks, csym_struct(cs));
+               return csym_struct(cs)->align;
+       default:
+               return ffi_type_align(type);
+       }
+}
+
+size_t csym_struct_offset(ktap_state *ks, csymbol_struct *csst, int idx)
+{
+       int nr = csymst_mb_nr(csst);
+       size_t off = 0;
+       size_t align = 1;
+       int i;
+
+       if (idx < 0 || idx > nr)
+               return -1;
+       for (i = 0; i < idx; i++) {
+               csymbol *var_cs = csymst_mb(ks, csst, i);
+               size_t var_size = csym_size(ks, var_cs);
+               size_t var_align = csym_align(ks, var_cs);
+               off = ALIGN(off, var_align);
+               off += var_size;
+               align = align > var_align ? align : var_align;
+       }
+       off = ALIGN(off, align);
+       return off;
+}
+
+void init_csym_struct(ktap_state *ks, csymbol_struct *csst)
+{
+       int nr = csymst_mb_nr(csst);
+       size_t size = 0;
+       size_t align = 1;
+       int i;
+
+       for (i = 0; i < nr; i++) {
+               csymbol *var_cs = csymst_mb(ks, csst, i);
+               size_t var_size = csym_size(ks, var_cs);
+               size_t var_align = csym_align(ks, var_cs);
+               size = ALIGN(size, var_align);
+               size += var_size;
+               align = align > var_align ? align : var_align;
+       }
+       size = ALIGN(size, align);
+       csst->size = size;
+       csst->align = align;
+}
diff --git a/drivers/staging/ktap/runtime/kp_amalg.c b/drivers/staging/ktap/runtime/kp_amalg.c
new file mode 100644 (file)
index 0000000..c244822
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * kp_amalg.c - ktapvm kernel module amalgamation.
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include "ktap.c"
+#include "kp_opcode.c"
+#include "kp_obj.c"
+#include "kp_load.c"
+#include "kp_str.c"
+#include "kp_tab.c"
+#include "kp_transport.c"
+#include "kp_vm.c"
+#include "lib_base.c"
+#include "lib_ansi.c"
+#include "lib_kdebug.c"
+#include "lib_timer.c"
+
+#ifdef CONFIG_KTAP_FFI
+#include "ffi/ffi_call.c"
+#include "ffi/ffi_type.c"
+#include "ffi/ffi_symbol.c"
+#include "ffi/cdata.c"
+#include "ffi/ffi_util.c"
+#include "lib_ffi.c"
+#endif
diff --git a/drivers/staging/ktap/runtime/kp_load.c b/drivers/staging/ktap/runtime/kp_load.c
new file mode 100644 (file)
index 0000000..74f1da4
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * kp_load.c - loader for ktap bytecode chunk file
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+ *  - The part of code in this file is copied from lua initially.
+ *  - lua's MIT license is compatible with GPL.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/slab.h>
+#include "../include/ktap_types.h"
+#include "../include/ktap_ffi.h"
+#include "ktap.h"
+#include "kp_load.h"
+#include "kp_obj.h"
+#include "kp_str.h"
+#include "kp_tab.h"
+#include "kp_vm.h"
+
+#define KTAPC_TAIL     "\x19\x93\r\n\x1a\n"
+
+struct load_state {
+       unsigned char *buff;
+       int pos;
+       ktap_state *ks;
+};
+
+#define READ_CHAR(S)  (S->buff[S->pos++])
+#define READ_BYTE(S)  READ_CHAR(S)
+#define READ_INT(S)  load_int(S)
+#define READ_NUMBER(S) load_number(S)
+#define READ_STRING(S) load_string(S)
+#define READ_VECTOR(S, dst, size)  \
+       do {    \
+               memcpy(dst, &S->buff[S->pos], size);    \
+               S->pos += size; \
+       } while(0)
+
+#define NEW_VECTOR(S, size)    kp_malloc(S->ks, size)
+#define FREE_VECTOR(S, v)      kp_free(S->ks, v)
+#define GET_CURRENT(S)         &S->buff[S->pos]
+#define ADD_POS(S, size)       S->pos += size
+
+
+static int load_function(struct load_state *S, ktap_proto *f);
+
+
+static int load_int(struct load_state *S)
+{
+       int x;
+
+       READ_VECTOR(S, &x, sizeof(int));
+       return x;
+}
+
+static long load_number(struct load_state *S)
+{
+       long x;
+
+       READ_VECTOR(S, &x, sizeof(ktap_number));
+       return x;
+}
+
+static ktap_string *load_string(struct load_state *S)
+{
+       ktap_string *ts;
+       size_t size;
+
+       size = READ_INT(S);
+
+       if (!size)
+               return NULL;
+       else {
+               char *s = GET_CURRENT(S);
+               ADD_POS(S, size);
+               /* remove trailing '\0' */
+               ts = kp_tstring_newlstr(S->ks, s, size - 1);
+               return ts;
+       }
+}
+
+
+static int load_code(struct load_state *S, ktap_proto *f)
+{
+       int n = READ_INT(S);
+
+       f->sizecode = n;
+       f->code = NEW_VECTOR(S, n * sizeof(ktap_instruction));
+       READ_VECTOR(S, f->code, n * sizeof(ktap_instruction));
+
+       return 0;
+}
+
+static int load_constants(struct load_state *S, ktap_proto *f)
+{
+       int i,n;
+
+       n = READ_INT(S);
+
+       f->sizek = n;
+       f->k = NEW_VECTOR(S, n * sizeof(ktap_value));
+       for (i = 0; i < n; i++)
+               set_nil(&f->k[i]);
+
+       for (i=0; i < n; i++) {
+               ktap_value *o = &f->k[i];
+
+               int t = READ_CHAR(S);
+               switch (t) {
+               case KTAP_TNIL:
+                       set_nil(o);
+                       break;
+               case KTAP_TBOOLEAN:
+                       set_boolean(o, READ_CHAR(S));
+                       break;
+               case KTAP_TNUMBER:
+                       /*
+                        * todo: kernel not support fp, check double when
+                        * loading
+                        */
+                       set_number(o, READ_NUMBER(S));
+                       break;
+               case KTAP_TSTRING:
+                       set_string(o, READ_STRING(S));
+                       break;
+               default:
+                       kp_error(S->ks, "ktap: load_constants: "
+                                       "unknow ktap_value\n");
+                       return -1;
+
+               }
+       }
+
+       n = READ_INT(S);
+       f->p = NEW_VECTOR(S, n * sizeof(ktap_proto));
+       f->sizep = n;
+       for (i = 0; i < n; i++)
+               f->p[i] = NULL;
+       for (i = 0; i < n; i++) {
+               f->p[i] = kp_newproto(S->ks);
+               if (load_function(S, f->p[i]))
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+static int load_upvalues(struct load_state *S, ktap_proto *f)
+{
+       int i,n;
+
+       n = READ_INT(S);
+       f->upvalues = NEW_VECTOR(S, n * sizeof(ktap_upvaldesc));
+       f->sizeupvalues = n;
+
+       for (i = 0; i < n; i++)
+               f->upvalues[i].name = NULL;
+
+       for (i = 0; i < n; i++) {
+               f->upvalues[i].instack = READ_BYTE(S);
+               f->upvalues[i].idx = READ_BYTE(S);
+       }
+
+       return 0;
+}
+
+static int load_debuginfo(struct load_state *S, ktap_proto *f)
+{
+       int i,n;
+
+       f->source = READ_STRING(S);
+       n = READ_INT(S);
+       f->sizelineinfo = n;
+       f->lineinfo = NEW_VECTOR(S, n * sizeof(int));
+       READ_VECTOR(S, f->lineinfo, n * sizeof(int));
+       n = READ_INT(S);
+       f->locvars = NEW_VECTOR(S, n * sizeof(struct ktap_locvar));
+       f->sizelocvars = n;
+       for (i = 0; i < n; i++)
+               f->locvars[i].varname = NULL;
+       for (i = 0; i < n; i++) {
+               f->locvars[i].varname = READ_STRING(S);
+               f->locvars[i].startpc = READ_INT(S);
+               f->locvars[i].endpc = READ_INT(S);
+       }
+       n = READ_INT(S);
+       for (i = 0; i < n; i++)
+               f->upvalues[i].name = READ_STRING(S);
+
+       return 0;
+}
+
+static int load_function(struct load_state *S, ktap_proto *f)
+{
+       f->linedefined = READ_INT(S);
+       f->lastlinedefined = READ_INT(S);
+       f->numparams = READ_BYTE(S);
+       f->is_vararg = READ_BYTE(S);
+       f->maxstacksize = READ_BYTE(S);
+       if (load_code(S, f))
+               return -1;
+       if (load_constants(S, f))
+               return -1;
+       if (load_upvalues(S, f))
+               return -1;
+       if (load_debuginfo(S, f))
+               return -1;
+
+       return 0;
+}
+
+
+#define error(S, why) \
+       kp_error(S->ks, "load failed: %s precompiled chunk\n", why)
+
+#define N0     KTAPC_HEADERSIZE
+#define N1     (sizeof(KTAP_SIGNATURE) - sizeof(char))
+#define N2     N1 + 2
+#define N3     N2 + 6
+
+static int load_header(struct load_state *S)
+{
+       u8 h[KTAPC_HEADERSIZE];
+       u8 s[KTAPC_HEADERSIZE];
+
+       kp_header(h);
+       READ_VECTOR(S, s, KTAPC_HEADERSIZE);
+
+       if (memcmp(h, s, N0) == 0)
+               return 0;
+       if (memcmp(h, s, N1) != 0)
+               error(S, "not a");
+       else if (memcmp(h, s, N2) != 0)
+               error(S, "version mismatch in");
+       else if (memcmp(h, s, N3) != 0)
+               error(S, "incompatible");
+       else
+               error(S,"corrupted");
+
+       return -1;
+}
+
+#ifdef CONFIG_KTAP_FFI
+void ffi_set_csym_arr(ktap_state *ks, int cs_nr, csymbol *new_arr);
+
+static void load_csymbol_func(struct load_state *S, csymbol *cs)
+{
+       csymbol_func *csf = csym_func(cs);
+       int arg_nr = csymf_arg_nr(csf);
+
+       if (arg_nr > 0) {
+               csf->arg_ids = NEW_VECTOR(S, arg_nr*sizeof(int));
+               READ_VECTOR(S, csf->arg_ids, arg_nr*sizeof(int));
+       } else {
+               csf->arg_ids = NULL;
+       }
+}
+
+static void load_csymbol_struct(struct load_state *S, csymbol *cs)
+{
+       csymbol_struct *csst = csym_struct(cs);
+       int mb_nr = csymst_mb_nr(csst);
+
+       csst->members = NEW_VECTOR(S, mb_nr*sizeof(struct_member));
+       READ_VECTOR(S, csst->members, mb_nr*sizeof(struct_member));
+}
+
+static int load_csymbols(struct load_state *S)
+{
+       csymbol *cs_arr, *cs;
+       int i, csym_nr;
+
+       /* read number of csymbols */
+       csym_nr = READ_INT(S);
+       if (csym_nr <= 0) {
+               ffi_set_csym_arr(S->ks, 0, NULL);
+               return 0;
+       }
+
+       /* csymbol size safty check */
+       if (sizeof(csymbol) != READ_INT(S)) {
+               kp_error(S->ks, "invalid csymbol size in chunk\n");
+               return -1;
+       }
+
+       cs_arr = NEW_VECTOR(S, sizeof(csymbol)*csym_nr);
+       for (i = 0; i < csym_nr; i++) {
+               cs = &cs_arr[i];
+               READ_VECTOR(S, cs, sizeof(csymbol));
+               switch (cs->type) {
+               case FFI_FUNC:
+                       load_csymbol_func(S, cs);
+                       break;
+               case FFI_STRUCT:
+                       load_csymbol_struct(S, cs);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       ffi_set_csym_arr(S->ks, csym_nr, cs_arr);
+
+       return 0;
+}
+#else
+static int load_csymbols(struct load_state *S)
+{
+       int csym_nr = READ_INT(S);
+
+       /* if FFI is disabled in ktapc, csym_nr should be 0 */
+       if (csym_nr != 0) {
+                /* skip corrupted csymbol chunk */
+               int cs_size = READ_INT(S);
+               ADD_POS(S, cs_size*csym_nr);
+               kp_error(S->ks, "VM compiled without FFI support!\n");
+               return -1;
+       }
+
+       return 0;
+}
+#endif
+
+static int verify_code(struct load_state *S, ktap_proto *f)
+{
+       /* not support now */
+       return 0;
+}
+
+
+ktap_closure *kp_load(ktap_state *ks, unsigned char *buff)
+{
+       struct load_state S;
+       ktap_closure *cl;
+       int ret, i;
+
+       S.ks = ks;
+       S.buff = buff;
+       S.pos = 0;
+
+       ret = load_header(&S);
+       if (ret)
+               return NULL;
+
+       ret = load_csymbols(&S);
+       if (ret)
+               return NULL;
+
+       cl = kp_newclosure(ks, 1);
+       if (!cl)
+               return cl;
+
+       /* put closure on the top, prepare to run with this closure */
+       set_closure(ks->top, cl);
+       incr_top(ks);
+
+       cl->p = kp_newproto(ks);
+       if (load_function(&S, cl->p))
+               return NULL;
+
+       if (cl->p->sizeupvalues != 1) {
+               ktap_proto *p = cl->p;
+               cl = kp_newclosure(ks, cl->p->sizeupvalues);
+               cl->p = p;
+               set_closure(ks->top - 1, cl);
+       }
+
+       for (i = 0; i < cl->nupvalues; i++) {  /* initialize upvalues */
+               ktap_upval *up = kp_newupval(ks);
+               cl->upvals[i] = up;
+       }
+
+       /* set global table as 1st upvalue of 'f' */
+       if (cl->nupvalues == 1) {
+               ktap_tab *reg = hvalue(&G(ks)->registry);
+               const ktap_value *gt = kp_tab_getint(reg, KTAP_RIDX_GLOBALS);
+               set_obj(cl->upvals[0]->v, gt);
+       }
+
+       verify_code(&S, cl->p);
+
+       return cl;
+}
+
diff --git a/drivers/staging/ktap/runtime/kp_load.h b/drivers/staging/ktap/runtime/kp_load.h
new file mode 100644 (file)
index 0000000..fc41fa9
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __KTAP_LOAD_H__
+#define __KTAP_LOAD_H__
+
+ktap_closure *kp_load(ktap_state *ks, unsigned char *buff);
+
+#endif /* __KTAP_LOAD_H__ */
diff --git a/drivers/staging/ktap/runtime/kp_obj.c b/drivers/staging/ktap/runtime/kp_obj.c
new file mode 100644 (file)
index 0000000..5149637
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+ * kp_obj.c - ktap object generic operation
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+ *  - The part of code in this file is copied from lua initially.
+ *  - lua's MIT license is compatible with GPL.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "../include/ktap_types.h"
+#include "../include/ktap_ffi.h"
+#include "kp_obj.h"
+#include "kp_str.h"
+#include "kp_tab.h"
+
+#ifdef __KERNEL__
+#include <linux/slab.h>
+#include "ktap.h"
+#include "kp_vm.h"
+#include "kp_transport.h"
+
+#define KTAP_ALLOC_FLAGS ((GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN) \
+                        & ~__GFP_WAIT)
+
+void *kp_malloc(ktap_state *ks, int size)
+{
+       void *addr;
+
+       /*
+        * Normally we don't want to trace under memory pressure,
+        * so we use a simple rule to handle memory allocation failure:
+        *
+        * retry until allocation success, this will make caller don't need
+        * to handle the unlikely failure case, then ktap exit.
+        *
+        * In this approach, if user find there have memory allocation failure,
+        * user should re-run the ktap script, or fix the memory pressure
+        * issue, or figure out why the script need so many memory.
+        *
+        * Perhaps return pre-allocated stub memory trunk when allocate failed
+        * is a better approch?
+        */
+       addr = kmalloc(size, KTAP_ALLOC_FLAGS);
+       if (unlikely(!addr)) {
+               kp_error(ks, "kmalloc size %d failed, retry again\n", size);
+               printk("ktap kmalloc size %d failed, retry again\n", size);
+               dump_stack();
+               while (1) {
+                       addr = kmalloc(size, KTAP_ALLOC_FLAGS);
+                       if (addr)
+                               break;
+               }
+               kp_printf(ks, "kmalloc retry success after failed, exit\n");
+       }
+
+       preempt_disable();
+       KTAP_STATS(ks)->nr_mem_allocate += 1;
+       KTAP_STATS(ks)->mem_allocated += size;
+       preempt_enable();
+
+       return addr;
+}
+
+void kp_free(ktap_state *ks, void *addr)
+{
+       preempt_disable();
+       KTAP_STATS(ks)->nr_mem_free += 1;
+       preempt_enable();
+
+       kfree(addr);
+}
+
+void *kp_reallocv(ktap_state *ks, void *addr, int oldsize, int newsize)
+{
+       void *new_addr;
+
+       new_addr = krealloc(addr, newsize, KTAP_ALLOC_FLAGS);
+       if (unlikely(!new_addr)) {
+               kp_error(ks, "krealloc size %d failed, retry again\n", newsize);
+               printk("ktap krealloc size %d failed, retry again\n", newsize);
+               dump_stack();
+               while (1) {
+                       new_addr = krealloc(addr, newsize, KTAP_ALLOC_FLAGS);
+                       if (new_addr)
+                               break;
+               }
+               kp_printf(ks, "krealloc retry success after failed, exit\n");
+       }
+
+       preempt_disable();
+       if (oldsize == 0) {
+               KTAP_STATS(ks)->nr_mem_allocate += 1;
+       }
+       KTAP_STATS(ks)->mem_allocated += newsize - oldsize;
+       preempt_enable();
+
+       return new_addr;
+}
+
+void *kp_zalloc(ktap_state *ks, int size)
+{
+       void *addr;
+
+       addr = kzalloc(size, KTAP_ALLOC_FLAGS);
+       if (unlikely(!addr)) {
+               kp_error(ks, "kzalloc size %d failed, retry again\n", size);
+               printk("ktap kzalloc size %d failed, retry again\n", size);
+               dump_stack();
+               while (1) {
+                       addr = kzalloc(size, KTAP_ALLOC_FLAGS);
+                       if (addr)
+                               break;
+               }
+               kp_printf(ks, "kzalloc retry success after failed, exit\n");
+       }
+
+       preempt_disable();
+       KTAP_STATS(ks)->nr_mem_allocate += 1;
+       KTAP_STATS(ks)->mem_allocated += size;
+       preempt_enable();
+
+       return addr;
+}
+#endif
+
+void kp_obj_dump(ktap_state *ks, const ktap_value *v)
+{
+       switch (ttype(v)) {
+       case KTAP_TNIL:
+               kp_puts(ks, "NIL");
+               break;
+       case KTAP_TNUMBER:
+               kp_printf(ks, "NUMBER %ld", nvalue(v));
+               break;
+       case KTAP_TBOOLEAN:
+               kp_printf(ks, "BOOLEAN %d", bvalue(v));
+               break;
+       case KTAP_TLIGHTUSERDATA:
+               kp_printf(ks, "LIGHTUSERDATA 0x%lx", (unsigned long)pvalue(v));
+               break;
+       case KTAP_TCFUNCTION:
+               kp_printf(ks, "LIGHTCFCUNTION 0x%lx", (unsigned long)fvalue(v));
+               break;
+       case KTAP_TSHRSTR:
+       case KTAP_TLNGSTR:
+               kp_printf(ks, "SHRSTR #%s", svalue(v));
+               break;
+       case KTAP_TTABLE:
+               kp_printf(ks, "TABLE 0x%lx", (unsigned long)hvalue(v));
+               break;
+        default:
+               kp_printf(ks, "GCVALUE 0x%lx", (unsigned long)gcvalue(v));
+               break;
+       }
+}
+
+#ifdef __KERNEL__
+#include <linux/stacktrace.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+
+static void kp_btrace_dump(ktap_state *ks, ktap_btrace *bt)
+{
+       char str[KSYM_SYMBOL_LEN];
+       unsigned long *entries = (unsigned long *)(bt + 1);
+       int i;
+
+       for (i = 0; i < bt->nr_entries; i++) {
+               unsigned long p = entries[i];
+
+               if (p == ULONG_MAX)
+                       break;
+
+               SPRINT_SYMBOL(str, p);
+               kp_printf(ks, "%s\n", str);
+       }
+}
+
+static int kp_btrace_equal(ktap_btrace *bt1, ktap_btrace *bt2)
+{
+       unsigned long *entries1 = (unsigned long *)(bt1 + 1);
+       unsigned long *entries2 = (unsigned long *)(bt2 + 1);
+       int i;
+
+       if (bt1->nr_entries != bt2->nr_entries)
+               return 0;
+
+       for (i = 0; i < bt1->nr_entries; i++) {
+               if (entries1[i] != entries2[i])
+                       return 0;
+       }
+
+       return 1;
+}
+#endif
+
+void kp_showobj(ktap_state *ks, const ktap_value *v)
+{
+       switch (ttype(v)) {
+       case KTAP_TNIL:
+               kp_puts(ks, "nil");
+               break;
+       case KTAP_TNUMBER:
+               kp_printf(ks, "%ld", nvalue(v));
+               break;
+       case KTAP_TBOOLEAN:
+               kp_puts(ks, (bvalue(v) == 1) ? "true" : "false");
+               break;
+       case KTAP_TLIGHTUSERDATA:
+               kp_printf(ks, "0x%lx", (unsigned long)pvalue(v));
+               break;
+       case KTAP_TCFUNCTION:
+               kp_printf(ks, "0x%lx", (unsigned long)fvalue(v));
+               break;
+       case KTAP_TSHRSTR:
+       case KTAP_TLNGSTR:
+               kp_puts(ks, svalue(v));
+               break;
+       case KTAP_TTABLE:
+               kp_tab_dump(ks, hvalue(v));
+               break;
+#ifdef __KERNEL__
+#ifdef CONFIG_KTAP_FFI
+       case KTAP_TCDATA:
+               kp_cdata_dump(ks, cdvalue(v));
+               break;
+#endif
+       case KTAP_TEVENT:
+               kp_transport_event_write(ks, evalue(v));
+               break;
+       case KTAP_TBTRACE:
+               kp_btrace_dump(ks, btvalue(v));
+               break;
+       case KTAP_TPTABLE:
+               kp_ptab_dump(ks, phvalue(v));
+               break;
+       case KTAP_TSTATDATA:
+               kp_statdata_dump(ks, sdvalue(v));
+               break;
+#endif
+        default:
+               kp_error(ks, "print unknown value type: %d\n", ttype(v));
+               break;
+       }
+}
+
+
+/*
+ * equality of ktap values. ks == NULL means raw equality
+ */
+int kp_equalobjv(ktap_state *ks, const ktap_value *t1, const ktap_value *t2)
+{
+       switch (ttype(t1)) {
+       case KTAP_TNIL:
+               return 1;
+       case KTAP_TNUMBER:
+               return nvalue(t1) == nvalue(t2);
+       case KTAP_TBOOLEAN:
+               return bvalue(t1) == bvalue(t2);  /* true must be 1 !! */
+       case KTAP_TLIGHTUSERDATA:
+               return pvalue(t1) == pvalue(t2);
+       case KTAP_TCFUNCTION:
+               return fvalue(t1) == fvalue(t2);
+       case KTAP_TSHRSTR:
+               return eqshrstr(rawtsvalue(t1), rawtsvalue(t2));
+       case KTAP_TLNGSTR:
+               return kp_tstring_eqlngstr(rawtsvalue(t1), rawtsvalue(t2));
+       case KTAP_TTABLE:
+               if (hvalue(t1) == hvalue(t2))
+                       return 1;
+               else if (ks == NULL)
+                       return 0;
+#ifdef __KERNEL__
+       case KTAP_TBTRACE:
+               return kp_btrace_equal(btvalue(t1), btvalue(t2));
+#endif
+       default:
+               return gcvalue(t1) == gcvalue(t2);
+       }
+
+       return 0;
+}
+
+/*
+ * ktap will not use lua's length operator on table meaning,
+ * also # is not for length operator any more in ktap.
+ */
+int kp_objlen(ktap_state *ks, const ktap_value *v)
+{
+       switch(v->type) {
+       case KTAP_TTABLE:
+               return kp_tab_length(ks, hvalue(v));
+       case KTAP_TSTRING:
+               return rawtsvalue(v)->tsv.len;
+       default:
+               kp_printf(ks, "cannot get length of type %d\n", v->type);
+               return -1;
+       }
+       return 0;
+}
+
+/* need to protect allgc field? */
+ktap_gcobject *kp_newobject(ktap_state *ks, int type, size_t size,
+                           ktap_gcobject **list)
+{
+       ktap_gcobject *o;
+
+       o = kp_malloc(ks, size);
+       if (list == NULL)
+               list = &G(ks)->allgc;
+
+       gch(o)->tt = type;
+       gch(o)->next = *list;
+       *list = o;
+
+       return o;
+}
+
+ktap_upval *kp_newupval(ktap_state *ks)
+{
+       ktap_upval *uv;
+
+       uv = &kp_newobject(ks, KTAP_TUPVAL, sizeof(ktap_upval), NULL)->uv;
+       uv->v = &uv->u.value;
+       set_nil(uv->v);
+       return uv;
+}
+
+static ktap_btrace *kp_newbacktrace(ktap_state *ks, int nr_entries,
+                                   ktap_gcobject **list)
+{
+       ktap_btrace *bt;
+       int size = sizeof(ktap_btrace) + nr_entries * sizeof(unsigned long);
+
+       bt = &kp_newobject(ks, KTAP_TBTRACE, size, list)->bt;
+       bt->nr_entries = nr_entries;
+       return bt;
+}
+
+void kp_objclone(ktap_state *ks, const ktap_value *o, ktap_value *newo,
+                ktap_gcobject **list)
+{
+       if (is_btrace(o)) {
+               int nr_entries = btvalue(o)->nr_entries;
+               ktap_btrace *bt;
+
+               bt = kp_newbacktrace(ks, nr_entries, list);
+               memcpy((unsigned long *)(bt + 1), btvalue(o) + 1,
+                       nr_entries * sizeof(unsigned long));
+               set_btrace(newo, bt);
+       } else {
+               kp_error(ks, "cannot clone ktap value type %d\n", ttype(o));
+               set_nil(newo);
+       }
+}
+
+ktap_closure *kp_newclosure(ktap_state *ks, int n)
+{
+       ktap_closure *cl;
+
+       cl = (ktap_closure *)kp_newobject(ks, KTAP_TCLOSURE, sizeof(*cl), NULL);
+       cl->p = NULL;
+       cl->nupvalues = n;
+       while (n--)
+               cl->upvals[n] = NULL;
+
+       return cl;
+}
+
+static void free_proto(ktap_state *ks, ktap_proto *f)
+{
+       kp_free(ks, f->code);
+       kp_free(ks, f->p);
+       kp_free(ks, f->k);
+       kp_free(ks, f->lineinfo);
+       kp_free(ks, f->locvars);
+       kp_free(ks, f->upvalues);
+       kp_free(ks, f);
+}
+
+ktap_proto *kp_newproto(ktap_state *ks)
+{
+       ktap_proto *f;
+       f = (ktap_proto *)kp_newobject(ks, KTAP_TPROTO, sizeof(*f), NULL);
+       f->k = NULL;
+       f->sizek = 0;
+       f->p = NULL;
+       f->sizep = 0;
+       f->code = NULL;
+       f->cache = NULL;
+       f->sizecode = 0;
+       f->lineinfo = NULL;
+       f->sizelineinfo = 0;
+       f->upvalues = NULL;
+       f->sizeupvalues = 0;
+       f->numparams = 0;
+       f->is_vararg = 0;
+       f->maxstacksize = 0;
+       f->locvars = NULL;
+       f->sizelocvars = 0;
+       f->linedefined = 0;
+       f->lastlinedefined = 0;
+       f->source = NULL;
+       return f;
+}
+
+void kp_free_gclist(ktap_state *ks, ktap_gcobject *o)
+{
+       while (o) {
+               ktap_gcobject *next;
+
+               next = gch(o)->next;
+               switch (gch(o)->tt) {
+               case KTAP_TTABLE:
+                       kp_tab_free(ks, (ktap_tab *)o);
+                       break;
+               case KTAP_TPROTO:
+                       free_proto(ks, (ktap_proto *)o);
+                       break;
+#ifdef __KERNEL__
+               case KTAP_TPTABLE:
+                       kp_ptab_free(ks, (ktap_ptab *)o);
+                       break;
+#endif
+               default:
+                       kp_free(ks, o);
+               }
+               o = next;
+       }
+}
+
+void kp_free_all_gcobject(ktap_state *ks)
+{
+       kp_free_gclist(ks, G(ks)->allgc);
+       G(ks)->allgc = NULL;
+}
+
+/******************************************************************************/
+
+/*
+ * make header for precompiled chunks
+ * if you change the code below be sure to update load_header and FORMAT above
+ * and KTAPC_HEADERSIZE in ktap_types.h
+ */
+void kp_header(u8 *h)
+{
+       int x = 1;
+
+       memcpy(h, KTAP_SIGNATURE, sizeof(KTAP_SIGNATURE) - sizeof(char));
+       h += sizeof(KTAP_SIGNATURE) - sizeof(char);
+       *h++ = (u8)VERSION;
+       *h++ = (u8)FORMAT;
+       *h++ = (u8)(*(char*)&x);                    /* endianness */
+       *h++ = (u8)(sizeof(int));
+       *h++ = (u8)(sizeof(size_t));
+       *h++ = (u8)(sizeof(ktap_instruction));
+       *h++ = (u8)(sizeof(ktap_number));
+       *h++ = (u8)(((ktap_number)0.5) == 0); /* is ktap_number integral? */
+       memcpy(h, KTAPC_TAIL, sizeof(KTAPC_TAIL) - sizeof(char));
+}
+
+
diff --git a/drivers/staging/ktap/runtime/kp_obj.h b/drivers/staging/ktap/runtime/kp_obj.h
new file mode 100644 (file)
index 0000000..02aea07
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __KTAP_OBJ_H__
+#define __KTAP_OBJ_H__
+
+#ifdef __KERNEL__
+void *kp_malloc(ktap_state *ks, int size);
+void kp_free(ktap_state *ks, void *addr);
+void *kp_reallocv(ktap_state *ks, void *addr, int oldsize, int newsize);
+void *kp_zalloc(ktap_state *ks, int size);
+#else
+#define kp_malloc(ks, size)                    malloc(size)
+#define kp_free(ks, block)                     free(block)
+#define kp_reallocv(ks, block, osize, nsize)   realloc(block, nsize)
+#endif
+
+void kp_obj_dump(ktap_state *ks, const ktap_value *v);
+void kp_showobj(ktap_state *ks, const ktap_value *v);
+int kp_objlen(ktap_state *ks, const ktap_value *rb);
+void kp_objclone(ktap_state *ks, const ktap_value *o, ktap_value *newo,
+                ktap_gcobject **list);
+ktap_gcobject *kp_newobject(ktap_state *ks, int type, size_t size, ktap_gcobject **list);
+int kp_equalobjv(ktap_state *ks, const ktap_value *t1, const ktap_value *t2);
+ktap_closure *kp_newclosure(ktap_state *ks, int n);
+ktap_proto *kp_newproto(ktap_state *ks);
+ktap_upval *kp_newupval(ktap_state *ks);
+void kp_free_gclist(ktap_state *ks, ktap_gcobject *o);
+void kp_free_all_gcobject(ktap_state *ks);
+void kp_header(u8 *h);
+
+#endif /* __KTAP_OBJ_H__ */
diff --git a/drivers/staging/ktap/runtime/kp_opcode.c b/drivers/staging/ktap/runtime/kp_opcode.c
new file mode 100644 (file)
index 0000000..bdafbb6
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * kp_opcode.c
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+ *  - The part of code in this file is copied from lua initially.
+ *  - lua's MIT license is compatible with GPL.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "../include/ktap_types.h"
+#include "../include/ktap_opcodes.h"
+
+const char *const ktap_opnames[NUM_OPCODES + 1] = {
+  "MOVE",
+  "LOADK",
+  "LOADKX",
+  "LOADBOOL",
+  "LOADNIL",
+  "GETUPVAL",
+  "GETTABUP",
+  "GETTABLE",
+  "SETTABUP",
+  "SETTABUP_INCR",
+  "SETTABUP_AGGR",
+  "SETUPVAL",
+  "SETTABLE",
+  "SETTABLE_INCR",
+  "SETTABLE_AGGR",
+  "NEWTABLE",
+  "SELF",
+  "ADD",
+  "SUB",
+  "MUL",
+  "DIV",
+  "MOD",
+  "POW",
+  "UNM",
+  "NOT",
+  "LEN",
+  "CONCAT",
+  "JMP",
+  "EQ",
+  "LT",
+  "LE",
+  "TEST",
+  "TESTSET",
+  "CALL",
+  "TAILCALL",
+  "RETURN",
+  "FORLOOP",
+  "FORPREP",
+  "TFORCALL",
+  "TFORLOOP",
+  "SETLIST",
+  "CLOSURE",
+  "VARARG",
+  "EXTRAARG",
+
+  "EVENT",
+  "EVENT_NAME",
+  "EVENT_ARG", /* arg1, arg2 .. arg9 */
+  NULL
+};
+
+
+#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))
+
+const u8 ktap_opmodes[NUM_OPCODES] = {
+/*       T  A    B       C     mode                opcode       */
+  opmode(0, 1, OpArgR, OpArgN, iABC)            /* OP_MOVE */
+ ,opmode(0, 1, OpArgK, OpArgN, iABx)            /* OP_LOADK */
+ ,opmode(0, 1, OpArgN, OpArgN, iABx)            /* OP_LOADKX */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC)            /* OP_LOADBOOL */
+ ,opmode(0, 1, OpArgU, OpArgN, iABC)            /* OP_LOADNIL */
+ ,opmode(0, 1, OpArgU, OpArgN, iABC)            /* OP_GETUPVAL */
+ ,opmode(0, 1, OpArgU, OpArgK, iABC)            /* OP_GETTABUP */
+ ,opmode(0, 1, OpArgR, OpArgK, iABC)            /* OP_GETTABLE */
+ ,opmode(0, 0, OpArgK, OpArgK, iABC)            /* OP_SETTABUP */
+ ,opmode(0, 0, OpArgK, OpArgK, iABC)            /* OP_SETTABUP_INCR */
+ ,opmode(0, 0, OpArgK, OpArgK, iABC)            /* OP_SETTABUP_AGGR */
+ ,opmode(0, 0, OpArgU, OpArgN, iABC)            /* OP_SETUPVAL */
+ ,opmode(0, 0, OpArgK, OpArgK, iABC)            /* OP_SETTABLE */
+ ,opmode(0, 0, OpArgK, OpArgK, iABC)            /* OP_SETTABUP_INCR */
+ ,opmode(0, 0, OpArgK, OpArgK, iABC)            /* OP_SETTABUP_AGGR */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC)            /* OP_NEWTABLE */
+ ,opmode(0, 1, OpArgR, OpArgK, iABC)            /* OP_SELF */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC)            /* OP_ADD */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC)            /* OP_SUB */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC)            /* OP_MUL */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC)            /* OP_DIV */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC)            /* OP_MOD */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC)            /* OP_POW */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC)            /* OP_UNM */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC)            /* OP_NOT */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC)            /* OP_LEN */
+ ,opmode(0, 1, OpArgR, OpArgR, iABC)            /* OP_CONCAT */
+ ,opmode(0, 0, OpArgR, OpArgN, iAsBx)           /* OP_JMP */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC)            /* OP_EQ */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC)            /* OP_LT */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC)            /* OP_LE */
+ ,opmode(1, 0, OpArgN, OpArgU, iABC)            /* OP_TEST */
+ ,opmode(1, 1, OpArgR, OpArgU, iABC)            /* OP_TESTSET */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC)            /* OP_CALL */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC)            /* OP_TAILCALL */
+ ,opmode(0, 0, OpArgU, OpArgN, iABC)            /* OP_RETURN */
+ ,opmode(0, 1, OpArgR, OpArgN, iAsBx)           /* OP_FORLOOP */
+ ,opmode(0, 1, OpArgR, OpArgN, iAsBx)           /* OP_FORPREP */
+ ,opmode(0, 0, OpArgN, OpArgU, iABC)            /* OP_TFORCALL */
+ ,opmode(0, 1, OpArgR, OpArgN, iAsBx)           /* OP_TFORLOOP */
+ ,opmode(0, 0, OpArgU, OpArgU, iABC)            /* OP_SETLIST */
+ ,opmode(0, 1, OpArgU, OpArgN, iABx)            /* OP_CLOSURE */
+ ,opmode(0, 1, OpArgU, OpArgN, iABC)            /* OP_VARARG */
+ ,opmode(0, 0, OpArgU, OpArgU, iAx)             /* OP_EXTRAARG */
+ ,opmode(0, 1, OpArgR, OpArgK, iABC)            /* OP_EVENT */
+ ,opmode(0, 1, OpArgR, OpArgK, iABC)            /* OP_EVENTNAME */
+ ,opmode(0, 1, OpArgR, OpArgK, iABC)            /* OP_EVENTARG */
+};
+
diff --git a/drivers/staging/ktap/runtime/kp_str.c b/drivers/staging/ktap/runtime/kp_str.c
new file mode 100644 (file)
index 0000000..1af42fe
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * kp_str.c - ktap string data struction manipulation
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+ *  - The part of code in this file is copied from lua initially.
+ *  - lua's MIT license is compatible with GPL.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "../include/ktap_types.h"
+#include "kp_obj.h"
+#include "kp_str.h"
+
+#ifdef __KERNEL__
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include "ktap.h"
+#include "kp_transport.h"
+#include "kp_vm.h"
+#endif
+
+#define STRING_MAXSHORTLEN     40
+
+int kp_tstring_cmp(const ktap_string *ls, const ktap_string *rs)
+{
+       const char *l = getstr(ls);
+       size_t ll = ls->tsv.len;
+       const char *r = getstr(rs);
+       size_t lr = rs->tsv.len;
+
+       for (;;) {
+               int temp = strcmp(l, r);
+               if (temp != 0)
+                       return temp;
+               else {
+                       /* strings are equal up to a `\0' */
+
+                       /* index of first `\0' in both strings */
+                       size_t len = strlen(l);
+
+                       /* r is finished? */
+                       if (len == lr)
+                               return (len == ll) ? 0 : 1;
+                       else if (len == ll)  /* l is finished? */
+                               return -1;
+
+                       /*
+                        * both strings longer than `len';
+                        * go on comparing (after the `\0')
+                        */
+                       len++;
+                       l += len; ll -= len; r += len; lr -= len;
+               }
+       }
+}
+
+/*
+ * equality for long strings
+ */
+int kp_tstring_eqlngstr(ktap_string *a, ktap_string *b)
+{
+       size_t len = a->tsv.len;
+
+       return (a == b) || ((len == b->tsv.len) &&
+               (memcmp(getstr(a), getstr(b), len) == 0));
+}
+
+/*
+ * equality for strings
+ */
+int kp_tstring_eqstr(ktap_string *a, ktap_string *b)
+{
+       return (a->tsv.tt == b->tsv.tt) &&
+              (a->tsv.tt == KTAP_TSHRSTR ? eqshrstr(a, b) :
+                               kp_tstring_eqlngstr(a, b));
+}
+
+#define STRING_HASHLIMIT       5
+unsigned int kp_string_hash(const char *str, size_t l, unsigned int seed)
+{
+       unsigned int h = seed ^ l;
+       size_t l1;
+       size_t step = (l >> STRING_HASHLIMIT) + 1;
+
+       for (l1 = l; l1 >= step; l1 -= step)
+               h = h ^ ((h<<5) + (h>>2) + (u8)(str[l1 - 1]));
+
+       return h;
+}
+
+
+/*
+ * resizes the string table
+ */
+void kp_tstring_resize(ktap_state *ks, int newsize)
+{
+       int i;
+       ktap_stringtable *tb = &G(ks)->strt;
+
+       if (newsize > tb->size) {
+               kp_realloc(ks, tb->hash, tb->size, newsize, ktap_gcobject *);
+
+       for (i = tb->size; i < newsize; i++)
+               tb->hash[i] = NULL;
+       }
+
+       /* rehash */
+       for (i = 0; i < tb->size; i++) {
+               ktap_gcobject *p = tb->hash[i];
+               tb->hash[i] = NULL;
+
+               while (p) {
+                       ktap_gcobject *next = gch(p)->next;
+                       unsigned int h = lmod(gco2ts(p)->hash, newsize);
+
+                       gch(p)->next = tb->hash[h];
+                       tb->hash[h] = p;
+                       p = next;
+               }
+       }
+
+       if (newsize < tb->size) {
+               /* shrinking slice must be empty */
+               kp_realloc(ks, tb->hash, tb->size, newsize, ktap_gcobject *);
+       }
+
+       tb->size = newsize;
+}
+
+/*
+ * creates a new string object
+ */
+static ktap_string *createstrobj(ktap_state *ks, const char *str, size_t l,
+                                int tag, unsigned int h, ktap_gcobject **list)
+{
+       ktap_string *ts;
+       size_t totalsize;  /* total size of TString object */
+
+       totalsize = sizeof(ktap_string) + ((l + 1) * sizeof(char));
+       ts = &kp_newobject(ks, tag, totalsize, list)->ts;
+       ts->tsv.len = l;
+       ts->tsv.hash = h;
+       ts->tsv.extra = 0;
+       memcpy(ts + 1, str, l * sizeof(char));
+       ((char *)(ts + 1))[l] = '\0';  /* ending 0 */
+       return ts;
+}
+
+/*
+ * creates a new short string, inserting it into string table
+ */
+static ktap_string *newshrstr(ktap_state *ks, const char *str, size_t l,
+                         unsigned int h)
+{
+       ktap_gcobject **list;
+       ktap_stringtable *tb = &G(ks)->strt;
+       ktap_string *s;
+
+       if (tb->nuse >= (int)tb->size)
+               kp_tstring_resize(ks, tb->size * 2);  /* too crowded */
+
+       list = &tb->hash[lmod(h, tb->size)];
+       s = createstrobj(ks, str, l, KTAP_TSHRSTR, h, list);
+       tb->nuse++;
+       return s;
+}
+
+/*
+ * checks whether short string exists and reuses it or creates a new one
+ */
+static ktap_string *internshrstr(ktap_state *ks, const char *str, size_t l)
+{
+       ktap_gcobject *o;
+       ktap_global_state *g = G(ks);
+       ktap_string *ts;
+       unsigned int h = kp_string_hash(str, l, g->seed);
+       unsigned long __maybe_unused flags;
+
+#ifdef __KERNEL__
+       local_irq_save(flags);
+       arch_spin_lock(&G(ks)->str_lock);
+#endif
+
+       for (o = g->strt.hash[lmod(h, g->strt.size)]; o != NULL;
+            o = gch(o)->next) {
+               ts = rawgco2ts(o);
+
+               if (h == ts->tsv.hash && ts->tsv.len == l &&
+                  (memcmp(str, getstr(ts), l * sizeof(char)) == 0))
+                       goto out;
+       }
+
+       ts = newshrstr(ks, str, l, h);  /* not found; create a new string */
+
+ out:
+#ifdef __KERNEL__
+       arch_spin_unlock(&G(ks)->str_lock);
+       local_irq_restore(flags);
+#endif
+       return ts;
+}
+
+
+/*
+ * new string (with explicit length)
+ */
+ktap_string *kp_tstring_newlstr(ktap_state *ks, const char *str, size_t l)
+{
+       /* short string? */
+       if (l <= STRING_MAXSHORTLEN)
+               return internshrstr(ks, str, l);
+       else
+               return createstrobj(ks, str, l, KTAP_TLNGSTR, G(ks)->seed,
+                                   NULL);
+}
+
+ktap_string *kp_tstring_newlstr_local(ktap_state *ks, const char *str, size_t l)
+{
+       return createstrobj(ks, str, l, KTAP_TLNGSTR, G(ks)->seed,
+                           &ks->gclist);
+}
+
+/*
+ * new zero-terminated string
+ */
+ktap_string *kp_tstring_new(ktap_state *ks, const char *str)
+{
+       return kp_tstring_newlstr(ks, str, strlen(str));
+}
+
+ktap_string *kp_tstring_new_local(ktap_state *ks, const char *str)
+{
+       return createstrobj(ks, str, strlen(str), KTAP_TLNGSTR, G(ks)->seed,
+                           &ks->gclist);
+}
+
+void kp_tstring_freeall(ktap_state *ks)
+{
+       ktap_global_state *g = G(ks);
+       int h;
+
+       for (h = 0; h < g->strt.size; h++) {
+               ktap_gcobject *o, *next;
+               o = g->strt.hash[h];
+               while (o) {
+                       next = gch(o)->next;
+                       kp_free(ks, o);
+                       o = next;
+               }
+               g->strt.hash[h] = NULL;
+       }
+
+       kp_free(ks, g->strt.hash);
+}
+
+/* todo: dump long string, strt table only contain short string */
+void kp_tstring_dump(ktap_state *ks)
+{
+       ktap_gcobject *o;
+       ktap_global_state *g = G(ks);
+       int h;
+
+       kp_printf(ks, "tstring dump: strt size: %d, nuse: %d\n", g->strt.size,
+                                                                g->strt.nuse);
+       for (h = 0; h < g->strt.size; h++) {
+               for (o = g->strt.hash[h]; o != NULL; o = gch(o)->next) {
+                       ktap_string *ts = rawgco2ts(o);
+                       kp_printf(ks, "%s [%d]\n", getstr(ts), (int)ts->tsv.len);
+               }
+       }
+}
+
+#ifdef __KERNEL__
+/* kp_str_fmt - printf implementation */
+
+/* macro to `unsign' a character */
+#define uchar(c)       ((unsigned char)(c))
+
+#define L_ESC          '%'
+
+/* valid flags in a format specification */
+#define FLAGS  "-+ #0"
+
+#define INTFRMLEN      "ll"
+#define INTFRM_T       long long
+
+/*
+ * maximum size of each format specification (such as '%-099.99d')
+ * (+10 accounts for %99.99x plus margin of error)
+ */
+#define MAX_FORMAT     (sizeof(FLAGS) + sizeof(INTFRMLEN) + 10)
+
+static const char *scanformat(ktap_state *ks, const char *strfrmt, char *form)
+{
+       const char *p = strfrmt;
+       while (*p != '\0' && strchr(FLAGS, *p) != NULL)
+               p++;  /* skip flags */
+
+       if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char)) {
+               kp_error(ks, "invalid format (repeated flags)\n");
+               return NULL;
+       }
+
+       if (isdigit(uchar(*p)))
+               p++;  /* skip width */
+
+       if (isdigit(uchar(*p)))
+               p++;  /* (2 digits at most) */
+
+       if (*p == '.') {
+               p++;
+               if (isdigit(uchar(*p)))
+                       p++;  /* skip precision */
+               if (isdigit(uchar(*p)))
+                       p++;  /* (2 digits at most) */
+       }
+
+       if (isdigit(uchar(*p))) {
+               kp_error(ks, "invalid format (width or precision too long)\n");
+               return NULL;
+       }
+
+       *(form++) = '%';
+       memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char));
+       form += p - strfrmt + 1;
+       *form = '\0';
+       return p;
+}
+
+
+/*
+ * add length modifier into formats
+ */
+static void addlenmod(char *form, const char *lenmod)
+{
+       size_t l = strlen(form);
+       size_t lm = strlen(lenmod);
+       char spec = form[l - 1];
+
+       strcpy(form + l - 1, lenmod);
+       form[l + lm - 1] = spec;
+       form[l + lm] = '\0';
+}
+
+
+static void ktap_argerror(ktap_state *ks, int narg, const char *extramsg)
+{
+       kp_error(ks, "bad argument #%d: (%s)\n", narg, extramsg);
+}
+
+int kp_str_fmt(ktap_state *ks, struct trace_seq *seq)
+{
+       int arg = 1;
+       size_t sfl;
+       ktap_value *arg_fmt = kp_arg(ks, 1);
+       int argnum = kp_arg_nr(ks);
+       const char *strfrmt, *strfrmt_end;
+
+       strfrmt = svalue(arg_fmt);
+       sfl = rawtsvalue(arg_fmt)->tsv.len;
+       strfrmt_end = strfrmt + sfl;
+
+       while (strfrmt < strfrmt_end) {
+               if (*strfrmt != L_ESC)
+                       trace_seq_putc(seq, *strfrmt++);
+               else if (*++strfrmt == L_ESC)
+                       trace_seq_putc(seq, *strfrmt++);
+               else { /* format item */
+                       char form[MAX_FORMAT];
+
+                       if (++arg > argnum) {
+                               ktap_argerror(ks, arg, "no value");
+                               return -1;
+                       }
+
+                       strfrmt = scanformat(ks, strfrmt, form);
+                       switch (*strfrmt++) {
+                       case 'c':
+                               trace_seq_printf(seq, form,
+                                                nvalue(kp_arg(ks, arg)));
+                               break;
+                       case 'd':  case 'i': {
+                               ktap_number n = nvalue(kp_arg(ks, arg));
+                               INTFRM_T ni = (INTFRM_T)n;
+                               addlenmod(form, INTFRMLEN);
+                               trace_seq_printf(seq, form, ni);
+                               break;
+                       }
+                       case 'p': {
+                               char str[KSYM_SYMBOL_LEN];
+                               SPRINT_SYMBOL(str, nvalue(kp_arg(ks, arg)));
+                               _trace_seq_puts(seq, str);
+                               break;
+                       }
+                       case 'o':  case 'u':  case 'x':  case 'X': {
+                               ktap_number n = nvalue(kp_arg(ks, arg));
+                               unsigned INTFRM_T ni = (unsigned INTFRM_T)n;
+                               addlenmod(form, INTFRMLEN);
+                               trace_seq_printf(seq, form, ni);
+                               break;
+                       }
+                       case 's': {
+                               ktap_value *v = kp_arg(ks, arg);
+                               const char *s;
+                               size_t l;
+
+                               if (is_nil(v)) {
+                                       _trace_seq_puts(seq, "nil");
+                                       return 0;
+                               }
+
+                               if (is_event(v)) {
+                                       kp_event_tostring(ks, seq);
+                                       return 0;
+                               }
+
+                               s = svalue(v);
+                               l = rawtsvalue(v)->tsv.len;
+                               if (!strchr(form, '.') && l >= 100) {
+                                       /*
+                                        * no precision and string is too long
+                                        * to be formatted;
+                                        * keep original string
+                                        */
+                                       _trace_seq_puts(seq, s);
+                                       break;
+                               } else {
+                                       trace_seq_printf(seq, form, s);
+                                       break;
+                               }
+                       }
+                       default: /* also treat cases `pnLlh' */
+                               kp_error(ks, "invalid option " KTAP_QL("%%%c")
+                                            " to " KTAP_QL("format"),
+                                            *(strfrmt - 1));
+                       }
+               }
+       }
+
+       return 0;
+}
+#endif
+
diff --git a/drivers/staging/ktap/runtime/kp_str.h b/drivers/staging/ktap/runtime/kp_str.h
new file mode 100644 (file)
index 0000000..9fc8da1
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __KTAP_STR_H__
+#define __KTAP_STR_H__
+
+ktap_string *kp_tstring_newlstr(ktap_state *ks, const char *str, size_t l);
+ktap_string *kp_tstring_newlstr_local(ktap_state *ks, const char *str, size_t l);
+ktap_string *kp_tstring_new(ktap_state *ks, const char *str);
+ktap_string *kp_tstring_new_local(ktap_state *ks, const char *str);
+int kp_tstring_eqstr(ktap_string *a, ktap_string *b);
+unsigned int kp_string_hash(const char *str, size_t l, unsigned int seed);
+int kp_tstring_eqlngstr(ktap_string *a, ktap_string *b);
+int kp_tstring_cmp(const ktap_string *ls, const ktap_string *rs);
+void kp_tstring_resize(ktap_state *ks, int newsize);
+void kp_tstring_freeall(ktap_state *ks);
+
+#ifdef __KERNEL__
+#include <linux/trace_seq.h>
+int kp_str_fmt(ktap_state *ks, struct trace_seq *seq);
+#endif
+
+#endif /* __KTAP_STR_H__ */
diff --git a/drivers/staging/ktap/runtime/kp_tab.c b/drivers/staging/ktap/runtime/kp_tab.c
new file mode 100644 (file)
index 0000000..422f401
--- /dev/null
@@ -0,0 +1,1396 @@
+/*
+ * kp_tab.c - ktap table data structure manipulation
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+ *  - The part of code in this file is copied from lua initially.
+ *  - lua's MIT license is compatible with GPL.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "../include/ktap_types.h"
+
+#ifdef __KERNEL__
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/sort.h>
+#include "ktap.h"
+#include "kp_vm.h"
+#else
+static inline void sort(void *base, size_t num, size_t size,
+                       int (*cmp_func)(const void *, const void *),
+                       void (*swap_func)(void *, void *, int size))
+{}
+#endif
+
+#include "kp_obj.h"
+#include "kp_str.h"
+
+#ifdef __KERNEL__
+#define kp_tab_lock_init(t)                                            \
+       do {                                                            \
+               (t)->lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; \
+       } while (0)
+#define kp_tab_lock(t)                                         \
+       do {                                                            \
+               local_irq_save(flags);                                  \
+               arch_spin_lock(&(t)->lock);                             \
+       } while (0)
+#define kp_tab_unlock(t)                                               \
+       do {                                                            \
+               arch_spin_unlock(&(t)->lock);                           \
+               local_irq_restore(flags);                               \
+       } while (0)
+
+#else
+#define kp_tab_lock_init(t)
+#define kp_tab_lock(t)
+#define kp_tab_unlock(t)
+#endif
+
+#define MAXBITS         30
+#define MAXASIZE        (1 << MAXBITS)
+
+
+#define NILCONSTANT     {NULL}, KTAP_TNIL
+const struct ktap_value ktap_nilobjectv = {NILCONSTANT};
+#define ktap_nilobject (&ktap_nilobjectv)
+
+static const ktap_tnode dummynode_ = {
+       {NILCONSTANT}, /* value */
+       {NULL, {NILCONSTANT}}, /* key */
+};
+
+#define gnode(t,i)      (&(t)->node[i])
+#define gkey(n)         (&(n)->i_key.tvk)
+#define gval(n)         (&(n)->i_val)
+#define gnext(n)        ((n)->i_key.next)
+
+#define twoto(x)        (1<<(x))
+#define sizenode(t)    (twoto((t)->lsizenode))
+
+#define hashpow2(t,n)           (gnode(t, lmod((n), sizenode(t))))
+
+#define hashmod(t,n)           (gnode(t, ((n) % ((sizenode(t)-1)|1))))
+
+#define hashstr(t,str)          hashpow2(t, (str)->tsv.hash)
+#define hashboolean(t,p)        hashpow2(t, p)
+#define hashnum(t, n)          hashmod(t, (unsigned int)n)
+#define hashpointer(t,p)       hashmod(t, (unsigned long)(p))
+
+#define dummynode      (&dummynode_)
+#define isdummy(n)     ((n) == dummynode)
+
+static void table_setint(ktap_state *ks, ktap_tab *t, int key, ktap_value *v);
+static ktap_value *table_set(ktap_state *ks, ktap_tab *t,
+                            const ktap_value *key);
+static void setnodevector(ktap_state *ks, ktap_tab *t, int size);
+
+static int ceillog2(unsigned int x)
+{
+       static const u8 log_2[256] = {
+       0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+       6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+       7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+       7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+       8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+       8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+       8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+       8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+       };
+
+       int l = 0;
+
+       x--;
+       while (x >= 256) { l += 8; x >>= 8; }
+       return l + log_2[x];
+}
+
+#ifdef __KERNEL__
+static inline ktap_stat_data *_read_sd(const ktap_value *v,
+                                   ktap_tnode *hnode, ktap_stat_data *hsd)
+{
+       ktap_tnode *node = container_of(v, ktap_tnode, i_val);
+       return hsd + (node - hnode);
+}
+
+static inline ktap_stat_data *read_sd(ktap_tab *t, const ktap_value *v)
+{
+       if (v >= &t->array[0] && v < &t->array[t->sizearray])
+               return &t->sd_arr[v - &t->array[0]];
+       else
+               return _read_sd(v, t->node, t->sd_rec);
+}
+
+#else
+static inline ktap_stat_data *_read_sd(const ktap_value *v,
+                                   ktap_tnode *hnode, ktap_stat_data *hsd)
+{
+       return NULL;
+}
+
+static inline ktap_stat_data *read_sd(ktap_tab *t, const ktap_value *v)
+{
+       return NULL;
+}
+#endif
+
+
+ktap_tab *kp_tab_new(ktap_state *ks)
+{
+       ktap_tab *t = &kp_newobject(ks, KTAP_TTABLE, sizeof(ktap_tab),
+                                     NULL)->h;
+       t->flags = (u8)(~0);
+       t->array = NULL;
+       t->sizearray = 0;
+       t->node = (ktap_tnode *)dummynode;
+       t->gclist = NULL;
+       t->with_stats = 0;
+       t->sd_arr = NULL;
+       t->sd_rec = NULL;
+       setnodevector(ks, t, 0);
+
+       t->sorted = NULL;
+       t->sort_head = NULL;
+
+       kp_tab_lock_init(t);
+       return t;
+}
+
+static const ktap_value *table_getint(ktap_tab *t, int key)
+{
+       ktap_tnode *n;
+
+       if ((unsigned int)(key - 1) < (unsigned int)t->sizearray)
+               return &t->array[key - 1];
+
+       n = hashnum(t, key);
+       do {
+               if (is_number(gkey(n)) && nvalue(gkey(n)) == key)
+                       return gval(n);
+               else
+                       n = gnext(n);
+       } while (n);
+
+       return ktap_nilobject;
+}
+
+const ktap_value *kp_tab_getint(ktap_tab *t, int key)
+{
+       const ktap_value *val;
+       unsigned long __maybe_unused flags;
+
+       kp_tab_lock(t);
+       val = table_getint(t, key);
+       kp_tab_unlock(t);
+
+       return val;
+}
+
+static ktap_tnode *mainposition (const ktap_tab *t, const ktap_value *key)
+{
+       switch (ttype(key)) {
+       case KTAP_TNUMBER:
+               return hashnum(t, nvalue(key));
+       case KTAP_TLNGSTR: {
+               ktap_string *s = rawtsvalue(key);
+               if (s->tsv.extra == 0) {  /* no hash? */
+                       s->tsv.hash = kp_string_hash(getstr(s), s->tsv.len,
+                                                    s->tsv.hash);
+                       s->tsv.extra = 1;  /* now it has its hash */
+               }
+               return hashstr(t, rawtsvalue(key));
+               }
+       case KTAP_TSHRSTR:
+               return hashstr(t, rawtsvalue(key));
+       case KTAP_TBOOLEAN:
+               return hashboolean(t, bvalue(key));
+       case KTAP_TLIGHTUSERDATA:
+               return hashpointer(t, pvalue(key));
+       case KTAP_TCFUNCTION:
+               return hashpointer(t, fvalue(key));
+       case KTAP_TBTRACE: {
+               /* use first entry as hash key, cannot use gcvalue as key */
+               unsigned long *entries = (unsigned long *)(btvalue(key) + 1);
+               return hashpointer(t, entries[0]);
+               }
+       default:
+               return hashpointer(t, gcvalue(key));
+       }
+}
+
+static int arrayindex(const ktap_value *key)
+{
+       if (is_number(key)) {
+               ktap_number n = nvalue(key);
+               int k = (int)n;
+               if ((ktap_number)k == n)
+                       return k;
+       }
+
+       /* `key' did not match some condition */
+       return -1;
+}
+
+/*
+ * returns the index of a `key' for table traversals. First goes all
+ * elements in the array part, then elements in the hash part. The
+ * beginning of a traversal is signaled by -1.
+ */
+static int findindex(ktap_state *ks, ktap_tab *t, StkId key)
+{
+       int i;
+
+       if (is_nil(key))
+               return -1;  /* first iteration */
+
+       i = arrayindex(key);
+       if (i > 0 && i <= t->sizearray)  /* is `key' inside array part? */
+               return i - 1;  /* yes; that's the index (corrected to C) */
+       else {
+               ktap_tnode *n = mainposition(t, key);
+               for (;;) {  /* check whether `key' is somewhere in the chain */
+                       /* key may be dead already, but it is ok to use it in `next' */
+                       if (kp_equalobjv(ks, gkey(n), key)) {
+                               i = n - gnode(t, 0);  /* key index in hash table */
+                               /* hash elements are numbered after array ones */
+                               return i + t->sizearray;
+                       } else
+                               n = gnext(n);
+
+                       if (n == NULL)
+                               /* key not found */
+                               kp_error(ks, "invalid table key to next");
+               }
+       }
+}
+
+int kp_tab_next(ktap_state *ks, ktap_tab *t, StkId key)
+{
+       unsigned long __maybe_unused flags;
+       int i;
+
+       kp_tab_lock(t);
+
+       i = findindex(ks, t, key);  /* find original element */
+
+       for (i++; i < t->sizearray; i++) {  /* try first array part */
+               if (!is_nil(&t->array[i])) {  /* a non-nil value? */
+                       set_number(key, i+1);
+                       set_obj(key+1, &t->array[i]);
+                       kp_tab_unlock(t);
+                       return 1;
+               }
+       }
+
+       for (i -= t->sizearray; i < sizenode(t); i++) {  /* then hash part */
+               if (!is_nil(gval(gnode(t, i)))) {  /* a non-nil value? */
+                       set_obj(key, gkey(gnode(t, i)));
+                       set_obj(key+1, gval(gnode(t, i)));
+                       kp_tab_unlock(t);
+                       return 1;
+               }
+       }
+
+       kp_tab_unlock(t);
+       return 0;  /* no more elements */
+}
+
+#ifdef __KERNEL__
+int kp_tab_sort_next(ktap_state *ks, ktap_tab *t, StkId key)
+{
+       unsigned long __maybe_unused flags;
+       ktap_tnode *node = t->sort_head;
+
+       kp_tab_lock(t);
+
+       if (is_nil(key)) {
+               /* first iteration */
+               set_obj(key, gkey(node));
+               set_obj(key + 1, gval(node));
+               kp_tab_unlock(t);
+               return 1;
+       }
+
+       while (node && !is_nil(gval(node))) {
+               if (kp_equalobjv(ks, gkey(node), key)) {
+                       node = gnext(node);
+                       if (!node)
+                               goto out;
+
+                       set_obj(key, gkey(node));
+                       set_obj(key + 1, gval(node));
+                       kp_tab_unlock(t);
+                       return 1;
+               }
+               node = gnext(node);
+       }
+
+ out:
+       kp_tab_unlock(t);
+       return 0;  /* no more elements */
+}
+
+
+static int default_compare(ktap_state *ks, ktap_closure *cmp_func,
+                               ktap_value *v1, ktap_value *v2)
+{
+       return nvalue(v1) < nvalue(v2);
+}
+
+static int closure_compare(ktap_state *ks, ktap_closure *cmp_func,
+                               ktap_value *v1, ktap_value *v2)
+{
+       ktap_value *func;
+       int res;
+
+       func = ks->top;
+       set_closure(ks->top++, cmp_func);
+       set_obj(ks->top++, v1);
+       set_obj(ks->top++, v2);
+
+       kp_call(ks, func, 1);
+
+       res = !is_false(ks->top - 1);
+
+       ks->top = func; /* restore ks->top */
+
+       return res;
+}
+
+static void insert_sorted_list(ktap_state *ks, ktap_tab *t,
+                               ktap_closure *cmp_func,
+                               ktap_value *key, ktap_value *val)
+{
+       ktap_tnode *node = t->sort_head;
+       ktap_tnode *newnode, *prevnode = NULL;
+       int (*compare)(ktap_state *ks, ktap_closure *cmp_func,
+                       ktap_value *v1, ktap_value *v2);
+       int i = 0;
+
+       if (is_nil(gval(node))) {
+               *gkey(node) = *key;
+               *gval(node) = *val;
+               return;
+       }
+
+       if (!cmp_func)
+               compare = default_compare;
+       else
+               compare = closure_compare;
+
+       while (node) {
+               //if (nvalue(gval(node)) < nvalue(val)) {
+               if (compare(ks, cmp_func, gval(node), val)) {
+                       prevnode = node;
+                       node = gnext(node);
+                       continue;
+               } else
+                       break;
+       }
+
+       /* find free position */
+       while (!is_nil(gval(&t->sorted[i]))) {
+               i++;
+       }
+
+       newnode = &t->sorted[i];
+       *gkey(newnode) = *key;
+       *gval(newnode) = *val;
+       gnext(newnode) = node;
+       if (prevnode)
+               gnext(prevnode) = newnode;
+       else
+               t->sort_head = newnode;
+}
+
+void kp_tab_sort(ktap_state *ks, ktap_tab *t, ktap_closure *cmp_func)
+{
+       unsigned long __maybe_unused flags;
+       int size = t->sizearray + sizenode(t);
+       int i;
+
+       kp_tab_lock(t);
+
+       kp_realloc(ks, t->sorted, 0, size, ktap_tnode);
+       memset(t->sorted, 0, size * sizeof(ktap_tnode));
+       t->sort_head = t->sorted;
+
+       for (i = 0; i < t->sizearray; i++) {
+               ktap_value *v = &t->array[i];
+               ktap_value key;
+
+               if (!is_nil(v)) {
+                       set_number(&key, i + 1);
+                       insert_sorted_list(ks, t, cmp_func, &key, v);
+               }
+       }
+
+       for (i = 0; i < sizenode(t); i++) {
+               ktap_tnode *node = &t->node[i];
+
+               if (is_nil(gkey(node)))
+                       continue;
+
+               insert_sorted_list(ks, t, cmp_func, gkey(node), gval(node));
+       }
+
+       kp_tab_unlock(t);
+}
+#endif
+
+static int computesizes (int nums[], int *narray)
+{
+       int i;
+       int twotoi;  /* 2^i */
+       int a = 0;  /* number of elements smaller than 2^i */
+       int na = 0;  /* number of elements to go to array part */
+       int n = 0;  /* optimal size for array part */
+
+       for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) {
+               if (nums[i] > 0) {
+                       a += nums[i];
+                       /* more than half elements present? */
+                       if (a > twotoi/2) {
+                               /* optimal size (till now) */
+                               n = twotoi;
+                               /*
+                                * all elements smaller than n will go to
+                                * array part
+                                */
+                               na = a;
+                       }
+               }
+               if (a == *narray)
+                       break;  /* all elements already counted */
+       }
+       *narray = n;
+       return na;
+}
+
+
+static int countint(const ktap_value *key, int *nums)
+{
+       int k = arrayindex(key);
+
+       /* is `key' an appropriate array index? */
+       if (0 < k && k <= MAXASIZE) {
+               nums[ceillog2(k)]++;  /* count as such */
+               return 1;
+       } else
+               return 0;
+}
+
+
+static int numusearray(const ktap_tab *t, int *nums)
+{
+       int lg;
+       int ttlg;  /* 2^lg */
+       int ause = 0;  /* summation of `nums' */
+       int i = 1;  /* count to traverse all array keys */
+
+       /* for each slice */
+       for (lg=0, ttlg=1; lg <= MAXBITS; lg++, ttlg *= 2) {
+               int lc = 0;  /* counter */
+               int lim = ttlg;
+
+               if (lim > t->sizearray) {
+                       lim = t->sizearray;  /* adjust upper limit */
+                       if (i > lim)
+                               break;  /* no more elements to count */
+               }
+
+               /* count elements in range (2^(lg-1), 2^lg] */
+               for (; i <= lim; i++) {
+                       if (!is_nil(&t->array[i-1]))
+                               lc++;
+               }
+               nums[lg] += lc;
+               ause += lc;
+       }
+       return ause;
+}
+
+static int numusehash(const ktap_tab *t, int *nums, int *pnasize)
+{
+       int totaluse = 0;  /* total number of elements */
+       int ause = 0;  /* summation of `nums' */
+       int i = sizenode(t);
+
+       while (i--) {
+               ktap_tnode *n = &t->node[i];
+               if (!is_nil(gval(n))) {
+                       ause += countint(gkey(n), nums);
+                       totaluse++;
+               }
+       }
+
+       *pnasize += ause;
+       return totaluse;
+}
+
+static void update_array_sd(ktap_tab *t)
+{
+       int i;
+
+       for (i = 0; i < t->sizearray; i++) {
+               ktap_value *v = &t->array[i];
+
+               if (!is_statdata(v))
+                       continue;
+
+               set_statdata(v, &t->sd_arr[i]);
+       }
+}
+
+static void setarrayvector(ktap_state *ks, ktap_tab *t, int size)
+{
+       int i;
+
+       kp_realloc(ks, t->array, t->sizearray, size, ktap_value);
+       if (t->with_stats) {
+               kp_realloc(ks, t->sd_arr, t->sizearray, size,
+                               ktap_stat_data);
+               update_array_sd(t);
+       }
+
+       for (i = t->sizearray; i < size; i++)
+               set_nil(&t->array[i]);
+
+       t->sizearray = size;
+}
+
+static void setnodevector(ktap_state *ks, ktap_tab *t, int size)
+{
+       int lsize;
+
+       if (size == 0) {  /* no elements to hash part? */
+               t->node = (ktap_tnode *)dummynode;  /* use common `dummynode' */
+               lsize = 0;
+       } else {
+               int i;
+               lsize = ceillog2(size);
+               if (lsize > MAXBITS) {
+                       kp_error(ks, "table overflow\n");
+                       return;
+               }
+
+               size = twoto(lsize);
+               t->node = kp_malloc(ks, size * sizeof(ktap_tnode));
+               if (t->with_stats)
+                       t->sd_rec = kp_malloc(ks, size *
+                                               sizeof(ktap_stat_data));
+               for (i = 0; i < size; i++) {
+                       ktap_tnode *n = gnode(t, i);
+                       gnext(n) = NULL;
+                       set_nil(gkey(n));
+                       set_nil(gval(n));
+               }
+       }
+
+       t->lsizenode = (u8)lsize;
+       t->lastfree = gnode(t, size);  /* all positions are free */
+}
+
+static void table_resize(ktap_state *ks, ktap_tab *t, int nasize, int nhsize)
+{
+       int oldasize = t->sizearray;
+       int oldhsize = t->lsizenode;
+       ktap_tnode *nold = t->node;  /* save old hash */
+       ktap_stat_data *sd_rec_old = t->sd_rec;  /* save stat_data */
+       int i;
+
+#ifdef __KERNEL__
+       kp_verbose_printf(ks, "table resize, nasize: %d, nhsize: %d\n",
+                               nasize, nhsize);
+#endif
+
+       if (nasize > oldasize)  /* array part must grow? */
+               setarrayvector(ks, t, nasize);
+
+       /* create new hash part with appropriate size */
+       setnodevector(ks, t, nhsize);
+
+       if (nasize < oldasize) {  /* array part must shrink? */
+               t->sizearray = nasize;
+               /* re-insert elements from vanishing slice */
+               for (i = nasize; i < oldasize; i++) {
+                       if (!is_nil(&t->array[i])) {
+                               ktap_value *v;
+                               v = (ktap_value *)table_getint(t, i + 1);
+                               set_obj(v, &t->array[i]);
+
+                               if (t->with_stats) {
+                                       *read_sd(t, v) = t->sd_arr[i];
+                                       set_statdata(v, read_sd(t, v));
+                               }
+                       }
+               }
+
+               /* shrink array */
+               kp_realloc(ks, t->array, oldasize, nasize, ktap_value);
+               if (t->with_stats) {
+                       kp_realloc(ks, t->sd_arr, oldasize, nasize,
+                                       ktap_stat_data);
+                       update_array_sd(t);
+               }
+       }
+
+       /* re-insert elements from hash part */
+       for (i = twoto(oldhsize) - 1; i >= 0; i--) {
+               ktap_tnode *old = nold + i;
+               if (!is_nil(gval(old))) {
+                       ktap_value *v = table_set(ks, t, gkey(old));
+                       /*
+                        * doesn't need barrier/invalidate cache, as entry was
+                        * already present in the table
+                        */
+                       set_obj(v, gval(old));
+
+                       if (t->with_stats) {
+                               ktap_stat_data *sd;
+
+                               sd = read_sd(t, v);
+                               *sd = *_read_sd(gval(old), nold, sd_rec_old);
+                               set_statdata(v, sd);
+                       }
+               }
+       }
+
+       if (!isdummy(nold)) {
+               kp_free(ks, nold); /* free old array */
+               kp_free(ks, sd_rec_old);
+       }
+}
+
+void kp_tab_resize(ktap_state *ks, ktap_tab *t, int nasize, int nhsize)
+{
+       unsigned long __maybe_unused flags;
+
+       kp_tab_lock(t);
+       table_resize(ks, t, nasize, nhsize);
+       kp_tab_unlock(t);
+}
+
+void kp_tab_resizearray(ktap_state *ks, ktap_tab *t, int nasize)
+{
+       unsigned long __maybe_unused flags;
+       int nsize;
+
+       kp_tab_lock(t);
+
+       nsize = isdummy(t->node) ? 0 : sizenode(t);
+       table_resize(ks, t, nasize, nsize);
+
+       kp_tab_unlock(t);
+}
+
+static void rehash(ktap_state *ks, ktap_tab *t, const ktap_value *ek)
+{
+       int nasize, na;
+       /* nums[i] = number of keys with 2^(i-1) < k <= 2^i */
+       int nums[MAXBITS+1];
+       int i;
+       int totaluse;
+
+       for (i = 0; i <= MAXBITS; i++)
+               nums[i] = 0;  /* reset counts */
+
+       nasize = numusearray(t, nums);  /* count keys in array part */
+       totaluse = nasize;  /* all those keys are integer keys */
+       totaluse += numusehash(t, nums, &nasize);  /* count keys in hash part */
+       /* count extra key */
+       nasize += countint(ek, nums);
+       totaluse++;
+       /* compute new size for array part */
+       na = computesizes(nums, &nasize);
+       /* resize the table to new computed sizes */
+       table_resize(ks, t, nasize, totaluse - na);
+}
+
+
+static ktap_tnode *getfreepos(ktap_tab *t)
+{
+       while (t->lastfree > t->node) {
+               t->lastfree--;
+               if (is_nil(gkey(t->lastfree)))
+                       return t->lastfree;
+       }
+       return NULL;  /* could not find a free place */
+}
+
+
+static ktap_value *table_newkey(ktap_state *ks, ktap_tab *t,
+                               const ktap_value *key)
+{
+       ktap_tnode *mp;
+       ktap_value newkey;
+
+       mp = mainposition(t, key);
+       if (!is_nil(gval(mp)) || isdummy(mp)) {  /* main position is taken? */
+               ktap_tnode *othern;
+               ktap_tnode *n = getfreepos(t);  /* get a free place */
+               if (n == NULL) {  /* cannot find a free place? */
+                       rehash(ks, t, key);  /* grow table */
+                       /* insert key into grown table */
+                       return table_set(ks, t, key);
+               }
+
+               othern = mainposition(t, gkey(mp));
+               if (othern != mp) {
+                       /* is colliding node out of its main position? */
+
+                       /* move colliding node into free position */
+                       while (gnext(othern) != mp)
+                               othern = gnext(othern);  /* find previous */
+
+                       /* redo the chain with `n' in place of `mp' */
+                       gnext(othern) = n;
+
+                       /* copy colliding node into free pos */
+                       *n = *mp;
+
+                       if (t->with_stats) {
+                               ktap_stat_data *sd = read_sd(t, gval(n));
+                               *sd = *read_sd(t, gval(mp));
+                               set_statdata(gval(n), sd);
+                       }
+
+                       gnext(mp) = NULL;  /* now `mp' is free */
+                       set_nil(gval(mp));
+               } else {
+                       /* colliding node is in its own main position */
+
+                       /* new node will go into free position */
+                       gnext(n) = gnext(mp);  /* chain new position */
+                       gnext(mp) = n;
+                       mp = n;
+               }
+       }
+
+       /* special handling for cloneable object, maily for btrace object */
+       if (is_needclone(key))
+               kp_objclone(ks, key, &newkey, &t->gclist);
+       else
+               newkey = *key;
+
+       set_obj(gkey(mp), &newkey);
+       return gval(mp);
+}
+
+
+/*
+ * search function for short strings
+ */
+static const ktap_value *table_getstr(ktap_tab *t, ktap_string *key)
+{
+       ktap_tnode *n = hashstr(t, key);
+
+       do {  /* check whether `key' is somewhere in the chain */
+               if (is_shrstring(gkey(n)) && eqshrstr(rawtsvalue(gkey(n)),
+                                                               key))
+                       return gval(n);  /* that's it */
+               else
+                       n = gnext(n);
+       } while (n);
+
+       return ktap_nilobject;
+}
+
+
+/*
+ * main search function
+ */
+static const ktap_value *table_get(ktap_tab *t, const ktap_value *key)
+{
+       switch (ttype(key)) {
+       case KTAP_TNIL:
+               return ktap_nilobject;
+       case KTAP_TSHRSTR:
+               return table_getstr(t, rawtsvalue(key));
+       case KTAP_TNUMBER: {
+               ktap_number n = nvalue(key);
+               int k = (int)n;
+               if ((ktap_number)k == nvalue(key)) /* index is int? */
+                       return table_getint(t, k);  /* use specialized version */
+               /* else go through */
+       }
+       default: {
+               ktap_tnode *n = mainposition(t, key);
+               do {  /* check whether `key' is somewhere in the chain */
+                       if (rawequalobj(gkey(n), key))
+                               return gval(n);  /* that's it */
+                       else
+                               n = gnext(n);
+               } while (n);
+
+               return ktap_nilobject;
+       }
+       }
+}
+
+const ktap_value *kp_tab_get(ktap_tab *t, const ktap_value *key)
+{
+       const ktap_value *val;
+       unsigned long __maybe_unused flags;
+
+       kp_tab_lock(t);
+       val = table_get(t, key);
+       kp_tab_unlock(t);
+
+       return val;
+}
+
+static ktap_value *table_set(ktap_state *ks, ktap_tab *t,
+                            const ktap_value *key)
+{
+       const ktap_value *p = table_get(t, key);
+
+       if (p != ktap_nilobject)
+               return (ktap_value *)p;
+       else
+               return table_newkey(ks, t, key);
+}
+
+void kp_tab_setvalue(ktap_state *ks, ktap_tab *t,
+                      const ktap_value *key, ktap_value *val)
+{
+       unsigned long __maybe_unused flags;
+
+       if (is_nil(key)) {
+               kp_printf(ks, "table index is nil\n");
+               kp_exit(ks);
+               return;
+       }
+
+       kp_tab_lock(t);
+       set_obj(table_set(ks, t, key), val);
+       kp_tab_unlock(t);
+}
+
+static void table_setint(ktap_state *ks, ktap_tab *t, int key, ktap_value *v)
+{
+       const ktap_value *p;
+       ktap_value *cell;
+
+       p = table_getint(t, key);
+
+       if (p != ktap_nilobject)
+               cell = (ktap_value *)p;
+       else {
+               ktap_value k;
+               set_number(&k, key);
+               cell = table_newkey(ks, t, &k);
+       }
+
+       set_obj(cell, v);
+}
+
+void kp_tab_setint(ktap_state *ks, ktap_tab *t, int key, ktap_value *val)
+{
+       unsigned long __maybe_unused flags;
+
+       kp_tab_lock(t);
+       table_setint(ks, t, key, val);
+       kp_tab_unlock(t);
+}
+
+void kp_tab_atomic_inc(ktap_state *ks, ktap_tab *t, ktap_value *key, int n)
+{
+       unsigned long __maybe_unused flags;
+       ktap_value *v;
+
+       if (is_nil(key)) {
+               kp_printf(ks, "table index is nil\n");
+               kp_exit(ks);
+               return;
+       }
+
+       kp_tab_lock(t);
+
+       v = table_set(ks, t, key);
+       if (is_nil(v)) {
+               set_number(v, n);
+       } else
+               set_number(v, nvalue(v) + n);
+
+       kp_tab_unlock(t);
+}
+
+int kp_tab_length(ktap_state *ks, ktap_tab *t)
+{
+       unsigned long __maybe_unused flags;
+       int i, len = 0;
+
+       kp_tab_lock(t);
+
+       for (i = 0; i < t->sizearray; i++) {
+               ktap_value *v = &t->array[i];
+
+               if (is_nil(v))
+                       continue;
+               len++;
+       }
+
+       for (i = 0; i < sizenode(t); i++) {
+               ktap_tnode *n = &t->node[i];
+
+               if (is_nil(gkey(n)))
+                       continue;
+
+               len++;
+       }
+
+       kp_tab_unlock(t);
+       return len;
+}
+
+void kp_tab_free(ktap_state *ks, ktap_tab *t)
+{
+       if (t->sizearray > 0) {
+               kp_free(ks, t->array);
+               kp_free(ks, t->sd_arr);
+       }
+
+       if (!isdummy(t->node)) {
+               kp_free(ks, t->node);
+               kp_free(ks, t->sd_rec);
+       }
+
+       kp_free(ks, t->sorted);
+       kp_free_gclist(ks, t->gclist);
+       kp_free(ks, t);
+}
+
+void kp_tab_dump(ktap_state *ks, ktap_tab *t)
+{
+       int i;
+
+       for (i = 0; i < t->sizearray; i++) {
+               ktap_value *v = &t->array[i];
+
+               if (is_nil(v))
+                       continue;
+
+               kp_printf(ks, "%d:\t", i + 1);
+               kp_showobj(ks, v);
+               kp_puts(ks, "\n");
+       }
+
+       for (i = 0; i < sizenode(t); i++) {
+               ktap_tnode *n = &t->node[i];
+
+               if (is_nil(gkey(n)))
+                       continue;
+
+               kp_showobj(ks, gkey(n));
+               kp_puts(ks, ":\t");
+               kp_showobj(ks, gval(n));
+               kp_puts(ks, "\n");
+       }
+}
+
+/*
+ * table-clear only set nil of all elements, not free t->array and nodes.
+ * we assume user will reuse table soon after clear table, so reserve array
+ * and nodes will avoid memory allocation when insert key-value again.
+ */
+void kp_tab_clear(ktap_state *ks, ktap_tab *t)
+{
+       unsigned long __maybe_unused flags;
+
+       kp_tab_lock(t);
+
+       memset(t->array, 0, t->sizearray * sizeof(ktap_value));
+       memset(t->node, 0, sizenode(t) * sizeof(ktap_tnode));
+
+       kp_tab_unlock(t);
+}
+
+#ifdef __KERNEL__
+static void string_convert(char *output, const char *input)
+{
+       if (strlen(input) > 32) {
+               strncpy(output, input, 32-4);
+               memset(output + 32-4, '.', 3);
+       } else
+               memcpy(output, input, strlen(input));
+}
+
+struct table_hist_record {
+       ktap_value key;
+       ktap_value val;
+};
+
+static int hist_record_cmp(const void *r1, const void *r2)
+{
+       const struct table_hist_record *i = r1;
+       const struct table_hist_record *j = r2;
+
+       if ((nvalue(&i->val) == nvalue(&j->val))) {
+               return 0;
+       } else if ((nvalue(&i->val) < nvalue(&j->val))) {
+               return 1;
+       } else
+               return -1;
+}
+
+/* todo: make histdump to be faster */
+
+/* histogram: key should be number or string, value must be number */
+static void table_histdump(ktap_state *ks, ktap_tab *t, int shownums)
+{
+       struct table_hist_record *thr;
+       unsigned long __maybe_unused flags;
+       char dist_str[40];
+       int i, ratio, total = 0, count = 0, top_num, is_kernel_address = 0;
+       int size, num;
+
+       size = sizeof(*thr) * (t->sizearray + sizenode(t));
+       thr = kp_malloc(ks, size);
+       if (!thr) {
+               kp_error(ks, "Cannot allocate %d of histogram memory", size);
+               return;
+       }
+
+       kp_tab_lock(t);
+
+       for (i = 0; i < t->sizearray; i++) {
+               ktap_value *v = &t->array[i];
+
+               if (is_nil(v))
+                       continue;
+
+               if (is_number(v))
+                       num = nvalue(v);
+               else if (is_statdata(v))
+                       num = sdvalue(v)->count;
+               else {
+                       kp_tab_unlock(t);
+                       goto error;
+               }
+
+               set_number(&thr[count].key, i + 1);
+               set_number(&thr[count].val, num);
+               count++;
+               total += num;
+       }
+
+       for (i = 0; i < sizenode(t); i++) {
+               ktap_tnode *n = &t->node[i];
+               ktap_value *v = gval(n);
+
+               if (is_nil(gkey(n)))
+                       continue;
+
+               if (is_number(v))
+                       num = nvalue(v);
+               else if (is_statdata(v))
+                       num = sdvalue(v)->count;
+               else {
+                       kp_tab_unlock(t);
+                       goto error;
+               }
+
+               set_obj(&thr[count].key, gkey(n));
+               set_number(&thr[count].val, num);
+               count++;
+               total += num;
+       }
+
+       kp_tab_unlock(t);
+
+       sort(thr, count, sizeof(struct table_hist_record),
+            hist_record_cmp, NULL);
+
+       dist_str[sizeof(dist_str) - 1] = '\0';
+
+       /* check the first key is a kernel text symbol or not */
+       if (is_number(&thr[0].key)) {
+               char str[KSYM_SYMBOL_LEN];
+
+               SPRINT_SYMBOL(str, nvalue(&thr[0].key));
+               if (str[0] != '0' || str[1] != 'x')
+                       is_kernel_address = 1;
+       }
+
+       top_num = min(shownums, count);
+       for (i = 0; i < top_num; i++) {
+               ktap_value *key = &thr[i].key;
+               ktap_value *val = &thr[i].val;
+
+               memset(dist_str, ' ', sizeof(dist_str) - 1);
+               ratio = (nvalue(val) * (sizeof(dist_str) - 1)) / total;
+               memset(dist_str, '@', ratio);
+
+               if (is_string(key)) {
+                       char buf[32 + 1] = {0};
+
+                       string_convert(buf, svalue(key));
+                       kp_printf(ks, "%32s |%s%-7d\n", buf, dist_str,
+                                     nvalue(val));
+               } else if (is_number(key)) {
+                       char str[KSYM_SYMBOL_LEN];
+                       char buf[32 + 1] = {0};
+
+                       if (is_kernel_address) {
+                               /* suppose it's a symbol, fix it in future */
+                               SPRINT_SYMBOL(str, nvalue(key));
+                               string_convert(buf, str);
+                               kp_printf(ks, "%32s |%s%-7d\n", buf, dist_str,
+                                               nvalue(val));
+                       } else {
+                               kp_printf(ks, "%32d |%s%-7d\n", nvalue(key),
+                                               dist_str, nvalue(val));
+                       }
+               }
+       }
+
+       if (count > shownums)
+               kp_printf(ks, "%32s |\n", "...");
+
+       goto out;
+
+ error:
+       kp_puts(ks, "error: table histogram only handle "
+                       " (key: string/number val: number)\n");
+ out:
+       kp_free(ks, thr);
+}
+
+#define HISTOGRAM_DEFAULT_TOP_NUM      20
+
+#define DISTRIBUTION_STR "------------- Distribution -------------"
+void kp_tab_histogram(ktap_state *ks, ktap_tab *t)
+{
+       kp_printf(ks, "%32s%s%s\n", "value ", DISTRIBUTION_STR, " count");
+       table_histdump(ks, t, HISTOGRAM_DEFAULT_TOP_NUM);
+}
+
+/*
+ * Parallel Table
+ */
+
+void kp_statdata_dump(ktap_state *ks, ktap_stat_data *sd)
+{
+       kp_printf(ks, "[count: %6d sum: %6d max: %6d min: %6d avg: %6d]",
+               sd->count, sd->sum, sd->max, sd->min, sd->sum/sd->count);
+}
+
+static void statdata_add(ktap_stat_data *sd1, ktap_stat_data *sd2)
+{
+       sd2->count += sd1->count;
+       sd2->sum += sd1->sum;
+       if (sd1->max > sd2->max)
+               sd2->max = sd1->max;
+       if (sd1->min < sd2->min)
+               sd2->min = sd1->min;
+}
+
+static void merge_table(ktap_state *ks, ktap_tab *t1, ktap_tab *t2)
+{
+       unsigned long __maybe_unused flags;
+       ktap_value *newv;
+       ktap_value n;
+       int i;
+
+       kp_tab_lock(t1);
+       kp_tab_lock(t2);
+
+       for (i = 0; i < t1->sizearray; i++) {
+               ktap_value *v = &t1->array[i];
+               ktap_stat_data *sd;
+
+               if (is_nil(v))
+                       continue;
+
+               set_number(&n, i);
+
+               newv = table_set(ks, t2, &n);
+               sd = read_sd(t2, newv);
+               if (is_nil(newv)) {
+                       *sd = *read_sd(t1, v);
+                       set_statdata(newv, sd);
+               } else
+                       statdata_add(read_sd(t1, v), sd);
+       }
+
+       for (i = 0; i < sizenode(t1); i++) {
+               ktap_tnode *node = &t1->node[i];
+
+               if (is_nil(gkey(node)))
+                       continue;
+
+               newv = table_set(ks, t2, gkey(node));
+               if (is_nil(newv)) {
+                       *read_sd(t2, newv) = *read_sd(t1, gval(node));
+                       set_statdata(newv, read_sd(t2, newv));
+               } else
+                       statdata_add(read_sd(t1, gval(node)),
+                                    read_sd(t2, newv));
+       }
+
+       kp_tab_unlock(t2);
+       kp_tab_unlock(t1);
+}
+
+ktap_tab *kp_ptab_synthesis(ktap_state *ks, ktap_ptab *ph)
+{
+       ktap_tab *agg;
+       int cpu;
+
+       agg = ph->agg;
+
+       /* clear the table content before store new elements */
+       kp_tab_clear(ks, agg);
+
+       for_each_possible_cpu(cpu) {
+               ktap_tab **t = per_cpu_ptr(ph->tbl, cpu);
+               merge_table(ks, *t, agg);
+       }
+
+       return agg;
+}
+
+void kp_ptab_dump(ktap_state *ks, ktap_ptab *ph)
+{
+       kp_tab_dump(ks, kp_ptab_synthesis(ks, ph));
+}
+
+ktap_ptab *kp_ptab_new(ktap_state *ks)
+{
+       ktap_ptab *ph;
+       int cpu;
+
+       ph = &kp_newobject(ks, KTAP_TPTABLE, sizeof(ktap_ptab),
+                       NULL)->ph;
+       ph->tbl = alloc_percpu(ktap_tab *);
+
+       for_each_possible_cpu(cpu) {
+               ktap_tab **t = per_cpu_ptr(ph->tbl, cpu);
+               *t = kp_tab_new(ks);
+
+               (*t)->with_stats = 1;
+
+               /* todo: make this value to be configuable, MAXENTRIES? */
+               table_resize(ks, *t, 0, 2000);
+       }
+
+       ph->agg = kp_tab_new(ks);
+       ph->agg->with_stats = 1;
+       table_resize(ks, ph->agg, 0, 2000);
+
+       return ph;
+}
+
+void kp_ptab_free(ktap_state *ks, ktap_ptab *ph)
+{
+       free_percpu(ph->tbl);
+       kp_free(ks, ph);
+}
+
+void kp_ptab_set(ktap_state *ks, ktap_ptab *ph,
+                                ktap_value *key, ktap_value *val)
+{
+       ktap_tab *t = *__this_cpu_ptr(ph->tbl);
+       unsigned long __maybe_unused flags;
+       ktap_value *v;
+       ktap_stat_data *sd;
+       int aggval;;
+
+       if (unlikely(!is_number(val))) {
+               kp_error(ks, "add non number value to aggregation table\n");
+               return;
+       }
+
+       aggval = nvalue(val);
+
+       kp_tab_lock(t);
+
+       v = table_set(ks, t, key);
+       sd = read_sd(t, v);
+
+       if (is_nil(v)) {
+               sd->count = 1;
+               sd->sum = sd->min = sd->max = aggval;
+               set_statdata(v, sd);
+               kp_tab_unlock(t);
+               return;
+       }
+
+       sd->count++;
+       sd->sum += aggval;
+       if (aggval > sd->max)
+               sd->max = aggval;
+       if (aggval < sd->min)
+               sd->min = aggval;
+
+       kp_tab_unlock(t);
+}
+
+void kp_ptab_get(ktap_state *ks, ktap_ptab *ph,
+                                ktap_value *key, ktap_value *val)
+{
+       unsigned long __maybe_unused flags;
+       ktap_stat_data sd, *aggsd;
+       const ktap_value *v;
+       ktap_value *aggval;
+       int cpu;
+
+       sd.count = sd.sum = sd.max = sd.min = -1;
+
+       for_each_possible_cpu(cpu) {
+               ktap_tab **t = per_cpu_ptr(ph->tbl, cpu);
+
+               kp_tab_lock(*t);
+               v = table_get(*t, key);
+               if (is_nil(v)) {
+                       kp_tab_unlock(*t);
+                       continue;
+               }
+
+               if (sd.count == -1) {
+                       sd = *read_sd(*t, v);
+                       kp_tab_unlock(*t);
+                       continue;
+               }
+
+               statdata_add(read_sd(*t, v), &sd);
+               kp_tab_unlock(*t);
+       }
+
+       if (sd.count == -1) {
+               set_nil(val);
+               return;
+       }
+
+       kp_tab_lock(ph->agg);
+       aggval = table_set(ks, ph->agg, key);
+       aggsd = read_sd(ph->agg, aggval);
+       *aggsd = sd;
+       set_statdata(aggval, aggsd);
+       set_statdata(val, aggsd);
+       kp_tab_unlock(ph->agg);
+}
+
+void kp_ptab_histogram(ktap_state *ks, ktap_ptab *ph)
+{
+       kp_tab_histogram(ks, kp_ptab_synthesis(ks, ph));
+}
+#endif
diff --git a/drivers/staging/ktap/runtime/kp_tab.h b/drivers/staging/ktap/runtime/kp_tab.h
new file mode 100644 (file)
index 0000000..51be4af
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef __KTAP_TAB_H__
+#define __KTAP_TAB_H__
+
+ktap_value *kp_tab_set(ktap_state *ks, ktap_tab *t, const ktap_value *key);
+ktap_tab *kp_tab_new(ktap_state *ks);
+const ktap_value *kp_tab_getint(ktap_tab *t, int key);
+void kp_tab_setint(ktap_state *ks, ktap_tab *t, int key, ktap_value *v);
+const ktap_value *kp_tab_get(ktap_tab *t, const ktap_value *key);
+void kp_tab_setvalue(ktap_state *ks, ktap_tab *t, const ktap_value *key, ktap_value *val);
+void kp_tab_resize(ktap_state *ks, ktap_tab *t, int nasize, int nhsize);
+void kp_tab_resizearray(ktap_state *ks, ktap_tab *t, int nasize);
+void kp_tab_free(ktap_state *ks, ktap_tab *t);
+int kp_tab_length(ktap_state *ks, ktap_tab *t);
+void kp_tab_dump(ktap_state *ks, ktap_tab *t);
+void kp_tab_clear(ktap_state *ks, ktap_tab *t);
+void kp_tab_histogram(ktap_state *ks, ktap_tab *t);
+int kp_tab_next(ktap_state *ks, ktap_tab *t, StkId key);
+int kp_tab_sort_next(ktap_state *ks, ktap_tab *t, StkId key);
+void kp_tab_sort(ktap_state *ks, ktap_tab *t, ktap_closure *cmp_func);
+void kp_tab_atomic_inc(ktap_state *ks, ktap_tab *t, ktap_value *key, int n);
+void kp_statdata_dump(ktap_state *ks, ktap_stat_data *sd);
+ktap_ptab *kp_ptab_new(ktap_state *ks);
+ktap_tab *kp_ptab_synthesis(ktap_state *ks, ktap_ptab *ph);
+void kp_ptab_dump(ktap_state *ks, ktap_ptab *ph);
+void kp_ptab_free(ktap_state *ks, ktap_ptab *ph);
+void kp_ptab_set(ktap_state *ks, ktap_ptab *ph,
+                       ktap_value *key, ktap_value *val);
+void kp_ptab_get(ktap_state *ks, ktap_ptab *ph,
+                       ktap_value *key, ktap_value *val);
+void kp_ptab_histogram(ktap_state *ks, ktap_ptab *ph);
+
+#endif /* __KTAP_TAB_H__ */
diff --git a/drivers/staging/ktap/runtime/kp_transport.c b/drivers/staging/ktap/runtime/kp_transport.c
new file mode 100644 (file)
index 0000000..5a1b1e1
--- /dev/null
@@ -0,0 +1,641 @@
+/*
+ * kp_transport.c - ktap transport functionality
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/ftrace_event.h>
+#include <linux/stacktrace.h>
+#include <linux/clocksource.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include "../include/ktap_types.h"
+#include "ktap.h"
+#include "kp_transport.h"
+
+struct ktap_trace_iterator {
+       struct ring_buffer      *buffer;
+       int                     print_timestamp;
+       void                    *private;
+
+       struct trace_iterator   iter;
+};
+
+enum ktap_trace_type {
+       __TRACE_FIRST_TYPE = 0,
+
+       TRACE_FN = 1, /* must be same as ftrace definition in kernel */
+       TRACE_PRINT,
+       TRACE_BPUTS,
+       TRACE_STACK,
+       TRACE_USER_STACK,
+
+       __TRACE_LAST_TYPE,
+};
+
+#define KTAP_TRACE_ITER(iter)  \
+       container_of(iter, struct ktap_trace_iterator, iter)
+
+static
+ssize_t _trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt)
+{
+       int len;
+       int ret;
+
+       if (!cnt)
+               return 0;
+
+       if (s->len <= s->readpos)
+               return -EBUSY;
+
+       len = s->len - s->readpos;
+       if (cnt > len)
+               cnt = len;
+       ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt);
+       if (ret == cnt)
+               return -EFAULT;
+
+       cnt -= ret;
+
+       s->readpos += cnt;
+       return cnt;
+}
+
+int _trace_seq_puts(struct trace_seq *s, const char *str)
+{
+       int len = strlen(str);
+
+       if (s->full)
+               return 0;
+
+       if (len > ((PAGE_SIZE - 1) - s->len)) {
+               s->full = 1;
+               return 0;
+       }
+
+       memcpy(s->buffer + s->len, str, len);
+       s->len += len;
+
+       return len;
+}
+
+static int trace_empty(struct trace_iterator *iter)
+{
+       struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
+       int cpu;
+
+       for_each_online_cpu(cpu) {
+               if (!ring_buffer_empty_cpu(ktap_iter->buffer, cpu))
+                       return 0;
+       }
+
+       return 1;
+}
+
+static void trace_consume(struct trace_iterator *iter)
+{
+       struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
+
+       ring_buffer_consume(ktap_iter->buffer, iter->cpu, &iter->ts,
+                           &iter->lost_events);
+}
+
+unsigned long long ns2usecs(cycle_t nsec)
+{
+       nsec += 500;
+       do_div(nsec, 1000);
+       return nsec;
+}
+
+static int trace_print_timestamp(struct trace_iterator *iter)
+{
+       struct trace_seq *s = &iter->seq;
+       unsigned long long t;
+       unsigned long secs, usec_rem;
+
+       t = ns2usecs(iter->ts);
+       usec_rem = do_div(t, USEC_PER_SEC);
+       secs = (unsigned long)t;
+
+       return trace_seq_printf(s, "%5lu.%06lu: ", secs, usec_rem);
+}
+
+/* todo: export kernel function ftrace_find_event in future, and make faster */
+static struct trace_event *(*ftrace_find_event)(int type);
+
+static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
+{
+       struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
+       struct trace_entry *entry = iter->ent;
+       struct trace_event *ev;
+
+       ev = ftrace_find_event(entry->type);
+
+       if (ktap_iter->print_timestamp && !trace_print_timestamp(iter))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       if (ev) {
+               int ret = ev->funcs->trace(iter, 0, ev);
+
+               /* overwrite '\n' at the ending */
+               iter->seq.buffer[iter->seq.len - 1] = '\0';
+               iter->seq.len--;
+               return ret;
+       }
+
+       return TRACE_TYPE_PARTIAL_LINE;
+}
+
+static enum print_line_t print_trace_stack(struct trace_iterator *iter)
+{
+       struct trace_entry *entry = iter->ent;
+       struct stack_trace trace;
+       char str[KSYM_SYMBOL_LEN];
+       int i;
+
+       trace.entries = (unsigned long *)(entry + 1);
+       trace.nr_entries = (iter->ent_size - sizeof(*entry)) /
+                          sizeof(unsigned long);
+
+       if (!_trace_seq_puts(&iter->seq, "<stack trace>\n"))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       for (i = 0; i < trace.nr_entries; i++) {
+               unsigned long p = trace.entries[i];
+
+               if (p == ULONG_MAX)
+                       break;
+
+               sprint_symbol(str, p);
+               if (!trace_seq_printf(&iter->seq, " => %s\n", str))
+                       return TRACE_TYPE_PARTIAL_LINE;
+       }
+
+       return TRACE_TYPE_HANDLED;
+}
+
+struct ktap_ftrace_entry {
+       struct trace_entry entry;
+       unsigned long ip;
+       unsigned long parent_ip;
+};
+
+static enum print_line_t print_trace_fn(struct trace_iterator *iter)
+{
+       struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
+       struct ktap_ftrace_entry *field = (struct ktap_ftrace_entry *)iter->ent;
+       char str[KSYM_SYMBOL_LEN];
+
+       if (ktap_iter->print_timestamp && !trace_print_timestamp(iter))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       sprint_symbol(str, field->ip);
+       if (!_trace_seq_puts(&iter->seq, str))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       if (!_trace_seq_puts(&iter->seq, " <- "))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       sprint_symbol(str, field->parent_ip);
+       if (!_trace_seq_puts(&iter->seq, str))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t print_trace_bputs(struct trace_iterator *iter)
+{
+       if (!_trace_seq_puts(&iter->seq,
+                           (const char *)(*(unsigned long *)(iter->ent + 1))))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t print_trace_line(struct trace_iterator *iter)
+{
+       struct trace_entry *entry = iter->ent;
+       char *str = (char *)(entry + 1);
+
+       if (entry->type == TRACE_PRINT) {
+               if (!trace_seq_printf(&iter->seq, "%s", str))
+                       return TRACE_TYPE_PARTIAL_LINE;
+
+               return TRACE_TYPE_HANDLED;
+       }
+
+       if (entry->type == TRACE_BPUTS)
+               return print_trace_bputs(iter);
+
+       if (entry->type == TRACE_STACK)
+               return print_trace_stack(iter);
+
+       if (entry->type == TRACE_FN)
+               return print_trace_fn(iter);
+
+       return print_trace_fmt(iter);
+}
+
+static struct trace_entry *
+peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts,
+               unsigned long *lost_events)
+{
+       struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
+       struct ring_buffer_event *event;
+
+       event = ring_buffer_peek(ktap_iter->buffer, cpu, ts, lost_events);
+       if (event) {
+               iter->ent_size = ring_buffer_event_length(event);
+               return ring_buffer_event_data(event);
+       }
+
+       return NULL;
+}
+
+static struct trace_entry *
+__find_next_entry(struct trace_iterator *iter, int *ent_cpu,
+                 unsigned long *missing_events, u64 *ent_ts)
+{
+       struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
+       struct ring_buffer *buffer = ktap_iter->buffer;
+       struct trace_entry *ent, *next = NULL;
+       unsigned long lost_events = 0, next_lost = 0;
+       u64 next_ts = 0, ts;
+       int next_cpu = -1;
+       int next_size = 0;
+       int cpu;
+
+       for_each_online_cpu(cpu) {
+               if (ring_buffer_empty_cpu(buffer, cpu))
+                       continue;
+
+               ent = peek_next_entry(iter, cpu, &ts, &lost_events);
+               /*
+                * Pick the entry with the smallest timestamp:
+                */
+               if (ent && (!next || ts < next_ts)) {
+                       next = ent;
+                       next_cpu = cpu;
+                       next_ts = ts;
+                       next_lost = lost_events;
+                       next_size = iter->ent_size;
+               }
+       }
+
+       iter->ent_size = next_size;
+
+       if (ent_cpu)
+               *ent_cpu = next_cpu;
+
+       if (ent_ts)
+               *ent_ts = next_ts;
+
+       if (missing_events)
+               *missing_events = next_lost;
+
+       return next;
+}
+
+/* Find the next real entry, and increment the iterator to the next entry */
+static void *trace_find_next_entry_inc(struct trace_iterator *iter)
+{
+       iter->ent = __find_next_entry(iter, &iter->cpu,
+                                     &iter->lost_events, &iter->ts);
+       if (iter->ent)
+               iter->idx++;
+
+       return iter->ent ? iter : NULL;
+}
+
+static void poll_wait_pipe(void)
+{
+       set_current_state(TASK_INTERRUPTIBLE);
+       /* sleep for 100 msecs, and try again. */
+       schedule_timeout(HZ / 10);
+}
+
+static int tracing_wait_pipe(struct file *filp)
+{
+       struct trace_iterator *iter = filp->private_data;
+       struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
+       ktap_state *ks = ktap_iter->private;
+
+       while (trace_empty(iter)) {
+
+               if ((filp->f_flags & O_NONBLOCK)) {
+                       return -EAGAIN;
+               }
+
+               mutex_unlock(&iter->mutex);
+
+               poll_wait_pipe();
+
+               mutex_lock(&iter->mutex);
+
+               if (G(ks)->wait_user && trace_empty(iter))
+                       return -EINTR;
+       }
+
+       return 1;
+}
+
+static ssize_t
+tracing_read_pipe(struct file *filp, char __user *ubuf, size_t cnt,
+                 loff_t *ppos)
+{
+       struct trace_iterator *iter = filp->private_data;
+       ssize_t sret;
+
+       /* return any leftover data */
+       sret = _trace_seq_to_user(&iter->seq, ubuf, cnt);
+       if (sret != -EBUSY)
+               return sret;
+       /*
+        * Avoid more than one consumer on a single file descriptor
+        * This is just a matter of traces coherency, the ring buffer itself
+        * is protected.
+        */
+       mutex_lock(&iter->mutex);
+
+waitagain:
+       sret = tracing_wait_pipe(filp);
+       if (sret <= 0)
+               goto out;
+
+       /* stop when tracing is finished */
+       if (trace_empty(iter)) {
+               sret = 0;
+               goto out;
+       }
+
+       if (cnt >= PAGE_SIZE)
+               cnt = PAGE_SIZE - 1;
+
+       /* reset all but tr, trace, and overruns */
+       memset(&iter->seq, 0,
+              sizeof(struct trace_iterator) -
+              offsetof(struct trace_iterator, seq));
+       iter->pos = -1;
+
+       while (trace_find_next_entry_inc(iter) != NULL) {
+               enum print_line_t ret;
+               int len = iter->seq.len;
+
+               ret = print_trace_line(iter);
+               if (ret == TRACE_TYPE_PARTIAL_LINE) {
+                       /* don't print partial lines */
+                       iter->seq.len = len;
+                       break;
+               }
+               if (ret != TRACE_TYPE_NO_CONSUME)
+                       trace_consume(iter);
+
+               if (iter->seq.len >= cnt)
+                       break;
+
+               /*
+                * Setting the full flag means we reached the trace_seq buffer
+                * size and we should leave by partial output condition above.
+                * One of the trace_seq_* functions is not used properly.
+                */
+               WARN_ONCE(iter->seq.full, "full flag set for trace type %d",
+                         iter->ent->type);
+       }
+
+       /* Now copy what we have to the user */
+       sret = _trace_seq_to_user(&iter->seq, ubuf, cnt);
+       if (iter->seq.readpos >= iter->seq.len)
+               trace_seq_init(&iter->seq);
+
+       /*
+        * If there was nothing to send to user, in spite of consuming trace
+        * entries, go back to wait for more entries.
+        */
+       if (sret == -EBUSY)
+               goto waitagain;
+
+out:
+       mutex_unlock(&iter->mutex);
+
+       return sret;
+}
+
+static int tracing_open_pipe(struct inode *inode, struct file *filp)
+{
+       struct ktap_trace_iterator *ktap_iter;
+       ktap_state *ks = inode->i_private;
+
+       /* create a buffer to store the information to pass to userspace */
+       ktap_iter = kzalloc(sizeof(*ktap_iter), GFP_KERNEL);
+       if (!ktap_iter)
+               return -ENOMEM;
+
+       ktap_iter->private = ks;
+       ktap_iter->buffer = G(ks)->buffer;
+       ktap_iter->print_timestamp = G(ks)->parm->print_timestamp;
+       mutex_init(&ktap_iter->iter.mutex);
+       filp->private_data = &ktap_iter->iter;
+
+       nonseekable_open(inode, filp);
+
+       return 0;
+}
+
+static int tracing_release_pipe(struct inode *inode, struct file *file)
+{
+       struct trace_iterator *iter = file->private_data;
+       struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
+
+       mutex_destroy(&iter->mutex);
+       kfree(ktap_iter);
+       return 0;
+}
+
+static const struct file_operations tracing_pipe_fops = {
+       .open           = tracing_open_pipe,
+       .read           = tracing_read_pipe,
+       .splice_read    = NULL,
+       .release        = tracing_release_pipe,
+       .llseek         = no_llseek,
+};
+
+/*
+ * preempt disabled in ring_buffer_lock_reserve
+ *
+ * The implementation is similar with funtion __ftrace_trace_stack.
+ */
+void kp_transport_print_backtrace(ktap_state *ks, int skip, int max_entries)
+{
+       struct ring_buffer *buffer = G(ks)->buffer;
+       struct ring_buffer_event *event;
+       struct trace_entry *entry;
+       int size;
+
+       size = max_entries * sizeof(unsigned long);
+       event = ring_buffer_lock_reserve(buffer, sizeof(*entry) + size);
+       if (!event) {
+               KTAP_STATS(ks)->events_missed += 1;
+               return;
+       } else {
+               struct stack_trace trace;
+
+               entry = ring_buffer_event_data(event);
+               tracing_generic_entry_update(entry, 0, 0);
+               entry->type = TRACE_STACK;
+
+               trace.nr_entries = 0;
+               trace.skip = skip;
+               trace.max_entries = max_entries;
+               trace.entries = (unsigned long *)(entry + 1);
+               save_stack_trace(&trace);
+
+               ring_buffer_unlock_commit(buffer, event);
+       }
+}
+
+void kp_transport_event_write(ktap_state *ks, struct ktap_event *e)
+{
+       struct ring_buffer *buffer = G(ks)->buffer;
+       struct ring_buffer_event *event;
+       struct trace_entry *entry;
+
+       event = ring_buffer_lock_reserve(buffer, e->entry_size +
+                                        sizeof(struct ftrace_event_call *));
+       if (!event) {
+               KTAP_STATS(ks)->events_missed += 1;
+               return;
+       } else {
+               entry = ring_buffer_event_data(event);
+
+               memcpy(entry, e->entry, e->entry_size);
+
+               ring_buffer_unlock_commit(buffer, event);
+       }
+}
+
+void kp_transport_write(ktap_state *ks, const void *data, size_t length)
+{
+       struct ring_buffer *buffer = G(ks)->buffer;
+       struct ring_buffer_event *event;
+       struct trace_entry *entry;
+       int size;
+
+       size = sizeof(struct trace_entry) + length;
+
+       event = ring_buffer_lock_reserve(buffer, size);
+       if (!event) {
+               KTAP_STATS(ks)->events_missed += 1;
+               return;
+       } else {
+               entry = ring_buffer_event_data(event);
+
+               tracing_generic_entry_update(entry, 0, 0);
+               entry->type = TRACE_PRINT;
+               memcpy(entry + 1, data, length);
+
+               ring_buffer_unlock_commit(buffer, event);
+       }
+}
+
+/* general print function */
+void kp_printf(ktap_state *ks, const char *fmt, ...)
+{
+       char buff[1024];
+       va_list args;
+       int len;
+
+       va_start(args, fmt);
+       len = vscnprintf(buff, 1024, fmt, args);
+       va_end(args);
+
+       buff[len] = '\0';
+       kp_transport_write(ks, buff, len + 1);
+}
+
+void __kp_puts(ktap_state *ks, const char *str)
+{
+       kp_transport_write(ks, str, strlen(str) + 1);
+}
+
+void __kp_bputs(ktap_state *ks, const char *str)
+{
+       struct ring_buffer *buffer = G(ks)->buffer;
+       struct ring_buffer_event *event;
+       struct trace_entry *entry;
+       int size;
+
+       size = sizeof(struct trace_entry) + sizeof(unsigned long *);
+
+       event = ring_buffer_lock_reserve(buffer, size);
+       if (!event) {
+               KTAP_STATS(ks)->events_missed += 1;
+               return;
+       } else {
+               entry = ring_buffer_event_data(event);
+
+               tracing_generic_entry_update(entry, 0, 0);
+               entry->type = TRACE_BPUTS;
+               *(unsigned long *)(entry + 1) = (unsigned long)str;
+
+               ring_buffer_unlock_commit(buffer, event);
+       }
+}
+
+void kp_transport_exit(ktap_state *ks)
+{
+       ring_buffer_free(G(ks)->buffer);
+       debugfs_remove(G(ks)->trace_pipe_dentry);
+}
+
+#define TRACE_BUF_SIZE_DEFAULT 1441792UL /* 16384 * 88 (sizeof(entry)) */
+
+int kp_transport_init(ktap_state *ks, struct dentry *dir)
+{
+       struct ring_buffer *buffer;
+       struct dentry *dentry;
+       char filename[32] = {0};
+
+       ftrace_find_event = (void *)kallsyms_lookup_name("ftrace_find_event");
+       if (!ftrace_find_event) {
+               printk("ktap: cannot lookup ftrace_find_event in kallsyms\n");
+               return -EINVAL;
+       }
+
+       buffer = ring_buffer_alloc(TRACE_BUF_SIZE_DEFAULT, RB_FL_OVERWRITE);
+       if (!buffer)
+               return -ENOMEM;
+
+       sprintf(filename, "trace_pipe_%d", (int)task_tgid_vnr(current));
+
+       dentry = debugfs_create_file(filename, 0444, dir,
+                                    ks, &tracing_pipe_fops);
+       if (!dentry) {
+               pr_err("ktapvm: cannot create trace_pipe file in debugfs\n");
+               ring_buffer_free(buffer);
+               return -1;
+       }
+
+       G(ks)->buffer = buffer;
+       G(ks)->trace_pipe_dentry = dentry;
+
+       return 0;
+}
+
diff --git a/drivers/staging/ktap/runtime/kp_transport.h b/drivers/staging/ktap/runtime/kp_transport.h
new file mode 100644 (file)
index 0000000..71b9c53
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __KTAP_TRANSPORT_H__
+#define __KTAP_TRANSPORT_H__
+
+void kp_transport_write(ktap_state *ks, const void *data, size_t length);
+void kp_transport_event_write(ktap_state *ks, struct ktap_event *e);
+void kp_transport_print_backtrace(ktap_state *ks, int skip, int max_entries);
+void *kp_transport_reserve(ktap_state *ks, size_t length);
+void kp_transport_exit(ktap_state *ks);
+int kp_transport_init(ktap_state *ks, struct dentry *dir);
+
+int _trace_seq_puts(struct trace_seq *s, const char *str);
+
+#endif /* __KTAP_TRANSPORT_H__ */
diff --git a/drivers/staging/ktap/runtime/kp_vm.c b/drivers/staging/ktap/runtime/kp_vm.c
new file mode 100644 (file)
index 0000000..9c498c9
--- /dev/null
@@ -0,0 +1,1496 @@
+/*
+ * kp_vm.c - ktap script virtual machine in Linux kernel
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+ *  - The part of code in this file is copied from lua initially.
+ *  - lua's MIT license is compatible with GPL.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/ftrace_event.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include "../include/ktap_types.h"
+#include "../include/ktap_opcodes.h"
+#include "../include/ktap_ffi.h"
+#include "ktap.h"
+#include "kp_obj.h"
+#include "kp_str.h"
+#include "kp_tab.h"
+#include "kp_transport.h"
+#include "kp_vm.h"
+
+#define KTAP_MIN_RESERVED_STACK_SIZE 20
+#define KTAP_STACK_SIZE                120 /* enlarge this value for big stack */
+#define KTAP_STACK_SIZE_BYTES  (KTAP_STACK_SIZE * sizeof(ktap_value))
+
+#define CIST_KTAP      (1 << 0) /* call is running a ktap function */
+#define CIST_REENTRY   (1 << 2)
+
+#define isktapfunc(ci) ((ci)->callstatus & CIST_KTAP)
+
+
+/* common helper function */
+int gettimeofday_us(void)
+{
+       struct timeval tv;
+
+       do_gettimeofday(&tv);
+       return tv.tv_sec * USEC_PER_SEC + tv.tv_usec;
+}
+
+
+static void ktap_concat(ktap_state *ks, int start, int end)
+{
+       int i, len = 0;
+       StkId top = ks->ci->u.l.base;
+       ktap_string *ts;
+       char *ptr, *buffer;
+
+       for (i = start; i <= end; i++) {
+               if (!is_string(top + i)) {
+                       kp_error(ks, "cannot concat non-string\n");
+                       set_nil(top + start);
+                       return;
+               }
+
+               len += rawtsvalue(top + i)->tsv.len;
+       }
+
+       if (len >= KTAP_PERCPU_BUFFER_SIZE) {
+               kp_error(ks, "Error: too long string concatenation\n");
+               return;
+       }
+
+       preempt_disable_notrace();
+
+       buffer = kp_percpu_data(ks, KTAP_PERCPU_DATA_BUFFER);
+       ptr = buffer;
+
+       for (i = start; i <= end; i++) {
+               int len = rawtsvalue(top + i)->tsv.len;
+               strncpy(ptr, svalue(top + i), len);
+               ptr += len;
+       }
+       ts = kp_tstring_newlstr(ks, buffer, len);
+       set_string(top + start, ts);
+
+       preempt_enable_notrace();
+}
+
+/* todo: compare l == r if both is tstring type? */
+static int lessthan(ktap_state *ks, const ktap_value *l, const ktap_value *r)
+{
+       if (is_number(l) && is_number(r))
+               return NUMLT(nvalue(l), nvalue(r));
+       else if (is_string(l) && is_string(r))
+               return kp_tstring_cmp(rawtsvalue(l), rawtsvalue(r)) < 0;
+
+       return 0;
+}
+
+static int lessequal(ktap_state *ks, const ktap_value *l, const ktap_value *r)
+{
+       if (is_number(l) && is_number(r))
+               return NUMLE(nvalue(l), nvalue(r));
+       else if (is_string(l) && is_string(r))
+               return kp_tstring_cmp(rawtsvalue(l), rawtsvalue(r)) <= 0;
+
+       return 0;
+}
+
+static int fb2int (int x)
+{
+       int e = (x >> 3) & 0x1f;
+       if (e == 0)
+               return x;
+       else
+               return ((x & 7) + 8) << (e - 1);
+}
+
+static const ktap_value *ktap_tonumber(const ktap_value *obj, ktap_value *n)
+{
+       if (is_number(obj))
+               return obj;
+
+       return NULL;
+}
+
+static ktap_upval *findupval(ktap_state *ks, StkId level)
+{
+       ktap_global_state *g = G(ks);
+       ktap_gcobject **pp = &ks->openupval;
+       ktap_upval *p;
+       ktap_upval *uv;
+
+       while (*pp != NULL && (p = gco2uv(*pp))->v >= level) {
+               if (p->v == level) {  /* found a corresponding upvalue? */
+                       return p;
+               }
+               pp = &p->next;
+       }
+
+       /* not found: create a new one */
+       uv = &kp_newobject(ks, KTAP_TUPVAL, sizeof(ktap_upval), pp)->uv;
+       uv->v = level;  /* current value lives in the stack */
+       uv->u.l.prev = &g->uvhead;  /* double link it in `uvhead' list */
+       uv->u.l.next = g->uvhead.u.l.next;
+       uv->u.l.next->u.l.prev = uv;
+       g->uvhead.u.l.next = uv;
+       return uv;
+}
+
+/* todo: implement this*/
+static void function_close (ktap_state *ks, StkId level)
+{
+}
+
+/* create a new closure */
+static void pushclosure(ktap_state *ks, ktap_proto *p, ktap_upval **encup,
+                       StkId base, StkId ra)
+{
+       int nup = p->sizeupvalues;
+       ktap_upvaldesc *uv = p->upvalues;
+       int i;
+       ktap_closure *ncl = kp_newclosure(ks, nup);
+
+       ncl->p = p;
+       set_closure(ra, ncl);  /* anchor new closure in stack */
+
+       /* fill in its upvalues */
+       for (i = 0; i < nup; i++) {
+               if (uv[i].instack) {
+                       /* upvalue refers to local variable? */
+                       ncl->upvals[i] = findupval(ks, base + uv[i].idx);
+               } else {
+                       /* get upvalue from enclosing function */
+                       ncl->upvals[i] = encup[uv[i].idx];
+               }
+       }
+       //p->cache = ncl;  /* save it on cache for reuse */
+}
+
+static void gettable(ktap_state *ks, const ktap_value *t, ktap_value *key,
+                    StkId val)
+{
+       if (is_table(t)) {
+               set_obj(val, kp_tab_get(hvalue(t), key));
+       } else if (is_ptable(t)) {
+               kp_ptab_get(ks, phvalue(t), key, val);
+       } else {
+               kp_error(ks, "get key from non-table\n");
+       }
+}
+
+static void settable(ktap_state *ks, const ktap_value *t, ktap_value *key,
+                    StkId val)
+{
+       if (is_table(t)) {
+               kp_tab_setvalue(ks, hvalue(t), key, val);
+       } else if (is_ptable(t)) {
+               kp_ptab_set(ks, phvalue(t), key, val);
+       } else {
+               kp_error(ks, "set key to non-table\n");
+       }
+}
+
+static void settable_incr(ktap_state *ks, const ktap_value *t, ktap_value *key,
+                         StkId val)
+{
+       if (unlikely(!is_table(t))) {
+               kp_error(ks, "use += operator for non-table\n");
+               return;
+       }
+
+       if (unlikely(!is_number(val))) {
+               kp_error(ks, "use non-number to += operator\n");
+               return;
+       }
+
+       kp_tab_atomic_inc(ks, hvalue(t), key, nvalue(val));
+}
+
+static inline int checkstack(ktap_state *ks, int n)
+{
+       if (unlikely(ks->stack_last - ks->top <= n)) {
+               kp_error(ks, "stack overflow, please enlarge stack size\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static StkId adjust_varargs(ktap_state *ks, ktap_proto *p, int actual)
+{
+       int i;
+       int nfixargs = p->numparams;
+       StkId base, fixed;
+
+       /* move fixed parameters to final position */
+       fixed = ks->top - actual;  /* first fixed argument */
+       base = ks->top;  /* final position of first argument */
+
+       for (i=0; i < nfixargs; i++) {
+               set_obj(ks->top++, fixed + i);
+               set_nil(fixed + i);
+       }
+
+       return base;
+}
+
+static int poscall(ktap_state *ks, StkId first_result)
+{
+       ktap_callinfo *ci;
+       StkId res;
+       int wanted, i;
+
+       ci = ks->ci;
+
+       res = ci->func;
+       wanted = ci->nresults;
+
+       ks->ci = ci = ci->prev;
+
+       for (i = wanted; i != 0 && first_result < ks->top; i--)
+               set_obj(res++, first_result++);
+
+       while(i-- > 0)
+               set_nil(res++);
+
+       ks->top = res;
+
+       return (wanted - (-1));
+}
+
+static ktap_callinfo *extend_ci(ktap_state *ks)
+{
+       ktap_callinfo *ci;
+
+       ci = kp_malloc(ks, sizeof(ktap_callinfo));
+       ks->ci->next = ci;
+       ci->prev = ks->ci;
+       ci->next = NULL;
+
+       return ci;
+}
+
+static void free_ci(ktap_state *ks)
+{
+       ktap_callinfo *ci = ks->ci;
+       ktap_callinfo *next;
+
+       if (!ci)
+               return;
+
+       next = ci->next;
+       ci->next = NULL;
+       while ((ci = next) != NULL) {
+               next = ci->next;
+               kp_free(ks, ci);
+       }
+}
+
+#define next_ci(ks) (ks->ci = ks->ci->next ? ks->ci->next : extend_ci(ks))
+#define savestack(ks, p)       ((char *)(p) - (char *)ks->stack)
+#define restorestack(ks, n)    ((ktap_value *)((char *)ks->stack + (n)))
+
+static int precall(ktap_state *ks, StkId func, int nresults)
+{
+       ktap_cfunction f;
+       ktap_callinfo *ci;
+       ktap_proto *p;
+#ifdef CONFIG_KTAP_FFI
+       ktap_cdata *cd;
+       csymbol *cs;
+#endif
+       StkId base;
+       ptrdiff_t funcr = savestack(ks, func);
+       int n;
+
+       switch (ttype(func)) {
+       case KTAP_TCFUNCTION: /* light C function */
+               f = fvalue(func);
+
+               if (checkstack(ks, KTAP_MIN_RESERVED_STACK_SIZE))
+                       return 1;
+
+               ci = next_ci(ks);
+               ci->nresults = nresults;
+               ci->func = restorestack(ks, funcr);
+               ci->top = ks->top + KTAP_MIN_RESERVED_STACK_SIZE;
+               ci->callstatus = 0;
+               n = (*f)(ks);
+               poscall(ks, ks->top - n);
+               return 1;
+       case KTAP_TCLOSURE:
+               p = clvalue(func)->p;
+
+               if (checkstack(ks, p->maxstacksize))
+                       return 1;
+
+               func = restorestack(ks, funcr);
+               n = (int)(ks->top - func) - 1; /* number of real arguments */
+
+               /* complete missing arguments */
+               for (; n < p->numparams; n++)
+                       set_nil(ks->top++);
+
+               base = (!p->is_vararg) ? func + 1 : adjust_varargs(ks, p, n);
+               ci = next_ci(ks);
+               ci->nresults = nresults;
+               ci->func = func;
+               ci->u.l.base = base;
+               ci->top = base + p->maxstacksize;
+               ci->u.l.savedpc = p->code; /* starting point */
+               ci->callstatus = CIST_KTAP;
+               ks->top = ci->top;
+               return 0;
+#ifdef CONFIG_KTAP_FFI
+       case KTAP_TCDATA:
+               cd = cdvalue(func);
+
+               if (checkstack(ks, KTAP_MIN_RESERVED_STACK_SIZE))
+                       return 1;
+
+               if (cd_type(ks, cd) != FFI_FUNC)
+                       kp_error(ks, "Value in cdata is not a c funcion\n");
+               cs = cd_csym(ks, cd);
+               kp_verbose_printf(ks, "calling ffi function [%s] with address %p\n",
+                               csym_name(cs), csym_func_addr(cs));
+
+               ci = next_ci(ks);
+               ci->nresults = nresults;
+               ci->func = restorestack(ks, funcr);
+               ci->top = ks->top + KTAP_MIN_RESERVED_STACK_SIZE;
+               ci->callstatus = 0;
+
+               n = kp_ffi_call(ks, csym_func(cs));
+               kp_verbose_printf(ks, "returned from ffi call...\n");
+               poscall(ks, ks->top - n);
+               return 1;
+#endif
+       default:
+               kp_error(ks, "attempt to call nil function\n");
+       }
+
+       return 0;
+}
+
+#define RA(i)   (base+GETARG_A(i))
+#define RB(i)   (base+GETARG_B(i))
+#define ISK(x)  ((x) & BITRK)
+#define RC(i)   base+GETARG_C(i)
+#define RKB(i) \
+        ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)
+#define RKC(i)  \
+        ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)
+
+#define dojump(ci,i,e) { \
+       ci->u.l.savedpc += GETARG_sBx(i) + e; }
+#define donextjump(ci)  { instr = *ci->u.l.savedpc; dojump(ci, instr, 1); }
+
+#define arith_op(ks, op) { \
+       ktap_value *rb = RKB(instr); \
+       ktap_value *rc = RKC(instr); \
+       if (is_number(rb) && is_number(rc)) { \
+               ktap_number nb = nvalue(rb), nc = nvalue(rc); \
+               set_number(ra, op(nb, nc)); \
+       } else {        \
+               kp_puts(ks, "Error: Cannot make arith operation\n");    \
+               return; \
+       } }
+
+static ktap_value *cfunction_cache_get(ktap_state *ks, int index);
+
+static void ktap_execute(ktap_state *ks)
+{
+       int exec_count = 0;
+       ktap_callinfo *ci;
+       ktap_closure *cl;
+       ktap_value *k;
+       unsigned int instr, opcode;
+       StkId base; /* stack pointer */
+       StkId ra; /* register pointer */
+       int res, nresults; /* temp varible */
+
+       ci = ks->ci;
+
+ newframe:
+       cl = clvalue(ci->func);
+       k = cl->p->k;
+       base = ci->u.l.base;
+
+ mainloop:
+       /* main loop of interpreter */
+
+       /* dead loop detaction */
+       if (exec_count++ == kp_max_exec_count) {
+               if (G(ks)->mainthread != ks) {
+                       kp_error(ks, "non-mainthread executed instructions "
+                                    "exceed max limit(%d)\n",
+                                       kp_max_exec_count);
+                       return;
+               }
+
+               cond_resched();
+               if (signal_pending(current)) {
+                       flush_signals(current);
+                       return;
+               }
+               exec_count = 0;
+       }
+
+       instr = *(ci->u.l.savedpc++);
+       opcode = GET_OPCODE(instr);
+
+       /* ra is target register */
+       ra = RA(instr);
+
+       switch (opcode) {
+       case OP_MOVE:
+               set_obj(ra, base + GETARG_B(instr));
+               break;
+       case OP_LOADK:
+               set_obj(ra, k + GETARG_Bx(instr));
+               break;
+       case OP_LOADKX:
+               set_obj(ra, k + GETARG_Ax(*ci->u.l.savedpc++));
+               break;
+       case OP_LOADBOOL:
+               set_boolean(ra, GETARG_B(instr));
+               if (GETARG_C(instr))
+                       ci->u.l.savedpc++;
+               break;
+       case OP_LOADNIL: {
+               int b = GETARG_B(instr);
+               do {
+                       set_nil(ra++);
+               } while (b--);
+               break;
+               }
+       case OP_GETUPVAL: {
+               int b = GETARG_B(instr);
+               set_obj(ra, cl->upvals[b]->v);
+               break;
+               }
+       case OP_GETTABUP: {
+               int b = GETARG_B(instr);
+               gettable(ks, cl->upvals[b]->v, RKC(instr), ra);
+               base = ci->u.l.base;
+               break;
+               }
+       case OP_GETTABLE:
+               gettable(ks, RB(instr), RKC(instr), ra);
+               base = ci->u.l.base;
+               break;
+       case OP_SETTABUP: {
+               int a = GETARG_A(instr);
+               settable(ks, cl->upvals[a]->v, RKB(instr), RKC(instr));
+               base = ci->u.l.base;
+               break;
+               }
+       case OP_SETTABUP_INCR: {
+               int a = GETARG_A(instr);
+               settable_incr(ks, cl->upvals[a]->v, RKB(instr), RKC(instr));
+               base = ci->u.l.base;
+               break;
+               }
+       case OP_SETTABUP_AGGR: {
+               int a = GETARG_A(instr);
+               ktap_value *v = cl->upvals[a]->v;
+               if (!is_ptable(v)) {
+                       kp_error(ks, "<<< must be operate on ptable\n");
+                       return;
+               }
+
+               kp_ptab_set(ks, phvalue(v), RKB(instr), RKC(instr));
+               base = ci->u.l.base;
+               break;
+               }
+       case OP_SETUPVAL: {
+               ktap_upval *uv = cl->upvals[GETARG_B(instr)];
+               set_obj(uv->v, ra);
+               break;
+               }
+       case OP_SETTABLE:
+               settable(ks, ra, RKB(instr), RKC(instr));
+               base = ci->u.l.base;
+               break;
+       case OP_SETTABLE_INCR:
+               settable_incr(ks, ra, RKB(instr), RKC(instr));
+               base = ci->u.l.base;
+               break;
+       case OP_SETTABLE_AGGR:
+               if (!is_ptable(ra)) {
+                       kp_error(ks, "<<< must be operate on ptable\n");
+                       return;
+               }
+
+               kp_ptab_set(ks, phvalue(ra), RKB(instr), RKC(instr));
+               base = ci->u.l.base;
+               break;
+       case OP_NEWTABLE: {
+               int b = GETARG_B(instr);
+               int c = GETARG_C(instr);
+               ktap_tab *t = kp_tab_new(ks);
+               set_table(ra, t);
+               if (b != 0 || c != 0)
+                       kp_tab_resize(ks, t, fb2int(b), fb2int(c));
+               break;
+               }
+       case OP_SELF: {
+               StkId rb = RB(instr);
+               set_obj(ra+1, rb);
+               gettable(ks, rb, RKC(instr), ra);
+               base = ci->u.l.base;
+               break;
+               }
+       case OP_ADD:
+               arith_op(ks, NUMADD);
+               break;
+       case OP_SUB:
+               arith_op(ks, NUMSUB);
+               break;
+       case OP_MUL:
+               arith_op(ks, NUMMUL);
+               break;
+       case OP_DIV:
+               /* divide 0 checking */
+               if (!nvalue(RKC(instr))) {
+                       kp_error(ks, "divide 0 arith operation\n");
+                       return;
+               }
+               arith_op(ks, NUMDIV);
+               break;
+       case OP_MOD:
+               /* divide 0 checking */
+               if (!nvalue(RKC(instr))) {
+                       kp_error(ks, "mod 0 arith operation\n");
+                       return;
+               }
+               arith_op(ks, NUMMOD);
+               break;
+       case OP_POW:
+               kp_error(ks, "ktap don't support pow arith in kernel\n");
+               return;
+       case OP_UNM: {
+               ktap_value *rb = RB(instr);
+               if (is_number(rb)) {
+                       ktap_number nb = nvalue(rb);
+                       set_number(ra, NUMUNM(nb));
+               }
+               break;
+               }
+       case OP_NOT:
+               res = is_false(RB(instr));
+               set_boolean(ra, res);
+               break;
+       case OP_LEN: {
+               int len = kp_objlen(ks, RB(instr));
+               if (len < 0)
+                       return;
+               set_number(ra, len);
+               break;
+               }
+       case OP_CONCAT: {
+               int b = GETARG_B(instr);
+               int c = GETARG_C(instr);
+               ktap_concat(ks, b, c);
+               break;
+               }
+       case OP_JMP:
+               dojump(ci, instr, 0);
+               break;
+       case OP_EQ: {
+               ktap_value *rb = RKB(instr);
+               ktap_value *rc = RKC(instr);
+               if ((int)rawequalobj(rb, rc) != GETARG_A(instr))
+                       ci->u.l.savedpc++;
+               else
+                       donextjump(ci);
+
+               base = ci->u.l.base;
+               break;
+               }
+       case OP_LT: {
+               if (lessthan(ks, RKB(instr), RKC(instr)) != GETARG_A(instr)) {
+                       ci->u.l.savedpc++;
+               } else
+                       donextjump(ci);
+               base = ci->u.l.base;
+               break;
+               }
+       case OP_LE:
+               if (lessequal(ks, RKB(instr), RKC(instr)) != GETARG_A(instr))
+                       ci->u.l.savedpc++;
+               else
+                       donextjump(ci);
+               base = ci->u.l.base;
+               break;
+       case OP_TEST:
+               if (GETARG_C(instr) ? is_false(ra) : !is_false(ra))
+                       ci->u.l.savedpc++;
+               else
+                       donextjump(ci);
+               break;
+       case OP_TESTSET: {
+               ktap_value *rb = RB(instr);
+               if (GETARG_C(instr) ? is_false(rb) : !is_false(rb))
+                       ci->u.l.savedpc++;
+               else {
+                       set_obj(ra, rb);
+                       donextjump(ci);
+               }
+               break;
+               }
+       case OP_CALL: {
+               int b = GETARG_B(instr);
+               int ret;
+
+               nresults = GETARG_C(instr) - 1;
+
+               if (b != 0)
+                       ks->top = ra + b;
+
+               ret = precall(ks, ra, nresults);
+               if (ret) { /* C function */
+                       if (nresults >= 0)
+                               ks->top = ci->top;
+                       base = ci->u.l.base;
+                       break;
+               } else { /* ktap function */
+                       ci = ks->ci;
+                       /* this flag is used for return time, see OP_RETURN */
+                       ci->callstatus |= CIST_REENTRY;
+                       goto newframe;
+               }
+               break;
+               }
+       case OP_TAILCALL: {
+               int b = GETARG_B(instr);
+
+               if (b != 0)
+                       ks->top = ra+b;
+               if (precall(ks, ra, -1))  /* C function? */
+                       base = ci->u.l.base;
+               else {
+                       int aux;
+
+                       /*
+                        * tail call: put called frame (n) in place of
+                        * caller one (o)
+                        */
+                       ktap_callinfo *nci = ks->ci;  /* called frame */
+                       ktap_callinfo *oci = nci->prev;  /* caller frame */
+                       StkId nfunc = nci->func;  /* called function */
+                       StkId ofunc = oci->func;  /* caller function */
+                       /* last stack slot filled by 'precall' */
+                       StkId lim = nci->u.l.base +
+                                   clvalue(nfunc)->p->numparams;
+
+                       /* close all upvalues from previous call */
+                       if (cl->p->sizep > 0)
+                               function_close(ks, oci->u.l.base);
+
+                       /* move new frame into old one */
+                       for (aux = 0; nfunc + aux < lim; aux++)
+                               set_obj(ofunc + aux, nfunc + aux);
+                       /* correct base */
+                       oci->u.l.base = ofunc + (nci->u.l.base - nfunc);
+                       /* correct top */
+                       oci->top = ks->top = ofunc + (ks->top - nfunc);
+                       oci->u.l.savedpc = nci->u.l.savedpc;
+                       /* remove new frame */
+                       ci = ks->ci = oci;
+                       /* restart ktap_execute over new ktap function */
+                       goto newframe;
+               }
+               break;
+               }
+       case OP_RETURN: {
+               int b = GETARG_B(instr);
+               if (b != 0)
+                       ks->top = ra+b-1;
+               if (cl->p->sizep > 0)
+                       function_close(ks, base);
+               b = poscall(ks, ra);
+
+               /* if it's called from external invocation, just return */
+               if (!(ci->callstatus & CIST_REENTRY))
+                       return;
+
+               ci = ks->ci;
+               if (b)
+                       ks->top = ci->top;
+               goto newframe;
+               }
+       case OP_FORLOOP: {
+               ktap_number step = nvalue(ra+2);
+               /* increment index */
+               ktap_number idx = NUMADD(nvalue(ra), step);
+               ktap_number limit = nvalue(ra+1);
+               if (NUMLT(0, step) ? NUMLE(idx, limit) : NUMLE(limit, idx)) {
+                       ci->u.l.savedpc += GETARG_sBx(instr);  /* jump back */
+                       set_number(ra, idx);  /* update internal index... */
+                       set_number(ra+3, idx);  /* ...and external index */
+               }
+               break;
+               }
+       case OP_FORPREP: {
+               const ktap_value *init = ra;
+               const ktap_value *plimit = ra + 1;
+               const ktap_value *pstep = ra + 2;
+
+               if (!ktap_tonumber(init, ra)) {
+                       kp_error(ks, KTAP_QL("for")
+                                " initial value must be a number\n");
+                       return;
+               } else if (!ktap_tonumber(plimit, ra + 1)) {
+                       kp_error(ks, KTAP_QL("for")
+                                " limit must be a number\n");
+                       return;
+               } else if (!ktap_tonumber(pstep, ra + 2)) {
+                       kp_error(ks, KTAP_QL("for") " step must be a number\n");
+                       return;
+               }
+
+               set_number(ra, NUMSUB(nvalue(ra), nvalue(pstep)));
+               ci->u.l.savedpc += GETARG_sBx(instr);
+               break;
+               }
+       case OP_TFORCALL: {
+               StkId cb = ra + 3;  /* call base */
+               set_obj(cb + 2, ra + 2);
+               set_obj(cb + 1, ra + 1);
+               set_obj(cb, ra);
+               ks->top = cb + 3;  /* func. + 2 args (state and index) */
+               kp_call(ks, cb, GETARG_C(instr));
+               base = ci->u.l.base;
+               ks->top = ci->top;
+               instr = *(ci->u.l.savedpc++);  /* go to next instruction */
+               ra = RA(instr);
+               }
+               /*go through */
+       case OP_TFORLOOP:
+               if (!is_nil(ra + 1)) {  /* continue loop? */
+                       set_obj(ra, ra + 1);  /* save control variable */
+                       ci->u.l.savedpc += GETARG_sBx(instr);  /* jump back */
+               }
+               break;
+       case OP_SETLIST: {
+               int n = GETARG_B(instr);
+               int c = GETARG_C(instr);
+               int last;
+               ktap_tab *h;
+
+               if (n == 0)
+                       n = (int)(ks->top - ra) - 1;
+               if (c == 0)
+                       c = GETARG_Ax(*ci->u.l.savedpc++);
+
+               h = hvalue(ra);
+               last = ((c - 1) * LFIELDS_PER_FLUSH) + n;
+               if (last > h->sizearray)  /* needs more space? */
+                       kp_tab_resizearray(ks, h, last);
+
+               for (; n > 0; n--) {
+                       ktap_value *val = ra+n;
+                       kp_tab_setint(ks, h, last--, val);
+               }
+               /* correct top (in case of previous open call) */
+               ks->top = ci->top;
+               break;
+               }
+       case OP_CLOSURE: {
+               /* need to use closure cache? (multithread contention issue)*/
+               ktap_proto *p = cl->p->p[GETARG_Bx(instr)];
+               pushclosure(ks, p, cl->upvals, base, ra);
+               break;
+               }
+       case OP_VARARG: {
+               int b = GETARG_B(instr) - 1;
+               int j;
+               int n = (int)(base - ci->func) - cl->p->numparams - 1;
+               if (b < 0) {  /* B == 0? */
+                       b = n;  /* get all var. arguments */
+                       if(checkstack(ks, n))
+                               return;
+                       /* previous call may change the stack */
+                       ra = RA(instr);
+                       ks->top = ra + n;
+               }
+               for (j = 0; j < b; j++) {
+                       if (j < n) {
+                               set_obj(ra + j, base - n + j);
+                       } else
+                               set_nil(ra + j);
+               }
+               break;
+               }
+       case OP_EXTRAARG:
+               return;
+
+       case OP_EVENT: {
+               struct ktap_event *e = ks->current_event;
+
+               if (unlikely(!e)) {
+                       kp_error(ks, "invalid event context\n");
+                       return;
+               }
+               set_event(ra, e);
+               break;
+               }
+
+       case OP_EVENTNAME: {
+               struct ktap_event *e = ks->current_event;
+
+               if (unlikely(!e)) {
+                       kp_error(ks, "invalid event context\n");
+                       return;
+               }
+               set_string(ra, kp_tstring_new(ks, e->call->name));
+               break;
+               }
+       case OP_EVENTARG:
+               if (unlikely(!ks->current_event)) {
+                       kp_error(ks, "invalid event context\n");
+                       return;
+               }
+
+               kp_event_getarg(ks, ra, GETARG_B(instr));
+               break;
+       case OP_LOAD_GLOBAL: {
+               ktap_value *cfunc = cfunction_cache_get(ks, GETARG_C(instr));
+               set_obj(ra, cfunc);
+               }
+               break;
+
+       case OP_EXIT:
+               return;
+       }
+
+       goto mainloop;
+}
+
+void kp_call(ktap_state *ks, StkId func, int nresults)
+{
+       if (!precall(ks, func, nresults))
+               ktap_execute(ks);
+}
+
+static int cfunction_cache_getindex(ktap_state *ks, ktap_value *fname);
+
+/*
+ * This function must be called before all code loaded.
+ */
+void kp_optimize_code(ktap_state *ks, int level, ktap_proto *f)
+{
+       int i;
+
+       for (i = 0; i < f->sizecode; i++) {
+               int instr = f->code[i];
+               ktap_value *k = f->k;
+
+               if (GET_OPCODE(instr) == OP_GETTABUP) {
+                       if ((GETARG_B(instr) == 0) && ISK(GETARG_C(instr))) {
+                               ktap_value *field = k + INDEXK(GETARG_C(instr));
+                               if (ttype(field) == KTAP_TSTRING) {
+                                       int index = cfunction_cache_getindex(ks,
+                                                                       field);
+                                       if (index == -1)
+                                               break;
+
+                                       SET_OPCODE(instr, OP_LOAD_GLOBAL);
+                                       SETARG_C(instr, index);
+                                       f->code[i] = instr;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /* continue optimize sub functions */
+       for (i = 0; i < f->sizep; i++)
+               kp_optimize_code(ks, level + 1, f->p[i]);
+}
+
+static ktap_value *cfunction_cache_get(ktap_state *ks, int index)
+{
+       return &G(ks)->cfunction_tbl[index];
+}
+
+static int cfunction_cache_getindex(ktap_state *ks, ktap_value *fname)
+{
+       const ktap_value *gt = kp_tab_getint(hvalue(&G(ks)->registry),
+                               KTAP_RIDX_GLOBALS);
+       const ktap_value *cfunc;
+       int nr, i;
+
+       nr = G(ks)->nr_builtin_cfunction;
+       cfunc = kp_tab_get(hvalue(gt), fname);
+
+       for (i = 0; i < nr; i++) {
+               if (rawequalobj(&G(ks)->cfunction_tbl[i], cfunc))
+                       return i;
+       }
+
+       return -1;
+}
+
+static void cfunction_cache_add(ktap_state *ks, ktap_value *func)
+{
+       int nr = G(ks)->nr_builtin_cfunction;
+       set_obj(&G(ks)->cfunction_tbl[nr], func);
+       G(ks)->nr_builtin_cfunction++;
+}
+
+static void cfunction_cache_exit(ktap_state *ks)
+{
+       kp_free(ks, G(ks)->cfunction_tbl);
+}
+
+static int cfunction_cache_init(ktap_state *ks)
+{
+       G(ks)->cfunction_tbl = kp_zalloc(ks, sizeof(ktap_value) * 128);
+       if (!G(ks)->cfunction_tbl)
+               return -ENOMEM;
+
+       return 0;
+}
+
+/* function for register library */
+void kp_register_lib(ktap_state *ks, const char *libname, const ktap_Reg *funcs)
+{
+       int i;
+       ktap_tab *target_tbl;
+       const ktap_value *gt = kp_tab_getint(hvalue(&G(ks)->registry),
+                                              KTAP_RIDX_GLOBALS);
+
+       /* lib is null when register baselib function */
+       if (libname == NULL)
+               target_tbl = hvalue(gt);
+       else {
+               ktap_value key, val;
+
+               target_tbl = kp_tab_new(ks);
+               kp_tab_resize(ks, target_tbl, 0,
+                               sizeof(*funcs) / sizeof(ktap_Reg));
+
+               set_string(&key, kp_tstring_new(ks, libname));
+               set_table(&val, target_tbl);
+               kp_tab_setvalue(ks, hvalue(gt), &key, &val);
+       }
+
+       for (i = 0; funcs[i].name != NULL; i++) {
+               ktap_value func_name, cl;
+
+               set_string(&func_name, kp_tstring_new(ks, funcs[i].name));
+               set_cfunction(&cl, funcs[i].func);
+               kp_tab_setvalue(ks, target_tbl, &func_name, &cl);
+
+               cfunction_cache_add(ks, &cl);
+       }
+}
+
+static void kp_init_registry(ktap_state *ks)
+{
+       ktap_value mt;
+       ktap_tab *registry = kp_tab_new(ks);
+
+       set_table(&G(ks)->registry, registry);
+       kp_tab_resize(ks, registry, KTAP_RIDX_LAST, 0);
+       set_thread(&mt, ks);
+       kp_tab_setint(ks, registry, KTAP_RIDX_MAINTHREAD, &mt);
+       set_table(&mt, kp_tab_new(ks));
+       kp_tab_setint(ks, registry, KTAP_RIDX_GLOBALS, &mt);
+}
+
+static int kp_init_arguments(ktap_state *ks, int argc, char __user **user_argv)
+{
+       const ktap_value *gt = kp_tab_getint(hvalue(&G(ks)->registry),
+                          KTAP_RIDX_GLOBALS);
+       ktap_tab *global_tbl = hvalue(gt);
+       ktap_tab *arg_tbl = kp_tab_new(ks);
+       ktap_value arg_tblval;
+       ktap_value arg_tsval;
+       char **argv;
+       int i, ret;
+
+       set_string(&arg_tsval, kp_tstring_new(ks, "arg"));
+       set_table(&arg_tblval, arg_tbl);
+       kp_tab_setvalue(ks, global_tbl, &arg_tsval, &arg_tblval);
+
+       if (!argc)
+               return 0;
+
+       if (argc > 1024)
+               return -EINVAL;
+
+       argv = kzalloc(argc * sizeof(char *), GFP_KERNEL);
+       if (!argv)
+               return -ENOMEM;
+
+       ret = copy_from_user(argv, user_argv, argc * sizeof(char *));
+       if (ret < 0) {
+               kfree(argv);
+               return -EFAULT;
+       }
+
+       kp_tab_resize(ks, arg_tbl, argc, 1);
+
+       ret = 0;
+       for (i = 0; i < argc; i++) {
+               ktap_value val;
+               char __user *ustr = argv[i];
+               char *kstr;
+               int len;
+               int res;
+
+               len = strlen_user(ustr);
+               if (len > 0x1000) {
+                       ret = -EINVAL;
+                       break;
+               }
+
+               kstr = kmalloc(len + 1, GFP_KERNEL);
+               if (!kstr) {
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               if (strncpy_from_user(kstr, ustr, len) < 0) {
+                       ret = -EFAULT;
+                       break;
+               }
+
+               kstr[len] = '\0';
+
+               if (!kstrtoint(kstr, 10, &res)) {
+                       set_number(&val, res);
+               } else
+                       set_string(&val, kp_tstring_new(ks, kstr));
+
+               kp_tab_setint(ks, arg_tbl, i, &val);
+
+               kfree(kstr);
+       }
+
+       kfree(argv);
+       return ret;
+}
+
+static void free_kp_percpu_data(ktap_state *ks)
+{
+       int i, j;
+
+       for (i = 0; i < KTAP_PERCPU_DATA_MAX; i++) {
+               for (j = 0; j < PERF_NR_CONTEXTS; j++)
+                       free_percpu(G(ks)->pcpu_data[i][j]);
+       }
+
+       for (j = 0; j < PERF_NR_CONTEXTS; j++)
+               if (G(ks)->recursion_context[j])
+                       free_percpu(G(ks)->recursion_context[j]);
+}
+
+static int alloc_kp_percpu_data(ktap_state *ks)
+{
+       int data_size[KTAP_PERCPU_DATA_MAX] = {
+               sizeof(ktap_state), KTAP_STACK_SIZE_BYTES,
+               KTAP_PERCPU_BUFFER_SIZE, KTAP_PERCPU_BUFFER_SIZE,
+               sizeof(ktap_btrace) + (KTAP_MAX_STACK_ENTRIES *
+                       sizeof(unsigned long))};
+       int i, j;
+
+       for (i = 0; i < KTAP_PERCPU_DATA_MAX; i++) {
+               for (j = 0; j < PERF_NR_CONTEXTS; j++) {
+                       void __percpu *data = __alloc_percpu(data_size[i],
+                                                            __alignof__(char));
+                       if (!data)
+                               goto fail;
+                       G(ks)->pcpu_data[i][j] = data;
+               }
+       }
+
+       for (j = 0; j < PERF_NR_CONTEXTS; j++) {
+               G(ks)->recursion_context[j] = alloc_percpu(int);
+               if (!G(ks)->recursion_context[j])
+                       goto fail;
+       }
+
+       return 0;
+
+ fail:
+       free_kp_percpu_data(ks);
+       return -ENOMEM;
+}
+
+static void kp_init_state(ktap_state *ks)
+{
+       ktap_callinfo *ci;
+
+       /* init all stack vaule to nil */
+       memset(ks->stack, 0, KTAP_STACK_SIZE_BYTES);
+
+       ks->top = ks->stack;
+       ks->stack_last = ks->stack + KTAP_STACK_SIZE;
+
+       ci = &ks->baseci;
+       ci->callstatus = 0;
+       ci->func = ks->top;
+       ci->top = ks->top + KTAP_MIN_RESERVED_STACK_SIZE;
+       ks->ci = ci;
+}
+
+static void free_all_ci(ktap_state *ks)
+{
+       int cpu, j;
+
+       for_each_possible_cpu(cpu) {
+               for (j = 0; j < PERF_NR_CONTEXTS; j++) {
+                       void *pcd = G(ks)->pcpu_data[KTAP_PERCPU_DATA_STATE][j];
+                       ktap_state *ks;
+
+                       if (!pcd)
+                               break;
+
+                       ks = per_cpu_ptr(pcd, cpu);
+                       if (!ks)
+                               break;
+
+                       free_ci(ks);
+               }
+       }
+
+       free_ci(ks);
+}
+
+void kp_exitthread(ktap_state *ks)
+{
+       /* free local allocation objects, like annotate strings */
+       kp_free_gclist(ks, ks->gclist);
+}
+
+ktap_state *kp_newthread(ktap_state *mainthread)
+{
+       ktap_state *ks;
+
+       ks = kp_percpu_data(mainthread, KTAP_PERCPU_DATA_STATE);
+       ks->stack = kp_percpu_data(mainthread, KTAP_PERCPU_DATA_STACK);
+       G(ks) = G(mainthread);
+       ks->gclist = NULL;
+       kp_init_state(ks);
+       return ks;
+}
+
+/*
+ * wait ktapio thread read all content in ring buffer.
+ *
+ * Here we use stupid approach to sync with ktapio thread,
+ * note that we cannot use semaphore/completion/other sync method,
+ * because ktapio thread could be killed by SIG_KILL in anytime, there
+ * have no safe way to up semaphore or wake waitqueue before thread exit.
+ *
+ * we also cannot use waitqueue of current->signal->wait_chldexit to sync
+ * exit, becasue mainthread and ktapio thread are in same thread group.
+ *
+ * Also ktap mainthread must wait ktapio thread exit, otherwise ktapio
+ * thread will oops when access ktap structure.
+ */
+static void wait_user_completion(ktap_state *ks)
+{
+       struct task_struct *tsk = G(ks)->task;
+       G(ks)->wait_user = 1;
+
+       while (1) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               /* sleep for 100 msecs, and try again. */
+               schedule_timeout(HZ / 10);
+
+               if (get_nr_threads(tsk) == 1)
+                       break;
+       }
+}
+
+static void sleep_loop(ktap_state *ks,
+                       int (*actor)(ktap_state *ks, void *arg), void *arg)
+{
+       while (!ks->stop) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               /* sleep for 100 msecs, and try again. */
+               schedule_timeout(HZ / 10);
+
+               if (actor(ks, arg))
+                       return;
+       }
+}
+
+static int sl_wait_task_pause_actor(ktap_state *ks, void *arg)
+{
+       struct task_struct *task = (struct task_struct *)arg;
+
+       if (task->state)
+               return 1;
+       else
+               return 0;
+}
+
+static int sl_wait_task_exit_actor(ktap_state *ks, void *arg)
+{
+       struct task_struct *task = (struct task_struct *)arg;
+
+       if (signal_pending(current)) {
+               flush_signals(current);
+
+               /* newline for handle CTRL+C display as ^C */
+               kp_puts(ks, "\n");
+               return 1;
+       }
+
+       /* stop waiting if target pid is exited */
+       if (task && task->state == TASK_DEAD)
+                       return 1;
+
+       return 0;
+}
+
+/* kp_wait: used for mainthread waiting for exit */
+static void kp_wait(ktap_state *ks)
+{
+       struct task_struct *task = G(ks)->trace_task;
+
+       if (G(ks)->exit)
+               return;
+
+       ks->stop = 0;
+
+       if (G(ks)->parm->workload) {
+               /* make sure workload is in pause state
+                * so it won't miss the signal */
+               sleep_loop(ks, sl_wait_task_pause_actor, task);
+               /* tell workload process to start executing */
+               send_sig(SIGINT, G(ks)->trace_task, 0);
+       }
+
+       if (!G(ks)->parm->quiet)
+               kp_printf(ks, "Tracing... Hit Ctrl-C to end.\n");
+
+       sleep_loop(ks, sl_wait_task_exit_actor, task);
+}
+
+static unsigned int kp_stub_exit_instr;
+
+static inline void set_next_as_exit(ktap_state *ks)
+{
+       ktap_callinfo *ci;
+
+       ci = ks->ci;
+       if (!ci)
+               return;
+
+       ci->u.l.savedpc = &kp_stub_exit_instr;
+
+       /* See precall, ci changed to ci->prev after invoke C function */
+       if (ci->prev) {
+               ci = ci->prev;
+               ci->u.l.savedpc = &kp_stub_exit_instr;
+       }
+}
+
+void kp_exit(ktap_state *ks)
+{
+       set_next_as_exit(ks);
+
+       G(ks)->mainthread->stop = 1;
+       G(ks)->exit = 1;
+}
+
+void kp_init_exit_instruction(void)
+{
+       SET_OPCODE(kp_stub_exit_instr, OP_EXIT);
+}
+
+/*
+ * Be careful in stats_cleanup, only can use kp_printf, since almost
+ * all ktap resources already freed now.
+ */
+static void kp_stats_cleanup(ktap_state *ks)
+{
+       ktap_stats __percpu *stats = G(ks)->stats;
+       int mem_allocated = 0, nr_mem_allocate = 0, nr_mem_free = 0;
+       int events_hits = 0, events_missed = 0;
+       int cpu;
+
+       for_each_possible_cpu(cpu) {
+               ktap_stats *per_stats = per_cpu_ptr(stats, cpu);
+               mem_allocated += per_stats->mem_allocated;
+               nr_mem_allocate += per_stats->nr_mem_allocate;
+               nr_mem_free += per_stats->nr_mem_free;
+               events_hits += per_stats->events_hits;
+               events_missed += per_stats->events_missed;
+       }
+
+       kp_verbose_printf(ks, "ktap stats:\n");
+       kp_verbose_printf(ks, "memory allocated size: %d\n", mem_allocated);
+       kp_verbose_printf(ks, "memory allocate num: %d\n", nr_mem_allocate);
+       kp_verbose_printf(ks, "memory free num: %d\n", nr_mem_free);
+       kp_verbose_printf(ks, "events_hits: %d\n", events_hits);
+       kp_verbose_printf(ks, "events_missed: %d\n", events_missed);
+
+       if (stats)
+               free_percpu(stats);
+}
+
+static int kp_stats_init(ktap_state *ks)
+{
+       ktap_stats __percpu *stats = alloc_percpu(ktap_stats);
+       if (!stats)
+               return -ENOMEM;
+
+       G(ks)->stats = stats;
+       return 0;
+}
+
+void kp_final_exit(ktap_state *ks)
+{
+       if (!list_empty(&G(ks)->probe_events_head) ||
+           !list_empty(&G(ks)->timers))
+               kp_wait(ks);
+
+       kp_exit_timers(ks);
+       kp_probe_exit(ks);
+
+       /* free all resources got by ktap */
+       kp_ffi_free_symbol(ks);
+       kp_tstring_freeall(ks);
+       kp_free_all_gcobject(ks);
+       cfunction_cache_exit(ks);
+
+       kp_exitthread(ks);
+       kp_free(ks, ks->stack);
+       free_all_ci(ks);
+
+       free_kp_percpu_data(ks);
+       free_cpumask_var(G(ks)->cpumask);
+
+       kp_stats_cleanup(ks);
+       wait_user_completion(ks);
+
+       /* should invoke after wait_user_completion */
+       if (G(ks)->trace_task)
+               put_task_struct(G(ks)->trace_task);
+
+       kp_transport_exit(ks);
+       kp_free(ks, ks);
+}
+
+/* ktap mainthread initization, main entry for ktap */
+ktap_state *kp_newstate(ktap_parm *parm, struct dentry *dir)
+{
+       ktap_state *ks;
+       pid_t pid;
+       int cpu;
+
+       ks = kzalloc(sizeof(ktap_state) + sizeof(ktap_global_state),
+                    GFP_KERNEL);
+       if (!ks)
+               return NULL;
+
+       G(ks) = (ktap_global_state *)(ks + 1);
+       G(ks)->mainthread = ks;
+       G(ks)->seed = 201236; /* todo: make more random in future */
+       G(ks)->task = current;
+       G(ks)->parm = parm;
+       G(ks)->str_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
+       INIT_LIST_HEAD(&(G(ks)->timers));
+       INIT_LIST_HEAD(&(G(ks)->probe_events_head));
+       G(ks)->exit = 0;
+
+       if (kp_stats_init(ks))
+               goto out;
+
+       if (kp_transport_init(ks, dir))
+               goto out;
+
+       ks->stack = kp_malloc(ks, KTAP_STACK_SIZE_BYTES);
+
+       pid = (pid_t)parm->trace_pid;
+       if (pid != -1) {
+               struct task_struct *task;
+
+               rcu_read_lock();
+               task = pid_task(find_vpid(pid), PIDTYPE_PID);
+               if (!task) {
+                       kp_error(ks, "cannot find pid %d\n", pid);
+                       rcu_read_unlock();
+                       goto out;
+               }
+               G(ks)->trace_task = task;
+               get_task_struct(task);
+               rcu_read_unlock();
+       }
+
+       if( !alloc_cpumask_var(&G(ks)->cpumask, GFP_KERNEL))
+               goto out;
+
+       cpumask_copy(G(ks)->cpumask, cpu_online_mask);
+
+       cpu = parm->trace_cpu;
+       if (cpu != -1) {
+               if (!cpu_online(cpu)) {
+                       kp_error(ks, "ktap: cpu %d is not online\n", cpu);
+                       goto out;
+               }
+
+               cpumask_clear(G(ks)->cpumask);
+               cpumask_set_cpu(cpu, G(ks)->cpumask);
+       }
+
+       if (cfunction_cache_init(ks))
+               goto out;
+
+       kp_tstring_resize(ks, 512); /* set inital string hashtable size */
+
+       kp_init_state(ks);
+       kp_init_registry(ks);
+       kp_init_arguments(ks, parm->argc, parm->argv);
+
+       /* init library */
+       kp_init_baselib(ks);
+       kp_init_kdebuglib(ks);
+       kp_init_timerlib(ks);
+       kp_init_ansilib(ks);
+       kp_init_ffilib(ks);
+
+       if (alloc_kp_percpu_data(ks))
+               goto out;
+
+       if (kp_probe_init(ks))
+               goto out;
+
+       return ks;
+
+ out:
+       G(ks)->exit = 1;
+       kp_final_exit(ks);
+       return NULL;
+}
+
diff --git a/drivers/staging/ktap/runtime/kp_vm.h b/drivers/staging/ktap/runtime/kp_vm.h
new file mode 100644 (file)
index 0000000..3c7dfa5
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __KTAP_VM_H__
+#define __KTAP_VM_H__
+
+int gettimeofday_us(void); /* common helper function */
+ktap_state *kp_newstate(struct ktap_parm *parm, struct dentry *dir);
+void kp_exit(ktap_state *ks);
+void kp_init_exit_instruction(void);
+void kp_final_exit(ktap_state *ks);
+ktap_state *kp_newthread(ktap_state *mainthread);
+void kp_exitthread(ktap_state *ks);
+void kp_call(ktap_state *ks, StkId func, int nresults);
+void kp_optimize_code(ktap_state *ks, int level, ktap_proto *f);
+void kp_register_lib(ktap_state *ks, const char *libname,
+                       const ktap_Reg *funcs);
+
+#endif /* __KTAP_VM_H__ */
diff --git a/drivers/staging/ktap/runtime/ktap.c b/drivers/staging/ktap/runtime/ktap.c
new file mode 100644 (file)
index 0000000..3df1928
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * ktap.c - ktapvm kernel module main entry
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * this file is the first file to be compile, add CONFIG_ checking in here.
+ * See Requirements in doc/introduction.txt
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+#error "Currently ktap don't support kernel older than 3.1"
+#endif
+
+#if !CONFIG_EVENT_TRACING
+#error "Please enable CONFIG_EVENT_TRACING before compile ktap"
+#endif
+
+#if !CONFIG_PERF_EVENTS
+#error "Please enable CONFIG_PERF_EVENTS before compile ktap"
+#endif
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/anon_inodes.h>
+#include <linux/debugfs.h>
+#include <linux/vmalloc.h>
+#include "../include/ktap_types.h"
+#include "ktap.h"
+#include "kp_load.h"
+#include "kp_vm.h"
+
+static int load_trunk(struct ktap_parm *parm, unsigned long **buff)
+{
+       int ret;
+       unsigned long *vmstart;
+
+       vmstart = vmalloc(parm->trunk_len);
+       if (!vmstart)
+               return -ENOMEM;
+
+       ret = copy_from_user(vmstart, (void __user *)parm->trunk,
+                            parm->trunk_len);
+       if (ret < 0) {
+               vfree(vmstart);
+               return -EFAULT;
+       }
+
+       *buff = vmstart;
+       return 0;
+}
+
+static struct dentry *kp_dir_dentry;
+
+/* Ktap Main Entry */
+static int ktap_main(struct file *file, ktap_parm *parm)
+{
+       unsigned long *buff = NULL;
+       ktap_state *ks;
+       ktap_closure *cl;
+       int start_time, delta_time;
+       int ret;
+
+       start_time = gettimeofday_us();
+
+       ks = kp_newstate(parm, kp_dir_dentry);
+       if (unlikely(!ks))
+               return -ENOEXEC;
+
+       file->private_data = ks;
+
+       ret = load_trunk(parm, &buff);
+       if (ret) {
+               pr_err("cannot load file\n");
+               return ret;
+       }
+
+       cl = kp_load(ks, (unsigned char *)buff);
+
+       vfree(buff);
+
+       if (cl) {
+               /* optimize bytecode before excuting */
+               kp_optimize_code(ks, 0, cl->p);
+
+               delta_time = gettimeofday_us() - start_time;
+               kp_verbose_printf(ks, "booting time: %d (us)\n", delta_time);
+               kp_call(ks, ks->top - 1, 0);
+       }
+
+       kp_final_exit(ks);
+       return ret;
+}
+
+
+static void print_version(void)
+{
+}
+
+static long ktap_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       ktap_parm parm;
+       int ret;
+
+       switch (cmd) {
+       case KTAP_CMD_IOC_VERSION:
+               print_version();
+               return 0;
+       case KTAP_CMD_IOC_RUN:
+               ret = copy_from_user(&parm, (void __user *)arg,
+                                    sizeof(ktap_parm));
+               if (ret < 0)
+                       return -EFAULT;
+
+               return ktap_main(file, &parm);
+       default:
+               return -EINVAL;
+       };
+
+        return 0;
+}
+
+static const struct file_operations ktap_fops = {
+       .llseek                 = no_llseek,
+       .unlocked_ioctl         = ktap_ioctl,
+};
+
+static long ktapvm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       int new_fd, err;
+       struct file *new_file;
+
+       new_fd = get_unused_fd();
+       if (new_fd < 0)
+               return new_fd;
+
+       new_file = anon_inode_getfile("[ktap]", &ktap_fops, NULL, O_RDWR);
+       if (IS_ERR(new_file)) {
+               err = PTR_ERR(new_file);
+               put_unused_fd(new_fd);
+               return err;
+       }
+
+       file->private_data = NULL;
+       fd_install(new_fd, new_file);
+       return new_fd;
+}
+
+static const struct file_operations ktapvm_fops = {
+       .owner  = THIS_MODULE,
+       .unlocked_ioctl         = ktapvm_ioctl,
+};
+
+static int __init init_ktap(void)
+{
+       struct dentry *ktapvm_dentry;
+
+       kp_dir_dentry = debugfs_create_dir("ktap", NULL);
+       if (!kp_dir_dentry) {
+               pr_err("ktap: debugfs_create_dir failed\n");
+               return -1;
+       }
+
+       ktapvm_dentry = debugfs_create_file("ktapvm", 0444, kp_dir_dentry, NULL,
+                                           &ktapvm_fops);
+
+       if (!ktapvm_dentry) {
+               pr_err("ktapvm: cannot create ktapvm file\n");
+               debugfs_remove_recursive(kp_dir_dentry);
+               return -1;
+       }
+
+       kp_init_exit_instruction();
+
+       return 0;
+}
+
+static void __exit exit_ktap(void)
+{
+       debugfs_remove_recursive(kp_dir_dentry);
+}
+
+module_init(init_ktap);
+module_exit(exit_ktap);
+
+MODULE_AUTHOR("Jovi Zhangwei <jovi.zhangwei@gmail.com>");
+MODULE_DESCRIPTION("ktap");
+MODULE_LICENSE("GPL");
+
+int kp_max_exec_count = 10000;
+module_param_named(max_exec_count, kp_max_exec_count, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_exec_count, "non-mainthread max instruction execution count");
+
diff --git a/drivers/staging/ktap/runtime/ktap.h b/drivers/staging/ktap/runtime/ktap.h
new file mode 100644 (file)
index 0000000..06ede8c
--- /dev/null
@@ -0,0 +1,130 @@
+#ifndef __KTAP_H__
+#define __KTAP_H__
+
+#ifdef __KERNEL__
+#include <linux/version.h>
+#include <linux/hardirq.h>
+#include <linux/trace_seq.h>
+#endif
+
+typedef struct ktap_Reg {
+        const char *name;
+        ktap_cfunction func;
+} ktap_Reg;
+
+struct ktap_probe_event {
+       struct list_head list;
+       struct perf_event *perf;
+       ktap_state *ks;
+       ktap_closure *cl;
+};
+
+/* this structure allocate on stack */
+struct ktap_event {
+       struct ktap_probe_event *pevent;
+       struct ftrace_event_call *call;
+       struct trace_entry *entry;
+       int entry_size;
+       struct pt_regs *regs;
+};
+
+#define KTAP_PERCPU_BUFFER_SIZE        (3 * PAGE_SIZE)
+
+void kp_init_baselib(ktap_state *ks);
+void kp_init_oslib(ktap_state *ks);
+void kp_init_kdebuglib(ktap_state *ks);
+void kp_init_timerlib(ktap_state *ks);
+void kp_init_ansilib(ktap_state *ks);
+#ifdef CONFIG_KTAP_FFI
+void kp_init_ffilib(ktap_state *ks);
+#else
+static void __maybe_unused kp_init_ffilib(ktap_state *ks)
+{
+       return;
+}
+#endif
+
+
+int kp_probe_init(ktap_state *ks);
+void kp_probe_exit(ktap_state *ks);
+
+void kp_perf_event_register(ktap_state *ks, struct perf_event_attr *attr,
+                           struct task_struct *task, char *filter,
+                           ktap_closure *cl);
+
+void kp_event_getarg(ktap_state *ks, ktap_value *ra, int n);
+void kp_event_tostring(ktap_state *ks, struct trace_seq *seq);
+void kp_exit_timers(ktap_state *ks);
+
+extern int kp_max_exec_count;
+
+/* get from kernel/trace/trace.h */
+static __always_inline int trace_get_context_bit(void)
+{
+       int bit;
+
+       if (in_interrupt()) {
+               if (in_nmi())
+                       bit = 0;
+               else if (in_irq())
+                       bit = 1;
+               else
+                       bit = 2;
+       } else
+               bit = 3;
+
+       return bit;
+}
+
+static __always_inline int get_recursion_context(ktap_state *ks)
+{
+       int rctx = trace_get_context_bit();
+       int *val = __this_cpu_ptr(G(ks)->recursion_context[rctx]);
+
+       if (*val)
+               return -1;
+
+       *val = true;
+       barrier();
+
+       return rctx;
+}
+
+static inline void put_recursion_context(ktap_state *ks, int rctx)
+{
+       int *val = __this_cpu_ptr(G(ks)->recursion_context[rctx]);
+
+       barrier();
+       *val = false;
+}
+
+static inline void *kp_percpu_data(ktap_state *ks, int type)
+{
+       return this_cpu_ptr(G(ks)->pcpu_data[type][trace_get_context_bit()]);
+}
+
+
+#define kp_verbose_printf(ks, ...) \
+       if (G(ks)->parm->verbose)       \
+               kp_printf(ks, "[verbose] "__VA_ARGS__);
+
+/* get argument operation macro */
+#define kp_arg(ks, n)  ((ks)->ci->func + (n))
+#define kp_arg_nr(ks)  ((int)(ks->top - (ks->ci->func + 1)))
+
+#define kp_arg_check(ks, narg, type)                           \
+       do {                                                    \
+               if (unlikely(ttypenv(kp_arg(ks, narg)) != type)) {      \
+                       kp_error(ks, "wrong type of argument %d\n", narg);\
+                       return -1;                              \
+               }                                               \
+       } while (0)
+
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 5, 0)
+#define SPRINT_SYMBOL  sprint_symbol_no_offset
+#else
+#define SPRINT_SYMBOL  sprint_symbol
+#endif
+
+#endif /* __KTAP_H__ */
diff --git a/drivers/staging/ktap/runtime/lib_ansi.c b/drivers/staging/ktap/runtime/lib_ansi.c
new file mode 100644 (file)
index 0000000..03f841a
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * ansilib.c - ANSI escape sequences library
+ *
+ * http://en.wikipedia.org/wiki/ANSI_escape_code
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "../include/ktap_types.h"
+#include "ktap.h"
+#include "kp_vm.h"
+
+/**
+ * function ansi.clear_screen - Move cursor to top left and clear screen.
+ *
+ * Description: Sends ansi code for moving cursor to top left and then the
+ * ansi code for clearing the screen from the cursor position to the end.
+ */
+
+static int ktap_lib_clear_screen(ktap_state *ks)
+{
+       kp_printf(ks, "\033[1;1H\033[J");
+       return 0;
+}
+
+/**
+ * function ansi.set_color - Set the ansi Select Graphic Rendition mode.
+ * @fg: Foreground color to set.
+ *
+ * Description: Sends ansi code for Select Graphic Rendition mode for the
+ * given forground color. Black (30), Blue (34), Green (32), Cyan (36),
+ * Red (31), Purple (35), Brown (33), Light Gray (37).
+ */
+
+static int ktap_lib_set_color(ktap_state *ks)
+{
+       int fg;
+
+       kp_arg_check(ks, 1, KTAP_TNUMBER);
+
+       fg = nvalue(kp_arg(ks, 1));
+       kp_printf(ks, "\033[%dm", fg);
+       return 0;
+}
+
+/**
+ * function ansi.set_color2 - Set the ansi Select Graphic Rendition mode.
+ * @fg: Foreground color to set.
+ * @bg: Background color to set.
+ *
+ * Description: Sends ansi code for Select Graphic Rendition mode for the
+ * given forground color, Black (30), Blue (34), Green (32), Cyan (36),
+ * Red (31), Purple (35), Brown (33), Light Gray (37) and the given
+ * background color, Black (40), Red (41), Green (42), Yellow (43),
+ * Blue (44), Magenta (45), Cyan (46), White (47).
+ */
+static int ktap_lib_set_color2(ktap_state *ks)
+{
+       int fg, bg;
+
+       kp_arg_check(ks, 1, KTAP_TNUMBER);
+       kp_arg_check(ks, 2, KTAP_TNUMBER);
+
+       fg = nvalue(kp_arg(ks, 1));
+       bg = nvalue(kp_arg(ks, 2));
+       kp_printf(ks, "\033[%d;%dm", fg, bg);
+       return 0;
+}
+
+/**
+ * function ansi.set_color3 - Set the ansi Select Graphic Rendition mode.
+ * @fg: Foreground color to set.
+ * @bg: Background color to set.
+ * @attr: Color attribute to set.
+ *
+ * Description: Sends ansi code for Select Graphic Rendition mode for the
+ * given forground color, Black (30), Blue (34), Green (32), Cyan (36),
+ * Red (31), Purple (35), Brown (33), Light Gray (37), the given
+ * background color, Black (40), Red (41), Green (42), Yellow (43),
+ * Blue (44), Magenta (45), Cyan (46), White (47) and the color attribute
+ * All attributes off (0), Intensity Bold (1), Underline Single (4),
+ * Blink Slow (5), Blink Rapid (6), Image Negative (7).
+ */
+static int ktap_lib_set_color3(ktap_state *ks)
+{
+       int fg, bg, attr;
+
+       kp_arg_check(ks, 1, KTAP_TNUMBER);
+       kp_arg_check(ks, 2, KTAP_TNUMBER);
+       kp_arg_check(ks, 3, KTAP_TNUMBER);
+
+       fg = nvalue(kp_arg(ks, 1));
+       bg = nvalue(kp_arg(ks, 2));
+       attr = nvalue(kp_arg(ks, 3));
+
+       if (attr)
+               kp_printf(ks, "\033[%d;%d;%dm", fg, bg, attr);
+       else
+               kp_printf(ks, "\033[%d;%dm", fg, bg);
+
+       return 0;
+}
+
+/**
+ * function ansi.reset_color - Resets Select Graphic Rendition mode.
+ *
+ * Description: Sends ansi code to reset foreground, background and color
+ * attribute to default values.
+ */
+static int ktap_lib_reset_color(ktap_state *ks)
+{
+       kp_printf(ks, "\033[0;0m");
+       return 0;
+}
+
+/**
+ * function ansi.new_line - Move cursor to new line.
+ *
+ * Description: Sends ansi code new line.
+ */
+static int ktap_lib_new_line (ktap_state *ks)
+{
+       kp_printf(ks, "\12");
+       return 0;
+}
+
+static const ktap_Reg ansi_funcs[] = {
+       {"clear_screen", ktap_lib_clear_screen},
+       {"set_color", ktap_lib_set_color},
+       {"set_color2", ktap_lib_set_color2},
+       {"set_color3", ktap_lib_set_color3},
+       {"reset_color", ktap_lib_reset_color},
+       {"new_line", ktap_lib_new_line},
+       {NULL}
+};
+
+void kp_init_ansilib(ktap_state *ks)
+{
+       kp_register_lib(ks, "ansi", ansi_funcs);
+}
diff --git a/drivers/staging/ktap/runtime/lib_base.c b/drivers/staging/ktap/runtime/lib_base.c
new file mode 100644 (file)
index 0000000..cf084df
--- /dev/null
@@ -0,0 +1,607 @@
+/*
+ * baselib.c - ktapvm kernel module base library
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/version.h>
+#include <linux/hardirq.h>
+#include <linux/kallsyms.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/utsname.h>
+#include <linux/time.h>
+#include <linux/clocksource.h>
+#include <linux/ring_buffer.h>
+#include <linux/stacktrace.h>
+#include <linux/cred.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+#include <linux/uidgid.h>
+#endif
+#include "../include/ktap_types.h"
+#include "ktap.h"
+#include "kp_obj.h"
+#include "kp_str.h"
+#include "kp_tab.h"
+#include "kp_transport.h"
+#include "kp_vm.h"
+
+static int ktap_lib_next(ktap_state *ks)
+{
+       ktap_tab *t = hvalue(ks->top - 2);
+
+       if (kp_tab_next(ks, t, ks->top-1)) {
+               ks->top += 1;
+               return 2;
+       } else {
+               ks->top -= 1;
+               set_nil(ks->top++);
+               return 1;
+       }
+}
+
+static int ktap_lib_pairs(ktap_state *ks)
+{
+       ktap_value *v = kp_arg(ks, 1);
+       ktap_tab *t;
+
+       if (is_table(v)) {
+               t = hvalue(v);
+       } else if (is_ptable(v)) {
+               t = kp_ptab_synthesis(ks, phvalue(v));
+       } else if (is_nil(v)) {
+               kp_error(ks, "table is nil in pairs\n");
+               return 0;
+       } else {
+               kp_error(ks, "wrong argument for pairs\n");
+               return 0;
+       }
+
+       set_cfunction(ks->top++, ktap_lib_next);
+       set_table(ks->top++, t);
+       set_nil(ks->top++);
+       return 3;
+}
+
+static int ktap_lib_sort_next(ktap_state *ks)
+{
+       ktap_tab *t = hvalue(ks->top - 2);
+
+       if (kp_tab_sort_next(ks, t, ks->top-1)) {
+               ks->top += 1;
+               return 2;
+       } else {
+               ks->top -= 1;
+               set_nil(ks->top++);
+               return 1;
+       }
+}
+
+static int ktap_lib_sort_pairs(ktap_state *ks)
+{
+       ktap_value *v = kp_arg(ks, 1);
+       ktap_closure *cmp_func = NULL;
+       ktap_tab *t;
+
+       if (is_table(v)) {
+               t = hvalue(v);
+       } else if (is_ptable(v)) {
+               t = kp_ptab_synthesis(ks, phvalue(v));
+       } else if (is_nil(v)) {
+               kp_error(ks, "table is nil in pairs\n");
+               return 0;
+       } else {
+               kp_error(ks, "wrong argument for pairs\n");
+               return 0;
+       }
+
+       if (kp_arg_nr(ks) > 1) {
+               kp_arg_check(ks, 2, KTAP_TFUNCTION);
+               cmp_func = clvalue(kp_arg(ks, 2));
+       }
+
+       kp_tab_sort(ks, t, cmp_func);
+       set_cfunction(ks->top++, ktap_lib_sort_next);
+       set_table(ks->top++, t);
+       set_nil(ks->top++);
+       return 3;
+}
+
+static int ktap_lib_len(ktap_state *ks)
+{
+       int len = kp_objlen(ks, kp_arg(ks, 1));
+
+       if (len < 0)
+               return -1;
+
+       set_number(ks->top, len);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_print(ktap_state *ks)
+{
+       int i;
+       int n = kp_arg_nr(ks);
+
+       for (i = 1; i <= n; i++) {
+               ktap_value *arg = kp_arg(ks, i);
+               if (i > 1)
+                       kp_puts(ks, "\t");
+               kp_showobj(ks, arg);
+       }
+
+       kp_puts(ks, "\n");
+
+       return 0;
+}
+
+/* don't engage with tstring when printf, use buffer directly */
+static int ktap_lib_printf(ktap_state *ks)
+{
+       struct trace_seq *seq;
+
+       preempt_disable_notrace();
+
+       seq = kp_percpu_data(ks, KTAP_PERCPU_DATA_BUFFER);
+       trace_seq_init(seq);
+
+       if (kp_str_fmt(ks, seq))
+               goto out;
+
+       seq->buffer[seq->len] = '\0';
+       kp_transport_write(ks, seq->buffer, seq->len + 1);
+
+ out:
+       preempt_enable_notrace();
+       return 0;
+}
+
+#ifdef CONFIG_STACKTRACE
+static int ktap_lib_print_backtrace(ktap_state *ks)
+{
+       int skip = 10, max_entries = 10;
+       int n = kp_arg_nr(ks);
+
+       if (n >= 1) {
+               kp_arg_check(ks, 1, KTAP_TNUMBER);
+               skip = nvalue(kp_arg(ks, 1));
+       }
+       if (n >= 2) {
+               kp_arg_check(ks, 2, KTAP_TNUMBER);
+               max_entries = nvalue(kp_arg(ks, 2));
+               max_entries = min(max_entries, KTAP_MAX_STACK_ENTRIES);
+       }
+
+       kp_transport_print_backtrace(ks, skip, max_entries);
+       return 0;
+}
+#else
+static int ktap_lib_print_backtrace(ktap_state *ks)
+{
+       kp_error(ks, "Please enable CONFIG_STACKTRACE before use "
+                    "ktap print_backtrace\n");
+       return 0;
+}
+#endif
+
+static int ktap_lib_backtrace(ktap_state *ks)
+{
+       struct stack_trace trace;
+       int skip = 10, max_entries = 10;
+       int n = kp_arg_nr(ks);
+       ktap_btrace *bt;
+
+       if (n >= 1) {
+               kp_arg_check(ks, 1, KTAP_TNUMBER);
+               skip = nvalue(kp_arg(ks, 1));
+       }
+       if (n >= 2) {
+               kp_arg_check(ks, 2, KTAP_TNUMBER);
+               max_entries = nvalue(kp_arg(ks, 2));
+               max_entries = min(max_entries, KTAP_MAX_STACK_ENTRIES);
+       }
+
+       bt = kp_percpu_data(ks, KTAP_PERCPU_DATA_BTRACE);
+
+       trace.nr_entries = 0;
+       trace.skip = skip;
+       trace.max_entries = max_entries;
+       trace.entries = (unsigned long *)(bt + 1);
+       save_stack_trace(&trace);
+
+       bt->nr_entries = trace.nr_entries;
+       set_btrace(ks->top, bt);
+       incr_top(ks);
+       return 1;
+}
+
+extern unsigned long long ns2usecs(cycle_t nsec);
+static int ktap_lib_print_trace_clock(ktap_state *ks)
+{
+       unsigned long long t;
+       unsigned long secs, usec_rem;
+       u64 timestamp;
+
+       /* use ring buffer's timestamp */
+       timestamp = ring_buffer_time_stamp(G(ks)->buffer, smp_processor_id());
+
+       t = ns2usecs(timestamp);
+       usec_rem = do_div(t, USEC_PER_SEC);
+       secs = (unsigned long)t;
+
+       kp_printf(ks, "%5lu.%06lu\n", secs, usec_rem);
+
+       return 0;
+}
+
+static int ktap_lib_exit(ktap_state *ks)
+{
+       kp_exit(ks);
+
+       /* do not execute bytecode any more in this thread */
+       return -1;
+}
+
+static int ktap_lib_pid(ktap_state *ks)
+{
+       set_number(ks->top, (int)current->pid);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_tid(ktap_state *ks)
+{
+       pid_t pid = task_pid_vnr(current);
+
+       set_number(ks->top, (int)pid);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_uid(ktap_state *ks)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+       uid_t uid = from_kuid_munged(current_user_ns(), current_uid());
+#else
+       uid_t uid = current_uid();
+#endif
+       set_number(ks->top, (int)uid);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_execname(ktap_state *ks)
+{
+       ktap_string *ts = kp_tstring_new(ks, current->comm);
+       set_string(ks->top, ts);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_cpu(ktap_state *ks)
+{
+       set_number(ks->top, smp_processor_id());
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_num_cpus(ktap_state *ks)
+{
+       set_number(ks->top, num_online_cpus());
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_in_interrupt(ktap_state *ks)
+{
+       int ret = in_interrupt();
+
+       set_number(ks->top, ret);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_arch(ktap_state *ks)
+{
+       set_string(ks->top, kp_tstring_new(ks, utsname()->machine));
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_kernel_v(ktap_state *ks)
+{
+       set_string(ks->top, kp_tstring_new(ks, utsname()->release));
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_kernel_string(ktap_state *ks)
+{
+       unsigned long addr;
+       char str[256] = {0};
+       char *ret;
+
+       kp_arg_check(ks, 1, KTAP_TNUMBER);
+
+       addr = nvalue(kp_arg(ks, 1));
+
+       ret = strncpy((void *)str, (const void *)addr, 256);
+       (void) &ret;  /* Silence compiler warning. */
+
+       str[255] = '\0';
+       set_string(ks->top, kp_tstring_new_local(ks, str));
+
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_user_string(ktap_state *ks)
+{
+       unsigned long addr;
+       char str[256] = {0};
+       int ret;
+
+       kp_arg_check(ks, 1, KTAP_TNUMBER);
+
+       addr = nvalue(kp_arg(ks, 1));
+
+       pagefault_disable();
+       ret = __copy_from_user_inatomic((void *)str, (const void *)addr, 256);
+       (void) &ret;  /* Silence compiler warning. */
+       pagefault_enable();
+       str[255] = '\0';
+       set_string(ks->top, kp_tstring_new(ks, str));
+
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_histogram(ktap_state *ks)
+{
+       ktap_value *v = kp_arg(ks, 1);
+
+       if (is_table(v))
+               kp_tab_histogram(ks, hvalue(v));
+       else if (is_ptable(v))
+               kp_ptab_histogram(ks, phvalue(v));
+
+       return 0;
+}
+
+static int ktap_lib_ptable(ktap_state *ks)
+{
+       ktap_ptab *ph;
+
+       ph = kp_ptab_new(ks);
+       set_ptable(ks->top, ph);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_count(ktap_state *ks)
+{
+       ktap_value *v = kp_arg(ks, 1);
+       ktap_stat_data *sd;
+
+       if (is_nil(v)) {
+               set_number(ks->top, 0);
+               incr_top(ks);
+               return 1;
+       }
+
+       kp_arg_check(ks, 1, KTAP_TSTATDATA);
+       sd = sdvalue(v);
+
+       set_number(ks->top, sd->count);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_max(ktap_state *ks)
+{
+       ktap_value *v = kp_arg(ks, 1);
+       ktap_stat_data *sd;
+
+       if (is_nil(v)) {
+               set_number(ks->top, 0);
+               incr_top(ks);
+               return 1;
+       }
+
+       kp_arg_check(ks, 1, KTAP_TSTATDATA);
+       sd = sdvalue(v);
+
+       set_number(ks->top, sd->max);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_min(ktap_state *ks)
+{
+       ktap_value *v = kp_arg(ks, 1);
+       ktap_stat_data *sd;
+
+       if (is_nil(v)) {
+               set_number(ks->top, 0);
+               incr_top(ks);
+               return 1;
+       }
+
+       kp_arg_check(ks, 1, KTAP_TSTATDATA);
+       sd = sdvalue(v);
+
+       set_number(ks->top, sd->min);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_sum(ktap_state *ks)
+{
+       ktap_value *v = kp_arg(ks, 1);
+       ktap_stat_data *sd;
+
+       if (is_nil(v)) {
+               set_number(ks->top, 0);
+               incr_top(ks);
+               return 1;
+       }
+
+       kp_arg_check(ks, 1, KTAP_TSTATDATA);
+       sd = sdvalue(v);
+
+       set_number(ks->top, sd->sum);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_avg(ktap_state *ks)
+{
+       ktap_value *v = kp_arg(ks, 1);
+       ktap_stat_data *sd;
+
+       if (is_nil(v)) {
+               set_number(ks->top, 0);
+               incr_top(ks);
+               return 1;
+       }
+
+       kp_arg_check(ks, 1, KTAP_TSTATDATA);
+       sd = sdvalue(v);
+
+       set_number(ks->top, sd->sum / sd->count);
+       incr_top(ks);
+       return 1;
+}
+
+static int ktap_lib_delete(ktap_state *ks)
+{
+       kp_arg_check(ks, 1, KTAP_TTABLE);
+
+       kp_tab_clear(ks, hvalue(kp_arg(ks, 1)));
+       return 0;
+}
+
+static int ktap_lib_gettimeofday_us(ktap_state *ks)
+{
+       set_number(ks->top, gettimeofday_us());
+       incr_top(ks);
+
+       return 1;
+}
+
+/*
+ * use gdb to get field offset of struct task_struct, for example:
+ *
+ * gdb vmlinux
+ * (gdb)p &(((struct task_struct *)0).prio)
+ */
+static int ktap_lib_curr_task_info(ktap_state *ks)
+{
+       int offset;
+       int fetch_bytes;
+
+       kp_arg_check(ks, 1, KTAP_TNUMBER);
+
+       offset = nvalue(kp_arg(ks, 1));
+
+       if (kp_arg_nr(ks) == 1)
+               fetch_bytes = 4; /* default fetch 4 bytes*/
+       else {
+               kp_arg_check(ks, 2, KTAP_TNUMBER);
+               fetch_bytes = nvalue(kp_arg(ks, 2));
+       }
+
+       if (offset >= sizeof(struct task_struct)) {
+               set_nil(ks->top++);
+               kp_error(ks, "access out of bound value of task_struct\n");
+               return 1;
+       }
+
+#define RET_VALUE ((unsigned long)current + offset)
+
+       switch (fetch_bytes) {
+       case 4:
+               set_number(ks->top, *(unsigned int *)RET_VALUE);
+               break;
+       case 8:
+               set_number(ks->top, *(unsigned long *)RET_VALUE);
+               break;
+       default:
+               kp_error(ks, "unsupported fetch bytes in curr_task_info\n");
+               set_nil(ks->top);
+               break;
+       }
+
+#undef RET_VALUE
+
+       incr_top(ks);
+       return 1;
+}
+
+/*
+ * This built-in function mainly purpose scripts/schedule/schedtimes.kp
+ */
+static int ktap_lib_in_iowait(ktap_state *ks)
+{
+       set_number(ks->top, current->in_iowait);
+       incr_top(ks);
+
+       return 1;
+}
+
+static const ktap_Reg base_funcs[] = {
+       {"pairs", ktap_lib_pairs},
+       {"sort_pairs", ktap_lib_sort_pairs},
+       {"len", ktap_lib_len},
+       {"print", ktap_lib_print},
+       {"printf", ktap_lib_printf},
+       {"print_backtrace", ktap_lib_print_backtrace},
+       {"backtrace", ktap_lib_backtrace},
+       {"print_trace_clock", ktap_lib_print_trace_clock},
+       {"in_interrupt", ktap_lib_in_interrupt},
+       {"exit", ktap_lib_exit},
+       {"pid", ktap_lib_pid},
+       {"tid", ktap_lib_tid},
+       {"uid", ktap_lib_uid},
+       {"execname", ktap_lib_execname},
+       {"cpu", ktap_lib_cpu},
+       {"num_cpus", ktap_lib_num_cpus},
+       {"arch", ktap_lib_arch},
+       {"kernel_v", ktap_lib_kernel_v},
+       {"kernel_string", ktap_lib_kernel_string},
+       {"user_string", ktap_lib_user_string},
+       {"histogram", ktap_lib_histogram},
+       {"ptable", ktap_lib_ptable},
+       {"count", ktap_lib_count},
+       {"max", ktap_lib_max},
+       {"min", ktap_lib_min},
+       {"sum", ktap_lib_sum},
+       {"avg", ktap_lib_avg},
+
+       {"delete", ktap_lib_delete},
+       {"gettimeofday_us", ktap_lib_gettimeofday_us},
+       {"curr_taskinfo", ktap_lib_curr_task_info},
+       {"in_iowait", ktap_lib_in_iowait},
+       {NULL}
+};
+
+void kp_init_baselib(ktap_state *ks)
+{
+       kp_register_lib(ks, NULL, base_funcs);
+}
diff --git a/drivers/staging/ktap/runtime/lib_ffi.c b/drivers/staging/ktap/runtime/lib_ffi.c
new file mode 100644 (file)
index 0000000..ac1e4ef
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * ffi.c - ktapvm kernel module ffi library
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "../include/ktap_types.h"
+#include "../include/ktap_ffi.h"
+#include "ktap.h"
+#include "kp_vm.h"
+
+/*@TODO Design how to implement ffi helper functions  22.11 2013 (unihorn)*/
+
+static int kp_ffi_new(ktap_state *ks)
+{
+       /*@TODO finish this  08.11 2013 (houqp)*/
+       return 0;
+}
+
+static int kp_ffi_sizeof(ktap_state *ks)
+{
+       /*@TODO finish this  08.11 2013 (houqp)*/
+       return 0;
+}
+
+static const ktap_Reg ffi_funcs[] = {
+       {"sizeof", kp_ffi_sizeof},
+       {"new", kp_ffi_new},
+       {NULL}
+};
+
+void kp_init_ffilib(ktap_state *ks)
+{
+       kp_register_lib(ks, "ffi", ffi_funcs);
+}
diff --git a/drivers/staging/ktap/runtime/lib_kdebug.c b/drivers/staging/ktap/runtime/lib_kdebug.c
new file mode 100644 (file)
index 0000000..409a632
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+ * kdebug.c - ktap probing core implementation
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/version.h>
+#include <linux/ftrace_event.h>
+#include "../include/ktap_types.h"
+#include "ktap.h"
+#include "kp_obj.h"
+#include "kp_str.h"
+#include "kp_transport.h"
+#include "kp_vm.h"
+
+static void ktap_call_probe_closure(ktap_state *mainthread, ktap_closure *cl,
+                                   struct ktap_event *e)
+{
+       ktap_state *ks;
+       ktap_value *func;
+
+       ks = kp_newthread(mainthread);
+       set_closure(ks->top, cl);
+       func = ks->top;
+       incr_top(ks);
+
+       ks->current_event = e;
+
+       kp_call(ks, func, 0);
+
+       ks->current_event = NULL;
+       kp_exitthread(ks);
+}
+
+void kp_event_tostring(ktap_state *ks, struct trace_seq *seq)
+{
+       struct ktap_event *e = ks->current_event;
+       struct trace_iterator *iter;
+       struct trace_event *ev;
+       enum print_line_t ret = TRACE_TYPE_NO_CONSUME;
+
+       /* Simulate the iterator */
+
+       /*
+        * use temp percpu buffer as trace_iterator
+        * we cannot use same temp buffer as printf.
+        */
+       iter = kp_percpu_data(ks, KTAP_PERCPU_DATA_BUFFER2);
+
+       trace_seq_init(&iter->seq);
+       iter->ent = e->entry;
+
+       ev = &(e->call->event);
+       if (ev)
+               ret = ev->funcs->trace(iter, 0, ev);
+
+       if (ret != TRACE_TYPE_NO_CONSUME) {
+               struct trace_seq *s = &iter->seq;
+               int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len;
+
+               s->buffer[len] = '\0';
+               _trace_seq_puts(seq, s->buffer);
+       }
+}
+
+/* This definition should keep update with kernel/trace/trace.h */
+struct ftrace_event_field {
+       struct list_head        link;
+       const char              *name;
+       const char              *type;
+       int                     filter_type;
+       int                     offset;
+       int                     size;
+       int                     is_signed;
+};
+
+static struct list_head *ktap_get_fields(struct ftrace_event_call *event_call)
+{
+       if (!event_call->class->get_fields)
+               return &event_call->class->fields;
+       return event_call->class->get_fields(event_call);
+}
+
+static void get_field_value(ktap_state *ks, struct ktap_event *e,
+                           struct ftrace_event_field *field, ktap_value *ra)
+{
+       void *value = (unsigned char *)e->entry + field->offset;
+
+       if (field->size == 4) {
+               int n = *(int *)value;
+               set_number(ra, n);
+               return;
+       } else if (field->size == 8) {
+               long n = *(long *)value;
+               set_number(ra, n);
+               return;
+       }
+
+       if (!strncmp(field->type, "char", 4)) {
+               set_string(ra, kp_tstring_new(ks, (char *)value));
+               return;
+       }
+}
+
+void kp_event_getarg(ktap_state *ks, ktap_value *ra, int n)
+{
+       struct ktap_event *e = ks->current_event;
+       int index = n;
+       struct ftrace_event_field *field;
+       struct list_head *head;
+
+       /* this is very slow and not safe, fix it in future */
+       head = ktap_get_fields(e->call);
+       list_for_each_entry_reverse(field, head, link) {
+               if (--index == 0) {
+                       get_field_value(ks, e, field, ra);
+                       return;
+               }
+       }
+
+       set_nil(ra);
+       return;
+}
+
+/* Callback function for perf event subsystem
+ * make ktap reentrant, don't disable irq in callback function,
+ * same as perf and ftrace. to make reentrant, we need some
+ * percpu data to be context isolation(irq/sirq/nmi/process)
+ *
+ * The recursion checking in here is mainly purpose for avoiding
+ * corrupt ktap_state with timer closure callback. For tracepoint
+ * recusion, perf core already handle it.
+ *
+ * Note tracepoint handler is calling with rcu_read_lock.
+ */
+static void ktap_overflow_callback(struct perf_event *event,
+                                  struct perf_sample_data *data,
+                                  struct pt_regs *regs)
+{
+       struct ktap_probe_event *ktap_pevent;
+       struct ktap_event e;
+       ktap_state  *ks;
+       int rctx;
+
+       ktap_pevent = event->overflow_handler_context;
+       ks = ktap_pevent->ks;
+
+       if (unlikely(ks->stop))
+               return;
+
+       rctx = get_recursion_context(ks);
+       if (rctx < 0)
+               return;
+
+       KTAP_STATS(ks)->events_hits += 1;
+
+       /* profile perf event don't have valid associated tp_event */
+       if (event->tp_event) {
+               e.call = event->tp_event;
+               e.entry = data->raw->data;
+               e.entry_size = data->raw->size;
+       }
+       e.pevent = ktap_pevent;
+       e.regs = regs;
+
+       ktap_call_probe_closure(ks, ktap_pevent->cl, &e);
+
+       put_recursion_context(ks, rctx);
+}
+
+static void perf_destructor(struct ktap_probe_event *ktap_pevent)
+{
+       perf_event_release_kernel(ktap_pevent->perf);
+}
+
+static int (*kp_ftrace_profile_set_filter)(struct perf_event *event,
+                                          int event_id, char *filter_str);
+
+/*
+ * Generic perf event register function
+ * used by tracepoints/kprobe/uprobe/profile-timer/hw_breakpoint.
+ */
+void kp_perf_event_register(ktap_state *ks, struct perf_event_attr *attr,
+                           struct task_struct *task, char *filter,
+                           ktap_closure *cl)
+{
+       struct ktap_probe_event *ktap_pevent;
+       struct kmem_cache *pevent_cache = G(ks)->pevent_cache;
+       struct perf_event *event;
+       int cpu, ret;
+
+       kp_verbose_printf(ks, "enable perf event id: %d, filter: %s "
+                             "pid: %d\n", attr->config, filter,
+                             task ? task_tgid_vnr(task) : -1);
+
+       /*
+        * don't tracing until ktap_wait, the reason is:
+        * 1). some event may hit before apply filter
+        * 2). more simple to manage tracing thread
+        * 3). avoid race with mainthread.
+        *
+        * Another way to do this is make attr.disabled as 1, then use
+        * perf_event_enable after filter apply, however, perf_event_enable
+        * was not exported in kernel older than 3.3, so we drop this method.
+        */
+       ks->stop = 1;
+
+       for_each_cpu(cpu, G(ks)->cpumask) {
+               ktap_pevent = kmem_cache_zalloc(pevent_cache, GFP_KERNEL);
+               if (!ktap_pevent)
+                       return;
+
+               ktap_pevent->ks = ks;
+               ktap_pevent->cl = cl;
+               event = perf_event_create_kernel_counter(attr, cpu, task,
+                                                        ktap_overflow_callback,
+                                                        ktap_pevent);
+               if (IS_ERR(event)) {
+                       int err = PTR_ERR(event);
+                       kp_error(ks, "unable register perf event %d on cpu %d, "
+                                    "err: %d\n", attr->config, cpu, err);
+                       kp_free(ks, ktap_pevent);
+                       return;
+               }
+
+               ktap_pevent->perf = event;
+               INIT_LIST_HEAD(&ktap_pevent->list);
+               list_add_tail(&ktap_pevent->list, &G(ks)->probe_events_head);
+
+               if (!filter)
+                       continue;
+
+               ret = kp_ftrace_profile_set_filter(event, attr->config, filter);
+               if (ret) {
+                       kp_error(ks, "unable set filter %s for event id %d, "
+                                    "ret: %d\n", filter, attr->config, ret);
+                       perf_destructor(ktap_pevent);
+                       list_del(&ktap_pevent->list);
+                       kp_free(ks, ktap_pevent);
+                       return;
+               }
+       }
+}
+
+static void end_probes(struct ktap_state *ks)
+{
+       struct ktap_probe_event *ktap_pevent;
+       struct list_head *tmp, *pos;
+       struct list_head *head = &G(ks)->probe_events_head;
+
+       list_for_each(pos, head) {
+               ktap_pevent = container_of(pos, struct ktap_probe_event,
+                                          list);
+               perf_destructor(ktap_pevent);
+        }
+               /*
+        * Ensure our callback won't be called anymore. The buffers
+        * will be freed after that.
+        */
+       tracepoint_synchronize_unregister();
+
+       list_for_each_safe(pos, tmp, head) {
+               ktap_pevent = container_of(pos, struct ktap_probe_event,
+                                          list);
+               list_del(&ktap_pevent->list);
+               kp_free(ks, ktap_pevent);
+       }
+}
+
+static int ktap_lib_probe_by_id(ktap_state *ks)
+{
+       ktap_closure *cl;
+       struct task_struct *task = G(ks)->trace_task;
+       ktap_eventdef_info evdef_info;
+       char *filter = NULL;
+       int *id_arr;
+       int ret, i;
+
+       /* the number is userspace address refer to ktap_eventdef_info */
+       kp_arg_check(ks, 1, KTAP_TNUMBER);
+       kp_arg_check(ks, 2, KTAP_TFUNCTION);
+
+       ret = copy_from_user(&evdef_info, (void *)nvalue(kp_arg(ks, 1)),
+                            sizeof(evdef_info));
+       if (ret < 0)
+               return -1;
+
+       if (evdef_info.filter) {
+               int len;
+
+               len = strlen_user(evdef_info.filter);
+               if (len > 0x1000)
+                       return -1;
+
+               filter = kmalloc(len + 1, GFP_KERNEL);
+               if (!filter)
+                       return -1;
+
+               if (strncpy_from_user(filter, evdef_info.filter, len) < 0) {
+                       kfree(filter);
+                       return -1;
+               }
+       }
+
+       id_arr = kmalloc(evdef_info.nr * sizeof(int), GFP_KERNEL);
+       if (!id_arr) {
+               kfree(filter);
+               return -1;
+       }
+
+       ret = copy_from_user(id_arr, evdef_info.id_arr,
+                            evdef_info.nr * sizeof(int));
+       if (ret < 0) {
+               kfree(filter);
+               kfree(id_arr);
+               return -1;
+       }
+
+       cl = clvalue(kp_arg(ks, 2));
+
+       for (i = 0; i < evdef_info.nr; i++) {
+               struct perf_event_attr attr;
+
+               memset(&attr, 0, sizeof(attr));
+               attr.type = PERF_TYPE_TRACEPOINT;
+               attr.config = id_arr[i];
+               attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
+                                  PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD;
+               attr.sample_period = 1;
+               attr.size = sizeof(attr);
+               attr.disabled = 0;
+
+               kp_perf_event_register(ks, &attr, task, filter, cl);
+       }
+
+       kfree(filter);
+       kfree(id_arr);
+       return 0;
+}
+
+static int ktap_lib_probe_end(ktap_state *ks)
+{
+       kp_arg_check(ks, 1, KTAP_TFUNCTION);
+
+       G(ks)->trace_end_closure = clvalue(kp_arg(ks, 1));
+       return 0;
+}
+
+static int ktap_lib_traceoff(ktap_state *ks)
+{
+       end_probes(ks);
+
+       /* call trace_end_closure after probed end */
+       if (G(ks)->trace_end_closure) {
+               set_closure(ks->top, G(ks)->trace_end_closure);
+               incr_top(ks);
+               kp_call(ks, ks->top - 1, 0);
+               G(ks)->trace_end_closure = NULL;
+       }
+
+       return 0;
+}
+
+void kp_probe_exit(ktap_state *ks)
+{
+       if (!G(ks)->trace_enabled)
+               return;
+
+       end_probes(ks);
+
+       /* call trace_end_closure after probed end */
+       if (!G(ks)->error && G(ks)->trace_end_closure) {
+               set_closure(ks->top, G(ks)->trace_end_closure);
+               incr_top(ks);
+               kp_call(ks, ks->top - 1, 0);
+               G(ks)->trace_end_closure = NULL;
+       }
+
+       kmem_cache_destroy(G(ks)->pevent_cache);
+       G(ks)->trace_enabled = 0;
+}
+
+int kp_probe_init(ktap_state *ks)
+{
+       G(ks)->pevent_cache = KMEM_CACHE(ktap_probe_event, SLAB_PANIC);
+       G(ks)->trace_enabled = 1;
+       return 0;
+}
+
+static const ktap_Reg kdebuglib_funcs[] = {
+       {"probe_by_id", ktap_lib_probe_by_id},
+       {"probe_end", ktap_lib_probe_end},
+       {"traceoff", ktap_lib_traceoff},
+       {NULL}
+};
+
+void kp_init_kdebuglib(ktap_state *ks)
+{
+       kp_ftrace_profile_set_filter =
+               (void *)kallsyms_lookup_name("ftrace_profile_set_filter");
+       if (!kp_ftrace_profile_set_filter) {
+               kp_error(ks, "ktap: cannot lookup ftrace_profile_set_filter "
+                               "in kallsyms\n");
+               return;
+       }
+
+       kp_register_lib(ks, "kdebug", kdebuglib_funcs);
+}
+
diff --git a/drivers/staging/ktap/runtime/lib_timer.c b/drivers/staging/ktap/runtime/lib_timer.c
new file mode 100644 (file)
index 0000000..2fa4575
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * timer.c - timer library support for ktap
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include "../include/ktap_types.h"
+#include "ktap.h"
+#include "kp_obj.h"
+#include "kp_vm.h"
+
+struct hrtimer_ktap {
+       struct hrtimer timer;
+       ktap_state *ks;
+       ktap_closure *cl;
+       u64 ns;
+       struct list_head list;
+};
+
+/*
+ * Currently ktap disallow tracing event in timer callback closure,
+ * that will corrupt ktap_state and ktap stack, because timer closure
+ * and event closure use same irq percpu ktap_state and stack.
+ * We can use a different percpu ktap_state and stack for timer purpuse,
+ * but that's don't bring any big value with cost on memory consuming.
+ *
+ * So just simply disable tracing in timer closure,
+ * get_recursion_context()/put_recursion_context() is used for this purpose.
+ */
+static enum hrtimer_restart hrtimer_ktap_fn(struct hrtimer *timer)
+{
+       struct hrtimer_ktap *t;
+       ktap_state *ks;
+       int rctx;
+
+       rcu_read_lock_sched_notrace();
+
+       t = container_of(timer, struct hrtimer_ktap, timer);
+       rctx = get_recursion_context(t->ks);
+
+       ks = kp_newthread(t->ks);
+       set_closure(ks->top, t->cl);
+       incr_top(ks);
+       kp_call(ks, ks->top - 1, 0);
+       kp_exitthread(ks);
+
+       hrtimer_add_expires_ns(timer, t->ns);
+
+       put_recursion_context(ks, rctx);
+       rcu_read_unlock_sched_notrace();
+
+       return HRTIMER_RESTART;
+}
+
+static void set_tick_timer(ktap_state *ks, u64 period, ktap_closure *cl)
+{
+       struct hrtimer_ktap *t;
+
+       t = kp_malloc(ks, sizeof(*t));
+       t->ks = ks;
+       t->cl = cl;
+       t->ns = period;
+
+       INIT_LIST_HEAD(&t->list);
+       list_add(&t->list, &(G(ks)->timers));
+
+       hrtimer_init(&t->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       t->timer.function = hrtimer_ktap_fn;
+       hrtimer_start(&t->timer, ns_to_ktime(period), HRTIMER_MODE_REL);
+}
+
+static void set_profile_timer(ktap_state *ks, u64 period, ktap_closure *cl)
+{
+       struct perf_event_attr attr;
+
+       memset(&attr, 0, sizeof(attr));
+       attr.type = PERF_TYPE_SOFTWARE;
+       attr.config = PERF_COUNT_SW_CPU_CLOCK;
+       attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
+                          PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD;
+       attr.sample_period = period;
+       attr.size = sizeof(attr);
+       attr.disabled = 0;
+
+       kp_perf_event_register(ks, &attr, NULL, NULL, cl);
+}
+
+static int do_tick_profile(ktap_state *ks, int is_tick)
+{
+       const char *str, *tmp;
+       char interval_str[32] = {0};
+       char suffix[10] = {0};
+       int n, i = 0;
+       int factor;
+
+       kp_arg_check(ks, 1, KTAP_TSTRING);
+       kp_arg_check(ks, 2, KTAP_TFUNCTION);
+
+       str = svalue(kp_arg(ks, 1));
+       tmp = str;
+       while (isdigit(*tmp))
+               tmp++;
+
+       strncpy(interval_str, str, tmp - str);
+       if (kstrtoint(interval_str, 10, &n))
+               goto error;
+
+       strncpy(suffix, tmp, 9);
+       while (suffix[i] != ' ' && suffix[i] != '\0')
+               i++;
+
+       suffix[i] = '\0';
+
+       if (!strcmp(suffix, "s") || !strcmp(suffix, "sec"))
+               factor = NSEC_PER_SEC;
+       else if (!strcmp(suffix, "ms") || !strcmp(suffix, "msec"))
+               factor = NSEC_PER_MSEC;
+       else if (!strcmp(suffix, "us") || !strcmp(suffix, "usec"))
+               factor = NSEC_PER_USEC;
+       else
+               goto error;
+
+       if (is_tick)
+               set_tick_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2)));
+       else
+               set_profile_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2)));
+
+       return 0;
+
+ error:
+       kp_error(ks, "cannot parse timer interval: %s\n", str);
+       return -1;
+}
+
+/*
+ * tick-n probes fire on only one CPU per interval.
+ * valid time suffixes: sec/s, msec/ms, usec/us
+ */
+static int ktap_lib_tick(ktap_state *ks)
+{
+       return do_tick_profile(ks, 1);
+}
+
+/*
+ * A profile-n probe fires every fixed interval on every CPU
+ * valid time suffixes: sec/s, msec/ms, usec/us
+ */
+static int ktap_lib_profile(ktap_state *ks)
+{
+       return do_tick_profile(ks, 0);
+}
+
+void kp_exit_timers(ktap_state *ks)
+{
+       struct hrtimer_ktap *t, *tmp;
+       struct list_head *timers_list = &(G(ks)->timers);
+
+       list_for_each_entry_safe(t, tmp, timers_list, list) {
+               hrtimer_cancel(&t->timer);
+               kp_free(ks, t);
+       }
+}
+
+static const ktap_Reg timerlib_funcs[] = {
+       {"profile",     ktap_lib_profile},
+       {"tick",        ktap_lib_tick},
+       {NULL}
+};
+
+void kp_init_timerlib(ktap_state *ks)
+{
+       kp_register_lib(ks, "timer", timerlib_funcs);
+}
+
diff --git a/drivers/staging/ktap/samples/ansi/ansi_color_demo.kp b/drivers/staging/ktap/samples/ansi/ansi_color_demo.kp
new file mode 100644 (file)
index 0000000..2998fe0
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env ktap
+
+#this script demonstrate how to use ktap to output color text.
+
+ansi.clear_screen()
+
+ansi.set_color(32)
+printf("this line should be Green color\n")
+
+ansi.set_color(31)
+printf("this line should be Red color\n")
+
+ansi.set_color2(34, 43)
+printf("this line should be Blue color, with Yellow background\n")
+
+ansi.reset_color()
+ansi.set_color3(34, 46, 4)
+printf("this line should be Blue color, with Cyan background, underline single attribute\n")
+
+ansi.reset_color()
+ansi.new_line()
+
diff --git a/drivers/staging/ktap/samples/basic/backtrace.kp b/drivers/staging/ktap/samples/basic/backtrace.kp
new file mode 100644 (file)
index 0000000..39b8c39
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env ktap
+
+trace sched:sched_switch {
+       print_backtrace()
+}
+
diff --git a/drivers/staging/ktap/samples/basic/event_trigger.kp b/drivers/staging/ktap/samples/basic/event_trigger.kp
new file mode 100644 (file)
index 0000000..3cc8b04
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env ktap
+
+soft_disabled = 1
+this_cpu = 0
+
+trace syscalls:sys_enter_open {
+       print(argevent)
+       soft_disabled = 0
+       this_cpu = cpu()
+}
+
+trace *:* {
+       if (soft_disabled == 0 && cpu() == this_cpu) {
+               print(argevent)
+       }
+}
+
+trace syscalls:sys_exit_open {
+       print(argevent)
+       if (cpu() == this_cpu) {
+               exit()
+       }
+}
+
diff --git a/drivers/staging/ktap/samples/basic/event_trigger_ftrace.kp b/drivers/staging/ktap/samples/basic/event_trigger_ftrace.kp
new file mode 100644 (file)
index 0000000..7e0d7d3
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env ktap
+
+
+#This ktap script will output all function calling between
+#sys_enter_open and sys_exit_open, in one cpu.
+
+soft_disabled = 1
+this_cpu = 0
+
+trace syscalls:sys_enter_open {
+       print(argevent)
+       soft_disabled = 0
+       this_cpu = cpu()
+}
+
+trace ftrace:function {
+       if (soft_disabled == 0 && cpu() == this_cpu) {
+               print(argevent)
+       }
+}
+
+trace syscalls:sys_exit_open {
+       print(argevent)
+       if (cpu() == this_cpu) {
+               exit()
+       }
+}
+
diff --git a/drivers/staging/ktap/samples/basic/ftrace.kp b/drivers/staging/ktap/samples/basic/ftrace.kp
new file mode 100644 (file)
index 0000000..9feca2b
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env ktap
+
+trace ftrace:function /ip==mutex*/ {
+       print(cpu(), pid(), execname(), argevent)
+}
+
diff --git a/drivers/staging/ktap/samples/basic/function_time.kp b/drivers/staging/ktap/samples/basic/function_time.kp
new file mode 100644 (file)
index 0000000..e7859a3
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/env ktap
+
+#Demo for thread-local variable
+#
+#Note this kind of function time tracing already handled concurrent issue,
+#but not aware on the recursion problem, user need to aware this limitation,
+#so don't use this script to trace function which could be called recursive.
+
+self = {}
+count_max = 0
+count_min = 0
+count_num = 0
+total_time = 0
+
+printf("measure time(us) of function vfs_read\n");
+
+trace probe:vfs_read {
+       if (execname() == "ktap") {
+               return
+       }
+
+       self[tid()] = gettimeofday_us()
+}
+
+trace probe:vfs_read%return {
+       if (execname() == "ktap") {
+               return
+       }
+
+       if (self[tid()] == nil) {
+               return
+       }
+
+       local durtion = gettimeofday_us() - self[tid()]
+       if (durtion > count_max) {
+               count_max = durtion
+       }
+       local min = count_min
+       if (min == 0 || durtion < min) {
+               count_min = durtion
+       }
+
+       count_num = count_num + 1
+       total_time = total_time + durtion
+
+       self[tid()] = nil
+}
+
+trace_end {
+       printf("avg\tmax\tmin\n");
+       printf("-------------------\n")
+
+       printf("%d\t%d\t%d\n", total_time/count_num,
+               count_max, count_min)
+}
+
+
diff --git a/drivers/staging/ktap/samples/basic/kretprobe.kp b/drivers/staging/ktap/samples/basic/kretprobe.kp
new file mode 100644 (file)
index 0000000..82de3cf
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env ktap
+
+trace probe:vfs_read%return fd=$retval {
+       print(execname(), argevent);
+}
+
diff --git a/drivers/staging/ktap/samples/ffi/ffi_kmalloc.kp b/drivers/staging/ktap/samples/ffi/ffi_kmalloc.kp
new file mode 100644 (file)
index 0000000..d8f0898
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/env ktap
+
+cdef[[
+       typedef unsigned gfp_t;
+       typedef unsigned long size_t;
+       void *__kmalloc( size_t size, gfp_t flags);
+       void kfree(const void *objp);
+]]
+
+t1 = gettimeofday_us()
+
+for (i = 1, 1000, 1) {
+       local object = C.__kmalloc(128, 208) #GFP_KERNEL is 208
+       C.kfree(object)
+}
+
+t2 = gettimeofday_us()
+
+printf("execution time: %d us\n", t2 - t1)
diff --git a/drivers/staging/ktap/samples/ffi/printk.kp b/drivers/staging/ktap/samples/ffi/printk.kp
new file mode 100644 (file)
index 0000000..dab9049
--- /dev/null
@@ -0,0 +1,10 @@
+cdef[[
+       int printk(char *fmt, ...);
+]]
+
+
+C.printk("This is printed out by ffi\n")
+C.printk("Show me the %s\n", "code")
+C.printk("%s should be at %02d/%02d %02d:%02d:%02d\n", "New Year", 1, 1, 0, 0, 0)
+C.printk("\'a\' + 5 = \'%c\'\n", 95 + 5)
+C.printk("The string is located at 0x%p\n", "str")
diff --git a/drivers/staging/ktap/samples/ffi/sched_clock.kp b/drivers/staging/ktap/samples/ffi/sched_clock.kp
new file mode 100644 (file)
index 0000000..2a94587
--- /dev/null
@@ -0,0 +1,6 @@
+cdef[[
+       unsigned long long sched_clock();
+]]
+
+ret = C.sched_clock()
+print("C.sched_clock returned, value: ", ret)
diff --git a/drivers/staging/ktap/samples/game/tetris.kp b/drivers/staging/ktap/samples/game/tetris.kp
new file mode 100644 (file)
index 0000000..26e1c9f
--- /dev/null
@@ -0,0 +1,293 @@
+#!/usr/bin/env ktap
+
+#
+# Tetris KTAP Script
+#
+# Copyright (C) 2013/OCT/05 Tadaki SAKAI
+#
+# based on stapgames (Systemtap Game Collection)
+#   https://github.com/mhiramat/stapgames/blob/master/games/tetris.stp
+#
+#   - Requirements
+#     Kernel Configuration: CONFIG_KPROBE_EVENT=y
+#                           CONFIG_EVENT_TRACING=y
+#                           CONFIG_PERF_EVENTS=y
+#                           CONFIG_DEBUG_FS=y
+#     CPU Architecture : x86_64
+#
+#   - Setup
+#     $ sudo mount -t debugfs none /sys/kernel/debug/
+#
+#     $ git clone https://github.com/ktap/ktap
+#     $ cd ktap
+#     $ make 2>&1 | tee ../make.log
+#     $ sudo make load
+#     $ sudo sh -c 'echo 50000 > /sys/module/ktapvm/parameters/max_exec_count'
+#
+#   - Run Tetris
+#     $ sudo ./ktap samples/game/tetris.kp
+#
+
+
+#
+# utils
+#
+
+function rand(max) {
+       r = gettimeofday_us()
+       if (r < 0) {
+               r = r * -1
+       }
+       return r % max
+}
+
+function update_display() {
+       for (i = 0, 239, 1) {
+               if ((i % 12 - 11) != 0) {
+                       tmp = ""
+               } else {
+                       tmp = "\n"
+               }
+
+               if (display_buffer[240 + i] == empty) {
+                       printf("  %s", tmp)
+               } else {
+                       color = display_buffer[240 + i] + 40
+                       ansi.set_color2(color, color)
+                       printf("  %s", tmp)
+                       ansi.reset_color()
+               }
+
+               # clear the display buffer
+               display_buffer[240 + i] = display_buffer[i]
+       }
+
+       printf("%d\n",point)
+}
+
+
+#
+# global value
+#
+
+empty = -1
+
+key_code = 0
+point = 0
+block_number = 0
+height = 0
+height_update = 0
+
+destination_position = {}
+display_buffer = {}
+
+block_data0 = {}
+block_data1 = {}
+block_data2 = {}
+block_data3 = {}
+block_data4 = {}
+block_data5 = {}
+block_data6 = {}
+block_table = {}
+
+#
+# Initialize
+#
+
+# Create blocks
+# block is represented by the position from the center.
+# Every block has "L" part in the center except for a bar.
+block_data0[0] = -11 # non-"L" part for each block
+block_data1[0] = -24
+block_data2[0] = 2
+block_data3[0] = 13
+block_data4[0] = -13
+block_data5[0] = -1
+block_data6[0] = 2
+
+block_table[0] = block_data0
+block_table[1] = block_data1
+block_table[2] = block_data2
+block_table[3] = block_data3
+block_table[4] = block_data4
+block_table[5] = block_data5
+block_table[6] = block_data6
+
+for (i = 0, len(block_table) - 1, 1) {
+       # common "L" part
+       block_table[i][1] = 0
+       block_table[i][2] = 1
+       block_table[i][3] = -12
+}
+
+block_table[6][3] = -1 # bar is not common
+# Position: 1 row has 12 columns,
+# and (x, y) is represented by h = x + y * 12.p
+height = 17 # First block position (center)
+
+for (i = 0, 240, 1) {
+       # Wall and Floor (sentinel)
+       if (((i % 12) < 2) || (i > 228)) {
+               tmp = 7 # White
+       } else {
+               tmp = empty
+       }
+       display_buffer[i - 1] = tmp
+       display_buffer[240 + i - 1] = tmp
+}
+
+block_number = rand(7)
+
+ansi.clear_screen()
+
+
+#
+# Key Input
+#
+
+trace probe:kbd_event handle=%di event_type=%si event_code=%dx value=%cx {
+       # Only can run it in x86_64
+       #
+       # Register follow x86_64 call conversion:
+       #
+       # x86_64:
+       #       %rcx    4 argument
+       #       %rdx    3 argument
+       #       %rsi    2 argument
+       #       %rdi    1 argument
+
+       local event_code = arg4
+       local value = arg5
+
+       if (value != 0) {
+               if ((event_code - 4) != 0) {
+                       key_code = event_code
+               }
+       }
+}
+
+
+#
+# timer
+#
+
+tick-200ms {
+       ansi.clear_screen()
+
+       f = 0 # move/rotate flag
+
+       if (key_code != 0) { # if key is pressed
+               if(key_code != 103) { #move left or right
+                       # d: movement direction
+                       if ((key_code - 105) != 0) {
+                               if ((key_code - 106) != 0) {
+                                       d = 0
+                               } else {
+                                       d = 1
+                               }
+                       } else {
+                               d = -1
+                       }
+
+                       for (i = 0, 3, 1) { # check if the block can be moved
+                               # destination is free
+                               if (display_buffer[height +
+                                       block_table[block_number][i] + d]
+                                   != empty) {
+                                       f = 1
+                               }
+                       }
+                       # move if destinations of every block are free
+                       if (f == 0) {
+                               height = height + d
+                       }
+               } else { # rotate
+                       for (i = 0, 3, 1) { # check if block can be rotated
+                               # each block position
+                               p = block_table[block_number][i]
+
+                               # destination x pos(p/12 rounded)
+                               v = (p * 2 + 252) / 24 - 10
+                               w = p - v * 12 # destination y pos
+
+                               # destination position
+                               destination_position[i] = w * 12 - v
+
+                               # check if desetination is free
+                               if (display_buffer[height +
+                                   destination_position[i]] != empty) {
+                                       f = 1
+                               }
+                       }
+
+                       if (f == 0) {
+                               # rotate if destinations of every block
+                               # are free
+                               for (i = 0, 3, 1) {
+                                       block_table[block_number][i] =
+                                               destination_position[i]
+                               }
+                       }
+               }
+       }
+       key_code = 0 # clear the input key
+
+       f = 0
+       for (i = 0, 3, 1) { # drop 1 row
+               # check if destination is free
+               p = height + block_table[block_number][i]
+               if (display_buffer[12 + p] != empty) {
+                       f = 1
+               }
+
+               # copy the moving block to display buffer
+               display_buffer[240 + p] = block_number
+       }
+
+       if ((f == 1) && (height == 17)) {
+               update_display()
+               exit() # exit if there are block at initial position
+       }
+
+       height_update = !height_update
+       if (height_update != 0) {
+               if(f != 0) { # the block can't drop anymore
+                       for (i = 0, 3, 1) {
+                               # fix the block
+                               display_buffer[height +
+                                 block_table[block_number][i]] = block_number
+                       }
+                       # determin the next block
+                       block_number = rand(7)
+                       height = 17 # make the block to initial position
+               } else {
+                       height = height + 12 # drop the block 1 row
+               }
+       }
+
+       k = 1
+       for (i = 18, 0, -1) { #check if line is filled
+               # search for filled line
+               j = 10
+               while ((j > 0) &&
+                      (display_buffer[i * 12 + j] != empty)) {
+                       j = j - 1
+               }
+
+               if (j == 0) { # filled!
+                       # add a point: 1 line - 1 point, ..., tetris - 10points
+                       point = point + k
+                       k = k + 1
+
+                       # drop every upper block
+                       j = (i + 1) * 12
+                       i = i + 1
+                       while (j > 2 * 12) {
+                               j = j - 1
+                               display_buffer[j] = display_buffer[j - 12]
+                       }
+               }
+       }
+
+       update_display()
+}
diff --git a/drivers/staging/ktap/samples/helloworld.kp b/drivers/staging/ktap/samples/helloworld.kp
new file mode 100644 (file)
index 0000000..5673c15
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env ktap
+
+print("Hello World! I am ktap")
diff --git a/drivers/staging/ktap/samples/interrupt/hardirq_time.kp b/drivers/staging/ktap/samples/interrupt/hardirq_time.kp
new file mode 100644 (file)
index 0000000..e570f15
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env ktap
+
+#this script output each average consumimg time of each hardirq
+s = ptable()
+map = {}
+
+trace irq:irq_handler_entry {
+       map[cpu()] = gettimeofday_us()
+}
+
+trace irq:irq_handler_exit {
+       local entry_time = map[cpu()]
+       if (entry_time == nil) {
+               return;
+       }
+
+       s[arg1] <<< gettimeofday_us() - entry_time
+       map[cpu()] = nil
+}
+
+trace_end {
+       print(s)
+}
+
diff --git a/drivers/staging/ktap/samples/interrupt/softirq_time.kp b/drivers/staging/ktap/samples/interrupt/softirq_time.kp
new file mode 100644 (file)
index 0000000..5aec09a
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env ktap
+
+#this script output each average consumimg time of each softirq line
+s = ptable()
+map = {}
+
+trace irq:softirq_entry {
+       map[cpu()] = gettimeofday_us()
+}
+
+trace irq:softirq_exit {
+       local entry_time = map[cpu()]
+       if (entry_time == nil) {
+               return;
+       }
+
+       s[arg1] <<< gettimeofday_us() - entry_time
+       map[cpu()] = nil
+}
+
+trace_end {
+       print(s)
+}
+
diff --git a/drivers/staging/ktap/samples/io/kprobes-do-sys-open.kp b/drivers/staging/ktap/samples/io/kprobes-do-sys-open.kp
new file mode 100644 (file)
index 0000000..a15f911
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/env ktap
+
+#Only can run it in x86_64
+#
+#Register follow x86_64 call conversion:
+#
+#x86_64:
+#      %rcx    4 argument
+#      %rdx    3 argument
+#      %rsi    2 argument
+#      %rdi    1 argument
+
+trace probe:do_sys_open dfd=%di filename=%si flags=%dx mode=%cx {
+       printf("[do_sys_open entry]: (%s) open file (%s)\n",
+               execname(),  user_string(arg3))
+}
+
+trace probe:do_sys_open%return fd=$retval {
+       printf("[do_sys_open exit]:  return fd (%d)\n", arg3)
+}
diff --git a/drivers/staging/ktap/samples/io/traceio.kp b/drivers/staging/ktap/samples/io/traceio.kp
new file mode 100644 (file)
index 0000000..89f979b
--- /dev/null
@@ -0,0 +1,54 @@
+#! /usr/bin/env ktap
+
+# Based on systemtap traceio.stp
+
+reads = ptable()
+writes = ptable()
+total_io = ptable()
+
+trace syscalls:sys_exit_read {
+       reads[execname()] <<< arg2
+       total_io[execname()] <<< arg2
+}
+
+trace syscalls:sys_exit_write {
+       writes[execname()] <<< arg2
+       total_io[execname()] <<< arg2
+}
+
+function humanread_digit(bytes) {
+       if (bytes > 1024*1024*1024) {
+               return bytes/1024/1024/1024
+       } elseif (bytes > 1024*1024) {
+               return bytes/1024/1024
+       } elseif (bytes > 1024) {
+               return bytes/1024
+       } else {
+               return bytes
+       }
+}
+
+function humanread_x(bytes) {
+       if (bytes > 1024*1024*1024) {
+               return " GiB"
+       } elseif (bytes > 1024*1024) {
+               return " MiB"
+       } elseif (bytes > 1024) {
+               return " KiB"
+       } else {
+               return "   B"
+       }
+}
+
+tick-1s {
+       ansi.clear_screen()
+       for (exec, _ in pairs(total_io)) {
+               local readnum = sum(reads[exec])
+               local writenum = sum(writes[exec])
+               printf("%15s r: %12d%s w: %12d%s\n", exec,
+                       humanread_digit(readnum), humanread_x(readnum),
+                       humanread_digit(writenum), humanread_x(writenum))
+       }
+       printf("\n")
+}
+
diff --git a/drivers/staging/ktap/samples/mem/kmalloc-top.kp b/drivers/staging/ktap/samples/mem/kmalloc-top.kp
new file mode 100644 (file)
index 0000000..f18ed9f
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env ktap
+
+kmalloc_stack = {}
+
+trace kmem:kmalloc {
+       kmalloc_stack[backtrace()] += 1
+}
+
+tick-60s {
+       for (k, v in pairs(kmalloc_stack)) {
+               print(k)
+               printf("%d\n\n", v)
+       }
+
+       exit()
+}
+
diff --git a/drivers/staging/ktap/samples/mem/kmem.kp b/drivers/staging/ktap/samples/mem/kmem.kp
new file mode 100644 (file)
index 0000000..c6f2c99
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/env ktap
+
+count1 = 0
+trace kmem:kmalloc {
+       count1 = count1 + 1
+}
+
+count2 = 0
+trace kmem:kfree {
+       count2 = count2 + 1
+}
+
+count3 = 0
+trace kmem:mm_page_alloc {
+       count3 = count3 + 1
+}
+
+count4 = 0
+trace kmem:mm_page_free {
+       count4 = count4 + 1
+}
+
+trace_end {
+       print("\n")
+       print("kmem:kmalloc:\t", count1)
+       print("kmem:kfree:\t", count2)
+       print("kmem:mm_page_alloc:", count3)
+       print("kmem:mm_page_free:", count4)
+       print("trace ending\n")
+}
diff --git a/drivers/staging/ktap/samples/profiling/function_profiler.kp b/drivers/staging/ktap/samples/profiling/function_profiler.kp
new file mode 100644 (file)
index 0000000..28f1fa6
--- /dev/null
@@ -0,0 +1,41 @@
+#!/usr/bin/env ktap
+
+#kernel function profile
+#You can use this script to know what function is called frequently,
+#without enable CONFIG_FUNCTION_PROFILER in kernel.
+
+s = ptable()
+
+trace ftrace:function {
+       s[arg1] <<< 1
+}
+
+trace_end {
+       histogram(s)
+}
+
+#sample output
+#^C
+#                          value ------------- Distribution ------------- count
+#               sub_preempt_count | @@@@@                                  34904
+#               add_preempt_count | @@@@@                                  33435
+#              nsecs_to_jiffies64 | @@@                                    19919
+# irqtime_account_process_tick... | @                                      9970
+#               account_idle_time | @                                      9880
+#                  _raw_spin_lock |                                        5100
+#                _raw_spin_unlock |                                        5021
+#     _raw_spin_unlock_irqrestore |                                        4235
+#          _raw_spin_lock_irqsave |                                        4232
+#                 __rcu_read_lock |                                        3373
+#               __rcu_read_unlock |                                        3373
+#                  lookup_address |                                        2392
+#             pfn_range_is_mapped |                                        2384
+#      update_cfs_rq_blocked_load |                                        1983
+#                        idle_cpu |                                        1808
+#                       ktime_get |                                        1394
+#            _raw_spin_unlock_irq |                                        1270
+#              _raw_spin_lock_irq |                                        1091
+#                     update_curr |                                        950
+#             irqtime_account_irq |                                        950
+#                             ... |
+#
diff --git a/drivers/staging/ktap/samples/profiling/stack_profile.kp b/drivers/staging/ktap/samples/profiling/stack_profile.kp
new file mode 100644 (file)
index 0000000..c88f850
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/env ktap
+
+# This ktap script samples stacktrace of system per 10us,
+# you can use generated output to make a flame graph.
+#
+# Flame Graphs:
+# http://dtrace.org/blogs/brendan/2012/03/17/linux-kernel-performance-flame-graphs/
+
+s = ptable()
+
+profile-10us {
+       #skip 12 stack entries, and dump all remain entries.
+       s[backtrace(12, -1)] <<< 1
+}
+
+tick-60s {
+       exit()
+}
+
+trace_end {
+       function cmp(v1, v2) {
+               return (count(v1) < count(v2))
+       }
+       for (k, v in sort_pairs(s, cmp)) {
+               print(k)
+               print(count(v))
+               print()
+       }
+}
+
diff --git a/drivers/staging/ktap/samples/schedule/sched_transition.kp b/drivers/staging/ktap/samples/schedule/sched_transition.kp
new file mode 100644 (file)
index 0000000..eb54b09
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/env ktap
+
+trace sched:sched_switch {
+       printf("%s ... ", arg1)
+}
diff --git a/drivers/staging/ktap/samples/schedule/schedtimes.kp b/drivers/staging/ktap/samples/schedule/schedtimes.kp
new file mode 100644 (file)
index 0000000..e8a6f9e
--- /dev/null
@@ -0,0 +1,125 @@
+#!/usr/vin/env ktap
+
+#schedtimer.kp
+#Initially inspired by Systemtap schedtimes.stp
+#and more bugfree compare with Systemtap's version
+#
+#Note that the time value is associate with pid, not with execname strictly,
+#sometime you will found there have sleep time for command "ls", the reason
+#is that sleep time is belong to parent process bash, so clear on this.
+
+RUNNING = 0
+QUEUED = 1
+SLEEPING = 2
+DEAD = 64
+
+run_time = {}
+queued_time = {}
+sleep_time = {}
+io_wait_time = {}
+
+pid_state = {}
+pid_names = {}
+prev_timestamp = {}
+io_wait = {}
+
+trace sched:sched_switch {
+       local prev_comm = arg1
+       local prev_pid = arg2
+       local prev_state = arg4
+       local next_comm = arg5
+       local next_pid = arg6
+       local t = gettimeofday_us()
+
+       if (pid_state[prev_pid] == nil) {
+               #do nothing
+       } elseif (pid_state[prev_pid] == RUNNING) {
+               run_time[prev_pid] += t - prev_timestamp[prev_pid]
+       } elseif (pid_state[prev_pid] == QUEUED) {
+               #found this:
+               #sched_wakeup comm=foo
+               #sched_switch prev_comm=foo
+               run_time[prev_pid] += t - prev_timestamp[prev_pid]
+       }
+
+       pid_names[prev_pid] = prev_comm
+       prev_timestamp[prev_pid] = t
+
+       if (prev_state == DEAD) {
+               pid_state[prev_pid] = DEAD
+       } elseif (prev_state > 0) {
+               if (in_iowait() == 1) {
+                       io_wait[prev_pid] = 1
+               }
+               pid_state[prev_pid] = SLEEPING
+       } elseif (prev_state == 0) {
+               pid_state[prev_pid] = QUEUED
+       }
+
+       if (pid_state[next_pid] == nil) {
+               pid_state[next_pid] = RUNNING
+       } elseif (pid_state[next_pid] == QUEUED) {
+               queued_time[next_pid] += t - prev_timestamp[next_pid]
+               pid_state[next_pid] = RUNNING
+       }
+
+       pid_names[next_pid] = next_comm
+       prev_timestamp[next_pid] = t
+}
+
+trace sched:sched_wakeup, sched:sched_wakeup_new {
+       local comm = arg1
+       local wakeup_pid = arg2
+       local success = arg4
+       local t = gettimeofday_us()
+
+       if (pid_state[wakeup_pid] == nil) {
+               #do nothing
+       } elseif (pid_state[wakeup_pid] == SLEEPING) {
+               local durtion = t - prev_timestamp[wakeup_pid]
+
+               sleep_time[wakeup_pid] += durtion
+               if (io_wait[wakeup_pid] == 1) {
+                       io_wait_time[wakeup_pid] += durtion
+                       io_wait[wakeup_pid] = 0
+               }
+       } elseif (pid_state[wakeup_pid] == RUNNING) {
+               return
+       }
+
+       pid_names[wakeup_pid] = comm
+       prev_timestamp[wakeup_pid] = t
+       pid_state[wakeup_pid] = QUEUED
+}
+
+trace_end {
+       local t = gettimeofday_us()
+
+       for (pid, state in pairs(pid_state)) {
+               local durtion = t - prev_timestamp[pid]
+               if (state == SLEEPING) {
+                       sleep_time[pid] += durtion
+               } elseif (state == QUEUED) {
+                       queued_time[pid] += durtion
+               } elseif (state == RUNNING) {
+                       run_time[pid] += durtion
+               }
+       }
+
+       printf ("%16s: %6s %10s %10s %10s %10s %10s\n\n",
+               "execname", "pid", "run(us)", "sleep(us)", "io_wait(us)",
+               "queued(us)", "total(us)")
+
+       for (pid, time in pairs(run_time)) {
+               if (sleep_time[pid] == nil) {
+                       sleep_time[pid] = 0
+               }
+               if (queued_time[pid] == nil) {
+                       queue_time[pid] = 0
+               }
+               printf("%16s: %6d %10d %10d %10d %10d %10d\n",
+                       pid_names[pid], pid, run_time[pid], sleep_time[pid],
+                       io_wait_time[pid], queued_time[pid],
+                       run_time[pid] + sleep_time[pid] + queued_time[pid]);
+       }
+}
diff --git a/drivers/staging/ktap/samples/syscalls/errinfo.kp b/drivers/staging/ktap/samples/syscalls/errinfo.kp
new file mode 100644 (file)
index 0000000..c8662d6
--- /dev/null
@@ -0,0 +1,145 @@
+#!/usr/bin/env ktap
+
+#errdesc get from include/uapi/asm-generic/errno*.h
+errdesc = {
+       [1] = "Operation not permitted",                #EPERM
+       [2] = "No such file or directory",              #ENOENT
+       [3] = "No such process",                        #ESRCH
+       [4] = "Interrupted system call",                #EINRT
+       [5] = "I/O error",                              #EIO
+       [6] = "No such device or address",              #ENXIO
+       [7] = "Argument list too long",                 #E2BIG
+       [8] = "Exec format error",                      #ENOEXEC
+       [9] = "Bad file number",                        #EBADF
+       [10] = "No child processes",                    #ECHILD
+       [11] = "Try again",                             #EAGAIN
+       [12] = "Out of memory",                         #ENOMEM
+       [13] = "Permission denied",                     #EACCES
+       [14] = "Bad address",                           #EFAULT
+       [15] = "Block device required",                 #ENOTBLK
+       [16] = "Device or resource busy",               #EBUSY
+       [17] = "File exists",                           #EEXIST
+       [18] = "Cross-device link",                     #EXDEV
+       [19] = "No such device",                        #ENODEV
+       [20] = "Not a directory",                       #ENOTDIR
+       [21] = "Is a directory",                        #EISDIR
+       [22] = "Invalid argument",                      #EINVAL
+       [23] = "File table overflow",                   #ENFILE
+       [24] = "Too many open files",                   #EMFILE
+       [25] = "Not a typewriter",                      #ENOTTY
+       [26] = "Text file busy",                        #ETXTBSY
+       [27] = "File too large",                        #EFBIG
+       [28] = "No space left on device",               #ENOSPC
+       [29] = "Illegal seek",                          #ESPIPE
+       [30] = "Read-only file system",                 #EROFS
+       [31] = "Too many links",                        #EMLINK
+       [32] = "Broken pipe",                           #EPIPE
+       [33] = "Math argument out of domain of func",   #EDOM
+       [34] = "Math result not representable",         #ERANGE
+
+       [35] = "Resource deadlock would occur",         #EDEADLK
+       [36] = "File name too long",                    #ENAMETOOLONG
+       [37] = "No record locks available",             #ENOLCK
+       [38] = "Function not implemented",              #ENOSYS
+       [39] = "Directory not empty",                   #ENOTEMPTY
+       [40] = "Too many symbolic links encountered",   #ELOOP
+       [42] = "No message of desired type",            #ENOMSG
+       [43] = "Identifier removed",                    #EIDRM
+       [44] = "Channel number out of range",           #ECHRNG
+       [45] = "Level 2 not synchronized",              #EL2NSYNC
+       [46] = "Level 3 halted",                        #EL3HLT
+       [47] = "Level 3 reset",                         #EL3RST
+       [48] = "Link number out of range",              #ELNRNG
+       [49] = "Protocol driver not attached",          #EUNATCH
+       [50] = "No CSI structure available",            #ENOCSI
+       [51] = "Level 2 halted",                        #EL2HLT
+       [52] = "Invalid exchange",                      #EBADE
+       [53] = "Invalid request descriptor",            #EBADR
+       [54] = "Exchange full",                         #EXFULL
+       [55] = "No anode",                              #ENOANO
+       [56] = "Invalid request code",                  #EBADRQC
+       [57] = "Invalid slot",                          #EBADSLT
+
+       [59] = "Bad font file format",                  #EBFONT
+       [60] = "Device not a stream",                   #ENOSTR
+       [61] = "No data available",                     #ENODATA
+       [62] = "Timer expired",                         #ETIME
+       [63] = "Out of streams resources",              #ENOSR
+       [64] = "Machine is not on the network",         #ENONET
+       [65] = "Package not installed",                 #ENOPKG
+       [66] = "Object is remote",                      #EREMOTE
+       [67] = "Link has been severed",                 #ENOLINK
+       [68] = "Advertise error",                       #EADV
+       [69] = "Srmount error",                         #ESRMNT
+       [70] = "Communication error on send",           #ECOMM
+       [71] = "Protocol error",                        #EPROTO
+       [72] = "Multihop attempted",                    #EMULTIHOP
+       [73] = "RFS specific error",                    #EDOTDOT
+       [74] = "Not a data message",                    #EBADMSG
+       [75] = "Value too large for defined data type", #EOVERFLOW
+       [76] = "Name not unique on network",            #ENOTUNIQ
+       [77] = "File descriptor in bad state",          #EBADFD
+       [78] = "Remote address changed",                #EREMCHG
+       [79] = "Can not access a needed shared library", #ELIBACC
+       [80] = "Accessing a corrupted shared library",  #ELIBBAD
+       [81] = ".lib section in a.out corrupted",       #ELIBSCN
+       [82] = "Attempting to link in too many shared libraries", #ELIBMAX
+       [83] = "Cannot exec a shared library directly", #ELIBEXEC
+       [84] = "Illegal byte sequence",                 #EILSEQ
+       [85] = "Interrupted system call should be restarted", #ERESTART
+       [86] = "Streams pipe error",                    #ESTRPIPE
+       [87] = "Too many users",                        #EUSERS
+       [88] = "Socket operation on non-socket",        #ENOTSOCK
+       [89] = "Destination address required",          #EDESTADDRREQ
+       [90] = "Message too long",                      #EMSGSIZE
+       [91] = "Protocol wrong type for socket",        #EPROTOTYPE
+       [92] = "Protocol not available",                #ENOPROTOOPT
+       [93] = "Protocol not supported",                #EPROTONOSUPPORT
+       [94] = "Socket type not supported",             #ESOCKTNOSUPPORT
+       [95] = "Operation not supported on transport endpoint", #EOPNOTSUPP
+       [96] = "Protocol family not supported",         #EPFNOSUPPORT
+       [97] = "Address family not supported by protocol", #EAFNOSUPPORT
+       [98] = "Address already in use",                #EADDRINUSE
+       [99] = "Cannot assign requested address",       #EADDRNOTAVAIL
+       [100] = "Network is down",                      #ENETDOWN
+       [101] = "Network is unreachable",               #ENETUNREACH
+       [102] = "Network dropped connection because of reset",  #ENETRESET
+       [103] = "Software caused connection abort",     #ECONNABORTED
+       [104] = "Connection reset by peer",             #ECONNRESET
+       [105] = "No buffer space available",            #ENOBUFS
+       [106] = "Transport endpoint is already connected", #EISCONN
+       [107] = "Transport endpoint is not connected",  #ENOTCONN
+       [108] = " Cannot send after transport endpoint shutdown", #ESHUTDOWN
+       [109] = "Too many references: cannot splice",   #ETOOMANYREFS
+       [110] = "Connection timed out",                 #ETIMEDOUT
+       [111] = "Connection refused",                   #ECONNREFUSED
+       [112] = "Host is down",                         #EHOSTDOWN
+       [113] = "No route to host",                     #EHOSTUNREACH
+       [114] = "Operation already in progress",        #EALREADY
+       [115] = "Operation now in progress",            #EINPROGRESS
+       [116] = "Stale NFS file handle",                #ESTALE
+       [117] = "Structure needs cleaning",             #EUCLEAN
+       [118] = "Not a XENIX named type file",          #ENOTNAM
+       [119] = "No XENIX semaphores available",        #ENAVAIL
+       [120] = "Is a named type file",                 #EISNAM
+       [121] = "Remote I/O error",                     #EREMOTEIO
+       [122] = "Quota exceeded",                       #EDQUOT
+       [123] = "No medium found",                      #ENOMEDIUM
+       [124] = "Wrong medium type",                    #EMEDIUMTYPE
+       [125] = "Operation Canceled",                   #ECANCELED
+       [126] = "Required key not available",           #ENOKEY
+       [127] = "Key has expired",                      #EKEYEXPIRED
+       [128] = "Key has been revoked",                 #EKEYREVOKED
+       [129] = "Key was rejected by service",          #EKEYREJECTED
+       [130] = "Owner died",                           #EOWNERDEAD
+       [131] = "State not recoverable",                #ENOTRECOVERABLE
+
+}
+
+trace syscalls:sys_exit_* {
+       if (arg2 < 0) {
+               local errno = -arg2
+               printf("%-15s%-20s\t%d\t%-30s\n",
+                       execname(), argname, errno, errdesc[errno])
+       }
+}
diff --git a/drivers/staging/ktap/samples/syscalls/execve.kp b/drivers/staging/ktap/samples/syscalls/execve.kp
new file mode 100644 (file)
index 0000000..e7019d6
--- /dev/null
@@ -0,0 +1,8 @@
+#!/usr/bin/env ktap
+
+#This script trace filename of process execution
+#only tested in x86-64
+
+trace probe:do_execve filename=%di {
+       printf("[do_execve entry]: (%s) name=%s\n", execname(), kernel_string(arg2))
+}
diff --git a/drivers/staging/ktap/samples/syscalls/opensnoop.kp b/drivers/staging/ktap/samples/syscalls/opensnoop.kp
new file mode 100644 (file)
index 0000000..7369ebe
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ktap -q
+#
+# opensnoop.kp trace open syscalls with pathnames and basic info
+#
+# 23-Nov-2013  Brendan Gregg   Created this
+
+path = {}
+
+printf("%5s %6s %-12s %3s %3s %s\n", "UID", "PID", "COMM", "FD", "ERR", "PATH");
+
+trace syscalls:sys_enter_open {
+       path[tid()] = user_string(arg2)
+}
+
+trace syscalls:sys_exit_open {
+       local fd
+       local errno
+
+       if (arg2 < 0) {
+               fd = 0
+               errno = -arg2
+       } else {
+               fd = arg2
+               errno = 0
+       }
+
+       printf("%5d %6d %-12s %3d %3d %s\n", uid(), pid(), execname(), fd,
+           errno, path[tid()])
+
+       path[tid()] = 0
+}
diff --git a/drivers/staging/ktap/samples/syscalls/sctop.kp b/drivers/staging/ktap/samples/syscalls/sctop.kp
new file mode 100644 (file)
index 0000000..a45b4cb
--- /dev/null
@@ -0,0 +1,13 @@
+#! /usr/bin/env ktap
+
+s = {}
+
+trace syscalls:sys_enter_* {
+       s[argname] += 1
+}
+
+tick-5s {
+       ansi.clear_screen()
+       histogram(s)
+       delete(s)
+}
diff --git a/drivers/staging/ktap/samples/syscalls/syscalls.kp b/drivers/staging/ktap/samples/syscalls/syscalls.kp
new file mode 100644 (file)
index 0000000..8bbaaca
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env ktap
+
+trace syscalls:* {
+       print(cpu(), pid(), execname(), argevent)
+}
+
diff --git a/drivers/staging/ktap/samples/syscalls/syscalls_count.kp b/drivers/staging/ktap/samples/syscalls/syscalls_count.kp
new file mode 100644 (file)
index 0000000..ed7d989
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/env ktap
+
+s = ptable()
+
+trace syscalls:sys_enter_* {
+       s[argname] <<< 1
+}
+
+trace_end {
+       histogram(s)
+}
+
+#Result:
+#
+#[root@jovi ktap]# ./ktap samples/syscalls_histogram.kp
+#^C
+#                          value ------------- Distribution ------------- count
+#        sys_enter_rt_sigprocmask |@@@@@@                                 326
+#                  sys_enter_read |@@@@@                                  287
+#                 sys_enter_close |@@@@                                   236
+#                  sys_enter_open |@@@@                                   222
+#                sys_enter_stat64 |@@                                     132
+#                sys_enter_select |@@                                     123
+#          sys_enter_rt_sigaction |@@                                     107
+#                  sys_enter_poll |@                                      72
+#                 sys_enter_write |@                                      70
+#            sys_enter_mmap_pgoff |@                                      58
+#               sys_enter_fstat64 |                                       41
+#             sys_enter_nanosleep |                                       23
+#                sys_enter_access |                                       20
+#              sys_enter_mprotect |                                       18
+#               sys_enter_geteuid |                                       17
+#               sys_enter_getegid |                                       16
+#                sys_enter_getuid |                                       16
+#                sys_enter_getgid |                                       16
+#                   sys_enter_brk |                                       15
+#               sys_enter_waitpid |                                       11
+#                  sys_enter_time |                                       10
+#                 sys_enter_ioctl |                                       9
+#                sys_enter_munmap |                                       9
+#               sys_enter_fcntl64 |                                       7
+#                  sys_enter_dup2 |                                       7
+#                 sys_enter_clone |                                       6
+#            sys_enter_exit_group |                                       6
+#                sys_enter_execve |                                       4
+#                  sys_enter_pipe |                                       3
+#          sys_enter_gettimeofday |                                       3
+#              sys_enter_getdents |                                       2
+#             sys_enter_getgroups |                                       2
+#              sys_enter_statfs64 |                                       2
+#                 sys_enter_lseek |                                       2
+#                sys_enter_openat |                                       1
+#              sys_enter_newuname |                                       1
+
diff --git a/drivers/staging/ktap/samples/syscalls/syscalls_count_by_proc.kp b/drivers/staging/ktap/samples/syscalls/syscalls_count_by_proc.kp
new file mode 100644 (file)
index 0000000..67d7bee
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env ktap
+
+s = ptable()
+
+trace syscalls:sys_enter_* {
+       s[execname()] <<< 1
+}
+
+trace_end {
+       histogram(s)
+}
+
+#Result:
+#
+#[root@jovi ktap]# ./ktap samples/syscalls_histogram2.kp
+#^C
+#                          value ------------- Distribution ------------- count
+#                            sshd |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@      196
+#                          iscsid |@@@@                                   24
+#                        sendmail |@                                      9
+
+
diff --git a/drivers/staging/ktap/samples/syscalls/syslatl.kp b/drivers/staging/ktap/samples/syscalls/syslatl.kp
new file mode 100644 (file)
index 0000000..5ba3219
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/env ktap
+#
+# syslatl.kp   syscall latency linear aggregation
+#
+# 10-Nov-2013  Brendan Gregg   Created this
+
+step = 10      # number of ms per step
+
+self = {}
+lats = {}
+max = 0
+
+trace syscalls:sys_enter_* {
+       self[tid()] = gettimeofday_us()
+}
+
+trace syscalls:sys_exit_* {
+       if (self[tid()] == nil) { return }
+       delta = (gettimeofday_us() - self[tid()]) / (step * 1000)
+       if (delta > max) { max = delta }
+       lats[delta] += 1
+       self[tid()] = nil
+}
+
+trace_end {
+       printf("   %8s %8s\n", "LAT(ms)+", "COUNT");
+       for (i = 0, max, 1) {
+               printf("   %8d %8d\n", i * step, lats[i]);
+       }
+}
diff --git a/drivers/staging/ktap/samples/syscalls/syslist.kp b/drivers/staging/ktap/samples/syscalls/syslist.kp
new file mode 100644 (file)
index 0000000..a4a3cb0
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/env ktap
+#
+# syslist.kp    syscall latency as a list with counts
+#
+# 10-Nov-2013   Brendan Gregg   Created this
+
+self = {}
+lats = {}
+order = {}  # a workaround for key sorting
+
+trace syscalls:sys_enter_* {
+    self[tid()] = gettimeofday_us()
+}
+
+trace syscalls:sys_exit_* {
+    if (self[tid()] == nil) { return }
+    delta = gettimeofday_us() - self[tid()]
+    lats[delta] += 1
+    order[delta] = delta
+    self[tid()] = nil
+}
+
+trace_end {
+    printf("   %8s %8s\n", "LAT(us)", "COUNT");
+    function cmp(v1, v2) {
+        return (v1 < v2)
+    }
+    for (lat, dummy in sort_pairs(order, cmp)) {
+        printf("   %8d %8d\n", lat, lats[lat]);
+    }
+}
diff --git a/drivers/staging/ktap/samples/tracepoints/eventcount.kp b/drivers/staging/ktap/samples/tracepoints/eventcount.kp
new file mode 100644 (file)
index 0000000..1bd37eb
--- /dev/null
@@ -0,0 +1,210 @@
+#!/usr/bin/env ktap
+
+# showing all tracepoints in histogram style
+
+s = ptable()
+
+trace *:* {
+       s[argname] <<< 1
+}
+
+trace_end {
+       histogram(s)
+}
+
+#Results:
+#^C
+#
+#                          value ------------- Distribution ------------- count
+#                 rcu_utilization |@@@@@                                  225289
+#                        cpu_idle |@@@                                    120168
+#                    sched_wakeup |@@                                     91950
+#                    timer_cancel |@@                                     91232
+#                     timer_start |@@                                     91201
+#                sched_stat_sleep |@@                                     90981
+#               timer_expire_exit |@@                                     90634
+#              timer_expire_entry |@@                                     90625
+#                  hrtimer_cancel |@                                      75411
+#                   hrtimer_start |@                                      74946
+#                   softirq_raise |@                                      63117
+#                    softirq_exit |@                                      63109
+#                   softirq_entry |@                                      63094
+#                    sched_switch |@                                      62331
+#                 sched_stat_wait |@                                      60491
+#             hrtimer_expire_exit |@                                      47538
+#            hrtimer_expire_entry |@                                      47530
+#              sched_stat_runtime |                                       2780
+#                 kmem_cache_free |                                       2684
+#                kmem_cache_alloc |                                       2415
+#                           kfree |                                       2288
+#                        sys_exit |                                       2145
+#                       sys_enter |                                       2145
+#         sys_exit_rt_sigprocmask |                                       1000
+#        sys_enter_rt_sigprocmask |                                       1000
+#                      timer_init |                                       912
+#              sched_stat_blocked |                                       685
+#                         kmalloc |                                       667
+#           workqueue_execute_end |                                       621
+#         workqueue_execute_start |                                       621
+#                sys_enter_select |                                       566
+#                 sys_exit_select |                                       566
+#                  sys_enter_read |                                       526
+#                   sys_exit_read |                                       526
+#                    mm_page_free |                                       478
+#                   mm_page_alloc |                                       427
+#            mm_page_free_batched |                                       382
+#                   net_dev_queue |                                       296
+#                    net_dev_xmit |                                       296
+#                     consume_skb |                                       296
+#                  sys_exit_write |                                       290
+#                 sys_enter_write |                                       290
+#                       kfree_skb |                                       289
+#           kmem_cache_alloc_node |                                       269
+#                    kmalloc_node |                                       263
+#                 sys_enter_close |                                       249
+#                  sys_exit_close |                                       249
+#                    hrtimer_init |                                       248
+#               netif_receive_skb |                                       242
+#                  sys_enter_open |                                       237
+#                   sys_exit_open |                                       237
+#                       napi_poll |                                       226
+#              sched_migrate_task |                                       207
+#                   sys_exit_poll |                                       173
+#                  sys_enter_poll |                                       173
+#            workqueue_queue_work |                                       152
+#         workqueue_activate_work |                                       152
+#                sys_enter_stat64 |                                       133
+#                 sys_exit_stat64 |                                       133
+#           sys_exit_rt_sigaction |                                       133
+#          sys_enter_rt_sigaction |                                       133
+#               irq_handler_entry |                                       125
+#                irq_handler_exit |                                       125
+#       mm_page_alloc_zone_locked |                                       99
+#             sys_exit_mmap_pgoff |                                       66
+#            sys_enter_mmap_pgoff |                                       66
+#                sys_exit_fstat64 |                                       54
+#               sys_enter_fstat64 |                                       54
+#             sys_enter_nanosleep |                                       51
+#              sys_exit_nanosleep |                                       51
+#                 block_bio_queue |                                       46
+#                 block_bio_remap |                                       46
+#              block_bio_complete |                                       46
+#                  mix_pool_bytes |                                       44
+#              mm_page_pcpu_drain |                                       31
+#                   sys_exit_time |                                       23
+#                  sys_enter_time |                                       23
+#                 sys_exit_access |                                       20
+#                sys_enter_access |                                       20
+#           mix_pool_bytes_nolock |                                       18
+#              sys_enter_mprotect |                                       18
+#               sys_exit_mprotect |                                       18
+#               sys_enter_geteuid |                                       17
+#                sys_exit_geteuid |                                       17
+#                sys_enter_munmap |                                       17
+#                 sys_exit_munmap |                                       17
+#                     block_getrq |                                       16
+#                sys_enter_getuid |                                       16
+#                sys_enter_getgid |                                       16
+#                 sys_exit_getgid |                                       16
+#                 sys_exit_getuid |                                       16
+#                  block_rq_issue |                                       16
+#         scsi_dispatch_cmd_start |                                       16
+#               block_rq_complete |                                       16
+#          scsi_dispatch_cmd_done |                                       16
+#               sys_enter_getegid |                                       16
+#                sys_exit_getegid |                                       16
+#                 block_rq_insert |                                       16
+#         skb_copy_datagram_iovec |                                       15
+#                   sys_enter_brk |                                       15
+#                    sys_exit_brk |                                       15
+#             credit_entropy_bits |                                       14
+#                   wbc_writepage |                                       14
+#                  sys_exit_clone |                                       12
+#              block_touch_buffer |                                       12
+#              sched_process_wait |                                       11
+#               sys_enter_waitpid |                                       11
+#                sys_exit_waitpid |                                       11
+#               writeback_written |                                       10
+#                 writeback_start |                                       10
+#              writeback_queue_io |                                       10
+#     ext4_es_lookup_extent_enter |                                       9
+#                 sys_enter_ioctl |                                       9
+#                  sys_exit_ioctl |                                       9
+#       ext4_ext_map_blocks_enter |                                       9
+#        ext4_ext_map_blocks_exit |                                       9
+#      ext4_es_lookup_extent_exit |                                       9
+#           ext4_es_insert_extent |                                       9
+#            ext4_ext_show_extent |                                       8
+#                 extract_entropy |                                       8
+#ext4_es_find_delayed_extent_exit |                                       8
+# ext4_es_find_delayed_extent_... |                                       8
+#         writeback_pages_written |                                       7
+#                   sys_exit_dup2 |                                       7
+#                  sys_enter_dup2 |                                       7
+#                 signal_generate |                                       7
+#               sys_enter_fcntl64 |                                       7
+#                sys_exit_fcntl64 |                                       7
+#              global_dirty_state |                                       7
+#     writeback_dirty_inode_start |                                       7
+#             block_bio_backmerge |                                       7
+#           writeback_dirty_inode |                                       7
+#                sched_wakeup_new |                                       6
+#              sched_process_free |                                       6
+#            sys_enter_exit_group |                                       6
+#                    task_newtask |                                       6
+#                 sys_enter_clone |                                       6
+#              sched_process_fork |                                       6
+#              sched_process_exit |                                       6
+#           sys_exit_gettimeofday |                                       5
+#                  signal_deliver |                                       5
+#          sys_enter_gettimeofday |                                       5
+#          writeback_single_inode |                                       4
+#                sys_enter_execve |                                       4
+#                     task_rename |                                       4
+#              sched_process_exec |                                       4
+#              block_dirty_buffer |                                       4
+#                 sys_exit_execve |                                       4
+#                    block_unplug |                                       4
+#               sched_stat_iowait |                                       4
+#    writeback_single_inode_start |                                       4
+#                      block_plug |                                       4
+#           writeback_write_inode |                                       3
+#                  sys_enter_pipe |                                       3
+#            writeback_dirty_page |                                       3
+#     writeback_write_inode_start |                                       3
+#           ext4_mark_inode_dirty |                                       3
+#              ext4_journal_start |                                       3
+#                   sys_exit_pipe |                                       3
+#           jbd2_drop_transaction |                                       2
+#             jbd2_commit_locking |                                       2
+#            jbd2_commit_flushing |                                       2
+#               jbd2_handle_start |                                       2
+#                  jbd2_run_stats |                                       2
+#               sys_exit_getdents |                                       2
+#           jbd2_checkpoint_stats |                                       2
+#             sys_enter_getgroups |                                       2
+#               jbd2_start_commit |                                       2
+#                 jbd2_end_commit |                                       2
+#              ext4_da_writepages |                                       2
+#               jbd2_handle_stats |                                       2
+#              sys_enter_statfs64 |                                       2
+#               sys_exit_statfs64 |                                       2
+#              sys_exit_getgroups |                                       2
+#                  sys_exit_lseek |                                       2
+#                 sys_enter_lseek |                                       2
+#              sys_enter_getdents |                                       2
+#             ext4_da_write_pages |                                       2
+#             jbd2_commit_logging |                                       2
+#             ext4_request_blocks |                                       1
+#                 sys_exit_openat |                                       1
+#     ext4_discard_preallocations |                                       1
+#              ext4_mballoc_alloc |                                       1
+#                sys_enter_openat |                                       1
+#       ext4_da_writepages_result |                                       1
+#            ext4_allocate_blocks |                                       1
+#              sys_enter_newuname |                                       1
+#    ext4_da_update_reserve_space |                                       1
+# ext4_get_reserved_cluster_alloc |                                       1
+#               sys_exit_newuname |                                       1
+#           writeback_wake_thread |                                       1
+
diff --git a/drivers/staging/ktap/samples/tracepoints/eventcount_by_proc.kp b/drivers/staging/ktap/samples/tracepoints/eventcount_by_proc.kp
new file mode 100644 (file)
index 0000000..5b9547e
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/env ktap
+
+# showing all tracepoints in histogram style
+
+s = ptable()
+
+trace *:* {
+       s[execname()] <<< 1
+}
+
+trace_end {
+       histogram(s)
+}
+
+#Results:
+#^C
+#                          value ------------- Distribution ------------- count
+#                       swapper/0 |@@@@@@@@@@@@                           354378
+#                       swapper/1 |@@@@@@@@@@                             284984
+#                              ps |@@@@                                   115697
+#                        ksmtuned |@@@                                    95857
+#                          iscsid |@@                                     80008
+#                             awk |@                                      30354
+#                      irqbalance |                                       16530
+#                       rcu_sched |                                       15892
+#                        sendmail |                                       14463
+#                     kworker/0:1 |                                       10540
+#                    kworker/u4:2 |                                       9250
+#                     kworker/1:2 |                                       7943
+#                           sleep |                                       7555
+#                           crond |                                       3911
+#                     ksoftirqd/0 |                                       3817
+#                            sshd |                                       2849
+#                 systemd-journal |                                       2209
+#                     migration/1 |                                       1601
+#                     migration/0 |                                       1350
+#                        dhclient |                                       1343
+#                 nm-dhcp-client. |                                       1208
+#                     ksoftirqd/1 |                                       1064
+#                      watchdog/1 |                                       966
+#                      watchdog/0 |                                       964
+#                      khugepaged |                                       776
+#                     dbus-daemon |                                       611
+#                         rpcbind |                                       607
+#                           gdbus |                                       529
+#                  NetworkManager |                                       399
+#                     jbd2/dm-1-8 |                                       378
+#                   modem-manager |                                       184
+#                  abrt-watch-log |                                       157
+#                         polkitd |                                       156
+#                   rs:main Q:Reg |                                       153
+#                    avahi-daemon |                                       151
+#                        rsyslogd |                                       102
+#                         systemd |                                       96
+#                    kworker/0:1H |                                       45
+#                          smartd |                                       30
+
diff --git a/drivers/staging/ktap/samples/tracepoints/tracepoints.kp b/drivers/staging/ktap/samples/tracepoints/tracepoints.kp
new file mode 100644 (file)
index 0000000..5d08869
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env ktap
+
+trace *:* {
+       print(cpu(), pid(), execname(), argevent)
+}
+
diff --git a/drivers/staging/ktap/samples/userspace/gcc_unwind.kp b/drivers/staging/ktap/samples/userspace/gcc_unwind.kp
new file mode 100644 (file)
index 0000000..0890872
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env ktap
+
+#only tested in x86-64 system,
+#if you run this script in x86_32, change the libc path.
+
+trace sdt:/lib/x86_64-linux-gnu/libgcc_s.so.1:unwind {
+       print(execname(), argevent)
+}
+
diff --git a/drivers/staging/ktap/samples/userspace/glibc_func_hist.kp b/drivers/staging/ktap/samples/userspace/glibc_func_hist.kp
new file mode 100644 (file)
index 0000000..5c30b3f
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/env ktap
+
+#This ktap script trace all glibc functions in histogram output
+
+#only tested in x86-64 system,
+#if you run this script in x86_32, change the libc path.
+
+s = {}
+
+trace probe:/lib64/libc.so.6:* {
+       s[argname] += 1
+}
+
+trace_end {
+       histogram(s)
+}
+
+# Example result:
+#[root@localhost ktap]# ./ktap ./glibc_func_hist.kp
+#Tracing... Ctrl-C to end.
+#^C
+#                          value ------------- Distribution ------------- count
+#                   _IO_sputbackc |                                       1536
+#                  __strncmp_sse2 |                                       1522
+#                    __GI_strncmp |                                       1522
+#                     __GI_memcpy |                                       1446
+#                   __memcpy_sse2 |                                       1446
+#        _dl_mcount_wrapper_check |                                       1433
+#   __GI__dl_mcount_wrapper_check |                                       1433
+# __gconv_transform_utf8_internal |                                       1429
+#                       __mbrtowc |                                       1425
+#                        mbrtoc32 |                                       1425
+#                  __GI___mbrtowc |                                       1425
+#                         mbrtowc |                                       1425
+#                    __GI_mbrtowc |                                       1425
+#                         strtouq |                                       1274
+#                        strtoull |                                       1274
+#                         strtoul |                                       1274
+#          __ctype_get_mb_cur_max |                                       984
+#         ____strtoull_l_internal |                                       970
+#     __GI_____strtoul_l_internal |                                       970
+#              __GI__IO_sputbackc |                                       960
+#                             ... |
+
diff --git a/drivers/staging/ktap/samples/userspace/glibc_sdt.kp b/drivers/staging/ktap/samples/userspace/glibc_sdt.kp
new file mode 100644 (file)
index 0000000..626151a
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env ktap
+
+#This ktap script trace all sdt notes in glibc
+
+#only tested in x86-64 system,
+#if you run this script in x86_32, change the libc path.
+
+trace sdt:/lib64/libc.so.6:* {
+       print(execname(), argevent)
+}
+
diff --git a/drivers/staging/ktap/samples/userspace/glibc_trace.kp b/drivers/staging/ktap/samples/userspace/glibc_trace.kp
new file mode 100644 (file)
index 0000000..ca03089
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env ktap
+
+#This ktap script trace all functions in glibc
+
+#only tested in x86-64 system,
+#if you run this script in x86_32, change the libc path.
+
+trace probe:/lib64/libc.so.6:* {
+       print(execname(), argevent)
+}
+
diff --git a/drivers/staging/ktap/samples/userspace/malloc_free.kp b/drivers/staging/ktap/samples/userspace/malloc_free.kp
new file mode 100644 (file)
index 0000000..7c7da25
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/env ktap
+
+#only tested in x86-64 system,
+#if you run this script in x86_32, change the libc path.
+
+trace probe:/lib64/libc.so.6:malloc {
+       print("malloc entry:", execname())
+}
+
+trace probe:/lib64/libc.so.6:malloc%return {
+       print("malloc exit:", execname())
+}
+
+trace probe:/lib64/libc.so.6:free {
+       print("free entry:", execname())
+}
+
+trace probe:/lib64/libc.so.6:free%return {
+       print("free exit:", execname())
+}
diff --git a/drivers/staging/ktap/samples/userspace/malloc_size_hist.kp b/drivers/staging/ktap/samples/userspace/malloc_size_hist.kp
new file mode 100644 (file)
index 0000000..9df89e6
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env ktap
+
+# Aggregate system or process malloc size
+
+# only tested in x86-64 system,
+# if you run this script in x86_32, change the libc path and register name.
+#
+# Examples:
+#
+# ktap malloc_size_hist.kp
+# ktap malloc_size_hist.kp -- ls
+
+m = {}
+
+trace probe:/lib64/libc.so.6:malloc size=%di {
+       #arg2 is argument "size" of malloc function
+       m[arg2] += 1
+}
+
+trace_end {
+       histogram(m)
+}
diff --git a/drivers/staging/ktap/test/arg.kp b/drivers/staging/ktap/test/arg.kp
new file mode 100644 (file)
index 0000000..0cf16eb
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#-----------------------------------------#
+
+if (!arg[0]) {
+       failed()
+}
+
+if (arg[1] != 1) {
+       failed()
+}
+
+if (arg[2] != "testing") {
+       failed()
+}
+
+if (arg[3] != "2 3 4") {
+       failed()
+}
diff --git a/drivers/staging/ktap/test/arithmetic.kp b/drivers/staging/ktap/test/arithmetic.kp
new file mode 100644 (file)
index 0000000..5063e50
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#-----------------------------------------#
+
+if (1 > 2) {
+       failed()
+}
+
+if (200 < 100) {
+       failed()
+}
+
+a = 4
+b = 5
+
+if ((a + b) != 9) {
+       failed()
+}
+
+if ((a - b) != -1) {
+       failed()
+}
+
+if ((a % b) != 4) {
+       failed()
+}
+
+if ((a / b) != 0) {
+       failed()
+}
+
+
+#below checking only valid for 64-bit system
+
+c = 0x1234567812345678
+d = 0x2
+
+if (c + d != 0x123456781234567a) {
+       failed()
+}
+
+if (-1 != 0xffffffffffffffff) {
+       failed()
+}
+
diff --git a/drivers/staging/ktap/test/benchmark/sembench.c b/drivers/staging/ktap/test/benchmark/sembench.c
new file mode 100644 (file)
index 0000000..0811934
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * copyright Oracle 2007.  Licensed under GPLv2
+ * To compile: gcc -Wall -o sembench sembench.c -lpthread
+ *
+ * usage: sembench -t thread count -w wakenum -r runtime -o op
+ * op can be: 0 (ipc sem) 1 (nanosleep) 2 (futexes)
+ *
+ * example:
+ *     sembench -t 1024 -w 512 -r 60 -o 2
+ * runs 1024 threads, waking up 512 at a time, running for 60 seconds using
+ * futex locking.
+ *
+ */
+#define  _GNU_SOURCE
+#define _POSIX_C_SOURCE 199309
+#include <fcntl.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/sem.h>
+#include <sys/ipc.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/syscall.h>
+#include <errno.h>
+
+#define VERSION "0.2"
+
+/* futexes have been around since 2.5.something, but it still seems I
+ * need to make my own syscall.  Sigh.
+ */
+#define FUTEX_WAIT              0
+#define FUTEX_WAKE              1
+#define FUTEX_FD                2
+#define FUTEX_REQUEUE           3
+#define FUTEX_CMP_REQUEUE       4
+#define FUTEX_WAKE_OP           5
+static inline int futex (int *uaddr, int op, int val,
+                        const struct timespec *timeout,
+                        int *uaddr2, int val3)
+{
+       return syscall(__NR_futex, uaddr, op, val, timeout, uaddr2, val3);
+}
+
+static void smp_mb(void)
+{
+       __sync_synchronize();
+}
+
+static int all_done = 0;
+static int timeout_test = 0;
+
+#define SEMS_PERID 250
+
+struct sem_operations;
+
+struct lockinfo {
+       unsigned long id;
+       unsigned long index;
+       int data;
+       pthread_t tid;
+       struct lockinfo *next;
+       struct sem_operations *ops;
+       unsigned long ready;
+};
+
+struct sem_wakeup_info {
+       int wakeup_count;
+       struct sembuf sb[SEMS_PERID];
+};
+
+struct sem_operations {
+       void (*wait)(struct lockinfo *l);
+       int (*wake)(struct sem_wakeup_info *wi, int num_semids, int num);
+       void (*setup)(struct sem_wakeup_info **wi, int num_semids);
+       void (*cleanup)(int num_semids);
+       char *name;
+};
+
+int *semid_lookup = NULL;
+
+pthread_mutex_t worklist_mutex = PTHREAD_MUTEX_INITIALIZER;
+static unsigned long total_burns = 0;
+static unsigned long min_burns = ~0UL;
+static unsigned long max_burns = 0;
+
+/* currently running threads */
+static int thread_count = 0;
+
+struct lockinfo *worklist = NULL;
+static int workers_started = 0;
+
+/* total threads started */
+static int num_threads = 2048;
+
+static void worklist_add(struct lockinfo *l)
+{
+       smp_mb();
+       l->ready = 1;
+}
+
+static struct lockinfo *worklist_rm(void)
+{
+       static int last_index = 0;
+       int i;
+       struct lockinfo *l;
+
+       for (i = 0; i < num_threads; i++) {
+               int test = (last_index + i) % num_threads;
+
+               l = worklist + test;
+               smp_mb();
+               if (l->ready) {
+                       l->ready = 0;
+                       last_index = test;
+                       return l;
+               }
+       }
+       return NULL;
+}
+
+/* ipc semaphore post& wait */
+void wait_ipc_sem(struct lockinfo *l)
+{
+       struct sembuf sb;
+       int ret;
+       struct timespec *tvp = NULL;
+       struct timespec tv = { 0, 1 };
+
+       sb.sem_num = l->index;
+       sb.sem_flg = 0;
+
+       sb.sem_op = -1;
+       l->data = 1;
+
+       if (timeout_test && (l->id % 5) == 0)
+               tvp = &tv;
+
+       worklist_add(l);
+       ret = semtimedop(semid_lookup[l->id], &sb, 1, tvp);
+
+       while(l->data != 0 && tvp) {
+               struct timespec tv2 = { 0, 500 };
+               nanosleep(&tv2, NULL);
+       }
+
+       if (l->data != 0) {
+               if (tvp)
+                       return;
+               fprintf(stderr, "wakeup without data update\n");
+               exit(1);
+       }
+       if (ret) {
+               if (errno == EAGAIN && tvp)
+                       return;
+               perror("semtimed op");
+               exit(1);
+       }
+}
+
+int ipc_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
+{
+       int i;
+       int ret;
+       struct lockinfo *l;
+       int found = 0;
+
+       for (i = 0; i < num_semids; i++) {
+               wi[i].wakeup_count = 0;
+       }
+       while(num > 0) {
+               struct sembuf *sb;
+               l = worklist_rm();
+               if (!l)
+                       break;
+               if (l->data != 1)
+                       fprintf(stderr, "warning, lockinfo data was %d\n",
+                               l->data);
+               l->data = 0;
+               sb = wi[l->id].sb + wi[l->id].wakeup_count;
+               sb->sem_num = l->index;
+               sb->sem_op = 1;
+               sb->sem_flg = IPC_NOWAIT;
+               wi[l->id].wakeup_count++;
+               found++;
+               num--;
+       }
+       if (!found)
+               return 0;
+       for (i = 0; i < num_semids; i++) {
+               int wakeup_total;
+               int cur;
+               int offset = 0;
+               if (!wi[i].wakeup_count)
+                       continue;
+               wakeup_total = wi[i].wakeup_count;
+               while(wakeup_total > 0) {
+                       cur = wakeup_total > 64 ? 64 : wakeup_total;
+                       ret = semtimedop(semid_lookup[i], wi[i].sb + offset,
+                                        cur, NULL);
+                       if (ret) {
+                               perror("semtimedop");
+                               exit(1);
+                       }
+                       offset += cur;
+                       wakeup_total -= cur;
+               }
+       }
+       return found;
+}
+
+void setup_ipc_sems(struct sem_wakeup_info **wi, int num_semids)
+{
+       int i;
+       *wi = malloc(sizeof(**wi) * num_semids);
+       semid_lookup = malloc(num_semids * sizeof(int));
+       for(i = 0; i < num_semids; i++) {
+               semid_lookup[i] = semget(IPC_PRIVATE, SEMS_PERID,
+                                        IPC_CREAT | 0777);
+               if (semid_lookup[i] < 0) {
+                       perror("semget");
+                       exit(1);
+               }
+       }
+       sleep(10);
+}
+
+void cleanup_ipc_sems(int num)
+{
+       int i;
+       for (i = 0; i < num; i++) {
+               semctl(semid_lookup[i], 0, IPC_RMID);
+       }
+}
+
+struct sem_operations ipc_sem_ops = {
+       .wait = wait_ipc_sem,
+       .wake = ipc_wake_some,
+       .setup = setup_ipc_sems,
+       .cleanup = cleanup_ipc_sems,
+       .name = "ipc sem operations",
+};
+
+/* futex post & wait */
+void wait_futex_sem(struct lockinfo *l)
+{
+       int ret;
+       l->data = 1;
+       worklist_add(l);
+       while(l->data == 1) {
+               ret = futex(&l->data, FUTEX_WAIT, 1, NULL, NULL, 0);
+               /*
+               if (ret && ret != EWOULDBLOCK) {
+                       perror("futex wait");
+                       exit(1);
+               }*/
+       }
+}
+
+int futex_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
+{
+       int i;
+       int ret;
+       struct lockinfo *l;
+       int found = 0;
+
+       for (i = 0; i < num; i++) {
+               l = worklist_rm();
+               if (!l)
+                       break;
+               if (l->data != 1)
+                       fprintf(stderr, "warning, lockinfo data was %d\n",
+                               l->data);
+               l->data = 0;
+               ret = futex(&l->data, FUTEX_WAKE, 1, NULL, NULL, 0);
+               if (ret < 0) {
+                       perror("futex wake");
+                       exit(1);
+               }
+               found++;
+       }
+       return found;
+}
+
+void setup_futex_sems(struct sem_wakeup_info **wi, int num_semids)
+{
+       return;
+}
+
+void cleanup_futex_sems(int num)
+{
+       return;
+}
+
+struct sem_operations futex_sem_ops = {
+       .wait = wait_futex_sem,
+       .wake = futex_wake_some,
+       .setup = setup_futex_sems,
+       .cleanup = cleanup_futex_sems,
+       .name = "futex sem operations",
+};
+
+/* nanosleep sems here */
+void wait_nanosleep_sem(struct lockinfo *l)
+{
+       int ret;
+       struct timespec tv = { 0, 1000000 };
+       int count = 0;
+
+       l->data = 1;
+       worklist_add(l);
+       while(l->data) {
+               ret = nanosleep(&tv, NULL);
+               if (ret) {
+                       perror("nanosleep");
+                       exit(1);
+               }
+               count++;
+       }
+}
+
+int nanosleep_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
+{
+       int i;
+       struct lockinfo *l;
+
+       for (i = 0; i < num; i++) {
+               l = worklist_rm();
+               if (!l)
+                       break;
+               if (l->data != 1)
+                       fprintf(stderr, "warning, lockinfo data was %d\n",
+                               l->data);
+               l->data = 0;
+       }
+       return i;
+}
+
+void setup_nanosleep_sems(struct sem_wakeup_info **wi, int num_semids)
+{
+       return;
+}
+
+void cleanup_nanosleep_sems(int num)
+{
+       return;
+}
+
+struct sem_operations nanosleep_sem_ops = {
+       .wait = wait_nanosleep_sem,
+       .wake = nanosleep_wake_some,
+       .setup = setup_nanosleep_sems,
+       .cleanup = cleanup_nanosleep_sems,
+       .name = "nano sleep sem operations",
+};
+
+void *worker(void *arg)
+{
+       struct lockinfo *l = (struct lockinfo *)arg;
+       int burn_count = 0;
+       pthread_t tid = pthread_self();
+       size_t pagesize = getpagesize();
+       char *buf = malloc(pagesize);
+
+       if (!buf) {
+               perror("malloc");
+               exit(1);
+       }
+
+       l->tid = tid;
+       workers_started = 1;
+       smp_mb();
+
+       while(!all_done) {
+               l->ops->wait(l);
+               if (all_done)
+                       break;
+               burn_count++;
+       }
+       pthread_mutex_lock(&worklist_mutex);
+       total_burns += burn_count;
+       if (burn_count < min_burns)
+               min_burns = burn_count;
+       if (burn_count > max_burns)
+               max_burns = burn_count;
+       thread_count--;
+       pthread_mutex_unlock(&worklist_mutex);
+       return (void *)0;
+}
+
+void print_usage(void)
+{
+       printf("usage: sembench [-t threads] [-w wake incr] [-r runtime]");
+       printf("                [-o num] (0=ipc, 1=nanosleep, 2=futex)\n");
+       exit(1);
+}
+
+#define NUM_OPERATIONS 3
+struct sem_operations *allops[NUM_OPERATIONS] = { &ipc_sem_ops,
+                                               &nanosleep_sem_ops,
+                                               &futex_sem_ops};
+
+int main(int ac, char **av) {
+       int ret;
+       int i;
+       int semid = 0;
+       int sem_num = 0;
+       int burn_count = 0;
+       struct sem_wakeup_info *wi = NULL;
+       struct timeval start;
+       struct timeval now;
+       int num_semids = 0;
+       int wake_num = 256;
+       int run_secs = 30;
+       int pagesize = getpagesize();
+       char *buf = malloc(pagesize);
+       struct sem_operations *ops = allops[0];
+       cpu_set_t cpu_mask;
+       cpu_set_t target_mask;
+       int target_cpu = 0;
+       int max_cpu = -1;
+
+       if (!buf) {
+               perror("malloc");
+               exit(1);
+       }
+       for (i = 1; i < ac; i++) {
+               if (strcmp(av[i], "-t") == 0) {
+                       if (i == ac -1)
+                               print_usage();
+                       num_threads = atoi(av[i+1]);
+                       i++;
+               } else if (strcmp(av[i], "-w") == 0) {
+                       if (i == ac -1)
+                               print_usage();
+                       wake_num = atoi(av[i+1]);
+                       i++;
+               } else if (strcmp(av[i], "-r") == 0) {
+                       if (i == ac -1)
+                               print_usage();
+                       run_secs = atoi(av[i+1]);
+                       i++;
+               } else if (strcmp(av[i], "-o") == 0) {
+                       int index;
+                       if (i == ac -1)
+                               print_usage();
+                       index = atoi(av[i+1]);
+                       if (index >= NUM_OPERATIONS) {
+                               fprintf(stderr, "invalid operations %d\n",
+                                       index);
+                               exit(1);
+                       }
+                       ops = allops[index];
+                       i++;
+               } else if (strcmp(av[i], "-T") == 0) {
+                       timeout_test = 1;
+               } else if (strcmp(av[i], "-h") == 0) {
+                       print_usage();
+               }
+       }
+       num_semids = (num_threads + SEMS_PERID - 1) / SEMS_PERID;
+       ops->setup(&wi, num_semids);
+
+       ret = sched_getaffinity(0, sizeof(cpu_set_t), &cpu_mask);
+       if (ret) {
+               perror("sched_getaffinity");
+               exit(1);
+       }
+       for (i = 0; i < CPU_SETSIZE; i++)
+               if (CPU_ISSET(i, &cpu_mask))
+                       max_cpu = i;
+       if (max_cpu == -1) {
+               fprintf(stderr, "sched_getaffinity returned empty mask\n");
+               exit(1);
+       }
+
+       CPU_ZERO(&target_mask);
+
+       worklist = malloc(sizeof(*worklist) * num_threads);
+       memset(worklist, 0, sizeof(*worklist) * num_threads);
+
+       for (i = 0; i < num_threads; i++) {
+               struct lockinfo *l;
+               pthread_t tid;
+               thread_count++;
+               l = worklist + i;
+               if (!l) {
+                       perror("malloc");
+                       exit(1);
+               }
+               l->id = semid;
+               l->index = sem_num++;
+               l->ops = ops;
+               if (sem_num >= SEMS_PERID) {
+                       semid++;
+                       sem_num = 0;
+               }
+               ret = pthread_create(&tid, NULL, worker, (void *)l);
+               if (ret) {
+                       perror("pthread_create");
+                       exit(1);
+               }
+
+               while (!CPU_ISSET(target_cpu, &cpu_mask)) {
+                       target_cpu++;
+                       if (target_cpu > max_cpu)
+                               target_cpu = 0;
+               }
+               CPU_SET(target_cpu, &target_mask);
+               ret = pthread_setaffinity_np(tid, sizeof(cpu_set_t),
+                                            &target_mask);
+               CPU_CLR(target_cpu, &target_mask);
+               target_cpu++;
+
+               ret = pthread_detach(tid);
+               if (ret) {
+                       perror("pthread_detach");
+                       exit(1);
+               }
+       }
+       while(!workers_started) {
+               smp_mb();
+               usleep(200);
+       }
+       gettimeofday(&start, NULL);
+       fprintf(stderr, "main loop going\n");
+       while(1) {
+               ops->wake(wi, num_semids, wake_num);
+               burn_count++;
+               gettimeofday(&now, NULL);
+               if (now.tv_sec - start.tv_sec >= run_secs)
+                       break;
+       }
+       fprintf(stderr, "all done\n");
+       all_done = 1;
+       while(thread_count > 0) {
+               ops->wake(wi, num_semids, wake_num);
+               usleep(200);
+       }
+       printf("%d threads, waking %d at a time\n", num_threads, wake_num);
+       printf("using %s\n", ops->name);
+       printf("main thread burns: %d\n", burn_count);
+       printf("worker burn count total %lu min %lu max %lu avg %lu\n",
+              total_burns, min_burns, max_burns, total_burns / num_threads);
+       printf("run time %d seconds %lu worker burns per second\n",
+               (int)(now.tv_sec - start.tv_sec),
+               total_burns / (now.tv_sec - start.tv_sec));
+       ops->cleanup(num_semids);
+       return 0;
+}
+
diff --git a/drivers/staging/ktap/test/benchmark/test.sh b/drivers/staging/ktap/test/benchmark/test.sh
new file mode 100644 (file)
index 0000000..f29a882
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+gcc -o sembench sembench.c -O2 -lpthread
+
+COMMAND="./sembench -t 200 -w 20 -r 30 -o 2"
+
+echo -e "\n\t\tPass 1 without tracing"
+$COMMAND
+echo -e "\n\t\tPass 2 without tracing"
+$COMMAND
+echo -e "\n\t\tPass 3 without tracing"
+$COMMAND
+
+echo ""
+
+../../ktap -e 'trace syscalls:sys_*_futex {}' &
+
+echo -e "\n\t\tPass 1 with tracing"
+$COMMAND
+echo -e "\n\t\tPass 2 with tracing"
+$COMMAND
+echo -e "\n\t\tPass 3 with tracing"
+$COMMAND
+
+pkill ktap
+rm -rf ./sembench
diff --git a/drivers/staging/ktap/test/concat.kp b/drivers/staging/ktap/test/concat.kp
new file mode 100644 (file)
index 0000000..be77bb7
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#----------------------------------------#
+
+a = "123"
+b = "456"
+
+if (a..b != "123456") {
+       failed()
+}
diff --git a/drivers/staging/ktap/test/count.kp b/drivers/staging/ktap/test/count.kp
new file mode 100644 (file)
index 0000000..26f962c
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#---------------------------------------#
+
+t = {}
+
+t["key"] += 1
+if (t["key"] != 1) {
+       failed()
+}
+
+t["key"] += 1
+if (t["key"] != 2) {
+       failed()
+}
diff --git a/drivers/staging/ktap/test/ffi/.gitignore b/drivers/staging/ktap/test/ffi/.gitignore
new file mode 100644 (file)
index 0000000..3fdfdc1
--- /dev/null
@@ -0,0 +1,2 @@
+cparser_test
+Module.symvers
diff --git a/drivers/staging/ktap/test/ffi/Makefile b/drivers/staging/ktap/test/ffi/Makefile
new file mode 100644 (file)
index 0000000..fd7016f
--- /dev/null
@@ -0,0 +1,46 @@
+obj-m += ktap_ffi_test.o
+
+all: funct_mod cparser_test
+
+funct_mod:
+       make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+
+INC=../../include
+U_DIR=../../userspace
+RUNTIME=../../runtime
+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)/*
+       $(QUIET_CC)$(CC) -DCONFIG_KTAP_FFI -o $@ -c $<
+
+ctype.o: $(U_FFI_DIR)/ctype.c $(INC)/*
+       $(QUIET_CC)$(CC) -DCONFIG_KTAP_FFI -o $@ -c $<
+
+ffi_type.o: $(RUNTIME)/ffi/ffi_type.c $(INC)/*
+       $(QUIET_CC)$(CC) -DCONFIG_KTAP_FFI -o $@ -c $<
+
+cparser_test: cparser_test.c $(CPARSER_FILES) $(INC)/*
+       $(QUIET_CC)$(CC) -DCONFIG_KTAP_FFI -I$(INC) -I$(U_DIR) $(KTAPC_CFLAGS) \
+               -o $@ $< $(CPARSER_FILES)
+
+load:
+       insmod ktap_ffi_test.ko
+
+unload:
+       rmmod ktap_ffi_test
+
+test: all
+       @echo "testing cparser:"
+       ./cparser_test
+       @echo "testing ffi module:"
+       rmmod ktap_ffi_test > /dev/null 2>&1 || true
+       insmod ktap_ffi_test.ko
+       ../../ktap ffi_test.kp
+       rmmod ktap_ffi_test.ko
+       @echo "[*] all ffi tests passed."
+
+clean:
+       make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
+       rm -rf cparser_test
diff --git a/drivers/staging/ktap/test/ffi/cparser_test.c b/drivers/staging/ktap/test/ffi/cparser_test.c
new file mode 100644 (file)
index 0000000..21a7814
--- /dev/null
@@ -0,0 +1,322 @@
+#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(const 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 {                                     \
+       ffi_cparser_init();                                     \
+       int ret;                                                \
+       printf("[*] start "#name" test...  ");                  \
+       ret = test_##name();                                    \
+       if (ret)                                                \
+               fprintf(stderr, "\n[!] "#name" test failed.\n");\
+       else                                                    \
+               printf(" passed.\n");                           \
+       ffi_cparser_free();                                     \
+} 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_fret_type(fcs, t) do {                          \
+       csymbol *ncs;                                           \
+       ncs = &cs_arr[fcs->ret_id];                             \
+       assert(ncs->type == t);                                 \
+} while (0)
+
+#define assert_farg_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 */
+       assert_fret_type(fcs, 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 */
+       assert_fret_type(fcs, 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 */
+       assert_fret_type(fcs, FFI_INT32);
+
+       /* check arguments */
+       assert(fcs->arg_nr == 4);
+       assert_farg_type(fcs, 0, FFI_UINT8);
+       assert_farg_type(fcs, 1, FFI_INT8);
+       assert_farg_type(fcs, 2, FFI_UINT16);
+       assert_farg_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 */
+       assert_fret_type(fcs, FFI_INT64);
+
+       /* check arguments */
+       assert(fcs->arg_nr == 7);
+       assert_farg_type(fcs, 0, FFI_UINT32);
+       assert_farg_type(fcs, 1, FFI_INT32);
+       assert_farg_type(fcs, 2, FFI_UINT64);
+       assert_farg_type(fcs, 3, FFI_INT64);
+       assert_farg_type(fcs, 4, FFI_UINT64);
+       assert_farg_type(fcs, 5, FFI_INT64);
+       assert_farg_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 */
+       assert_fret_type(fcs, FFI_PTR);
+
+       /* check arguments */
+       assert(fcs->arg_nr == 1);
+       assert_farg_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_farg_type(fcs, 0, FFI_INT64);
+
+       /* check second argument */
+       assert_farg_type(fcs, 1, FFI_INT32);
+       /* check third argument */
+       assert_farg_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 test_var_arg_function()
+{
+       csymbol_func *fcs;
+
+       ffi_cdef("int printk(char *fmt, ...);");
+
+       fcs = csym_func(cp_id_to_csym(lookup_csymbol_id_by_name("printk")));
+
+       /* var arg function needs void * type argument type checking */
+       assert(lookup_csymbol_id_by_name("void *") >= 0);
+
+       assert_fret_type(fcs, FFI_INT32);
+       assert_farg_type(fcs, 0, FFI_PTR);
+       assert(fcs->has_var_arg);
+
+       return 0;
+}
+
+int main (int argc, char *argv[])
+{
+       DO_TEST(func_sched_clock);
+       DO_TEST(func_funct_module);
+       DO_TEST(struct_timespec);
+       DO_TEST(func_time_to_tm);
+       DO_TEST(pointer_symbols);
+       DO_TEST(var_arg_function);
+
+       return 0;
+}
diff --git a/drivers/staging/ktap/test/ffi/ffi_test.kp b/drivers/staging/ktap/test/ffi/ffi_test.kp
new file mode 100644 (file)
index 0000000..20a9a5e
--- /dev/null
@@ -0,0 +1,47 @@
+function failed(msg) {
+       printf("failed: " .. msg);
+       printf("\n")
+       exit(-1);
+}
+
+
+cdef[[
+       void ffi_test_void();
+       int ffi_test_int1(unsigned char a, char b, unsigned short c, short d);
+       long long ffi_test_int2(unsigned int a, int b, unsigned long c, long d,
+                               unsigned long long e, long long f, long long g);
+       void *ffi_test_pointer1(char *a);
+       long long ffi_test_var_arg(int n, ...);
+       unsigned long long ffi_test_sched_clock(void);
+]]
+
+
+ret = C.ffi_test_void()
+if (ret != nil) {
+       failed("ffi_test_void should return nil")
+}
+
+ret = C.ffi_test_int1(1111, 1111, 1111, 1111)
+if (ret != 2396) {
+       failed("ffi_test_int1(1111, 1111, 1111, 1111) should return 2396")
+}
+
+ret = C.ffi_test_int2(90, 7800, 560000, 34000000, 1200000000, 900000000000, 78000000000000)
+if (ret != 78901234567890) {
+       failed("ffi_test_int2 should return 78901234567890")
+}
+
+ret = C.ffi_test_pointer1("")
+if (ret == nil) {
+       failed("ffi_test_pointer1 shoudl return address around 0xffff8800--------")
+}
+
+ret = C.ffi_test_var_arg(7, 90, 7800, 560000, 34000000, 1200000000, 900000000000, 78000000000000)
+if (ret != 78901234567890) {
+       failed("ffi_test_var_arg should return 78901234567890")
+}
+
+ret = C.ffi_test_sched_clock()
+if (ret == nil) {
+       failed("ffi_test_clock should not return nil")
+}
diff --git a/drivers/staging/ktap/test/ffi/ktap_ffi_test.c b/drivers/staging/ktap/test/ffi/ktap_ffi_test.c
new file mode 100644 (file)
index 0000000..9434244
--- /dev/null
@@ -0,0 +1,64 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+
+void ffi_test_void(void)
+{
+}
+EXPORT_SYMBOL(ffi_test_void);
+
+int ffi_test_int1(unsigned char a, char b, unsigned short c, short d)
+{
+       return a + b + c + d;
+}
+EXPORT_SYMBOL(ffi_test_int1);
+
+long long ffi_test_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(ffi_test_int2);
+
+void *ffi_test_pointer1(char *a) {
+       return a;
+}
+EXPORT_SYMBOL(ffi_test_pointer1);
+
+long long ffi_test_var_arg(int n, ...) {
+       va_list ap;
+       int i;
+       long long sum = 0;
+       va_start(ap, n);
+       for (i = 0; i < n; i++) {
+               sum += va_arg(ap, long long);
+       }
+       va_end(ap);
+       return sum;
+}
+EXPORT_SYMBOL(ffi_test_var_arg);
+
+unsigned long long ffi_test_sched_clock(void)
+{
+       return sched_clock();
+}
+EXPORT_SYMBOL(ffi_test_sched_clock);
+
+
+
+static int __init ffi_test_init(void)
+{
+       return 0;
+}
+
+static void __exit ffi_test_exit(void)
+{
+}
+
+
+MODULE_DESCRIPTION("ktap ffi test module");
+MODULE_LICENSE("GPL");
+
+module_init(ffi_test_init);
+module_exit(ffi_test_exit);
diff --git a/drivers/staging/ktap/test/fibonacci.kp b/drivers/staging/ktap/test/fibonacci.kp
new file mode 100644 (file)
index 0000000..7e141da
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#---------------fibonacci----------------
+
+
+#regular recursive fibonacci
+function fib(n) {
+       if (n < 2) {
+               return n
+       }
+       return fib(n-1) + fib(n-2)
+}
+
+if (fib(20) != 6765) {
+       failed()
+}
+
+#tail recursive fibonacci
+function fib(n) {
+       f = function (iter, res, next) {
+               if (iter == 0) {
+                       return res;
+               }
+               return f(iter-1, next, res+next)
+       }
+       return f(n, 0, 1)
+}
+
+if (fib(20) != 6765) {
+       failed()
+}
diff --git a/drivers/staging/ktap/test/function.kp b/drivers/staging/ktap/test/function.kp
new file mode 100644 (file)
index 0000000..b808b91
--- /dev/null
@@ -0,0 +1,88 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+### basic function call ###
+function f1(a, b) {
+       return a + b
+}
+
+if (f1(2, 3) != 5) {
+       failed();
+}
+
+### return string ###
+function f2() {
+       return "function return"
+}
+
+if (f2() != "function return") {
+       failed();
+}
+
+### mutli-value return ###
+function f3(a, b) {
+       return a+b, a-b;
+}
+
+c, d = f3(2, 3);
+if(c != 5 || d != -1) {
+       failed();
+}
+
+
+### closure testing ###
+function f4() {
+       f5 = function(a, b) {
+               return a * b
+       }
+       return f5
+}
+
+local f = f4()
+if (f(9, 9) != 81) {
+       failed();
+}
+
+### closure with lexcial variable ###
+# issue: variable cannot be local
+i = 1
+function f6() {
+       i = 5
+       f7 = function(a, b) {
+               return a * b + i
+       }
+       return f7
+}
+
+f = f6()
+if (f(9, 9) != 81 + i) {
+       failed();
+}
+
+i = 6
+if (f(9, 9) != 81 + i) {
+       failed();
+}
+
+### tail call
+### stack should not overflow in tail call mechanism
+a = 0
+function f8(i) {
+       if (i == 1000000) {
+               a = 1000000
+               return
+       }
+       # must add return here, otherwise stack overflow
+       return f8(i+1)
+}
+
+f8(0)
+if (a != 1000000) {
+       failed();
+}
+
+
diff --git a/drivers/staging/ktap/test/if.kp b/drivers/staging/ktap/test/if.kp
new file mode 100644 (file)
index 0000000..3122084
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#-----------------------------------------#
+
+if (false) {
+       failed()
+}
+
+if (nil) {
+       failed()
+}
+
+# ktap only think false and nil is "real false", number 0 is true
+# it's same as lua
+# Might change it in future, to make similar with C
+if (0) {
+       #failed()
+}
+
diff --git a/drivers/staging/ktap/test/kprobe.kp b/drivers/staging/ktap/test/kprobe.kp
new file mode 100644 (file)
index 0000000..483bccd
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/env ktap
+
+n = 0
+trace probe:schedule {
+       n = n + 1
+}
+
+# share same event id with previous one
+trace probe:schedule {
+}
+
+
+tick-1s {
+       if (n == 0) {
+               printf("failed\n");
+       }
+       exit()
+}
+
diff --git a/drivers/staging/ktap/test/kretprobe.kp b/drivers/staging/ktap/test/kretprobe.kp
new file mode 100644 (file)
index 0000000..e311e84
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env ktap
+
+n = 0
+trace probe:__schedule%return {
+       n = n + 1
+}
+
+tick-1s {
+       if (n == 0) {
+               printf("failed\n");
+       }
+       exit()
+}
+
diff --git a/drivers/staging/ktap/test/ksym.kp b/drivers/staging/ktap/test/ksym.kp
new file mode 100644 (file)
index 0000000..736acfe
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#-----------------------------------------#
+
+a = `generic_file_buffered_write`
+b = `generic_file_mmap`
+
+printf("generic_file_buffered_write: 0x%x\n", a);
+printf("generic_file_mmap: 0x%x\n", b);
+
+# test read symbol in kernel module
+printf("kp_call: 0x%x\n", `kp_call`)
diff --git a/drivers/staging/ktap/test/len.kp b/drivers/staging/ktap/test/len.kp
new file mode 100644 (file)
index 0000000..697d915
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#-----------------------------------------#
+
+a = "123456789"
+
+if (len(a) != 9) {
+       failed()
+}
+
+b = {}
+b[0] = 0
+b[1] = 1
+b["keys"] = "values"
+
+if (len(b) != 3) {
+       failed()
+}
+
+
diff --git a/drivers/staging/ktap/test/looping.kp b/drivers/staging/ktap/test/looping.kp
new file mode 100644 (file)
index 0000000..fe48051
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+### basic while-loop testing
+a = 1
+while (a < 1000) {
+       a = a + 1
+}
+
+if (a != 1000) {
+       failed()
+}
+
+### break testing
+### Note that ktap don't have continue keyword
+a = 1
+while (a < 1000) {
+       if (a == 10) {
+               break
+       }
+       a = a + 1
+}
+
+if (a != 10) {
+       failed()
+}
+
+### for-loop testing
+b=0
+for (c = 0, 1000, 1) {
+       b = b + 1
+}
+
+if (b != 1001) {
+       failed()
+}
diff --git a/drivers/staging/ktap/test/pairs.kp b/drivers/staging/ktap/test/pairs.kp
new file mode 100644 (file)
index 0000000..c062f16
--- /dev/null
@@ -0,0 +1,84 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#-----------------------------------------#
+
+t = {}
+t[1] = 101
+t[2] = 102
+t[3] = 103
+t["key_1"] = "value_1"
+t["key_2"] = "value_2"
+t["key_3"] = "value_3"
+
+local n = 0
+
+for (k, v in pairs(t)) {
+       n = n + 1
+
+       if (k == 1 && v != 101) {
+               failed()
+       }
+       if (k == 2 && v != 102) {
+               failed()
+       }
+       if (k == 3 && v != 103) {
+               failed()
+       }
+       if (k == "key_1" && v != "value_1") {
+               failed()
+       }
+       if (k == "key_2" && v != "value_2") {
+               failed()
+       }
+       if (k == "key_3" && v != "value_3") {
+               failed()
+       }
+}
+
+if (n != len(t)) {
+       failed()
+}
+
+
+#-------------------------------------------------#
+
+s = {}
+s[1] = 12
+s[2] = 2
+s[3] = 3
+s["124"] = 100
+s["125"] = -1
+
+ordered = {}
+
+number = 0
+
+function cmp(v1, v2) {
+       return (v1 > v2)
+}
+
+for (k, v in sort_pairs(s, cmp)) {
+       number += 1
+       ordered[number] = v
+}
+
+if (ordered[1] != 100) {
+       failed()
+}
+if (ordered[2] != 12) {
+       failed()
+}
+if (ordered[3] != 3) {
+       failed()
+}
+if (ordered[4] != 2) {
+       failed()
+}
+if (ordered[5] != -1) {
+       failed()
+}
diff --git a/drivers/staging/ktap/test/ptable.kp b/drivers/staging/ktap/test/ptable.kp
new file mode 100644 (file)
index 0000000..0f394cf
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#---------------------------------#
+
+s = ptable()
+
+for (i = 1, 100, 1) {
+       s["k"] <<< i
+}
+
+if (count(s["k"]) != 100) {
+       failed()
+}
+if (sum(s["k"]) != 5050) {
+       failed()
+}
+if (max(s["k"]) != 100) {
+       failed()
+}
+if (min(s["k"]) != 1) {
+       failed()
+}
+
+for (i = 1, 10000, 1) {
+       s[i] <<< i
+}
+
+if (min(s[1]) != 1) {
+       failed()
+}
+
+if (sum(s[10]) != 10) {
+       failed()
+}
+
+if (max(s[100]) != 100) {
+       failed()
+}
+
+
+
diff --git a/drivers/staging/ktap/test/run_test.sh b/drivers/staging/ktap/test/run_test.sh
new file mode 100644 (file)
index 0000000..d33105e
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+rmmod ktapvm > /dev/null 2>&1
+insmod ../ktapvm.ko
+if test $? -ne 0; then
+       echo "Cannot insmod ../ktapvm.ko"
+       exit -1
+fi
+
+KTAP=../ktap
+ktaprun() {
+       echo "$KTAP $@"
+       $KTAP $@
+}
+
+
+
+#######################################################
+# Use $ktap directly if the arguments contains strings
+$KTAP arg.kp 1 testing "2 3 4"
+$KTAP -e 'print("one-liner testing")'
+$KTAP -e 'exit()'
+$KTAP -o /dev/null -e 'trace syscalls:* { print(argevent) }' \
+               -- ls > /dev/null
+
+$KTAP -o /dev/null -e 'trace syscalls:* { print(argevent) }' \
+               -- $KTAP -e 'print("trace ktap by self")'
+
+ktaprun arithmetic.kp
+ktaprun -o /dev/null stack_overflow.kp
+ktaprun concat.kp
+ktaprun count.kp
+ktaprun fibonacci.kp
+ktaprun function.kp
+ktaprun if.kp
+ktaprun -q kprobe.kp
+ktaprun -q kretprobe.kp
+ktaprun len.kp
+ktaprun looping.kp
+ktaprun pairs.kp
+ktaprun table.kp
+ktaprun ptable.kp
+ktaprun -q timer.kp
+ktaprun -q tracepoint.kp
+ktaprun -o /dev/null zerodivide.kp
+ktaprun -o /dev/null ksym.kp
+
+echo "testing kill deadloop ktap script"
+$KTAP -e 'while (1) {}' &
+sleep 1
+pkill ktap
+sleep 1
+
+cd ffi && make --quiet --no-print-directory test && cd -
+
+#####################################################
+rmmod ktapvm
+if test $? -ne 0; then
+       echo "Error in rmmod ../ktapvm.ko, leak module refcount?"
+       exit -1
+fi
+
diff --git a/drivers/staging/ktap/test/stack_overflow.kp b/drivers/staging/ktap/test/stack_overflow.kp
new file mode 100644 (file)
index 0000000..ce33e62
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env ktap
+
+#this script check overflow in ktap
+
+function f(a) {
+       return 1 + f(a+1)
+}
+
+print(f(0))
diff --git a/drivers/staging/ktap/test/table.kp b/drivers/staging/ktap/test/table.kp
new file mode 100644 (file)
index 0000000..fc96b73
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+### table testing ###
+x = {}
+x[1] = "1"
+if (x[1] != "1") {
+       failed()
+}
+
+x[1] = 22222222222222222222222222222222222222222
+if (x[1] != 22222222222222222222222222222222222222222) {
+       failed()
+}
+
+x[1] = "jovi"
+if (x[1] != "jovi") {
+       failed()
+}
+
+x[11111111111111111111111111111111] = "jovi"
+if (x[11111111111111111111111111111111] != "jovi") {
+       failed()
+}
+
+x["jovi"] = 1
+if (x["jovi"] != 1) {
+       failed()
+}
+
+x["long string....................................."] = 1
+if (x["long string....................................."] != 1) {
+       failed()
+}
+
+# issue: subx must declare firstly, otherwise kernel will oops
+subx = {}
+subx["test"] = "this is test"
+x["test"] = subx
+if (x["test"]["test"] != "this is test") {
+       failed()
+}
+
+tbl = {}
+i = 1
+while (i < 100000) {
+       tbl[i] = i
+       i = i + 1
+}
+
+i = 1
+while (i < 100000) {
+       if (tbl[i] != i) {
+               failed()
+       }
+       i = i + 1
+}
+
+#### table initization
+days = {"Sunday", "Monday", "Tuesday", "Wednesday",
+       "Thursday", "Friday", "Saturday"}
+
+if (days[2] != "Monday") {
+       failed()
+}
+
+
diff --git a/drivers/staging/ktap/test/timer.kp b/drivers/staging/ktap/test/timer.kp
new file mode 100644 (file)
index 0000000..02e8a61
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#---------------------------------------#
+
+n1 = 0
+n2 = 0
+
+tick-1s {
+       n1 = n1 + 1
+}
+
+tick-1s {
+       n2 = n2 + 1
+}
+
+tick-4s {
+       if (n1 == 0 || n2 == 0) {
+               failed()
+       }
+       exit()
+}
+
+
diff --git a/drivers/staging/ktap/test/tracepoint.kp b/drivers/staging/ktap/test/tracepoint.kp
new file mode 100644 (file)
index 0000000..fb036e6
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env ktap
+
+function failed() {
+       printf("failed\n");
+       exit(-1);
+}
+
+#----------------------------------------#
+
+n = 0
+
+trace sched:* {
+       n = n + 1
+}
+
+tick-1s {
+       if (n == 0) {
+               failed()
+       }
+       exit()
+}
+
diff --git a/drivers/staging/ktap/test/zerodivide.kp b/drivers/staging/ktap/test/zerodivide.kp
new file mode 100644 (file)
index 0000000..abb1eae
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/env ktap
+
+a = 1/0
+#should not go here
+printf("Failed\n")
diff --git a/drivers/staging/ktap/userspace/code.c b/drivers/staging/ktap/userspace/code.c
new file mode 100644 (file)
index 0000000..e7317f6
--- /dev/null
@@ -0,0 +1,998 @@
+/*
+ * code.c - Code generator for ktap
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+ *  - The part of code in this file is copied from lua initially.
+ *  - lua's MIT license is compatible with GPL.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/ktap_types.h"
+#include "../include/ktap_opcodes.h"
+#include "ktapc.h"
+#include "../runtime/kp_obj.h"
+
+
+#define hasjumps(e)    ((e)->t != (e)->f)
+
+void codegen_patchtohere (ktap_funcstate *fs, int list);
+
+static int isnumeral(ktap_expdesc *e)
+{
+       return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP);
+}
+
+void codegen_nil(ktap_funcstate *fs, int from, int n)
+{
+       ktap_instruction *previous;
+       int l = from + n - 1;  /* last register to set nil */
+
+       if (fs->pc > fs->lasttarget) {  /* no jumps to current position? */
+               previous = &fs->f->code[fs->pc-1];
+               if (GET_OPCODE(*previous) == OP_LOADNIL) {
+                       int pfrom = GETARG_A(*previous);
+                       int pl = pfrom + GETARG_B(*previous);
+
+                       if ((pfrom <= from && from <= pl + 1) ||
+                               (from <= pfrom && pfrom <= l + 1)) {  /* can connect both? */
+                               if (pfrom < from)
+                                       from = pfrom;  /* from = min(from, pfrom) */
+                               if (pl > l)
+                                       l = pl;  /* l = max(l, pl) */
+                               SETARG_A(*previous, from);
+                               SETARG_B(*previous, l - from);
+                               return;
+                       }
+               }  /* else go through */
+       }
+       codegen_codeABC(fs, OP_LOADNIL, from, n - 1, 0);  /* else no optimization */
+}
+
+int codegen_jump(ktap_funcstate *fs)
+{
+       int jpc = fs->jpc;  /* save list of jumps to here */
+       int j;
+
+       fs->jpc = NO_JUMP;
+       j = codegen_codeAsBx(fs, OP_JMP, 0, NO_JUMP);
+       codegen_concat(fs, &j, jpc);  /* keep them on hold */
+       return j;
+}
+
+void codegen_ret(ktap_funcstate *fs, int first, int nret)
+{
+       codegen_codeABC(fs, OP_RETURN, first, nret+1, 0);
+}
+
+static int condjump(ktap_funcstate *fs, OpCode op, int A, int B, int C)
+{
+       codegen_codeABC(fs, op, A, B, C);
+       return codegen_jump(fs);
+}
+
+static void fixjump(ktap_funcstate *fs, int pc, int dest)
+{
+       ktap_instruction *jmp = &fs->f->code[pc];
+       int offset = dest-(pc+1);
+
+       ktap_assert(dest != NO_JUMP);
+       if (abs(offset) > MAXARG_sBx)
+               lex_syntaxerror(fs->ls, "control structure too long");
+       SETARG_sBx(*jmp, offset);
+}
+
+/*
+ * returns current `pc' and marks it as a jump target (to avoid wrong
+ * optimizations with consecutive instructions not in the same basic block).
+ */
+int codegen_getlabel(ktap_funcstate *fs)
+{
+       fs->lasttarget = fs->pc;
+       return fs->pc;
+}
+
+static int getjump(ktap_funcstate *fs, int pc)
+{
+       int offset = GETARG_sBx(fs->f->code[pc]);
+
+       if (offset == NO_JUMP)  /* point to itself represents end of list */
+               return NO_JUMP;  /* end of list */
+       else
+               return (pc+1)+offset;  /* turn offset into absolute position */
+}
+
+static ktap_instruction *getjumpcontrol(ktap_funcstate *fs, int pc)
+{
+       ktap_instruction *pi = &fs->f->code[pc];
+       if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))
+               return pi-1;
+       else
+               return pi;
+}
+
+/*
+ * check whether list has any jump that do not produce a value
+ * (or produce an inverted value)
+ */
+static int need_value(ktap_funcstate *fs, int list)
+{
+       for (; list != NO_JUMP; list = getjump(fs, list)) {
+               ktap_instruction i = *getjumpcontrol(fs, list);
+               if (GET_OPCODE(i) != OP_TESTSET)
+                       return 1;
+       }
+       return 0;  /* not found */
+}
+
+static int patchtestreg(ktap_funcstate *fs, int node, int reg)
+{
+       ktap_instruction *i = getjumpcontrol(fs, node);
+       if (GET_OPCODE(*i) != OP_TESTSET)
+               return 0;  /* cannot patch other instructions */
+       if (reg != NO_REG && reg != GETARG_B(*i))
+               SETARG_A(*i, reg);
+       else  /* no register to put value or register already has the value */
+               *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i));
+
+       return 1;
+}
+
+static void removevalues(ktap_funcstate *fs, int list)
+{
+       for (; list != NO_JUMP; list = getjump(fs, list))
+               patchtestreg(fs, list, NO_REG);
+}
+
+static void patchlistaux(ktap_funcstate *fs, int list, int vtarget, int reg,
+                        int dtarget)
+{
+       while (list != NO_JUMP) {
+               int next = getjump(fs, list);
+               if (patchtestreg(fs, list, reg))
+                       fixjump(fs, list, vtarget);
+               else
+                       fixjump(fs, list, dtarget);  /* jump to default target */
+               list = next;
+       }
+}
+
+static void dischargejpc(ktap_funcstate *fs)
+{
+       patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc);
+       fs->jpc = NO_JUMP;
+}
+
+void codegen_patchlist(ktap_funcstate *fs, int list, int target)
+{
+       if (target == fs->pc)
+               codegen_patchtohere(fs, list);
+       else {
+               ktap_assert(target < fs->pc);
+               patchlistaux(fs, list, target, NO_REG, target);
+       }
+}
+
+void codegen_patchclose(ktap_funcstate *fs, int list, int level)
+{
+       level++;  /* argument is +1 to reserve 0 as non-op */
+       while (list != NO_JUMP) {
+               int next = getjump(fs, list);
+               ktap_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP &&
+                          (GETARG_A(fs->f->code[list]) == 0 ||
+                           GETARG_A(fs->f->code[list]) >= level));
+               SETARG_A(fs->f->code[list], level);
+               list = next;
+       }
+}
+
+void codegen_patchtohere(ktap_funcstate *fs, int list)
+{
+       codegen_getlabel(fs);
+       codegen_concat(fs, &fs->jpc, list);
+}
+
+void codegen_concat(ktap_funcstate *fs, int *l1, int l2)
+{
+       if (l2 == NO_JUMP)
+               return;
+       else if (*l1 == NO_JUMP)
+               *l1 = l2;
+       else {
+               int list = *l1;
+               int next;
+               while ((next = getjump(fs, list)) != NO_JUMP)  /* find last element */
+                       list = next;
+               fixjump(fs, list, l2);
+       }
+}
+
+static int codegen_code(ktap_funcstate *fs, ktap_instruction i)
+{
+       ktap_proto *f = fs->f;
+
+       dischargejpc(fs);  /* `pc' will change */
+
+       /* put new instruction in code array */
+       ktapc_growvector(f->code, fs->pc, f->sizecode, ktap_instruction,
+                        MAX_INT, "opcodes");
+       f->code[fs->pc] = i;
+
+       /* save corresponding line information */
+       ktapc_growvector(f->lineinfo, fs->pc, f->sizelineinfo, int,
+                        MAX_INT, "opcodes");
+       f->lineinfo[fs->pc] = fs->ls->lastline;
+       return fs->pc++;
+}
+
+int codegen_codeABC(ktap_funcstate *fs, OpCode o, int a, int b, int c)
+{
+       ktap_assert(getOpMode(o) == iABC);
+       //ktap_assert(getBMode(o) != OpArgN || b == 0);
+       //ktap_assert(getCMode(o) != OpArgN || c == 0);
+       //ktap_assert(a <= MAXARG_A && b <= MAXARG_B && c <= MAXARG_C);
+       return codegen_code(fs, CREATE_ABC(o, a, b, c));
+}
+
+int codegen_codeABx(ktap_funcstate *fs, OpCode o, int a, unsigned int bc)
+{
+       ktap_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
+       ktap_assert(getCMode(o) == OpArgN);
+       ktap_assert(a <= MAXARG_A && bc <= MAXARG_Bx);
+       return codegen_code(fs, CREATE_ABx(o, a, bc));
+}
+
+static int codeextraarg(ktap_funcstate *fs, int a)
+{
+       ktap_assert(a <= MAXARG_Ax);
+       return codegen_code(fs, CREATE_Ax(OP_EXTRAARG, a));
+}
+
+int codegen_codek(ktap_funcstate *fs, int reg, int k)
+{
+       if (k <= MAXARG_Bx)
+               return codegen_codeABx(fs, OP_LOADK, reg, k);
+       else {
+               int p = codegen_codeABx(fs, OP_LOADKX, reg, 0);
+               codeextraarg(fs, k);
+               return p;
+       }
+}
+
+void codegen_checkstack(ktap_funcstate *fs, int n)
+{
+       int newstack = fs->freereg + n;
+
+       if (newstack > fs->f->maxstacksize) {
+               if (newstack >= MAXSTACK)
+                       lex_syntaxerror(fs->ls, "function or expression too complex");
+               fs->f->maxstacksize = (u8)(newstack);
+       }
+}
+
+void codegen_reserveregs(ktap_funcstate *fs, int n)
+{
+       codegen_checkstack(fs, n);
+       fs->freereg += n;
+}
+
+static void freereg(ktap_funcstate *fs, int reg)
+{
+       if (!ISK(reg) && reg >= fs->nactvar) {
+               fs->freereg--;
+               ktap_assert(reg == fs->freereg);
+       }
+}
+
+static void freeexp(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       if (e->k == VNONRELOC)
+               freereg(fs, e->u.info);
+}
+
+static int addk(ktap_funcstate *fs, ktap_value *key, ktap_value *v)
+{
+       const ktap_value *idx = ktapc_table_get(fs->h, key);
+       ktap_proto *f = fs->f;
+       ktap_value kn;
+       int k, oldsize;
+
+       if (is_number(idx)) {
+               ktap_number n = nvalue(idx);
+               kp_number2int(k, n);
+               if (ktapc_equalobj(&f->k[k], v))
+                       return k;
+               /* else may be a collision (e.g., between 0.0 and "\0\0\0\0\0\0\0\0");
+                       go through and create a new entry for this value */
+       }
+       /* constant not found; create a new entry */
+       oldsize = f->sizek;
+       k = fs->nk;
+
+       /* numerical value does not need GC barrier;
+          table has no metatable, so it does not need to invalidate cache */
+       set_number(&kn, (ktap_number)k);
+       ktapc_table_setvalue(fs->h, key, &kn);
+       ktapc_growvector(f->k, k, f->sizek, ktap_value, MAXARG_Ax, "constants");
+       while (oldsize < f->sizek)
+               set_nil(&f->k[oldsize++]);
+       set_obj(&f->k[k], v);
+       fs->nk++;
+       return k;
+}
+
+int codegen_stringK(ktap_funcstate *fs, ktap_string *s)
+{
+       ktap_value o;
+
+       set_string(&o, s);
+       return addk(fs, &o, &o);
+}
+
+int codegen_numberK(ktap_funcstate *fs, ktap_number r)
+{
+       int n;
+       ktap_value o, s;
+
+       set_number(&o, r);
+       if (r == 0 || ktap_numisnan(NULL, r)) {  /* handle -0 and NaN */
+               /* use raw representation as key to avoid numeric problems */
+               set_string(&s, ktapc_ts_newlstr((char *)&r, sizeof(r)));
+               //   incr_top(L);
+               n = addk(fs, &s, &o);
+               //   L->top--;
+       } else
+               n = addk(fs, &o, &o);  /* regular case */
+       return n;
+}
+
+static int boolK(ktap_funcstate *fs, int b)
+{
+       ktap_value o;
+       set_boolean(&o, b);
+       return addk(fs, &o, &o);
+}
+
+static int nilK(ktap_funcstate *fs)
+{
+       ktap_value k, v;
+       set_nil(&v);
+       /* cannot use nil as key; instead use table itself to represent nil */
+       set_table(&k, fs->h);
+       return addk(fs, &k, &v);
+}
+
+void codegen_setreturns(ktap_funcstate *fs, ktap_expdesc *e, int nresults)
+{
+       if (e->k == VCALL) {  /* expression is an open function call? */
+               SETARG_C(getcode(fs, e), nresults+1);
+       }
+       else if (e->k == VVARARG) {
+               SETARG_B(getcode(fs, e), nresults+1);
+               SETARG_A(getcode(fs, e), fs->freereg);
+               codegen_reserveregs(fs, 1);
+       }
+}
+
+void codegen_setoneret(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       if (e->k == VCALL) {  /* expression is an open function call? */
+               e->k = VNONRELOC;
+               e->u.info = GETARG_A(getcode(fs, e));
+       } else if (e->k == VVARARG) {
+               SETARG_B(getcode(fs, e), 2);
+               e->k = VRELOCABLE;  /* can relocate its simple result */
+       }
+}
+
+void codegen_dischargevars(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       switch (e->k) {
+       case VLOCAL: {
+               e->k = VNONRELOC;
+               break;
+       }
+       case VUPVAL: {
+               e->u.info = codegen_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0);
+               e->k = VRELOCABLE;
+               break;
+       }
+       case VINDEXED: {
+               OpCode op = OP_GETTABUP;  /* assume 't' is in an upvalue */
+               freereg(fs, e->u.ind.idx);
+               if (e->u.ind.vt == VLOCAL) {  /* 't' is in a register? */
+                       freereg(fs, e->u.ind.t);
+                       op = OP_GETTABLE;
+               }
+               e->u.info = codegen_codeABC(fs, op, 0, e->u.ind.t, e->u.ind.idx);
+               e->k = VRELOCABLE;
+               break;
+       }
+       case VVARARG:
+       case VCALL: {
+               codegen_setoneret(fs, e);
+               break;
+       }
+       default:
+               break;  /* there is one value available (somewhere) */
+       }
+}
+
+static int code_label(ktap_funcstate *fs, int A, int b, int jump)
+{
+       codegen_getlabel(fs);  /* those instructions may be jump targets */
+       return codegen_codeABC(fs, OP_LOADBOOL, A, b, jump);
+}
+
+static void discharge2reg(ktap_funcstate *fs, ktap_expdesc *e, int reg)
+{
+       codegen_dischargevars(fs, e);
+       switch (e->k) {
+       case VNIL: {
+               codegen_nil(fs, reg, 1);
+               break;
+       }
+       case VFALSE:  case VTRUE: {
+               codegen_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
+               break;
+       }
+       case VEVENT:
+               codegen_codeABC(fs, OP_EVENT, reg, 0, 0);
+               break;
+       case VEVENTNAME:
+               codegen_codeABC(fs, OP_EVENTNAME, reg, 0, 0);
+               break;
+       case VEVENTARG:
+               codegen_codeABC(fs, OP_EVENTARG, reg, e->u.info, 0);
+               break;
+       case VK: {
+               codegen_codek(fs, reg, e->u.info);
+               break;
+       }
+       case VKNUM: {
+               codegen_codek(fs, reg, codegen_numberK(fs, e->u.nval));
+               break;
+       }
+       case VRELOCABLE: {
+               ktap_instruction *pc = &getcode(fs, e);
+               SETARG_A(*pc, reg);
+               break;
+       }
+       case VNONRELOC: {
+               if (reg != e->u.info)
+                       codegen_codeABC(fs, OP_MOVE, reg, e->u.info, 0);
+               break;
+       }
+       default:
+               ktap_assert(e->k == VVOID || e->k == VJMP);
+               return;  /* nothing to do... */
+       }
+
+       e->u.info = reg;
+       e->k = VNONRELOC;
+}
+
+static void discharge2anyreg(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       if (e->k != VNONRELOC) {
+               codegen_reserveregs(fs, 1);
+               discharge2reg(fs, e, fs->freereg-1);
+       }
+}
+
+static void exp2reg(ktap_funcstate *fs, ktap_expdesc *e, int reg)
+{
+       discharge2reg(fs, e, reg);
+       if (e->k == VJMP)
+               codegen_concat(fs, &e->t, e->u.info);  /* put this jump in `t' list */
+       if (hasjumps(e)) {
+               int final;  /* position after whole expression */
+               int p_f = NO_JUMP;  /* position of an eventual LOAD false */
+               int p_t = NO_JUMP;  /* position of an eventual LOAD true */
+
+               if (need_value(fs, e->t) || need_value(fs, e->f)) {
+                       int fj = (e->k == VJMP) ? NO_JUMP : codegen_jump(fs);
+
+                       p_f = code_label(fs, reg, 0, 1);
+                       p_t = code_label(fs, reg, 1, 0);
+                       codegen_patchtohere(fs, fj);
+               }
+               final = codegen_getlabel(fs);
+               patchlistaux(fs, e->f, final, reg, p_f);
+               patchlistaux(fs, e->t, final, reg, p_t);
+       }
+       e->f = e->t = NO_JUMP;
+       e->u.info = reg;
+       e->k = VNONRELOC;
+}
+
+void codegen_exp2nextreg(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       codegen_dischargevars(fs, e);
+       freeexp(fs, e);
+       codegen_reserveregs(fs, 1);
+       exp2reg(fs, e, fs->freereg - 1);
+}
+
+int codegen_exp2anyreg(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       codegen_dischargevars(fs, e);
+       if (e->k == VNONRELOC) {
+               if (!hasjumps(e))
+                       return e->u.info;  /* exp is already in a register */
+               if (e->u.info >= fs->nactvar) {  /* reg. is not a local? */
+                       exp2reg(fs, e, e->u.info);  /* put value on it */
+                       return e->u.info;
+               }
+       }
+       codegen_exp2nextreg(fs, e);  /* default */
+       return e->u.info;
+}
+
+void codegen_exp2anyregup(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       if (e->k != VUPVAL || hasjumps(e))
+               codegen_exp2anyreg(fs, e);
+}
+
+void codegen_exp2val(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       if (hasjumps(e))
+               codegen_exp2anyreg(fs, e);
+       else
+               codegen_dischargevars(fs, e);
+}
+
+int codegen_exp2RK(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       codegen_exp2val(fs, e);
+       switch (e->k) {
+       case VTRUE:
+       case VFALSE:
+       case VNIL: {
+               if (fs->nk <= MAXINDEXRK) {  /* constant fits in RK operand? */
+                       e->u.info = (e->k == VNIL) ? nilK(fs) :
+                                                    boolK(fs, (e->k == VTRUE));
+                       e->k = VK;
+                       return RKASK(e->u.info);
+               }
+               else
+                       break;
+       }
+       case VKNUM: {
+               e->u.info = codegen_numberK(fs, e->u.nval);
+               e->k = VK;
+               /* go through */
+       }
+       case VK: {
+               if (e->u.info <= MAXINDEXRK)  /* constant fits in argC? */
+                       return RKASK(e->u.info);
+               else
+                       break;
+       }
+       default:
+               break;
+       }
+       /* not a constant in the right range: put it in a register */
+       return codegen_exp2anyreg(fs, e);
+}
+
+void codegen_storevar(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex)
+{
+       switch (var->k) {
+       case VLOCAL: {
+               freeexp(fs, ex);
+               exp2reg(fs, ex, var->u.info);
+               return;
+       }
+       case VUPVAL: {
+               int e = codegen_exp2anyreg(fs, ex);
+               codegen_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
+               break;
+       }
+       case VINDEXED: {
+               OpCode op = (var->u.ind.vt == VLOCAL) ? OP_SETTABLE : OP_SETTABUP;
+               int e = codegen_exp2RK(fs, ex);
+               codegen_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e);
+               break;
+       }
+       default:
+               ktap_assert(0);  /* invalid var kind to store */
+               break;
+       }
+
+       freeexp(fs, ex);
+}
+
+void codegen_storeincr(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex)
+{
+       switch (var->k) {
+#if 0 /*current not supported */
+       case VLOCAL: {
+               freeexp(fs, ex);
+               exp2reg(fs, ex, var->u.info);
+               return;
+       }
+       case VUPVAL: {
+               int e = codegen_exp2anyreg(fs, ex);
+               codegen_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
+               break;
+       }
+#endif
+       case VINDEXED: {
+               OpCode op = (var->u.ind.vt == VLOCAL) ? OP_SETTABLE_INCR :
+                               OP_SETTABUP_INCR;
+               int e = codegen_exp2RK(fs, ex);
+               codegen_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e);
+               break;
+       }
+       default:
+               ktap_assert(0);  /* invalid var kind to store */
+               break;
+       }
+
+       freeexp(fs, ex);
+}
+
+void codegen_store_aggr(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex)
+{
+       switch (var->k) {
+#if 0 /*current not supported */
+       case VLOCAL: {
+               freeexp(fs, ex);
+               exp2reg(fs, ex, var->u.info);
+               return;
+       }
+       case VUPVAL: {
+               int e = codegen_exp2anyreg(fs, ex);
+               codegen_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
+               break;
+       }
+#endif
+       case VINDEXED: {
+               OpCode op = (var->u.ind.vt == VLOCAL) ? OP_SETTABLE_AGGR :
+                               OP_SETTABUP_AGGR;
+               int e = codegen_exp2RK(fs, ex);
+               codegen_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e);
+               break;
+       }
+       default:
+               ktap_assert(0);  /* invalid var kind to store */
+               break;
+       }
+
+       freeexp(fs, ex);
+}
+
+void codegen_self(ktap_funcstate *fs, ktap_expdesc *e, ktap_expdesc *key)
+{
+       int ereg;
+
+       codegen_exp2anyreg(fs, e);
+       ereg = e->u.info;  /* register where 'e' was placed */
+       freeexp(fs, e);
+       e->u.info = fs->freereg;  /* base register for op_self */
+       e->k = VNONRELOC;
+       codegen_reserveregs(fs, 2);  /* function and 'self' produced by op_self */
+       codegen_codeABC(fs, OP_SELF, e->u.info, ereg, codegen_exp2RK(fs, key));
+       freeexp(fs, key);
+}
+
+static void invertjump(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       ktap_instruction *pc = getjumpcontrol(fs, e->u.info);
+       ktap_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
+                       GET_OPCODE(*pc) != OP_TEST);
+       SETARG_A(*pc, !(GETARG_A(*pc)));
+}
+
+static int jumponcond(ktap_funcstate *fs, ktap_expdesc *e, int cond)
+{
+       if (e->k == VRELOCABLE) {
+               ktap_instruction ie = getcode(fs, e);
+               if (GET_OPCODE(ie) == OP_NOT) {
+                       fs->pc--;  /* remove previous OP_NOT */
+                       return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
+               }
+               /* else go through */
+       }
+       discharge2anyreg(fs, e);
+       freeexp(fs, e);
+       return condjump(fs, OP_TESTSET, NO_REG, e->u.info, cond);
+}
+
+void codegen_goiftrue(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       int pc;  /* pc of last jump */
+
+       codegen_dischargevars(fs, e);
+       switch (e->k) {
+       case VJMP: {
+               invertjump(fs, e);
+               pc = e->u.info;
+               break;
+       }
+       case VK: case VKNUM: case VTRUE: {
+               pc = NO_JUMP;  /* always true; do nothing */
+               break;
+       }
+       default:
+               pc = jumponcond(fs, e, 0);
+               break;
+       }
+
+       codegen_concat(fs, &e->f, pc);  /* insert last jump in `f' list */
+       codegen_patchtohere(fs, e->t);
+       e->t = NO_JUMP;
+}
+
+void codegen_goiffalse(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       int pc;  /* pc of last jump */
+       codegen_dischargevars(fs, e);
+
+       switch (e->k) {
+       case VJMP: {
+               pc = e->u.info;
+               break;
+       }
+       case VNIL: case VFALSE: {
+               pc = NO_JUMP;  /* always false; do nothing */
+               break;
+       }
+       default:
+               pc = jumponcond(fs, e, 1);
+               break;
+       }
+       codegen_concat(fs, &e->t, pc);  /* insert last jump in `t' list */
+       codegen_patchtohere(fs, e->f);
+       e->f = NO_JUMP;
+}
+
+static void codenot(ktap_funcstate *fs, ktap_expdesc *e)
+{
+       codegen_dischargevars(fs, e);
+       switch (e->k) {
+       case VNIL: case VFALSE: {
+               e->k = VTRUE;
+               break;
+       }
+       case VK: case VKNUM: case VTRUE: {
+               e->k = VFALSE;
+               break;
+       }
+       case VJMP: {
+               invertjump(fs, e);
+               break;
+       }
+       case VRELOCABLE:
+       case VNONRELOC: {
+               discharge2anyreg(fs, e);
+               freeexp(fs, e);
+               e->u.info = codegen_codeABC(fs, OP_NOT, 0, e->u.info, 0);
+               e->k = VRELOCABLE;
+               break;
+       }
+       default:
+               ktap_assert(0);  /* cannot happen */
+               break;
+       }
+
+       /* interchange true and false lists */
+       { int temp = e->f; e->f = e->t; e->t = temp; }
+       removevalues(fs, e->f);
+       removevalues(fs, e->t);
+}
+
+void codegen_indexed(ktap_funcstate *fs, ktap_expdesc *t, ktap_expdesc *k)
+{
+       ktap_assert(!hasjumps(t));
+       t->u.ind.t = t->u.info;
+       t->u.ind.idx = codegen_exp2RK(fs, k);
+       t->u.ind.vt = (t->k == VUPVAL) ? VUPVAL
+                       : check_exp(vkisinreg(t->k), VLOCAL);
+       t->k = VINDEXED;
+}
+
+static int constfolding(OpCode op, ktap_expdesc *e1, ktap_expdesc *e2)
+{
+       ktap_number r;
+
+       if (!isnumeral(e1) || !isnumeral(e2))
+               return 0;
+
+       if ((op == OP_DIV || op == OP_MOD) && e2->u.nval == 0)
+               return 0;  /* do not attempt to divide by 0 */
+
+       if (op == OP_POW)
+               return 0; /* ktap current do not suppor pow arith */
+
+       r = ktapc_arith(op - OP_ADD + KTAP_OPADD, e1->u.nval, e2->u.nval);
+       e1->u.nval = r;
+       return 1;
+}
+
+static void codearith(ktap_funcstate *fs, OpCode op,
+                     ktap_expdesc *e1, ktap_expdesc *e2, int line)
+{
+       if (constfolding(op, e1, e2))
+               return;
+       else {
+               int o2 = (op != OP_UNM && op != OP_LEN) ? codegen_exp2RK(fs, e2) : 0;
+               int o1 = codegen_exp2RK(fs, e1);
+
+               if (o1 > o2) {
+                       freeexp(fs, e1);
+                       freeexp(fs, e2);
+               } else {
+                       freeexp(fs, e2);
+                       freeexp(fs, e1);
+               }
+               e1->u.info = codegen_codeABC(fs, op, 0, o1, o2);
+               e1->k = VRELOCABLE;
+               codegen_fixline(fs, line);
+       }
+}
+
+static void codecomp(ktap_funcstate *fs, OpCode op, int cond, ktap_expdesc *e1,
+                    ktap_expdesc *e2)
+{
+       int o1 = codegen_exp2RK(fs, e1);
+       int o2 = codegen_exp2RK(fs, e2);
+
+       freeexp(fs, e2);
+       freeexp(fs, e1);
+       if (cond == 0 && op != OP_EQ) {
+               int temp;  /* exchange args to replace by `<' or `<=' */
+               temp = o1; o1 = o2; o2 = temp;  /* o1 <==> o2 */
+               cond = 1;
+       }
+       e1->u.info = condjump(fs, op, cond, o1, o2);
+       e1->k = VJMP;
+}
+
+void codegen_prefix(ktap_funcstate *fs, UnOpr op, ktap_expdesc *e, int line)
+{
+       ktap_expdesc e2;
+
+       e2.t = e2.f = NO_JUMP;
+       e2.k = VKNUM;
+       e2.u.nval = 0;
+
+       switch (op) {
+       case OPR_MINUS: {
+               if (isnumeral(e))  /* minus constant? */
+                       e->u.nval = ktap_numunm(e->u.nval);  /* fold it */
+               else {
+                       codegen_exp2anyreg(fs, e);
+                       codearith(fs, OP_UNM, e, &e2, line);
+               }
+               break;
+       }
+       case OPR_NOT:
+               codenot(fs, e);
+               break;
+       case OPR_LEN: {
+               codegen_exp2anyreg(fs, e);  /* cannot operate on constants */
+               codearith(fs, OP_LEN, e, &e2, line);
+               break;
+       }
+       default:
+               ktap_assert(0);
+       }
+}
+
+void codegen_infix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *v)
+{
+       switch (op) {
+       case OPR_AND: {
+               codegen_goiftrue(fs, v);
+               break;
+       }
+       case OPR_OR: {
+               codegen_goiffalse(fs, v);
+               break;
+       }
+       case OPR_CONCAT: {
+               codegen_exp2nextreg(fs, v);  /* operand must be on the `stack' */
+               break;
+       }
+       case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
+       case OPR_MOD: case OPR_POW: {
+               if (!isnumeral(v)) codegen_exp2RK(fs, v);
+                       break;
+       }
+       default:
+               codegen_exp2RK(fs, v);
+               break;
+       }
+}
+
+void codegen_posfix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *e1, ktap_expdesc *e2, int line)
+{
+       switch (op) {
+       case OPR_AND: {
+               ktap_assert(e1->t == NO_JUMP);  /* list must be closed */
+               codegen_dischargevars(fs, e2);
+               codegen_concat(fs, &e2->f, e1->f);
+               *e1 = *e2;
+               break;
+       }
+       case OPR_OR: {
+               ktap_assert(e1->f == NO_JUMP);  /* list must be closed */
+               codegen_dischargevars(fs, e2);
+               codegen_concat(fs, &e2->t, e1->t);
+               *e1 = *e2;
+               break;
+       }
+       case OPR_CONCAT: {
+               codegen_exp2val(fs, e2);
+               if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) {
+                       ktap_assert(e1->u.info == GETARG_B(getcode(fs, e2))-1);
+                       freeexp(fs, e1);
+                       SETARG_B(getcode(fs, e2), e1->u.info);
+                       e1->k = VRELOCABLE; e1->u.info = e2->u.info;
+               } else {
+                       codegen_exp2nextreg(fs, e2);  /* operand must be on the 'stack' */
+                       codearith(fs, OP_CONCAT, e1, e2, line);
+               }
+               break;
+       }
+       case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
+       case OPR_MOD: case OPR_POW: {
+               codearith(fs, (OpCode)(op - OPR_ADD + OP_ADD), e1, e2, line);
+               break;
+       }
+       case OPR_EQ: case OPR_LT: case OPR_LE: {
+               codecomp(fs, (OpCode)(op - OPR_EQ + OP_EQ), 1, e1, e2);
+               break;
+       }
+       case OPR_NE: case OPR_GT: case OPR_GE: {
+               codecomp(fs, (OpCode)(op - OPR_NE + OP_EQ), 0, e1, e2);
+               break;
+       }
+       default:
+               ktap_assert(0);
+       }
+}
+
+void codegen_fixline(ktap_funcstate *fs, int line)
+{
+       fs->f->lineinfo[fs->pc - 1] = line;
+}
+
+void codegen_setlist(ktap_funcstate *fs, int base, int nelems, int tostore)
+{
+       int c =  (nelems - 1)/LFIELDS_PER_FLUSH + 1;
+       int b = (tostore == KTAP_MULTRET) ? 0 : tostore;
+
+       ktap_assert(tostore != 0);
+       if (c <= MAXARG_C)
+               codegen_codeABC(fs, OP_SETLIST, base, b, c);
+       else if (c <= MAXARG_Ax) {
+               codegen_codeABC(fs, OP_SETLIST, base, b, 0);
+               codeextraarg(fs, c);
+       } else
+               lex_syntaxerror(fs->ls, "constructor too long");
+       fs->freereg = base + 1;  /* free registers with list values */
+}
+
diff --git a/drivers/staging/ktap/userspace/cparser.h b/drivers/staging/ktap/userspace/cparser.h
new file mode 100644 (file)
index 0000000..f2a9004
--- /dev/null
@@ -0,0 +1,202 @@
+#ifndef __KTAP_CPARSER_H__
+#define __KTAP_CPARSER_H__
+
+/*
+ * Copyright (c) 2011 James R. McKaskill
+ *
+ * This software is licensed under the stock MIT license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------------
+ */
+
+/*
+ * Adapted from luaffi commit: abc638c9341025580099dcf77795c4b320ba0e63
+ *
+ * Copyright (c) 2013 Yicheng Qin, Qingping Hou
+ */
+
+#ifdef CONFIG_KTAP_FFI
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "../include/ktap_ffi.h"
+
+#define PTR_ALIGN_MASK (sizeof(void*) - 1)
+#define FUNCTION_ALIGN_MASK (sizeof(void (*)()) - 1)
+#define DEFAULT_ALIGN_MASK 7
+
+struct parser {
+       int line;
+       const char *next;
+       const char *prev;
+       unsigned align_mask;
+};
+
+enum {
+       C_CALL,
+       STD_CALL,
+       FAST_CALL,
+};
+
+
+#define MAX_TYPE_NAME_LEN CSYM_NAME_MAX_LEN
+
+enum {
+       /* 0 - 4 */
+       INVALID_TYPE,
+       VOID_TYPE,
+       BOOL_TYPE,
+       INT8_TYPE,
+       INT16_TYPE,
+       /* 5 - 9 */
+       INT32_TYPE,
+       INT64_TYPE,
+       INTPTR_TYPE,
+       ENUM_TYPE,
+       UNION_TYPE,
+       /* 10 - 12 */
+       STRUCT_TYPE,
+       FUNCTION_TYPE,
+       FUNCTION_PTR_TYPE,
+};
+
+
+#define IS_CHAR_UNSIGNED (((char) -1) > 0)
+
+#define POINTER_BITS 2
+#define POINTER_MAX ((1 << POINTER_BITS) - 1)
+
+#define ALIGNOF(S) ((int) ((char*) &S.v - (char*) &S - 1))
+
+
+/* Note: if adding a new member that is associated with a struct/union
+ * definition then it needs to be copied over in ctype.c:set_defined for when
+ * we create types based off of the declaration alone.
+ *
+ * Since this is used as a header for every ctype and cdata, and we create a
+ * ton of them on the stack, we try and minimise its size.
+ */
+struct cp_ctype {
+       size_t base_size; /* size of the base type in bytes */
+       int ffi_cs_id; /* index for csymbol from ktap vm */
+       union {
+               /* valid if is_bitfield */
+               struct {
+                       /* size of bitfield in bits */
+                       unsigned bit_size : 7;
+                       /* offset within the current byte between 0-63 */
+                       unsigned bit_offset : 6;
+               };
+               /* Valid if is_array */
+               size_t array_size;
+               /* Valid for is_variable_struct or is_variable_array. If
+                * variable_size_known (only used for is_variable_struct)
+                * then this is the total increment otherwise this is the
+                * per element increment.
+                */
+               size_t variable_increment;
+       };
+       size_t offset;
+       /* as (align bytes - 1) eg 7 gives 8 byte alignment */
+       unsigned align_mask : 4;
+       /* number of dereferences to get to the base type
+        * including +1 for arrays */
+       unsigned pointers : POINTER_BITS;
+       /* const pointer mask, LSB is current pointer, +1 for the whether
+        * the base type is const */
+       unsigned const_mask : POINTER_MAX + 1;
+       unsigned type : 5; /* value given by type enum above */
+       unsigned is_reference : 1;
+       unsigned is_array : 1;
+       unsigned is_defined : 1;
+       unsigned is_null : 1;
+       unsigned has_member_name : 1;
+       unsigned calling_convention : 2;
+       unsigned has_var_arg : 1;
+       /* set for variable array types where we don't know
+        * the variable size yet */
+       unsigned is_variable_array : 1;
+       unsigned is_variable_struct : 1;
+       /* used for variable structs after we know the variable size */
+       unsigned variable_size_known : 1;
+       unsigned is_bitfield : 1;
+       unsigned has_bitfield : 1;
+       unsigned is_jitted : 1;
+       unsigned is_packed : 1;
+       unsigned is_unsigned : 1;
+};
+
+#define ALIGNED_DEFAULT (__alignof__(void* __attribute__((aligned))) - 1)
+
+csymbol *cp_id_to_csym(int id);
+#define ct_ffi_cs(ct) (cp_id_to_csym((ct)->ffi_cs_id))
+
+size_t ctype_size(const struct cp_ctype* ct);
+int cp_ctype_init();
+int cp_ctype_free();
+struct cp_ctype *ctype_lookup_type(char *name);
+void cp_ctype_dump_stack();
+void cp_error(const char *err_msg_fmt, ...);
+struct cp_ctype *cp_ctype_reg_type(char *name, struct cp_ctype *ct);
+
+void cp_push_ctype_with_name(struct cp_ctype *ct, const char *name, int nlen);
+void cp_push_ctype(struct cp_ctype *ct);
+void cp_set_defined(struct cp_ctype *ct);
+
+int cp_symbol_build_func(struct cp_ctype *type,
+               const char *fname, int fn_size);
+int cp_symbol_build_struct(const char *stname);
+int cp_symbol_build_pointer(struct cp_ctype *ct);
+
+int ffi_cdef(const char *s);
+void ffi_cparser_init(void);
+void ffi_cparser_free(void);
+
+
+static inline csymbol *cp_csymf_ret(csymbol_func *csf)
+{
+       return cp_id_to_csym(csf->ret_id);
+}
+
+static inline csymbol *cp_csymf_arg(csymbol_func *csf, int idx)
+{
+       return cp_id_to_csym(csf->arg_ids[idx]);
+}
+
+
+#else
+static void __maybe_unused ffi_cparser_init(void)
+{
+       return;
+}
+static void __maybe_unused ffi_cparser_free(void)
+{
+       return;
+}
+#endif /* CONFIG_KTAP_FFI */
+
+
+#endif /* __KTAP_CPARSER_H__ */
diff --git a/drivers/staging/ktap/userspace/dump.c b/drivers/staging/ktap/userspace/dump.c
new file mode 100644 (file)
index 0000000..eb0acc1
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * dump.c - save precompiled ktap chunks
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+ *  - The part of code in this file is copied from lua initially.
+ *  - lua's MIT license is compatible with GPL.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/ktap_types.h"
+#include "../include/ktap_opcodes.h"
+#include "ktapc.h"
+#include "../runtime/kp_obj.h"
+#include "cparser.h"
+
+
+typedef struct {
+       ktap_writer writer;
+       void *data;
+       int strip;
+       int status;
+} DumpState;
+
+#define DumpMem(b, n, size, D) DumpBlock(b, (n)*(size), D)
+#define DumpVar(x, D)          DumpMem(&x, 1, sizeof(x), D)
+
+static void DumpBlock(const void *b, size_t size, DumpState *D)
+{
+       if (D->status == 0)
+               D->status = ((D->writer))(b, size, D->data);
+}
+
+static void DumpChar(int y, DumpState *D)
+{
+       char x = (char)y;
+       DumpVar(x, D);
+}
+
+static void DumpInt(int x, DumpState *D)
+{
+       DumpVar(x, D);
+}
+
+static void DumpNumber(ktap_number x, DumpState *D)
+{
+       DumpVar(x,D);
+}
+
+static void DumpVector(const void *b, int n, size_t size, DumpState *D)
+{
+       DumpInt(n, D);
+       DumpMem(b, n, size, D);
+}
+
+static void DumpString(const ktap_string *s, DumpState *D)
+{
+       if (s == NULL) {
+               int size = 0;
+               DumpVar(size, D);
+       } else {
+               int size = s->tsv.len + 1;              /* include trailing '\0' */
+               DumpVar(size, D);
+               DumpBlock(getstr(s), size * sizeof(char), D);
+       }
+}
+
+#define DumpCode(f, D)  DumpVector(f->code, f->sizecode, sizeof(ktap_instruction), D)
+
+static void DumpFunction(const ktap_proto *f, DumpState *D);
+
+static void DumpConstants(const ktap_proto *f, DumpState *D)
+{
+       int i, n = f->sizek;
+
+       DumpInt(n, D);
+       for (i = 0; i < n; i++) {
+               const ktap_value* o=&f->k[i];
+               DumpChar(ttypenv(o), D);
+               switch (ttypenv(o)) {
+               case KTAP_TNIL:
+                       break;
+               case KTAP_TBOOLEAN:
+                       DumpChar(bvalue(o), D);
+                       break;
+               case KTAP_TNUMBER:
+                       DumpNumber(nvalue(o), D);
+                       break;
+               case KTAP_TSTRING:
+                       DumpString(rawtsvalue(o), D);
+                       break;
+               default:
+                       printf("ktap: DumpConstants with unknown vaule type %d\n", ttypenv(o));
+                       ktap_assert(0);
+               }
+       }
+       n = f->sizep;
+       DumpInt(n, D);
+       for (i = 0; i < n; i++)
+               DumpFunction(f->p[i], D);
+}
+
+static void DumpUpvalues(const ktap_proto *f, DumpState *D)
+{
+       int i, n = f->sizeupvalues;
+
+       DumpInt(n, D);
+       for (i = 0; i < n; i++) {
+               DumpChar(f->upvalues[i].instack, D);
+               DumpChar(f->upvalues[i].idx, D);
+       }
+}
+
+static void DumpDebug(const ktap_proto *f, DumpState *D)
+{
+       int i,n;
+
+       DumpString((D->strip) ? NULL : f->source, D);
+       n= (D->strip) ? 0 : f->sizelineinfo;
+       DumpVector(f->lineinfo, n, sizeof(int), D);
+       n = (D->strip) ? 0 : f->sizelocvars;
+       DumpInt(n, D);
+
+       for (i = 0; i < n; i++) {
+               DumpString(f->locvars[i].varname, D);
+               DumpInt(f->locvars[i].startpc, D);
+               DumpInt(f->locvars[i].endpc, D);
+       }
+       n = (D->strip) ? 0 : f->sizeupvalues;
+       DumpInt(n, D);
+       for (i = 0; i < n; i++)
+               DumpString(f->upvalues[i].name, D);
+}
+
+static void DumpFunction(const ktap_proto *f, DumpState *D)
+{
+       DumpInt(f->linedefined, D);
+       DumpInt(f->lastlinedefined, D);
+       DumpChar(f->numparams, D);
+       DumpChar(f->is_vararg, D);
+       DumpChar(f->maxstacksize, D);
+       DumpCode(f, D);
+       DumpConstants(f, D);
+       DumpUpvalues(f, D);
+       DumpDebug(f, D);
+}
+
+static void DumpHeader(DumpState *D)
+{
+       u8 h[KTAPC_HEADERSIZE];
+
+       kp_header(h);
+       DumpBlock(h, KTAPC_HEADERSIZE, D);
+}
+
+#ifdef CONFIG_KTAP_FFI
+static void DumpCSymbolFunc(csymbol *cs, DumpState *D)
+{
+       csymbol_func *csf = csym_func(cs);
+
+       DumpBlock(cs, sizeof(csymbol), D);
+       /* dump csymbol index for argument types */
+       DumpBlock(csf->arg_ids, csf->arg_nr*sizeof(int), D);
+}
+
+static void DumpCSymbolStruct(csymbol *cs, DumpState *D)
+{
+       csymbol_struct *csst = csym_struct(cs);
+
+       DumpBlock(cs, sizeof(csymbol), D);
+       /* dump csymbol index for argument types */
+       DumpBlock(csst->members, csst->memb_nr*sizeof(struct_member), D);
+}
+
+static void DumpCSymbols(DumpState *D)
+{
+       int i, cs_nr;
+       cp_csymbol_state *cs_state;
+       csymbol *cs, *cs_arr;
+
+       cs_state = ctype_get_csym_state();
+       cs_arr = cs_state->cs_arr;
+       cs_nr = cs_state->cs_nr;
+
+       if (!cs_arr || cs_nr == 0) {
+               DumpInt(0, D);
+               return;
+       }
+
+       /* dump number of csymbols */
+       DumpInt(cs_nr, D);
+       /* dump size of csymbol, for safty check in vm */
+       DumpInt(sizeof(csymbol), D);
+       for (i = 0; i < cs_nr; i++) {
+               cs = &cs_arr[i];
+               switch (cs->type) {
+               case FFI_FUNC:
+                       DumpCSymbolFunc(cs, D);
+                       break;
+               case FFI_STRUCT:
+                       DumpCSymbolStruct(cs, D);
+                       break;
+               default:
+                       DumpBlock(cs, sizeof(csymbol), D);
+                       break;
+               }
+       }
+}
+#else
+static void DumpCSymbols(DumpState *D)
+{
+       /* always dump zero when FFI is disabled */
+       DumpInt(0, D);
+}
+#endif /* CONFIG_KTAP_FFI */
+
+/*
+ * dump ktap function as precompiled chunk
+ */
+int ktapc_dump(const ktap_proto *f, ktap_writer w, void *data, int strip)
+{
+       DumpState D;
+
+       D.writer = w;
+       D.data = data;
+       D.strip = strip;
+       D.status = 0;
+       DumpHeader(&D);
+       DumpCSymbols(&D);
+       DumpFunction(f, &D);
+       return D.status;
+}
diff --git a/drivers/staging/ktap/userspace/eventdef.c b/drivers/staging/ktap/userspace/eventdef.c
new file mode 100644 (file)
index 0000000..9896dd3
--- /dev/null
@@ -0,0 +1,857 @@
+/*
+ * eventdef.c - ktap eventdef parser
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "../include/ktap_types.h"
+#include "../include/ktap_opcodes.h"
+#include "ktapc.h"
+#include "symbol.h"
+
+#define TRACING_EVENTS_DIR "/sys/kernel/debug/tracing/events"
+
+static u8 *idmap;
+static int idmap_size = 1024; /* set init size */
+static int id_nr = 0;
+
+static int idmap_init(void)
+{
+       idmap = malloc(idmap_size);
+       if (!idmap)
+               return -1;
+
+       memset(idmap, 0, idmap_size);
+       return 0;
+}
+
+static void idmap_free(void)
+{
+       free(idmap);
+}
+
+static inline int idmap_is_set(int id)
+{
+       return idmap[id / 8] & (1 << (id % 8));
+}
+
+static void idmap_set(int id)
+{
+       if (id >= idmap_size * 8) {
+               int newsize = id + 100; /* allocate extra 800 id */
+               idmap = realloc(idmap, newsize);
+               memset(idmap + idmap_size, 0, newsize - idmap_size);
+               idmap_size = newsize;
+       }
+
+       if (!idmap_is_set(id))
+               id_nr++;
+
+       idmap[id / 8] = idmap[id / 8] | (1 << (id % 8));
+}
+
+static void idmap_clear(int id)
+{
+       id_nr--;
+       idmap[id / 8] = idmap[id / 8] & ~ (1 << (id % 8));
+}
+
+static int idmap_get_max_id(void)
+{
+       return idmap_size * 8;
+}
+
+static int *get_id_array()
+{
+       int *id_array;
+       int i, j = 0;
+
+       id_array = malloc(sizeof(int) * id_nr);
+       if (!id_array)
+               return NULL;
+
+       for (i = 0; i < idmap_get_max_id(); i++) {
+               if (idmap_is_set(i))
+                       id_array[j++] = i;
+       }
+
+       return id_array;
+}
+
+static int add_event(char *evtid_path)
+{
+       char id_buf[24];
+       int id, fd;
+
+       fd = open(evtid_path, O_RDONLY);
+       if (fd < 0) {
+               /*
+                * some tracepoint doesn't have id file, like ftrace,
+                * return success in here, and don't print error.
+                */
+               verbose_printf("warning: cannot open file %s\n", evtid_path);
+               return 0;
+       }
+
+       if (read(fd, id_buf, sizeof(id_buf)) < 0) {
+               fprintf(stderr, "read file error %s\n", evtid_path);
+               close(fd);
+               return -1;
+       }
+
+       id = atoll(id_buf);
+
+       idmap_set(id);
+
+       close(fd);
+       return 0;
+}
+
+static int add_tracepoint(char *sys_name, char *evt_name)
+{
+       char evtid_path[PATH_MAX] = {0};
+
+
+       snprintf(evtid_path, PATH_MAX, "%s/%s/%s/id", TRACING_EVENTS_DIR,
+                                       sys_name, evt_name);
+       return add_event(evtid_path);
+}
+
+static int add_tracepoint_multi_event(char *sys_name, char *evt_name)
+{
+       char evt_path[PATH_MAX];
+       struct dirent *evt_ent;
+       DIR *evt_dir;
+       int ret = 0;
+
+       snprintf(evt_path, PATH_MAX, "%s/%s", TRACING_EVENTS_DIR, sys_name);
+       evt_dir = opendir(evt_path);
+       if (!evt_dir) {
+               perror("Can't open event dir");
+               return -1;
+       }
+
+       while (!ret && (evt_ent = readdir(evt_dir))) {
+               if (!strcmp(evt_ent->d_name, ".")
+                   || !strcmp(evt_ent->d_name, "..")
+                   || !strcmp(evt_ent->d_name, "enable")
+                   || !strcmp(evt_ent->d_name, "filter"))
+                       continue;
+
+               if (!strglobmatch(evt_ent->d_name, evt_name))
+                       continue;
+
+               ret = add_tracepoint(sys_name, evt_ent->d_name);
+       }
+
+       closedir(evt_dir);
+       return ret;
+}
+
+static int add_tracepoint_event(char *sys_name, char *evt_name)
+{
+       return strpbrk(evt_name, "*?") ?
+              add_tracepoint_multi_event(sys_name, evt_name) :
+              add_tracepoint(sys_name, evt_name);
+}
+
+static int add_tracepoint_multi_sys(char *sys_name, char *evt_name)
+{
+       struct dirent *events_ent;
+       DIR *events_dir;
+       int ret = 0;
+
+       events_dir = opendir(TRACING_EVENTS_DIR);
+       if (!events_dir) {
+               perror("Can't open event dir");
+               return -1;
+       }
+
+       while (!ret && (events_ent = readdir(events_dir))) {
+               if (!strcmp(events_ent->d_name, ".")
+                   || !strcmp(events_ent->d_name, "..")
+                   || !strcmp(events_ent->d_name, "enable")
+                   || !strcmp(events_ent->d_name, "header_event")
+                   || !strcmp(events_ent->d_name, "header_page"))
+                       continue;
+
+               if (!strglobmatch(events_ent->d_name, sys_name))
+                       continue;
+
+               ret = add_tracepoint_event(events_ent->d_name,
+                                          evt_name);
+       }
+
+       closedir(events_dir);
+       return ret;
+}
+
+static int parse_events_add_tracepoint(char *sys, char *event)
+{
+       if (strpbrk(sys, "*?"))
+               return add_tracepoint_multi_sys(sys, event);
+       else
+               return add_tracepoint_event(sys, event);
+}
+
+enum {
+       KPROBE_EVENT,
+       UPROBE_EVENT,
+};
+
+struct probe_list {
+       struct probe_list *next;
+       int type;
+       char event[64];
+};
+
+static struct probe_list *probe_list_head; /* for cleanup resources */
+
+/*
+ * Some symbol format cannot write to uprobe_events in debugfs, like:
+ * symbol "check_one_fd.part.0" in glibc.
+ * For those symbols, we change the format, get rid of invalid chars,
+ * "check_one_fd.part.0" -> "check_one_fd"
+ *
+ * This function copy is_good_name function in linux/kernel/trace/trace_probe.h
+ */
+static char *format_symbol_name(const char *old_symbol)
+{
+       char *new_name = strdup(old_symbol);
+       char *name = new_name;
+
+        if (!isalpha(*name) && *name != '_')
+               *name = '\0';
+
+        while (*++name != '\0') {
+                if (!isalpha(*name) && !isdigit(*name) && *name != '_') {
+                       *name = '\0';
+                       break;
+               }
+        }
+
+       /* this is a good name */
+        return new_name;
+}
+
+
+#define KPROBE_EVENTS_PATH "/sys/kernel/debug/tracing/kprobe_events"
+
+/**
+ * @return 0 on success, otherwise -1
+ */
+static int
+write_kprobe_event(int fd, int ret_probe, const char *symbol, char *fetch_args)
+{
+       char probe_event[128] = {0};
+       char event[64] = {0};
+       struct probe_list *pl;
+       char event_id_path[128] = {0};
+       char *symbol_name;
+       int id_fd, ret;
+
+       /* In case some symbols cannot write to uprobe_events debugfs file */
+       symbol_name = format_symbol_name(symbol);
+
+       if (!fetch_args)
+               fetch_args = " ";
+
+       if (ret_probe) {
+               snprintf(event, 64, "ktap_kprobes_%d/ret_%s",
+                        getpid(), symbol_name);
+               snprintf(probe_event, 128, "r:%s %s %s",
+                        event, symbol, fetch_args);
+       } else {
+               snprintf(event, 64, "ktap_kprobes_%d/%s",
+                        getpid(), symbol_name);
+               snprintf(probe_event, 128, "p:%s %s %s",
+                        event, symbol, fetch_args);
+       }
+
+       sprintf(event_id_path, "/sys/kernel/debug/tracing/events/%s/id", event);
+       /* if event id already exist, then don't write to kprobes_event again */
+       id_fd = open(event_id_path, O_RDONLY);
+       if (id_fd > 0) {
+               close(id_fd);
+
+               /* remember add event id to ids_array */
+               ret = add_event(event_id_path);
+               if (ret)
+                       goto error;
+
+               goto out;
+       }
+
+       verbose_printf("write kprobe event %s\n", probe_event);
+
+       if (write(fd, probe_event, strlen(probe_event)) <= 0) {
+               fprintf(stderr, "Cannot write %s to %s\n", probe_event,
+                               KPROBE_EVENTS_PATH);
+               goto error;
+       }
+
+       /* add to cleanup list */
+       pl = malloc(sizeof(struct probe_list));
+       if (!pl)
+               goto error;
+
+       pl->type = KPROBE_EVENT;
+       pl->next = probe_list_head;
+       memcpy(pl->event, event, 64);
+       probe_list_head = pl;
+
+       ret = add_event(event_id_path);
+       if (ret < 0)
+               goto error;
+
+ out:
+       free(symbol_name);
+       return 0;
+
+ error:
+       free(symbol_name);
+       return -1;
+}
+
+static unsigned long core_kernel_text_start;
+static unsigned long core_kernel_text_end;
+static unsigned long kprobes_text_start;
+static unsigned long kprobes_text_end;
+
+static void init_kprobe_prohibited_area(void)
+{
+       static int once = 0;
+
+       if (once > 0)
+               return;
+
+       once = 1;
+
+       core_kernel_text_start = find_kernel_symbol("_stext");
+       core_kernel_text_end   = find_kernel_symbol("_etext");
+       kprobes_text_start     = find_kernel_symbol("__kprobes_text_start");
+       kprobes_text_end       = find_kernel_symbol("__kprobes_text_end");
+}
+
+static int check_kprobe_addr_prohibited(unsigned long addr)
+{
+       if (addr <= core_kernel_text_start || addr >= core_kernel_text_end)
+               return -1;
+
+       if (addr >= kprobes_text_start && addr <= kprobes_text_end)
+               return -1;
+
+       return 0;
+}
+
+struct probe_cb_base {
+       int fd;
+       int ret_probe;
+       const char *event;
+       char *binary;
+       char *symbol;
+       char *fetch_args;
+};
+
+static int kprobe_symbol_actor(void *arg, const char *name, char type,
+                              unsigned long start)
+{
+       struct probe_cb_base *base = (struct probe_cb_base *)arg;
+
+       /* only can probe text function */
+       if (type != 't' && type != 'T')
+               return 0;
+
+       if (!strglobmatch(name, base->symbol))
+               return 0;
+
+       if (check_kprobe_addr_prohibited(start))
+               return 0;
+
+       return write_kprobe_event(base->fd, base->ret_probe, name,
+                                 base->fetch_args);
+}
+
+static int parse_events_add_kprobe(char *event)
+{
+       char *symbol, *end;
+       struct probe_cb_base base;
+       int fd, ret;
+
+       fd = open(KPROBE_EVENTS_PATH, O_WRONLY);
+       if (fd < 0) {
+               fprintf(stderr, "Cannot open %s\n", KPROBE_EVENTS_PATH);
+               return -1;
+       }
+
+       end = strpbrk(event, "% ");
+       if (end)
+               symbol = strndup(event, end - event);
+       else
+               symbol = strdup(event);
+
+       base.fd = fd;
+       base.ret_probe = !!strstr(event, "%return");
+       base.symbol = symbol;
+       base.fetch_args = strchr(event, ' ');
+
+       init_kprobe_prohibited_area();
+
+       ret = kallsyms_parse(&base, kprobe_symbol_actor);
+       if (ret < 0)
+               fprintf(stderr, "cannot parse symbol \"%s\"\n", symbol);
+
+       free(symbol);
+       close(fd);
+
+       return ret;
+}
+
+#define UPROBE_EVENTS_PATH "/sys/kernel/debug/tracing/uprobe_events"
+
+/**
+ * @return 0 on success, otherwise -1
+ */
+static int
+write_uprobe_event(int fd, int ret_probe, const char *binary,
+                  const char *symbol, unsigned long addr,
+                  char *fetch_args)
+{
+       char probe_event[128] = {0};
+       char event[64] = {0};
+       struct probe_list *pl;
+       char event_id_path[128] = {0};
+       char *symbol_name;
+       int id_fd, ret;
+
+       /* In case some symbols cannot write to uprobe_events debugfs file */
+       symbol_name = format_symbol_name(symbol);
+
+       if (!fetch_args)
+               fetch_args = " ";
+
+       if (ret_probe) {
+               snprintf(event, 64, "ktap_uprobes_%d/ret_%s",
+                        getpid(), symbol_name);
+               snprintf(probe_event, 128, "r:%s %s:0x%lx %s",
+                        event, binary, addr, fetch_args);
+       } else {
+               snprintf(event, 64, "ktap_uprobes_%d/%s",
+                        getpid(), symbol_name);
+               snprintf(probe_event, 128, "p:%s %s:0x%lx %s",
+                        event, binary, addr, fetch_args);
+       }
+
+       sprintf(event_id_path, "/sys/kernel/debug/tracing/events/%s/id", event);
+       /* if event id already exist, then don't write to uprobes_event again */
+       id_fd = open(event_id_path, O_RDONLY);
+       if (id_fd > 0) {
+               close(id_fd);
+
+               /* remember add event id to ids_array */
+               ret = add_event(event_id_path);
+               if (ret)
+                       goto error;
+
+               goto out;
+       }
+
+       verbose_printf("write uprobe event %s\n", probe_event);
+
+       if (write(fd, probe_event, strlen(probe_event)) <= 0) {
+               fprintf(stderr, "Cannot write %s to %s\n", probe_event,
+                               UPROBE_EVENTS_PATH);
+               goto error;
+       }
+
+       /* add to cleanup list */
+       pl = malloc(sizeof(struct probe_list));
+       if (!pl)
+               goto error;
+
+       pl->type = UPROBE_EVENT;
+       pl->next = probe_list_head;
+       memcpy(pl->event, event, 64);
+       probe_list_head = pl;
+
+       ret = add_event(event_id_path);
+       if (ret < 0)
+               goto error;
+
+ out:
+       free(symbol_name);
+       return 0;
+
+ error:
+       free(symbol_name);
+       return -1;
+}
+
+/**
+ * TODO: avoid copy-paste stuff
+ *
+ * @return 1 on success, otherwise 0
+ */
+#ifdef NO_LIBELF
+static int parse_events_resolve_symbol(int fd, char *event, int type)
+{
+       char *colon, *binary, *fetch_args;
+       unsigned long symbol_address;
+
+       colon = strchr(event, ':');
+       if (!colon)
+               return -1;
+
+       symbol_address = strtol(colon + 1 /* skip ":" */, NULL, 0);
+
+       fetch_args = strchr(event, ' ');
+
+       /**
+        * We already have address, no need in resolving.
+        */
+       if (symbol_address) {
+               int ret;
+
+               binary = strndup(event, colon - event);
+               ret = write_uprobe_event(fd, !!strstr(event, "%return"), binary,
+                                        "NULL", symbol_address, fetch_args);
+               free(binary);
+               return ret;
+       }
+
+       fprintf(stderr, "error: cannot resolve event \"%s\" without libelf, "
+                       "please recompile ktap with NO_LIBELF disabled\n",
+                       event);
+       exit(EXIT_FAILURE);
+       return -1;
+}
+
+#else
+static int uprobe_symbol_actor(const char *name, vaddr_t addr, void *arg)
+{
+       struct probe_cb_base *base = (struct probe_cb_base *)arg;
+       int ret;
+
+       if (!strglobmatch(name, base->symbol))
+               return 0;
+
+       verbose_printf("uprobe: binary: \"%s\" symbol \"%s\" "
+                       "resolved to 0x%lx\n",
+                       base->binary, base->symbol, addr);
+
+       ret = write_uprobe_event(base->fd, base->ret_probe, base->binary,
+                                name, addr, base->fetch_args);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int parse_events_resolve_symbol(int fd, char *event, int type)
+{
+       char *colon, *end;
+       vaddr_t symbol_address;
+       int ret;
+       struct probe_cb_base base = {
+               .fd = fd,
+               .event = event
+       };
+
+       colon = strchr(event, ':');
+       if (!colon)
+               return 0;
+
+       base.ret_probe = !!strstr(event, "%return");
+       symbol_address = strtol(colon + 1 /* skip ":" */, NULL, 0);
+       base.binary = strndup(event, colon - event);
+
+       base.fetch_args = strchr(event, ' ');
+
+       /*
+        * We already have address, no need in resolving.
+        */
+       if (symbol_address) {
+               int ret;
+               ret = write_uprobe_event(fd, base.ret_probe, base.binary,
+                                        "NULL", symbol_address,
+                                        base.fetch_args);
+               free(base.binary);
+               return ret;
+       }
+
+       end = strpbrk(event, "% ");
+       if (end)
+               base.symbol = strndup(colon + 1, end - 1 - colon);
+       else
+               base.symbol = strdup(colon + 1);
+
+       ret = parse_dso_symbols(base.binary, type, uprobe_symbol_actor,
+                               (void *)&base);
+       if (!ret) {
+               fprintf(stderr, "error: cannot find symbol %s in binary %s\n",
+                       base.symbol, base.binary);
+               ret = -1;
+       } else if(ret > 0) {
+               /* no error found when parse symbols */
+               ret = 0;
+       }
+
+       free(base.binary);
+       free(base.symbol);
+
+       return ret;
+}
+#endif
+
+static int parse_events_add_uprobe(char *old_event, int type)
+{
+       int ret;
+       int fd;
+
+       fd = open(UPROBE_EVENTS_PATH, O_WRONLY);
+       if (fd < 0) {
+               fprintf(stderr, "Cannot open %s\n", UPROBE_EVENTS_PATH);
+               return -1;
+       }
+
+       ret = parse_events_resolve_symbol(fd, old_event, type);
+
+       close(fd);
+       return ret;
+}
+
+static int parse_events_add_probe(char *old_event)
+{
+       char *separator;
+
+       separator = strchr(old_event, ':');
+       if (!separator || (separator == old_event))
+               return parse_events_add_kprobe(old_event);
+       else
+               return parse_events_add_uprobe(old_event, FIND_SYMBOL);
+}
+
+static int parse_events_add_sdt(char *old_event)
+{
+       return parse_events_add_uprobe(old_event, FIND_STAPSDT_NOTE);
+}
+
+static void strim(char *s)
+{
+       size_t size;
+       char *end;
+
+       size = strlen(s);
+       if (!size)
+               return;
+
+       end = s + size -1;
+       while (end >= s && isspace(*end))
+               end--;
+
+       *(end + 1) = '\0';
+}
+
+static int get_sys_event_filter_str(char *start,
+                                   char **sys, char **event, char **filter)
+{
+       char *separator, *separator2, *ptr, *end;
+
+       while (*start == ' ')
+               start++;
+
+       /* find sys */
+       separator = strchr(start, ':');
+       if (!separator || (separator == start)) {
+               return -1;
+       }
+
+       ptr = malloc(separator - start + 1);
+       if (!ptr)
+               return -1;
+
+       strncpy(ptr, start, separator - start);
+       ptr[separator - start] = '\0';
+
+       strim(ptr);
+       *sys = ptr;
+
+       if (!strcmp(*sys, "probe") && (*(separator + 1) == '/')) {
+               /* it's uprobe event */
+               separator2 = strchr(separator + 1, ':');
+               if (!separator2)
+                       return -1;
+       } else
+               separator2 = separator;
+
+       /* find filter */
+       end = start + strlen(start);
+       while (*--end == ' ') {
+       }
+
+       if (*end == '/') {
+               char *filter_start;
+
+               filter_start = strchr(separator2, '/');
+               if (filter_start == end)
+                       return -1;
+
+               ptr = malloc(end - filter_start + 2);
+               if (!ptr)
+                       return -1;
+
+               memcpy(ptr, filter_start, end - filter_start + 1);
+               ptr[end - filter_start + 1] = '\0';
+
+               *filter = ptr;
+
+               end = filter_start;
+       } else {
+               *filter = NULL;
+               end++;
+       }
+
+       /* find event */
+       ptr = malloc(end - separator);
+       if (!ptr)
+               return -1;
+
+       memcpy(ptr, separator + 1, end - separator - 1);
+       ptr[end - separator - 1] = '\0';
+
+       strim(ptr);
+       *event = ptr;
+
+       return 0;
+}
+
+static char *get_next_eventdef(char *str)
+{
+       char *separator;
+
+       separator = strchr(str, ',');
+       if (!separator)
+               return str + strlen(str);
+
+       *separator = '\0';
+       return separator + 1;
+}
+
+ktap_eventdef_info *ktapc_parse_eventdef(const char *eventdef)
+{
+       char *str = strdup(eventdef);
+       char *sys, *event, *filter, *next;
+       ktap_eventdef_info *evdef_info;
+       int ret;
+
+       idmap_init();
+
+ parse_next_eventdef:
+       next = get_next_eventdef(str);
+
+       if (get_sys_event_filter_str(str, &sys, &event, &filter))
+               goto error;
+
+       verbose_printf("parse_eventdef: sys[%s], event[%s], filter[%s]\n",
+                      sys, event, filter);
+
+       if (!strcmp(sys, "probe"))
+               ret = parse_events_add_probe(event);
+       else if (!strcmp(sys, "sdt"))
+               ret = parse_events_add_sdt(event);
+       else
+               ret = parse_events_add_tracepoint(sys, event);
+
+       if (ret)
+               goto error;
+
+       /* don't trace ftrace:function when all tracepoints enabled */
+       if (!strcmp(sys, "*"))
+               idmap_clear(1);
+
+
+       if (filter && *next != '\0') {
+               fprintf(stderr, "Error: eventdef only can append one filter\n");
+               goto error;
+       }
+
+       str = next;
+       if (*next != '\0')
+               goto parse_next_eventdef;
+
+       evdef_info = malloc(sizeof(*evdef_info));
+       if (!evdef_info)
+               goto error;
+
+       evdef_info->nr = id_nr;
+       evdef_info->id_arr = get_id_array();
+       evdef_info->filter = filter;
+
+       idmap_free();
+       return evdef_info;
+ error:
+       idmap_free();
+       cleanup_event_resources();
+       return NULL;
+}
+
+void cleanup_event_resources(void)
+{
+       struct probe_list *pl;
+       const char *path;
+       char probe_event[128] = {0};
+       int fd, ret;
+
+       for (pl = probe_list_head; pl; pl = pl->next) {
+               if (pl->type == KPROBE_EVENT)
+                       path = KPROBE_EVENTS_PATH;
+               else if (pl->type == UPROBE_EVENT)
+                       path = UPROBE_EVENTS_PATH;
+               else {
+                       fprintf(stderr, "Cannot cleanup event type %d\n",
+                                       pl->type);
+                       continue;
+               }
+
+               snprintf(probe_event, 128, "-:%s", pl->event);
+
+               fd = open(path, O_WRONLY);
+               if (fd < 0) {
+                       fprintf(stderr, "Cannot open %s\n", UPROBE_EVENTS_PATH);
+                       continue;
+               }
+
+               ret = write(fd, probe_event, strlen(probe_event));
+               if (ret <= 0) {
+                       fprintf(stderr, "Cannot write %s to %s\n", probe_event,
+                                       path);
+                       close(fd);
+                       continue;
+               }
+
+               close(fd);
+       }
+}
+
diff --git a/drivers/staging/ktap/userspace/ffi/cparser.c b/drivers/staging/ktap/userspace/ffi/cparser.c
new file mode 100644 (file)
index 0000000..9f9a429
--- /dev/null
@@ -0,0 +1,1755 @@
+#include <stdarg.h>
+#include "../cparser.h"
+
+#define IS_CONST(tok) (IS_LITERAL(tok, "const") || IS_LITERAL(tok, "__const") \
+                       || IS_LITERAL(tok, "__const__"))
+#define IS_VOLATILE(tok) (IS_LITERAL(tok, "volatile") || \
+                               IS_LITERAL(tok, "__volatile") || \
+                               IS_LITERAL(tok, "__volatile__"))
+#define IS_RESTRICT(tok) (IS_LITERAL(tok, "restrict") || \
+                               IS_LITERAL(tok, "__restrict") || \
+                               IS_LITERAL(tok, "__restrict__"))
+
+#define max(a,b) ((a) < (b) ? (b) : (a))
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+
+enum etoken {
+       /* 0 - 3 */
+       TOK_NIL,
+       TOK_NUMBER,
+       TOK_STRING,
+       TOK_TOKEN,
+
+       /* the order of these values must match the token strings in lex.c */
+
+       /* 4 - 5 */
+       TOK_3_BEGIN,
+       TOK_VA_ARG,
+
+       /* 6 - 14 */
+       TOK_2_BEGIN,
+       TOK_LEFT_SHIFT, TOK_RIGHT_SHIFT, TOK_LOGICAL_AND, TOK_LOGICAL_OR,
+       TOK_LESS_EQUAL, TOK_GREATER_EQUAL, TOK_EQUAL, TOK_NOT_EQUAL,
+
+       /* 15 - 20 */
+       TOK_1_BEGIN,
+       TOK_OPEN_CURLY, TOK_CLOSE_CURLY, TOK_SEMICOLON, TOK_COMMA, TOK_COLON,
+       /* 21 - 30 */
+       TOK_ASSIGN, TOK_OPEN_PAREN, TOK_CLOSE_PAREN, TOK_OPEN_SQUARE, TOK_CLOSE_SQUARE,
+       TOK_DOT, TOK_AMPERSAND, TOK_LOGICAL_NOT, TOK_BITWISE_NOT, TOK_MINUS,
+       /* 31 - 40 */
+       TOK_PLUS, TOK_STAR, TOK_DIVIDE, TOK_MODULUS, TOK_LESS,
+       TOK_GREATER, TOK_BITWISE_XOR, TOK_BITWISE_OR, TOK_QUESTION, TOK_POUND,
+
+       /* 41 - 43 */
+       TOK_REFERENCE = TOK_AMPERSAND,
+       TOK_MULTIPLY = TOK_STAR,
+       TOK_BITWISE_AND = TOK_AMPERSAND,
+};
+
+struct token {
+       enum etoken type;
+       int64_t integer;
+       const char *str;
+       size_t size;
+};
+
+#define IS_LITERAL(TOK, STR) \
+       (((TOK).size == sizeof(STR) - 1) && \
+               0 == memcmp((TOK).str, STR, sizeof(STR) - 1))
+
+
+static int parse_type_name(struct parser *P, char *type_name);
+static void parse_argument(struct parser *P, struct cp_ctype *ct,
+               struct token *pname, struct parser *asmname);
+static int parse_attribute(struct parser *P, struct token *tok,
+               struct cp_ctype *ct, struct parser *asmname);
+static int parse_record(struct parser *P, struct cp_ctype *ct);
+static void instantiate_typedef(struct parser *P, struct cp_ctype *tt,
+               const struct cp_ctype *ft);
+
+
+/* the order of tokens _must_ match the order of the enum etoken enum */
+
+static char tok3[][4] = {
+       "...", /* unused ">>=", "<<=", */
+};
+
+static char tok2[][3] = {
+       "<<", ">>", "&&", "||", "<=",
+       ">=", "==", "!=",
+       /* unused "+=", "-=", "*=", "/=", "%=", "&=", "^=",
+        * "|=", "++", "--", "->", "::", */
+};
+
+static char tok1[] = {
+       '{', '}', ';', ',', ':',
+       '=', '(', ')', '[', ']',
+       '.', '&', '!', '~', '-',
+       '+', '*', '/', '%', '<',
+       '>', '^', '|', '?', '#'
+};
+
+
+/* this function never returns, but it's an idiom to use it in C functions
+ * as return cp_error */
+void cp_error(const char *err_msg_fmt, ...)
+{
+       va_list ap;
+
+       fprintf(stderr, "cparser error:\n");
+
+       va_start(ap, err_msg_fmt);
+       vfprintf(stderr, err_msg_fmt, ap);
+       va_end(ap);
+
+       exit(EXIT_FAILURE);
+}
+
+static int set_struct_type_name(char *dst, const char *src, int len)
+{
+       int prefix_len = sizeof("struct ");
+
+       if (len + prefix_len > MAX_TYPE_NAME_LEN)
+               return -1;
+
+       memset(dst, 0, MAX_TYPE_NAME_LEN);
+       strcpy(dst, "struct ");
+       strncat(dst, src, len);
+
+       return 0;
+}
+
+static void increase_ptr_deref_level(struct parser *P, struct cp_ctype *ct)
+{
+       if (ct->pointers == POINTER_MAX) {
+               cp_error("maximum number of pointer derefs reached - use a "
+                       "struct to break up the pointers on line %d", P->line);
+       } else {
+               ct->pointers++;
+               ct->const_mask <<= 1;
+       }
+}
+
+static int next_token(struct parser *P, struct token *tok)
+{
+       size_t i;
+       const char *s = P->next;
+
+       /* UTF8 BOM */
+       if (s[0] == '\xEF' && s[1] == '\xBB' && s[2] == '\xBF') {
+               s += 3;
+       }
+
+       /* consume whitespace and comments */
+       for (;;) {
+               /* consume whitespace */
+               while (*s == '\t' || *s == '\n' || *s == ' '
+                               || *s == '\v' || *s == '\r') {
+                       if (*s == '\n') {
+                               P->line++;
+                       }
+                       s++;
+               }
+
+               /* consume comments */
+               if (*s == '/' && *(s+1) == '/') {
+
+                       s = strchr(s, '\n');
+                       if (!s) {
+                               cp_error("non-terminated comment");
+                       }
+
+               } else if (*s == '/' && *(s+1) == '*') {
+                       s += 2;
+
+                       for (;;) {
+                               if (s[0] == '\0') {
+                                       cp_error("non-terminated comment");
+                               } else if (s[0] == '*' && s[1] == '/') {
+                                       s += 2;
+                                       break;
+                               } else if (s[0] == '\n') {
+                                       P->line++;
+                               }
+                               s++;
+                       }
+
+               } else if (*s == '\0') {
+                       tok->type = TOK_NIL;
+                       return 0;
+
+               } else {
+                       break;
+               }
+       }
+
+       P->prev = s;
+
+       for (i = 0; i < sizeof(tok3) / sizeof(tok3[0]); i++) {
+               if (s[0] == tok3[i][0] && s[1] == tok3[i][1] && s[2] == tok3[i][2]) {
+                       tok->type = (enum etoken) (TOK_3_BEGIN + 1 + i);
+                       P->next = s + 3;
+                       goto end;
+               }
+       }
+
+       for (i = 0; i < sizeof(tok2) / sizeof(tok2[0]); i++) {
+               if (s[0] == tok2[i][0] && s[1] == tok2[i][1]) {
+                       tok->type = (enum etoken) (TOK_2_BEGIN + 1 + i);
+                       P->next = s + 2;
+                       goto end;
+               }
+       }
+
+       for (i = 0; i < sizeof(tok1) / sizeof(tok1[0]); i++) {
+               if (s[0] == tok1[i]) {
+                       tok->type = (enum etoken) (TOK_1_BEGIN + 1 + i);
+                       P->next = s + 1;
+                       goto end;
+               }
+       }
+
+       if (*s == '.' || *s == '-' || ('0' <= *s && *s <= '9')) {
+               /* number */
+               tok->type = TOK_NUMBER;
+
+               /* split out the negative case so we get the full range of
+                * bits for unsigned (eg to support 0xFFFFFFFF where
+                * sizeof(long) == 4 */
+               if (*s == '-') {
+                       tok->integer = strtol(s, (char**) &s, 0);
+               } else {
+                       tok->integer = strtoul(s, (char**) &s, 0);
+               }
+
+               while (*s == 'u' || *s == 'U' || *s == 'l' || *s == 'L') {
+                       s++;
+               }
+
+               P->next = s;
+               goto end;
+
+       } else if (*s == '\'' || *s == '\"') {
+               /* "..." or '...' */
+               char quote = *s;
+               s++; /* jump over " */
+
+               tok->type = TOK_STRING;
+               tok->str = s;
+
+               while (*s != quote) {
+                       if (*s == '\0' || (*s == '\\' && *(s+1) == '\0')) {
+                               cp_error("string not finished\n");
+                       }
+                       if (*s == '\\') {
+                               s++;
+                       }
+                       s++;
+               }
+
+               tok->size = s - tok->str;
+               s++; /* jump over " */
+               P->next = s;
+               goto end;
+
+       } else if (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z')
+                       || *s == '_') {
+               /* tokens */
+               tok->type = TOK_TOKEN;
+               tok->str = s;
+
+               while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z')
+                               || *s == '_' || ('0' <= *s && *s <= '9')) {
+                       s++;
+               }
+
+               tok->size = s - tok->str;
+               P->next = s;
+               goto end;
+       } else {
+               cp_error("invalid character %d", P->line);
+       }
+
+end:
+       return 1;
+}
+
+static void require_token(struct parser *P, struct token *tok)
+{
+       if (!next_token(P, tok)) {
+               cp_error("unexpected end");
+       }
+}
+
+static void check_token(struct parser *P, int type, const char *str,
+                               const char *err, ...)
+{
+       va_list ap;
+       struct token tok;
+       if (!next_token(P, &tok) || tok.type != type
+                       || (tok.type == TOK_TOKEN && (tok.size != strlen(str)
+                               || memcmp(tok.str, str, tok.size) != 0))) {
+
+               va_start(ap, err);
+               vfprintf(stderr, err, ap);
+               va_end(ap);
+
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void put_back(struct parser *P) {
+       P->next = P->prev;
+}
+
+int64_t calculate_constant(struct parser *P);
+
+/* parses out the base type of a type expression in a function declaration,
+ * struct definition, typedef etc
+ *
+ * leaves the usr value of the type on the stack
+ */
+int parse_type(struct parser *P, struct cp_ctype *ct)
+{
+       struct token tok;
+
+       memset(ct, 0, sizeof(*ct));
+
+       require_token(P, &tok);
+
+       /* get function attributes before the return type */
+       while (parse_attribute(P, &tok, ct, NULL)) {
+               require_token(P, &tok);
+       }
+
+       /* get const/volatile before the base type */
+       for (;;) {
+               if (tok.type != TOK_TOKEN) {
+                       cp_error("unexpected value before type name on line %d",
+                                       P->line);
+                       return 0;
+               } else if (IS_CONST(tok)) {
+                       ct->const_mask = 1;
+                       require_token(P, &tok);
+               } else if (IS_VOLATILE(tok) || IS_RESTRICT(tok)) {
+                       /* ignored for now */
+                       require_token(P, &tok);
+               } else {
+                       break;
+               }
+       }
+
+       /* get base type */
+       if (tok.type != TOK_TOKEN) {
+               cp_error("unexpected value before type name on line %d", P->line);
+               return 0;
+       } else if (IS_LITERAL(tok, "struct")) {
+               ct->type = STRUCT_TYPE;
+               parse_record(P, ct);
+       } else if (IS_LITERAL(tok, "union")) {
+               ct->type = UNION_TYPE;
+               parse_record(P, ct);
+       } else if (IS_LITERAL(tok, "enum")) {
+               ct->type = ENUM_TYPE;
+               parse_record(P, ct);
+       } else {
+               put_back(P);
+
+               /* lookup type */
+               struct cp_ctype *lct;
+               char cur_type_name[MAX_TYPE_NAME_LEN];
+
+               memset(cur_type_name, 0, MAX_TYPE_NAME_LEN);
+               parse_type_name(P, cur_type_name);
+               lct = ctype_lookup_type(cur_type_name);
+               if (!lct)
+                       cp_error("unknow type: \"%s\"\n", cur_type_name);
+
+               instantiate_typedef(P, ct, lct);
+       }
+
+       while (next_token(P, &tok)) {
+               if (tok.type != TOK_TOKEN) {
+                       put_back(P);
+                       break;
+               } else if (IS_CONST(tok) || IS_VOLATILE(tok)) {
+                       /* ignore for now */
+               } else {
+                       put_back(P);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+enum test {TEST};
+
+/* Parses an enum definition from after the open curly through to the close
+ * curly. Expects the user table to be on the top of the stack
+ */
+static int parse_enum(struct parser *P, struct cp_ctype *type)
+{
+       struct token tok;
+       int value = -1;
+
+       /*@TODO clean up this function when enum support is added*/
+       cp_error("TODO: enum not supported!\n");
+
+       for (;;) {
+               require_token(P, &tok);
+
+               if (tok.type == TOK_CLOSE_CURLY) {
+                       break;
+               } else if (tok.type != TOK_TOKEN) {
+                       cp_error("unexpected token in enum at line %d", P->line);
+                       return 0;
+               }
+               require_token(P, &tok);
+
+               if (tok.type == TOK_COMMA || tok.type == TOK_CLOSE_CURLY) {
+                       /* we have an auto calculated enum value */
+                       value++;
+               } else if (tok.type == TOK_ASSIGN) {
+                       /* we have an explicit enum value */
+                       value = (int) calculate_constant(P);
+                       require_token(P, &tok);
+               } else {
+                       cp_error("unexpected token in enum at line %d", P->line);
+                       return 0;
+               }
+
+               if (tok.type == TOK_CLOSE_CURLY) {
+                       break;
+               } else if (tok.type != TOK_COMMA) {
+                       cp_error("unexpected token in enum at line %d", P->line);
+                       return 0;
+               }
+       }
+
+       type->base_size = sizeof(enum test);
+       type->align_mask = sizeof(enum test) - 1;
+
+       return 0;
+}
+
+/* Parses a struct from after the open curly through to the close curly. */
+static int parse_struct(struct parser *P, const struct cp_ctype *ct)
+{
+       struct token tok;
+
+       /* parse members */
+       for (;;) {
+               struct cp_ctype mbase;
+
+               /* see if we're at the end of the struct */
+               require_token(P, &tok);
+               if (tok.type == TOK_CLOSE_CURLY) {
+                       break;
+               } else if (ct->is_variable_struct) {
+                       cp_error("can't have members after a variable sized "
+                                       "member on line %d", P->line);
+                       return -1;
+               } else {
+                       put_back(P);
+               }
+
+               /* members are of the form
+                * <base type> <arg>, <arg>, <arg>;
+                * eg struct foo bar, *bar2[2];
+                * mbase is 'struct foo'
+                * mtype is '' then '*[2]'
+                * mname is 'bar' then 'bar2'
+                */
+
+               parse_type(P, &mbase);
+
+               for (;;) {
+                       struct token mname;
+                       struct cp_ctype mt = mbase;
+
+                       memset(&mname, 0, sizeof(mname));
+
+                       if (ct->is_variable_struct) {
+                               cp_error("can't have members after a variable "
+                                       "sized member on line %d", P->line);
+                               return -1;
+                       }
+
+                       parse_argument(P, &mt, &mname, NULL);
+
+                       if (!mt.is_defined && (mt.pointers - mt.is_array) == 0) {
+                               cp_error("member type is undefined on line %d",
+                                               P->line);
+                               return -1;
+                       }
+
+                       if (mt.type == VOID_TYPE
+                                       && (mt.pointers - mt.is_array) == 0) {
+                               cp_error("member type can not be void on line %d",
+                                               P->line);
+                               return -1;
+                       }
+
+                       mt.has_member_name = (mname.size > 0);
+                       if (mt.has_member_name) {
+                               cp_push_ctype_with_name(&mt,
+                                               mname.str, mname.size);
+                       } else {
+                               /* @TODO handle unnamed member (houqp) */
+                               cp_error("TODO: unnamed member not supported.");
+                               cp_push_ctype(&mt);
+                       }
+
+                       require_token(P, &tok);
+                       if (tok.type == TOK_SEMICOLON) {
+                               break;
+                       } else if (tok.type != TOK_COMMA) {
+                               cp_error("unexpected token in struct "
+                                       "definition on line %d", P->line);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/* copy over attributes that could be specified before the typedef eg
+ * __attribute__(packed) const type_t */
+static void instantiate_typedef(struct parser *P, struct cp_ctype *tt,
+               const struct cp_ctype *ft)
+{
+       struct cp_ctype pt = *tt;
+       *tt = *ft;
+
+       tt->const_mask |= pt.const_mask;
+       tt->is_packed = pt.is_packed;
+
+       if (tt->is_packed) {
+               tt->align_mask = 0;
+       } else {
+               /* Instantiate the typedef in the current packing. This may be
+                * further updated if a pointer is added or another alignment
+                * attribute is applied. If pt.align_mask is already non-zero
+                * than an increased alignment via __declspec(aligned(#)) has
+                * been set. */
+               tt->align_mask = max(min(P->align_mask, tt->align_mask),
+                                       pt.align_mask);
+       }
+}
+
+/* this parses a struct or union starting with the optional
+ * name before the opening brace
+ * leaves the type usr value on the stack */
+static int parse_record(struct parser *P, struct cp_ctype *ct)
+{
+       struct token tok;
+       char cur_type_name[MAX_TYPE_NAME_LEN];
+
+       require_token(P, &tok);
+
+       /* name is optional */
+       if (tok.type == TOK_TOKEN) {
+               /* declaration */
+               struct cp_ctype *lct;
+
+               memset(cur_type_name, 0, MAX_TYPE_NAME_LEN);
+               set_struct_type_name(cur_type_name, tok.str, tok.size);
+;
+               /* lookup the name to see if we've seen this type before */
+               lct = ctype_lookup_type(cur_type_name);
+
+               if (!lct) {
+                       /* new type, delay type registration to the end
+                        * of this function */
+               } else {
+                       /* get the exsting declared type */
+                       if (lct->type != ct->type) {
+                               cp_error("type '%s' previously declared as '%s'",
+                                       cur_type_name,
+                                       csym_name(ct_ffi_cs(lct)));
+                       }
+
+                       instantiate_typedef(P, ct, lct);
+               }
+
+               /* if a name is given then we may be at the end of the string
+                * eg for ffi.new('struct foo') */
+               if (!next_token(P, &tok)) {
+                       return 0;
+               }
+       } else {
+               /* create a new unnamed record */
+
+               /*@TODO clean this up after unnamed record support is added */
+               cp_error("TODO: support unnamed record.\n");
+       }
+
+       if (tok.type != TOK_OPEN_CURLY) {
+               /* this may just be a declaration or use of the type as an
+                * argument or member */
+               put_back(P);
+               return 0;
+       }
+
+       if (ct->is_defined) {
+               cp_error("redefinition in line %d", P->line);
+               return 0;
+       }
+
+       if (ct->type == ENUM_TYPE) {
+               parse_enum(P, ct);
+       } else {
+               /* we do a two stage parse, where we parse the content first
+                * and build up the temp user table. We then iterate over that
+                * to calculate the offsets and fill out ct_usr. This is so we
+                * can handle out of order members (eg vtable) and attributes
+                * specified at the end of the struct. */
+               parse_struct(P, ct);
+               /* build symbol for vm */
+               ct->ffi_cs_id = cp_symbol_build_struct(cur_type_name);
+               /* save cp_ctype for parser */
+               cp_ctype_reg_type(cur_type_name, ct);
+       }
+
+       cp_set_defined(ct);
+       return 0;
+}
+
+/* parses single or multi work built in types, and pushes it onto the stack */
+static int parse_type_name(struct parser *P, char *type_name)
+{
+       struct token tok;
+       int flags = 0;
+
+       enum {
+               UNSIGNED = 0x01,
+               SIGNED = 0x02,
+               LONG = 0x04,
+               SHORT = 0x08,
+               INT = 0x10,
+               CHAR = 0x20,
+               LONG_LONG = 0x40,
+               INT8 = 0x80,
+               INT16 = 0x100,
+               INT32 = 0x200,
+               INT64 = 0x400,
+       };
+
+       require_token(P, &tok);
+
+       /* we have to manually decode the builtin types since they can take up
+        * more then one token */
+       for (;;) {
+               if (tok.type != TOK_TOKEN) {
+                       break;
+               } else if (IS_LITERAL(tok, "unsigned")) {
+                       flags |= UNSIGNED;
+               } else if (IS_LITERAL(tok, "signed")) {
+                       flags |= SIGNED;
+               } else if (IS_LITERAL(tok, "short")) {
+                       flags |= SHORT;
+               } else if (IS_LITERAL(tok, "char")) {
+                       flags |= CHAR;
+               } else if (IS_LITERAL(tok, "long")) {
+                       flags |= (flags & LONG) ? LONG_LONG : LONG;
+               } else if (IS_LITERAL(tok, "int")) {
+                       flags |= INT;
+               } else if (IS_LITERAL(tok, "__int8")) {
+                       flags |= INT8;
+               } else if (IS_LITERAL(tok, "__int16")) {
+                       flags |= INT16;
+               } else if (IS_LITERAL(tok, "__int32")) {
+                       flags |= INT32;
+               } else if (IS_LITERAL(tok, "__int64")) {
+                       flags |= INT64;
+               } else if (IS_LITERAL(tok, "register")) {
+                       /* ignore */
+               } else {
+                       break;
+               }
+
+               if (!next_token(P, &tok)) {
+                       break;
+               }
+       }
+
+       if (flags) {
+               put_back(P);
+       }
+
+       if (flags & CHAR) {
+               if (flags & SIGNED) {
+                       strcpy(type_name, "int8_t");
+               } else if (flags & UNSIGNED) {
+                       strcpy(type_name, "uint8_t");
+               } else {
+                       if (((char) -1) > 0) {
+                               strcpy(type_name, "uint8_t");
+                       } else {
+                               strcpy(type_name, "int8_t");
+                       }
+               }
+       } else if (flags & INT8) {
+               strcpy(type_name, (flags & UNSIGNED) ? "uint8_t" : "int8_t");
+       } else if (flags & INT16) {
+               strcpy(type_name, (flags & UNSIGNED) ? "uint16_t" : "int16_t");
+       } else if (flags & INT32) {
+               strcpy(type_name, (flags & UNSIGNED) ? "uint32_t" : "int32_t");
+       } else if (flags & INT64) {
+               strcpy(type_name, (flags & UNSIGNED) ? "uint64_t" : "int64_t");
+       } else if (flags & LONG_LONG) {
+               strcpy(type_name, (flags & UNSIGNED) ? "uint64_t" : "int64_t");
+       } else if (flags & SHORT) {
+#define SHORT_TYPE(u) (sizeof(short) == sizeof(int64_t) ? \
+               u "int64_t" : sizeof(short) == sizeof(int32_t) ? \
+               u "int32_t" : u "int16_t")
+               if (flags & UNSIGNED) {
+                       strcpy(type_name, SHORT_TYPE("u"));
+               } else {
+                       strcpy(type_name, SHORT_TYPE(""));
+               }
+#undef SHORT_TYPE
+       } else if (flags & LONG) {
+#define LONG_TYPE(u) (sizeof(long) == sizeof(int64_t) ? \
+               u "int64_t" : u "int32_t")
+               if (flags & UNSIGNED) {
+                       strcpy(type_name, LONG_TYPE("u"));
+               } else {
+                       strcpy(type_name, LONG_TYPE(""));
+               }
+#undef LONG_TYPE
+       } else if (flags) {
+#define INT_TYPE(u) (sizeof(int) == sizeof(int64_t) ? \
+               u "int64_t" : sizeof(int) == sizeof(int32_t) ? \
+               u "int32_t" : u "int16_t")
+               if (flags & UNSIGNED) {
+                       strcpy(type_name, INT_TYPE("u"));
+               } else {
+                       strcpy(type_name, INT_TYPE(""));
+               }
+#undef INT_TYPE
+       } else {
+               strncpy(type_name, tok.str, tok.size);
+       }
+
+       return 0;
+}
+
+/* parse_attribute parses a token to see if it is an attribute. It may then
+ * parse some following tokens to decode the attribute setting the appropriate
+ * fields in ct. It will return 1 if the token was used (and possibly some
+ * more following it) or 0 if not. If the token was used, the next token must
+ * be retrieved using next_token/require_token. */
+static int parse_attribute(struct parser *P, struct token *tok,
+               struct cp_ctype *ct, struct parser *asmname)
+{
+       if (tok->type != TOK_TOKEN) {
+               return 0;
+       } else if (asmname && (IS_LITERAL(*tok, "__asm__")
+                               || IS_LITERAL(*tok, "__asm"))) {
+               check_token(P, TOK_OPEN_PAREN, NULL,
+                               "unexpected token after __asm__ on line %d",
+                               P->line);
+               *asmname = *P;
+
+               require_token(P, tok);
+               while (tok->type == TOK_STRING) {
+                       require_token(P, tok);
+               }
+
+               if (tok->type != TOK_CLOSE_PAREN) {
+                       cp_error("unexpected token after __asm__ on line %d",
+                                       P->line);
+               }
+               return 1;
+
+       } else if (IS_LITERAL(*tok, "__attribute__")
+                       || IS_LITERAL(*tok, "__declspec")) {
+               int parens = 1;
+               check_token(P, TOK_OPEN_PAREN, NULL,
+                               "expected parenthesis after __attribute__ or "
+                               "__declspec on line %d", P->line);
+
+               for (;;) {
+                       require_token(P, tok);
+                       if (tok->type == TOK_OPEN_PAREN) {
+                               parens++;
+                       } else if (tok->type == TOK_CLOSE_PAREN) {
+                               if (--parens == 0) {
+                                       break;
+                               }
+
+                       } else if (tok->type != TOK_TOKEN) {
+                               /* ignore unknown symbols within parentheses */
+
+                       } else if (IS_LITERAL(*tok, "align") ||
+                                       IS_LITERAL(*tok, "aligned") ||
+                                       IS_LITERAL(*tok, "__aligned__")) {
+                               unsigned align = 0;
+                               require_token(P, tok);
+
+                               switch (tok->type) {
+                               case TOK_CLOSE_PAREN:
+                                       align = ALIGNED_DEFAULT;
+                                       put_back(P);
+                                       break;
+
+                               case TOK_OPEN_PAREN:
+                                       require_token(P, tok);
+
+                                       if (tok->type != TOK_NUMBER) {
+                                               cp_error("expected align(#) "
+                                                       "on line %d", P->line);
+                                       }
+
+                                       switch (tok->integer) {
+                                       case 1: align = 0; break;
+                                       case 2: align = 1; break;
+                                       case 4: align = 3; break;
+                                       case 8: align = 7; break;
+                                       case 16: align = 15; break;
+                                       default:
+                                               cp_error("unsupported align "
+                                                       "size on line %d",
+                                                       P->line);
+                                       }
+
+                                       check_token(P, TOK_CLOSE_PAREN, NULL,
+                                               "expected align(#) on line %d",
+                                               P->line);
+                                       break;
+
+                               default:
+                                       cp_error("expected align(#) on line %d",
+                                                       P->line);
+                               }
+
+                               /* __attribute__(aligned(#)) is only supposed
+                                * to increase alignment */
+                               ct->align_mask = max(align, ct->align_mask);
+
+                       } else if (IS_LITERAL(*tok, "packed")
+                                       || IS_LITERAL(*tok, "__packed__")) {
+                               ct->align_mask = 0;
+                               ct->is_packed = 1;
+
+                       } else if (IS_LITERAL(*tok, "mode")
+                                       || IS_LITERAL(*tok, "__mode__")) {
+
+                               check_token(P, TOK_OPEN_PAREN, NULL,
+                                       "expected mode(MODE) on line %d",
+                                       P->line);
+
+                               require_token(P, tok);
+                               if (tok->type != TOK_TOKEN) {
+                                       cp_error("expected mode(MODE) on line %d",
+                                                       P->line);
+                               }
+
+
+                               struct {char ch; uint16_t v;} a16;
+                               struct {char ch; uint32_t v;} a32;
+                               struct {char ch; uint64_t v;} a64;
+
+                               if (IS_LITERAL(*tok, "QI")
+                                               || IS_LITERAL(*tok, "__QI__")
+                                               || IS_LITERAL(*tok, "byte")
+                                               || IS_LITERAL(*tok, "__byte__")
+                                  ) {
+                                       ct->type = INT8_TYPE;
+                                       ct->base_size = sizeof(uint8_t);
+                                       ct->align_mask = 0;
+
+                               } else if (IS_LITERAL(*tok, "HI")
+                                               || IS_LITERAL(*tok, "__HI__")) {
+                                       ct->type = INT16_TYPE;
+                                       ct->base_size = sizeof(uint16_t);
+                                       ct->align_mask = ALIGNOF(a16);
+
+                               } else if (IS_LITERAL(*tok, "SI")
+                                               || IS_LITERAL(*tok, "__SI__")
+#if defined ARCH_X86 || defined ARCH_ARM
+                                               || IS_LITERAL(*tok, "word")
+                                               || IS_LITERAL(*tok, "__word__")
+                                               || IS_LITERAL(*tok, "pointer")
+                                               || IS_LITERAL(*tok, "__pointer__")
+#endif
+                                         ) {
+                                       ct->type = INT32_TYPE;
+                                       ct->base_size = sizeof(uint32_t);
+                                       ct->align_mask = ALIGNOF(a32);
+
+                               } else if (IS_LITERAL(*tok, "DI")
+                                               || IS_LITERAL(*tok, "__DI__")
+#if defined ARCH_X64
+                                               || IS_LITERAL(*tok, "word")
+                                               || IS_LITERAL(*tok, "__word__")
+                                               || IS_LITERAL(*tok, "pointer")
+                                               || IS_LITERAL(*tok, "__pointer__")
+#endif
+                                         ) {
+                                       ct->type = INT64_TYPE;
+                                       ct->base_size = sizeof(uint64_t);
+                                       ct->align_mask = ALIGNOF(a64);
+
+                               } else {
+                                       cp_error("unexpected mode on line %d",
+                                                       P->line);
+                               }
+
+                               check_token(P, TOK_CLOSE_PAREN, NULL,
+                                       "expected mode(MODE) on line %d", P->line);
+
+                       } else if (IS_LITERAL(*tok, "cdecl")
+                                       || IS_LITERAL(*tok, "__cdecl__")) {
+                               ct->calling_convention = C_CALL;
+
+                       } else if (IS_LITERAL(*tok, "fastcall")
+                                       || IS_LITERAL(*tok, "__fastcall__")) {
+                               ct->calling_convention = FAST_CALL;
+
+                       } else if (IS_LITERAL(*tok, "stdcall")
+                                       || IS_LITERAL(*tok, "__stdcall__")) {
+                               ct->calling_convention = STD_CALL;
+                       }
+                       /* ignore unknown tokens within parentheses */
+               }
+               return 1;
+
+       } else if (IS_LITERAL(*tok, "__cdecl")) {
+               ct->calling_convention = C_CALL;
+               return 1;
+
+       } else if (IS_LITERAL(*tok, "__fastcall")) {
+               ct->calling_convention = FAST_CALL;
+               return 1;
+
+       } else if (IS_LITERAL(*tok, "__stdcall")) {
+               ct->calling_convention = STD_CALL;
+               return 1;
+
+       } else if (IS_LITERAL(*tok, "__extension__")
+                       || IS_LITERAL(*tok, "extern")) {
+               /* ignore */
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+/* parses from after the opening paranthesis to after the closing parenthesis */
+static void parse_function_arguments(struct parser* P, struct cp_ctype* ct)
+{
+       struct token tok;
+       int args = 0;
+
+       for (;;) {
+               require_token(P, &tok);
+
+               if (tok.type == TOK_CLOSE_PAREN)
+                       break;
+
+               if (args) {
+                       if (tok.type != TOK_COMMA) {
+                               cp_error("unexpected token in function "
+                                               "argument %d on line %d",
+                                               args, P->line);
+                       }
+                       require_token(P, &tok);
+               }
+
+               if (tok.type == TOK_VA_ARG) {
+                       ct->has_var_arg = true;
+                       check_token(P, TOK_CLOSE_PAREN, "",
+                                       "unexpected token after ... in "
+                                       "function on line %d",
+                                       P->line);
+                       break;
+               } else if (tok.type == TOK_TOKEN) {
+                       struct cp_ctype at;
+
+                       put_back(P);
+                       parse_type(P, &at);
+                       parse_argument(P, &at, NULL, NULL);
+
+                       /* array arguments are just treated as their
+                        * base pointer type */
+                       at.is_array = 0;
+
+                       /* check for the c style int func(void) and error
+                        * on other uses of arguments of type void */
+                       if (at.type == VOID_TYPE && at.pointers == 0) {
+                               if (args) {
+                                       cp_error("can't have argument of type "
+                                                       "void on line %d",
+                                                       P->line);
+                               }
+
+                               check_token(P, TOK_CLOSE_PAREN, "",
+                                       "unexpected void in function on line %d",
+                                       P->line);
+                               break;
+                       }
+                       cp_push_ctype(&at);
+                       args++;
+               } else {
+                       cp_error("unexpected token in function argument %d "
+                                       "on line %d", args+1, P->line);
+               }
+       }
+}
+
+static int max_bitfield_size(int type)
+{
+       switch (type) {
+       case BOOL_TYPE:
+               return 1;
+       case INT8_TYPE:
+               return 8;
+       case INT16_TYPE:
+               return 16;
+       case INT32_TYPE:
+       case ENUM_TYPE:
+               return 32;
+       case INT64_TYPE:
+               return 64;
+       default:
+               return -1;
+       }
+}
+
+static struct cp_ctype *parse_argument2(struct parser *P, struct cp_ctype *ct,
+               struct token *name, struct parser *asmname);
+
+/* parses from after the first ( in a function declaration or function pointer
+ * can be one of:
+ * void foo(...) before ...
+ * void (foo)(...) before foo
+ * void (* <>)(...) before <> which is the inner type */
+static struct cp_ctype *parse_function(struct parser *P, struct cp_ctype *ct,
+                               struct token *name, struct parser *asmname)
+{
+       /* We have a function pointer or a function. The usr table will
+        * get replaced by the canonical one (if there is one) in
+        * find_canonical_usr after all the arguments and returns have
+        * been parsed. */
+       struct token tok;
+       struct cp_ctype *ret = ct;
+
+       cp_push_ctype(ct);
+
+       memset(ct, 0, sizeof(*ct));
+       ct->base_size = sizeof(void (*)());
+       ct->align_mask = min(FUNCTION_ALIGN_MASK, P->align_mask);
+       ct->type = FUNCTION_TYPE;
+       ct->is_defined = 1;
+
+       if (name->type == TOK_NIL) {
+               for (;;) {
+                       require_token(P, &tok);
+
+                       if (tok.type == TOK_STAR) {
+                               if (ct->type == FUNCTION_TYPE) {
+                                       ct->type = FUNCTION_PTR_TYPE;
+                               } else {
+                                       increase_ptr_deref_level(P, ct);
+                               }
+                       } else if (parse_attribute(P, &tok, ct, asmname)) {
+                               /* parse_attribute sets the appropriate fields */
+                       } else {
+                               /* call parse_argument to handle the inner
+                                * contents e.g. the <> in "void (* <>)
+                                * (...)". Note that the inner contents can
+                                * itself be a function, a function ptr,
+                                * array, etc (e.g. "void (*signal(int sig,
+                                * void (*func)(int)))(int)" ). */
+                               cp_error("TODO: inner function not supported for now.");
+                               put_back(P);
+                               ct = parse_argument2(P, ct, name, asmname);
+                               break;
+                       }
+               }
+
+               check_token(P, TOK_CLOSE_PAREN, NULL,
+                       "unexpected token in function on line %d", P->line);
+               check_token(P, TOK_OPEN_PAREN, NULL,
+                       "unexpected token in function on line %d", P->line);
+       }
+
+       parse_function_arguments(P, ct);
+
+       /*@TODO support for inner function  24.11 2013 (houqp)*/
+       /* if we have an inner function then set the outer function ptr as its
+        * return type and return the inner function
+        * e.g. for void (* <signal(int, void (*)(int))> )(int) inner is
+        * surrounded by <>, return type is void (*)(int) */
+
+       return ret;
+}
+
+static struct cp_ctype *parse_argument2(struct parser *P, struct cp_ctype *ct,
+                               struct token *name, struct parser *asmname)
+{
+       struct token tok;
+
+       for (;;) {
+               if (!next_token(P, &tok)) {
+                       /* we've reached the end of the string */
+                       break;
+               } else if (tok.type == TOK_STAR) {
+                       increase_ptr_deref_level(P, ct);
+
+                       /* __declspec(align(#)) may come before the type in a
+                        * member */
+                       if (!ct->is_packed) {
+                               ct->align_mask = max(min(PTR_ALIGN_MASK, P->align_mask),
+                                                       ct->align_mask);
+                       }
+               } else if (tok.type == TOK_REFERENCE) {
+                       cp_error("NYI: c++ reference types");
+                       return 0;
+               } else if (parse_attribute(P, &tok, ct, asmname)) {
+                       /* parse attribute has filled out appropriate fields in type */
+
+               } else if (tok.type == TOK_OPEN_PAREN) {
+                       ct = parse_function(P, ct, name, asmname);
+               } else if (tok.type == TOK_OPEN_SQUARE) {
+                       /* array */
+                       if (ct->pointers == POINTER_MAX) {
+                               cp_error("maximum number of pointer derefs "
+                                       "reached - use a struct to break up "
+                                       "the pointers");
+                       }
+                       ct->is_array = 1;
+                       ct->pointers++;
+                       ct->const_mask <<= 1;
+                       require_token(P, &tok);
+
+                       if (ct->pointers == 1 && !ct->is_defined) {
+                               cp_error("array of undefined type on line %d",
+                                               P->line);
+                       }
+
+                       if (ct->is_variable_struct || ct->is_variable_array) {
+                               cp_error("can't have an array of a variably "
+                                       "sized type on line %d", P->line);
+                       }
+
+                       if (tok.type == TOK_QUESTION) {
+                               ct->is_variable_array = 1;
+                               ct->variable_increment = (ct->pointers > 1) ?
+                                               sizeof(void*) : ct->base_size;
+                               check_token(P, TOK_CLOSE_SQUARE, "",
+                                       "invalid character in array on line %d",
+                                       P->line);
+
+                       } else if (tok.type == TOK_CLOSE_SQUARE) {
+                               ct->array_size = 0;
+
+                       } else if (tok.type == TOK_TOKEN && IS_RESTRICT(tok)) {
+                               /* odd gcc extension foo[__restrict] for arguments */
+                               ct->array_size = 0;
+                               check_token(P, TOK_CLOSE_SQUARE, "",
+                                       "invalid character in array on line %d",
+                                       P->line);
+                       } else {
+                               int64_t asize;
+                               put_back(P);
+                               asize = calculate_constant(P);
+                               if (asize < 0) {
+                                       cp_error("array size can not be "
+                                               "negative on line %d", P->line);
+                                       return 0;
+                               }
+                               ct->array_size = (size_t) asize;
+                               check_token(P, TOK_CLOSE_SQUARE, "",
+                                       "invalid character in array on line %d",
+                                       P->line);
+                       }
+
+               } else if (tok.type == TOK_COLON) {
+                       int64_t bsize = calculate_constant(P);
+
+                       if (ct->pointers || bsize < 0
+                                       || bsize > max_bitfield_size(ct->type)) {
+                               cp_error("invalid bitfield on line %d", P->line);
+                       }
+
+                       ct->is_bitfield = 1;
+                       ct->bit_size = (unsigned) bsize;
+
+               } else if (tok.type != TOK_TOKEN) {
+                       /* we've reached the end of the declaration */
+                       put_back(P);
+                       break;
+
+               } else if (IS_CONST(tok)) {
+                       ct->const_mask |= 1;
+
+               } else if (IS_VOLATILE(tok) || IS_RESTRICT(tok)) {
+                       /* ignored for now */
+
+               } else {
+                       *name = tok;
+               }
+       }
+
+       return ct;
+}
+
+
+
+/* parses after the main base type of a typedef, function argument or
+ * struct/union member
+ * eg for const void* bar[3] the base type is void with the subtype so far of
+ * const, this parses the "* bar[3]" and updates the type argument
+ *
+ * type must be as filled out by parse_type
+ *
+ * pushes the updated user value on the top of the stack
+ */
+void parse_argument(struct parser *P, struct cp_ctype *ct, struct token *pname,
+                       struct parser *asmname)
+{
+       struct token tok, name;
+
+       memset(&name, 0, sizeof(name));
+       parse_argument2(P, ct, &name, asmname);
+
+       for (;;) {
+               if (!next_token(P, &tok)) {
+                       break;
+               } else if (parse_attribute(P, &tok, ct, asmname)) {
+                       /* parse_attribute sets the appropriate fields */
+               } else {
+                       put_back(P);
+                       break;
+               }
+       }
+
+       if (pname) {
+               *pname = name;
+       }
+}
+
+static void parse_typedef(struct parser *P)
+{
+       struct token tok;
+       struct cp_ctype base_type;
+       char typedef_name[MAX_TYPE_NAME_LEN];
+
+       parse_type(P, &base_type);
+
+       for (;;) {
+               struct cp_ctype arg_type = base_type;
+               struct token name;
+
+               memset(&name, 0, sizeof(name));
+
+               parse_argument(P, &arg_type, &name, NULL);
+
+               if (!name.size) {
+                       cp_error("Can't have a typedef without a name on line %d",
+                                       P->line);
+               } else if (arg_type.is_variable_array) {
+                       cp_error("Can't typedef a variable length array on line %d",
+                                       P->line);
+               }
+
+               memset(typedef_name, 0, sizeof(typedef_name));
+               strncpy(typedef_name, name.str, name.size);
+               /* link typedef name with ctype for parser */
+               cp_ctype_reg_type(typedef_name, &arg_type);
+
+               require_token(P, &tok);
+
+               if (tok.type == TOK_SEMICOLON) {
+                       break;
+               } else if (tok.type != TOK_COMMA) {
+                       cp_error("Unexpected character in typedef on line %d",
+                                       P->line);
+               }
+       }
+}
+
+#define END 0
+#define PRAGMA_POP 1
+
+static int parse_root(struct parser *P)
+{
+       struct token tok;
+
+       while (next_token(P, &tok)) {
+               /* we can have:
+                * struct definition
+                * enum definition
+                * union definition
+                * struct/enum/union declaration
+                * typedef
+                * function declaration
+                * pragma pack
+                */
+
+               if (tok.type == TOK_SEMICOLON) {
+                       /* empty semicolon in root continue on */
+
+               } else if (tok.type == TOK_POUND) {
+
+                       check_token(P, TOK_TOKEN, "pragma",
+                               "unexpected pre processor directive on line %d",
+                               P->line);
+                       check_token(P, TOK_TOKEN, "pack",
+                               "unexpected pre processor directive on line %d",
+                               P->line);
+                       check_token(P, TOK_OPEN_PAREN, "",
+                               "invalid pack directive on line %d",
+                               P->line);
+                       require_token(P, &tok);
+
+                       if (tok.type == TOK_NUMBER) {
+                               if (tok.integer != 1 && tok.integer != 2
+                                               && tok.integer != 4
+                                               && tok.integer != 8
+                                               && tok.integer != 16) {
+                                       cp_error("pack directive with invalid "
+                                                       "pack size on line %d",
+                                                       P->line);
+                                       return 0;
+                               }
+
+                               P->align_mask = (unsigned) (tok.integer - 1);
+                               check_token(P, TOK_CLOSE_PAREN, "",
+                                       "invalid pack directive on line %d",
+                                       P->line);
+                       } else if (tok.type == TOK_TOKEN && IS_LITERAL(tok, "push")) {
+                               /*int line = P->line;*/
+                               unsigned previous_alignment = P->align_mask;
+
+                               check_token(P, TOK_CLOSE_PAREN, "",
+                                       "invalid pack directive on line %d",
+                                       P->line);
+
+                               if (parse_root(P) != PRAGMA_POP) {
+                                       cp_error("reached end of string "
+                                               "without a pragma pop to "
+                                               "match the push on line %d",
+                                               P->line);
+                                       return 0;
+                               }
+
+                               P->align_mask = previous_alignment;
+
+                       } else if (tok.type == TOK_TOKEN && IS_LITERAL(tok, "pop")) {
+                               check_token(P, TOK_CLOSE_PAREN, "",
+                                       "invalid pack directive on line %d",
+                                               P->line);
+                               return PRAGMA_POP;
+                       } else {
+                               cp_error("invalid pack directive on line %d",
+                                               P->line);
+                               return 0;
+                       }
+               } else if (tok.type != TOK_TOKEN) {
+                       cp_error("unexpected character on line %d", P->line);
+                       return 0;
+               } else if (IS_LITERAL(tok, "__extension__")) {
+                       /* ignore */
+                       continue;
+               } else if (IS_LITERAL(tok, "extern")) {
+                       /* ignore extern as data and functions can only be
+                        * extern */
+                       continue;
+               } else if (IS_LITERAL(tok, "typedef")) {
+                       parse_typedef(P);
+               } else if (IS_LITERAL(tok, "static")) {
+                       /*@TODO we haven't tested static so far */
+                       cp_error("TODO: support static keyword.\n");
+               } else {
+                       /* type declaration, type definition, or function
+                        * declaration */
+                       struct cp_ctype type;
+                       struct token name;
+                       struct parser asmname;
+
+                       memset(&name, 0, sizeof(name));
+                       memset(&asmname, 0, sizeof(asmname));
+
+                       put_back(P);
+                       parse_type(P, &type);
+
+                       for (;;) {
+                               parse_argument(P, &type, &name, &asmname);
+
+                               if (name.size) {
+                                       /* global/function declaration */
+                                       cp_symbol_build_func(&type, name.str, name.size);
+                                       /* @TODO asmname is not used for now
+                                        * since we are not supporting __asm__
+                                        * as this point.
+                                        * might need to bind it with function
+                                        * name later. */
+                               } else {
+                                       /* type declaration/definition -
+                                        * already been processed */
+                               }
+                               require_token(P, &tok);
+
+                               if (tok.type == TOK_SEMICOLON) {
+                                       break;
+                               } else if (tok.type != TOK_COMMA) {
+                                       cp_error("missing semicolon on line %d",
+                                                       P->line);
+                               }
+                       }
+               }
+       }
+
+       return END;
+}
+
+static int64_t calculate_constant2(struct parser *P, struct token *tok);
+
+/* () */
+static int64_t calculate_constant1(struct parser *P, struct token *tok)
+{
+       int64_t ret;
+
+       if (tok->type == TOK_NUMBER) {
+               ret = tok->integer;
+               next_token(P, tok);
+               return ret;
+
+       } else if (tok->type == TOK_TOKEN) {
+               /* look up name in constants table */
+               cp_error("TODO: support name lookup in constant table\n");
+               next_token(P, tok);
+               return ret;
+
+       } else if (tok->type == TOK_OPEN_PAREN) {
+               struct parser before_cast = *P;
+               cp_error("TODO: handle open parent token in constant1\n");
+               *P = before_cast;
+               ret = calculate_constant(P);
+
+               require_token(P, tok);
+               if (tok->type != TOK_CLOSE_PAREN) {
+                       cp_error("error whilst parsing constant at line %d",
+                                       P->line);
+               }
+
+               next_token(P, tok);
+               return ret;
+       } else {
+               cp_error("unexpected token whilst parsing constant at line %d",
+                               P->line);
+               return 0;
+       }
+}
+
+/* ! and ~, unary + and -, and sizeof */
+static int64_t calculate_constant2(struct parser *P, struct token *tok)
+{
+       if (tok->type == TOK_LOGICAL_NOT) {
+               require_token(P, tok);
+               return !calculate_constant2(P, tok);
+
+       } else if (tok->type == TOK_BITWISE_NOT) {
+               require_token(P, tok);
+               return ~calculate_constant2(P, tok);
+
+       } else if (tok->type == TOK_PLUS) {
+               require_token(P, tok);
+               return calculate_constant2(P, tok);
+
+       } else if (tok->type == TOK_MINUS) {
+               require_token(P, tok);
+               return -calculate_constant2(P, tok);
+
+       } else if (tok->type == TOK_TOKEN &&
+                       (IS_LITERAL(*tok, "sizeof")
+                        || IS_LITERAL(*tok, "alignof")
+                        || IS_LITERAL(*tok, "__alignof__")
+                        || IS_LITERAL(*tok, "__alignof"))) {
+               cp_error("TODO: support sizeof\n");
+               bool issize = IS_LITERAL(*tok, "sizeof");
+               struct cp_ctype type;
+
+               require_token(P, tok);
+               if (tok->type != TOK_OPEN_PAREN) {
+                       cp_error("invalid sizeof at line %d", P->line);
+               }
+
+               parse_type(P, &type);
+               parse_argument(P, &type, NULL, NULL);
+
+               require_token(P, tok);
+               if (tok->type != TOK_CLOSE_PAREN) {
+                       cp_error("invalid sizeof at line %d", P->line);
+               }
+
+               next_token(P, tok);
+
+               return issize ? ctype_size(&type) : type.align_mask + 1;
+
+       } else {
+               return calculate_constant1(P, tok);
+       }
+}
+
+/* binary * / and % (left associative) */
+static int64_t calculate_constant3(struct parser *P, struct token *tok)
+{
+       int64_t left = calculate_constant2(P, tok);
+
+       for (;;) {
+               if (tok->type == TOK_MULTIPLY) {
+                       require_token(P, tok);
+                       left *= calculate_constant2(P, tok);
+
+               } else if (tok->type == TOK_DIVIDE) {
+                       require_token(P, tok);
+                       left /= calculate_constant2(P, tok);
+
+               } else if (tok->type == TOK_MODULUS) {
+                       require_token(P, tok);
+                       left %= calculate_constant2(P, tok);
+
+               } else {
+                       return left;
+               }
+       }
+}
+
+/* binary + and - (left associative) */
+static int64_t calculate_constant4(struct parser *P, struct token *tok)
+{
+       int64_t left = calculate_constant3(P, tok);
+
+       for (;;) {
+               if (tok->type == TOK_PLUS) {
+                       require_token(P, tok);
+                       left += calculate_constant3(P, tok);
+
+               } else if (tok->type == TOK_MINUS) {
+                       require_token(P, tok);
+                       left -= calculate_constant3(P, tok);
+
+               } else {
+                       return left;
+               }
+       }
+}
+
+/* binary << and >> (left associative) */
+static int64_t calculate_constant5(struct parser *P, struct token *tok)
+{
+       int64_t left = calculate_constant4(P, tok);
+
+       for (;;) {
+               if (tok->type == TOK_LEFT_SHIFT) {
+                       require_token(P, tok);
+                       left <<= calculate_constant4(P, tok);
+
+               } else if (tok->type == TOK_RIGHT_SHIFT) {
+                       require_token(P, tok);
+                       left >>= calculate_constant4(P, tok);
+
+               } else {
+                       return left;
+               }
+       }
+}
+
+/* binary <, <=, >, and >= (left associative) */
+static int64_t calculate_constant6(struct parser *P, struct token *tok)
+{
+       int64_t left = calculate_constant5(P, tok);
+
+       for (;;) {
+               if (tok->type == TOK_LESS) {
+                       require_token(P, tok);
+                       left = (left < calculate_constant5(P, tok));
+
+               } else if (tok->type == TOK_LESS_EQUAL) {
+                       require_token(P, tok);
+                       left = (left <= calculate_constant5(P, tok));
+
+               } else if (tok->type == TOK_GREATER) {
+                       require_token(P, tok);
+                       left = (left > calculate_constant5(P, tok));
+
+               } else if (tok->type == TOK_GREATER_EQUAL) {
+                       require_token(P, tok);
+                       left = (left >= calculate_constant5(P, tok));
+
+               } else {
+                       return left;
+               }
+       }
+}
+
+/* binary ==, != (left associative) */
+static int64_t calculate_constant7(struct parser *P, struct token *tok)
+{
+       int64_t left = calculate_constant6(P, tok);
+
+       for (;;) {
+               if (tok->type == TOK_EQUAL) {
+                       require_token(P, tok);
+                       left = (left == calculate_constant6(P, tok));
+
+               } else if (tok->type == TOK_NOT_EQUAL) {
+                       require_token(P, tok);
+                       left = (left != calculate_constant6(P, tok));
+
+               } else {
+                       return left;
+               }
+       }
+}
+
+/* binary & (left associative) */
+static int64_t calculate_constant8(struct parser *P, struct token *tok)
+{
+       int64_t left = calculate_constant7(P, tok);
+
+       for (;;) {
+               if (tok->type == TOK_BITWISE_AND) {
+                       require_token(P, tok);
+                       left = (left & calculate_constant7(P, tok));
+
+               } else {
+                       return left;
+               }
+       }
+}
+
+/* binary ^ (left associative) */
+static int64_t calculate_constant9(struct parser *P, struct token *tok)
+{
+       int64_t left = calculate_constant8(P, tok);
+
+       for (;;) {
+               if (tok->type == TOK_BITWISE_XOR) {
+                       require_token(P, tok);
+                       left = (left ^ calculate_constant8(P, tok));
+
+               } else {
+                       return left;
+               }
+       }
+}
+
+/* binary | (left associative) */
+static int64_t calculate_constant10(struct parser *P, struct token *tok)
+{
+       int64_t left = calculate_constant9(P, tok);
+
+       for (;;) {
+               if (tok->type == TOK_BITWISE_OR) {
+                       require_token(P, tok);
+                       left = (left | calculate_constant9(P, tok));
+
+               } else {
+                       return left;
+               }
+       }
+}
+
+/* binary && (left associative) */
+static int64_t calculate_constant11(struct parser *P, struct token *tok)
+{
+       int64_t left = calculate_constant10(P, tok);
+
+       for (;;) {
+               if (tok->type == TOK_LOGICAL_AND) {
+                       require_token(P, tok);
+                       left = (left && calculate_constant10(P, tok));
+
+               } else {
+                       return left;
+               }
+       }
+}
+
+/* binary || (left associative) */
+static int64_t calculate_constant12(struct parser *P, struct token *tok)
+{
+       int64_t left = calculate_constant11(P, tok);
+
+       for (;;) {
+               if (tok->type == TOK_LOGICAL_OR) {
+                       require_token(P, tok);
+                       left = (left || calculate_constant11(P, tok));
+
+               } else {
+                       return left;
+               }
+       }
+}
+
+/* ternary ?: (right associative) */
+static int64_t calculate_constant13(struct parser *P, struct token *tok)
+{
+       int64_t left = calculate_constant12(P, tok);
+
+       if (tok->type == TOK_QUESTION) {
+               int64_t middle, right;
+               require_token(P, tok);
+               middle = calculate_constant13(P, tok);
+               if (tok->type != TOK_COLON) {
+                       cp_error("invalid ternery (? :) in constant on line %d",
+                                       P->line);
+               }
+               require_token(P, tok);
+               right = calculate_constant13(P, tok);
+               return left ? middle : right;
+
+       } else {
+               return left;
+       }
+}
+
+int64_t calculate_constant(struct parser* P)
+{
+       struct token tok;
+       int64_t ret;
+       require_token(P, &tok);
+       ret = calculate_constant13(P, &tok);
+
+       if (tok.type != TOK_NIL) {
+               put_back(P);
+       }
+
+       return ret;
+}
+
+int ffi_cdef(const char *s)
+{
+       struct parser P;
+
+       memset(&P, 0, sizeof(struct parser));
+       P.line = 1;
+       P.prev = P.next = s;
+       P.align_mask = DEFAULT_ALIGN_MASK;
+
+       if (parse_root(&P) == PRAGMA_POP) {
+               cp_error("pragma pop without an associated push on line %d",
+                               P.line);
+       }
+
+       return 0;
+}
+
+void ffi_cparser_init(void)
+{
+       cp_ctype_init();
+}
+
+void ffi_cparser_free(void)
+{
+       cp_ctype_free();
+}
diff --git a/drivers/staging/ktap/userspace/ffi/ctype.c b/drivers/staging/ktap/userspace/ffi/ctype.c
new file mode 100644 (file)
index 0000000..40d1d9d
--- /dev/null
@@ -0,0 +1,551 @@
+#include "../../include/ktap_types.h"
+#include "../../include/ktap_opcodes.h"
+#include "../ktapc.h"
+#include "../cparser.h"
+
+
+/* for ktap vm */
+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)
+
+csymbol *cp_id_to_csym(int id)
+{
+       return &cs_arr[id];
+}
+
+
+typedef struct cp_ctype_entry {
+       char name[MAX_TYPE_NAME_LEN];
+       struct cp_ctype ct;
+} cp_ctype_entry;
+
+#define DEFAULT_CTYPE_ARR_SIZE 100
+static int cte_nr;
+static int cte_arr_size;
+static cp_ctype_entry *cte_arr;
+
+
+/* stack to help maintain state during parsing */
+typedef struct cp_ctype_stack {
+       int size;
+       int top;
+       cp_ctype_entry *stack;
+} ctype_stack;
+
+
+static ctype_stack cts;
+
+#define ct_stack(id) (&(cts.stack[id]))
+#define ct_stack_ct(id) (&(cts.stack[id].ct))
+
+
+
+int cp_ctype_reg_csymbol(csymbol *cs);
+
+
+size_t ctype_size(const struct cp_ctype *ct)
+{
+       if (ct->pointers - ct->is_array) {
+               return sizeof(void*) * (ct->is_array ? ct->array_size : 1);
+
+       } else if (!ct->is_defined || ct->type == VOID_TYPE) {
+               cp_error("can't calculate size of an undefined type");
+               return 0;
+       } else if (ct->variable_size_known) {
+               assert(ct->is_variable_struct && !ct->is_array);
+               return ct->base_size + ct->variable_increment;
+       } else if (ct->is_variable_array || ct->is_variable_struct) {
+               cp_error("internal error: calc size of variable type with "
+                               "unknown size");
+               return 0;
+       } else {
+               return ct->base_size * (ct->is_array ? ct->array_size : 1);
+       }
+}
+
+#define MAX_STACK_SIZE 100
+int ctype_stack_grow(int size)
+{
+       struct cp_ctype_entry *new_st;
+
+       assert(cts.size + size < MAX_STACK_SIZE);
+
+       new_st = realloc(cts.stack, (cts.size+size)*sizeof(cp_ctype_entry));
+       if (new_st)
+               cts.stack = new_st;
+       else
+               return -1;
+
+       cts.size += size;
+
+       return size;
+}
+
+int ctype_stack_free_space()
+{
+       return cts.size - cts.top;
+}
+
+void ctype_stack_reset()
+{
+       cts.top = 0;
+}
+
+/* push ctype to stack, create new csymbol if needed */
+void cp_push_ctype_with_name(struct cp_ctype *ct, const char *name, int nlen)
+{
+       int i;
+       struct cp_ctype *nct;
+
+       if (ctype_stack_free_space() < 1)
+               ctype_stack_grow(4);
+
+       /* we have to check pointer here because does type lookup by name
+        * before parsing '*', and for pointers, ct will always be the
+        * original type */
+       if (ct->pointers) {
+               for (i = 0; i < cte_nr; i++) {
+                       nct = &(cte_arr[i].ct);
+                       if (nct->type == ct->type &&
+                                       nct->pointers == ct->pointers) {
+                               break;
+                       }
+               }
+
+               if (i == cte_nr) {
+                       /* pointer type not found
+                        * create a new pointer symbol for this type */
+                       /* associate ctype with new csymbol */
+                       ct->ffi_cs_id = cp_symbol_build_pointer(ct);
+                       /* register wit new pointer name */
+                       cp_ctype_reg_type(csym_name(ct_ffi_cs(ct)), ct);
+               } else {
+                       /* pointer type already registered, reinstantiate ct */
+                       *ct = cte_arr[i].ct;
+               }
+       }
+       memset(ct_stack(cts.top), 0, sizeof(cp_ctype_entry));
+       ct_stack(cts.top)->ct = *ct;
+       if (name)
+               strncpy(ct_stack(cts.top)->name, name, nlen);
+       cts.top++;
+}
+
+void cp_push_ctype(struct cp_ctype *ct)
+{
+       cp_push_ctype_with_name(ct, NULL, 0);
+}
+
+void cp_set_defined(struct cp_ctype *ct)
+{
+       ct->is_defined = 1;
+
+       /* @TODO: update ctypes and cdatas that were created before the
+        * definition came in */
+}
+
+void cp_ctype_dump_stack()
+{
+       int i;
+       struct cp_ctype *ct;
+
+       printf("---------------------------\n");
+       printf("start of ctype stack (%d) dump: \n", cts.top);
+       for (i = 0; i < cts.top; i++) {
+               ct = ct_stack_ct(i);
+               printf("[%d] -> cp_ctype: %d, sym_type: %d, pointer: %d "
+                       "symbol_id: %d, name: %s\n",
+                       i, ct->type,
+                       csym_type(ct_ffi_cs(ct)), ct->pointers, ct->ffi_cs_id,
+                       ct_stack(i)->name);
+       }
+}
+
+int ctype_reg_table_grow()
+{
+       cp_ctype_entry *new_arr;
+
+       new_arr = realloc(cte_arr, sizeof(cp_ctype_entry)*cte_arr_size*2);
+       if (!new_arr)
+               cp_error("failed to allocate memory for ctype array\n");
+
+       cte_arr_size = cte_arr_size * 2;
+       return 0;
+}
+
+/* return index in csymbol array */
+int cp_ctype_reg_csymbol(csymbol *cs)
+{
+       if (cs_nr >= cs_arr_size) {
+               cs_arr_size *= 2;
+               cs_arr = realloc(cs_arr, cs_arr_size*sizeof(csymbol));
+               if (!cs_arr)
+                       cp_error("failed to extend csymbol array!\n");
+       }
+
+       cs_arr[cs_nr] = *cs;
+       cs_nr++;
+
+       return cs_nr-1;
+}
+
+void __cp_symbol_dump_struct(csymbol *cs)
+{
+       int i;
+       csymbol *ncs;
+       csymbol_struct *stcs = csym_struct(cs);
+
+       printf("=== [%s] definition ==================\n", csym_name(cs));
+       for (i = 0; i < stcs->memb_nr; i++) {
+               printf("\t(%d) ", i);
+               printf("csym_id: %d, ", stcs->members[i].id);
+               ncs = &cs_arr[stcs->members[i].id];
+               printf("name: %s, ffi_ctype: %d, %s\n",
+                       stcs->members[i].name, ncs->type, csym_name(ncs));
+       }
+}
+
+void cp_symbol_dump_struct(int id)
+{
+       __cp_symbol_dump_struct(&cs_arr[id]);
+}
+
+int cp_symbol_build_struct(const char *stname)
+{
+       int i, id, memb_size;
+       cp_ctype_entry *cte;
+       csymbol nst;
+       struct_member *st_membs;
+       csymbol_struct *stcs;
+
+       if (cts.top <= 0 || !stname) {
+               cp_error("invalid struct definition.\n");
+       }
+
+       memb_size = cts.top;
+       st_membs = malloc(memb_size*sizeof(struct_member));
+       if (!st_membs)
+               cp_error("failed to allocate memory for struct members.\n");
+       memset(st_membs, 0, memb_size*sizeof(struct_member));
+
+       nst.type = FFI_STRUCT;
+       strcpy(nst.name, stname);
+
+       stcs = csym_struct(&nst);
+       stcs->memb_nr = memb_size;
+       stcs->members = st_membs;
+
+       for (i = 0; i < memb_size; i++) {
+               assert(i < cts.top);
+               cte = ct_stack(i);
+               if (cte->name)
+                       strcpy(st_membs[i].name, cte->name);
+               st_membs[i].id = ct_stack_ct(i)->ffi_cs_id;
+       }
+
+       id = cp_ctype_reg_csymbol(&nst);
+
+       ctype_stack_reset();
+
+       return id;
+}
+
+/* build pointer symbol from given csymbol */
+int cp_symbol_build_pointer(struct cp_ctype *ct)
+{
+       int id, ret;
+       csymbol ncspt;
+       csymbol *ref_cs = ct_ffi_cs(ct);
+
+       /* TODO: Check correctness of multi-level pointer 24.11.2013(unihorn) */
+       memset(&ncspt, 0, sizeof(csymbol));
+       ncspt.type = FFI_PTR;
+       ret = sprintf(ncspt.name, "%s *", csym_name(ref_cs));
+       assert(ret < MAX_TYPE_NAME_LEN);
+
+       csym_set_ptr_deref_id(&ncspt, ct->ffi_cs_id);
+       id = cp_ctype_reg_csymbol(&ncspt);
+
+       return id;
+}
+
+void __cp_symbol_dump_func(csymbol *cs)
+{
+       int i;
+       csymbol *ncs;
+       csymbol_func *fcs = csym_func(cs);
+
+       printf("=== [%s] function definition =============\n", csym_name(cs));
+       ncs = cp_csymf_ret(fcs);
+       printf("address: %p\n", fcs->addr);
+       printf("return type: \n");
+       printf("\tcsym_id: %d, ffi_ctype: %d, %s\n",
+                       fcs->ret_id, ncs->type, csym_name(ncs));
+       printf("args type (%d): \n", fcs->arg_nr);
+       for (i = 0; i < csymf_arg_nr(fcs); i++) {
+           printf("\t (%d) ", i);
+           printf("csym_id: %d, ", fcs->arg_ids[i]);
+           ncs = cp_csymf_arg(fcs, i);
+           printf("ffi_ctype: %d, %s\n", ncs->type, csym_name(ncs));
+       }
+}
+
+void cp_symbol_dump_func(int id)
+{
+       __cp_symbol_dump_func(&cs_arr[id]);
+}
+
+int cp_symbol_build_func(struct cp_ctype *type, const char *fname, int fn_size)
+{
+       int i = 1, arg_nr, id;
+       int *argsym_id_arr;
+       csymbol nfcs;
+       csymbol_func *fcs;
+
+       if (cts.top == 0 || fn_size < 0 || !fname) {
+               cp_error("invalid function definition.\n");
+       }
+
+       argsym_id_arr = NULL;
+       memset(&nfcs, 0, sizeof(csymbol));
+       csym_type(&nfcs) = FFI_FUNC;
+
+       strncpy(csym_name(&nfcs), fname, fn_size);
+
+       fcs = csym_func(&nfcs);
+       fcs->has_var_arg = type->has_var_arg;
+       /* Type needed for handling variable args handle */
+       if (fcs->has_var_arg && !ctype_lookup_type("void *"))
+               cp_symbol_build_pointer(ctype_lookup_type("void"));
+
+       /* Fetch start address of function  */
+       fcs->addr = (void *)find_kernel_symbol(csym_name(&nfcs));
+       if (!fcs->addr)
+               cp_error("wrong function address for %s\n", csym_name(&nfcs));
+
+       /* bottom of the stack is return type */
+       fcs->ret_id = ct_stack_ct(0)->ffi_cs_id;
+
+       /* the rest is argument type */
+       if (cts.top == 1) {
+               /* function takes no argument */
+               arg_nr = 0;
+       } else {
+               arg_nr = cts.top - 1;
+               argsym_id_arr = malloc(arg_nr * sizeof(int));
+               if (!argsym_id_arr)
+                       cp_error("failed to allocate memory for function args.\n");
+               for (i = 0; i < arg_nr; i++) {
+                       argsym_id_arr[i] = ct_stack_ct(i+1)->ffi_cs_id;
+               }
+       }
+       fcs->arg_nr = arg_nr;
+       fcs->arg_ids = argsym_id_arr;
+
+       id = cp_ctype_reg_csymbol(&nfcs);
+
+       /* clear stack since we have consumed all the ctypes */
+       ctype_stack_reset();
+
+       return id;
+}
+
+struct cp_ctype *cp_ctype_reg_type(char *name, struct cp_ctype *ct)
+{
+       if (cte_nr >= cte_arr_size)
+               ctype_reg_table_grow();
+
+       memset(cte_arr[cte_nr].name, 0, MAX_TYPE_NAME_LEN);
+       strcpy(cte_arr[cte_nr].name, name);
+
+       cte_arr[cte_nr].ct = *ct;
+       cte_nr++;
+
+       return &(cte_arr[cte_nr-1].ct);
+}
+
+#if 0
+/* TODO: used for size calculation */
+static ffi_type ffi_int_type(ktap_state *ks, int size, bool sign)
+{
+       switch(size) {
+       case 1:
+               if (!sign)
+                       return FFI_UINT8;
+               else
+                       return FFI_INT8;
+       case 2:
+               if (!sign)
+                       return FFI_UINT16;
+               else
+                       return FFI_INT16;
+       case 4:
+               if (!sign)
+                       return FFI_UINT32;
+               else
+                       return FFI_INT32;
+       case 8:
+               if (!sign)
+                       return FFI_UINT64;
+               else
+                       return FFI_INT64;
+       default:
+               kp_error(ks, "Error: Have not support int type of size %d\n", size);
+               return FFI_UNKNOWN;
+       }
+
+       /* NEVER reach here, silence compiler */
+       return -1;
+}
+#endif
+
+
+static inline void ct_set_type(struct cp_ctype *ct, int type, int is_unsigned)
+{
+       ct->type = type;
+       ct->is_unsigned = is_unsigned;
+}
+
+static void init_builtin_type(struct cp_ctype *ct, ffi_type ftype)
+{
+       csymbol cs;
+       int cs_id;
+
+       csym_type(&cs) = ftype;
+       strncpy(csym_name(&cs), ffi_type_name(ftype), CSYM_NAME_MAX_LEN);
+       cs_id = cp_ctype_reg_csymbol(&cs);
+
+       memset(ct, 0, sizeof(*ct));
+       ct->ffi_cs_id = cs_id;
+       switch (ftype) {
+       case FFI_VOID:          ct_set_type(ct, VOID_TYPE, 0); break;
+       case FFI_UINT8:         ct_set_type(ct, INT8_TYPE, 1); break;
+       case FFI_INT8:          ct_set_type(ct, INT8_TYPE, 0); break;
+       case FFI_UINT16:        ct_set_type(ct, INT16_TYPE, 1); break;
+       case FFI_INT16:         ct_set_type(ct, INT16_TYPE, 0); break;
+       case FFI_UINT32:        ct_set_type(ct, INT32_TYPE, 1); break;
+       case FFI_INT32:         ct_set_type(ct, INT32_TYPE, 0); break;
+       case FFI_UINT64:        ct_set_type(ct, INT64_TYPE, 1); break;
+       case FFI_INT64:         ct_set_type(ct, INT64_TYPE, 0); break;
+       default:                break;
+       }
+       ct->base_size = ffi_type_size(ftype);
+       ct->align_mask = ffi_type_align(ftype) - 1;
+       ct->is_defined = 1;
+}
+
+/*
+ * lookup and register builtin C type on demand
+ * You should ensure that the type with name doesn't appear in
+ * csymbol table before calling.
+ */
+struct cp_ctype *ctype_lookup_builtin_type(char *name)
+{
+       struct cp_ctype ct;
+
+       if (!strncmp(name, "void", sizeof("void"))) {
+               init_builtin_type(&ct, FFI_VOID);
+               return cp_ctype_reg_type("void", &ct);
+       } else if (!strncmp(name, "int8_t", sizeof("int8_t"))) {
+               init_builtin_type(&ct, FFI_INT8);
+               return cp_ctype_reg_type("int8_t", &ct);
+       } else if (!strncmp(name, "uint8_t", sizeof("uint8_t"))) {
+               init_builtin_type(&ct, FFI_UINT8);
+               return cp_ctype_reg_type("uint8_t", &ct);
+       } else if (!strncmp(name, "int16_t", sizeof("int16_t"))) {
+               init_builtin_type(&ct, FFI_INT16);
+               return cp_ctype_reg_type("int16_t", &ct);
+       } else if (!strncmp(name, "uint16_t", sizeof("uint16_t"))) {
+               init_builtin_type(&ct, FFI_UINT16);
+               return cp_ctype_reg_type("uint16_t", &ct);
+       } else if (!strncmp(name, "int32_t", sizeof("int32_t"))) {
+               init_builtin_type(&ct, FFI_INT32);
+               return cp_ctype_reg_type("int32_t", &ct);
+       } else if (!strncmp(name, "uint32_t", sizeof("uint32_t"))) {
+               init_builtin_type(&ct, FFI_UINT32);
+               return cp_ctype_reg_type("uint32_t", &ct);
+       } else if (!strncmp(name, "int64_t", sizeof("int64_t"))) {
+               init_builtin_type(&ct, FFI_INT64);
+               return cp_ctype_reg_type("int64_t", &ct);
+       } else if (!strncmp(name, "uint64_t", sizeof("uint64_t"))) {
+               init_builtin_type(&ct, FFI_UINT64);
+               return cp_ctype_reg_type("uint64_t", &ct);
+       } else {
+               /* no builtin type matched */
+               return NULL;
+       }
+}
+
+/* start ctype reg table */
+struct cp_ctype *ctype_lookup_type(char *name)
+{
+       int i;
+       struct cp_ctype *ct;
+
+       for (i = 0; i < cte_nr; i++) {
+               ct = &cte_arr[i].ct;
+               if (!strcmp(name, cte_arr[i].name))
+                       return ct;
+       }
+
+       /* see if it's a builtin C type
+        * return NULL if still no match */
+       return ctype_lookup_builtin_type(name);
+}
+
+cp_csymbol_state *ctype_get_csym_state(void)
+{
+       return &csym_state;
+}
+
+#define DEFAULT_STACK_SIZE 20
+#define DEFAULT_SYM_ARR_SIZE 20
+int cp_ctype_init()
+{
+       cts.size = DEFAULT_STACK_SIZE;
+       cts.top = 0;
+       cts.stack = malloc(sizeof(cp_ctype_entry)*DEFAULT_STACK_SIZE);
+
+       cs_nr = 0;
+       cs_arr_size = DEFAULT_SYM_ARR_SIZE;
+       cs_arr = malloc(sizeof(csymbol)*DEFAULT_SYM_ARR_SIZE);
+       memset(cs_arr, 0, sizeof(csymbol)*DEFAULT_SYM_ARR_SIZE);
+
+       cte_nr = 0;
+       cte_arr_size = DEFAULT_CTYPE_ARR_SIZE;
+       cte_arr = malloc(sizeof(cp_ctype_entry)*DEFAULT_CTYPE_ARR_SIZE);
+
+       return 0;
+}
+
+int cp_ctype_free()
+{
+       int i;
+       csymbol *cs;
+
+       if (cts.stack)
+               free(cts.stack);
+
+       if (cs_arr) {
+               for (i = 0; i < cs_nr; i++) {
+                       cs = &cs_arr[i];
+                       if (csym_type(cs) == FFI_FUNC) {
+                               if (csym_func(cs)->arg_ids)
+                                       free(csym_func(cs)->arg_ids);
+                       } else if (csym_type(cs) == FFI_STRUCT) {
+                               if (csym_struct(cs)->members)
+                                       free(csym_struct(cs)->members);
+                       }
+               }
+               free(cs_arr);
+       }
+
+       if (cte_arr) {
+               free(cte_arr);
+       }
+
+       return 0;
+}
diff --git a/drivers/staging/ktap/userspace/ktapc.h b/drivers/staging/ktap/userspace/ktapc.h
new file mode 100644 (file)
index 0000000..76348df
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ * ktapc.h
+ * only can be included by userspace compiler
+ */
+
+#include <ctype.h>
+
+typedef int bool;
+#define false 0
+#define true 1
+
+#define MAX_INT         ((int)(~0U>>1))
+#define UCHAR_MAX      255
+
+#define MAX_SIZET  ((size_t)(~(size_t)0)-2)
+
+#define KTAP_ERRSYNTAX 3
+
+/*
+ * KTAP_IDSIZE gives the maximum size for the description of the source
+ * of a function in debug information.
+ * CHANGE it if you want a different size.
+ */
+#define KTAP_IDSIZE      60
+
+
+#define FIRST_RESERVED  257
+
+/*
+ * maximum depth for nested C calls and syntactical nested non-terminals
+ * in a program. (Value must fit in an unsigned short int.)
+ */
+#define KTAP_MAXCCALLS          200
+
+#define KTAP_MULTRET     (-1)
+
+
+#define SHRT_MAX       UCHAR_MAX
+
+#define MAXUPVAL   UCHAR_MAX
+
+
+/* maximum stack for a ktap function */
+#define MAXSTACK        250
+
+#define islalpha(c)   (isalpha(c) || (c) == '_')
+#define islalnum(c)   (isalnum(c) || (c) == '_')
+
+#define isreserved(s) ((s)->tsv.tt == KTAP_TSHRSTR && (s)->tsv.extra > 0)
+
+#define ktap_numeq(a,b)                ((a)==(b))
+#define ktap_numisnan(L,a)     (!ktap_numeq((a), (a)))
+
+#define ktap_numunm(a)         (-(a))
+
+/*
+ * ** Comparison and arithmetic functions
+ * */
+
+#define KTAP_OPADD       0       /* ORDER TM */
+#define KTAP_OPSUB       1
+#define KTAP_OPMUL       2
+#define KTAP_OPDIV       3
+#define KTAP_OPMOD       4
+#define KTAP_OPPOW       5
+#define KTAP_OPUNM       6
+
+#define KTAP_OPEQ        0
+#define KTAP_OPLT        1
+#define KTAP_OPLE        2
+
+
+/*
+ * WARNING: if you change the order of this enumeration,
+ * grep "ORDER RESERVED"
+ */
+enum RESERVED {
+       /* terminal symbols denoted by reserved words */
+       TK_TRACE = FIRST_RESERVED, TK_TRACE_END,
+       TK_ARGEVENT, TK_ARGNAME,
+       TK_FFI_CDEF,
+       TK_ARG1, TK_ARG2, TK_ARG3, TK_ARG4, TK_ARG5, TK_ARG6, TK_ARG7, TK_ARG8,
+       TK_ARG9, TK_PROFILE, TK_TICK, TK_AGGR_ASSIGN,
+       TK_AND, TK_BREAK,
+       TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
+       TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
+       TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
+       /* other terminal symbols */
+       TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_INCR, TK_DBCOLON,
+       TK_EOS, TK_NUMBER, TK_NAME, TK_STRING, TK_KSYM
+};
+
+/* number of reserved words */
+#define NUM_RESERVED    ((int)(TK_WHILE-FIRST_RESERVED + 1))
+
+#define EOZ     (0)                    /* end of stream */
+
+typedef union {
+       ktap_number r;
+       ktap_string *ts;
+} ktap_seminfo;  /* semantics information */
+
+
+typedef struct ktap_token {
+       int token;
+       ktap_seminfo seminfo;
+} ktap_token;
+
+typedef struct ktap_mbuffer {
+       char *buffer;
+       size_t n;
+       size_t buffsize;
+} ktap_mbuffer;
+
+#define mbuff_init(buff)       ((buff)->buffer = NULL, (buff)->buffsize = 0)
+#define mbuff(buff)            ((buff)->buffer)
+#define mbuff_reset(buff)      ((buff)->n = 0, memset((buff)->buffer, 0, (buff)->buffsize))
+#define mbuff_len(buff)                ((buff)->n)
+#define mbuff_size(buff)       ((buff)->buffsize)
+
+#define mbuff_resize(buff, size) \
+       (ktapc_realloc((buff)->buffer, (buff)->buffsize, size, char), \
+       (buff)->buffsize = size)
+
+#define mbuff_free(buff)        mbuff_resize(buff, 0)
+
+
+/*
+ * state of the lexer plus state of the parser when shared by all
+ * functions
+ */
+typedef struct ktap_lexstate {
+       char *ptr; /* source file reading position */
+       int current;  /* current character (charint) */
+       int linenumber;  /* input line counter */
+       int lastline;  /* line of last token `consumed' */
+       ktap_token t;  /* current token */
+       ktap_token lookahead;  /* look ahead token */
+       struct ktap_funcstate *fs;  /* current function (parser) */
+       ktap_mbuffer *buff;  /* buffer for tokens */
+       struct ktap_dyndata *dyd;  /* dynamic structures used by the parser */
+       ktap_string *source;  /* current source name */
+       ktap_string *envn;  /* environment variable name */
+       char decpoint;  /* locale decimal point */
+       int nCcalls;
+} ktap_lexstate;
+
+
+/*
+ * Expression descriptor
+ */
+typedef enum {
+       VVOID,        /* no value */
+       VNIL,
+       VTRUE,
+       VFALSE,
+       VK,           /* info = index of constant in `k' */
+       VKNUM,        /* nval = numerical value */
+       VNONRELOC,    /* info = result register */
+       VLOCAL,       /* info = local register */
+       VUPVAL,       /* info = index of upvalue in 'upvalues' */
+       VINDEXED,     /* t = table register/upvalue; idx = index R/K */
+       VJMP,         /* info = instruction pc */
+       VRELOCABLE,   /* info = instruction pc */
+       VCALL,        /* info = instruction pc */
+       VVARARG,      /* info = instruction pc */
+       VEVENT,
+       VEVENTNAME,
+       VEVENTARG,
+} expkind;
+
+
+#define vkisvar(k)      (VLOCAL <= (k) && (k) <= VINDEXED)
+#define vkisinreg(k)    ((k) == VNONRELOC || (k) == VLOCAL)
+
+typedef struct ktap_expdesc {
+       expkind k;
+       union {
+               struct {  /* for indexed variables (VINDEXED) */
+                       short idx;  /* index (R/K) */
+                       u8 t;  /* table (register or upvalue) */
+                       u8 vt;  /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */
+               } ind;
+               int info;  /* for generic use */
+               ktap_number nval;  /* for VKNUM */
+       } u;
+       int t;  /* patch list of `exit when true' */
+       int f;  /* patch list of `exit when false' */
+} ktap_expdesc;
+
+
+typedef struct ktap_vardesc {
+       short idx;  /* variable index in stack */
+} ktap_vardesc;
+
+
+/* description of pending goto statements and label statements */
+typedef struct ktap_labeldesc {
+       ktap_string *name;  /* label identifier */
+       int pc;  /* position in code */
+       int line;  /* line where it appeared */
+       u8 nactvar;  /* local level where it appears in current block */
+} ktap_labeldesc;
+
+
+/* list of labels or gotos */
+typedef struct ktap_labellist {
+       ktap_labeldesc *arr;  /* array */
+       int n;  /* number of entries in use */
+       int size;  /* array size */
+} ktap_labellist;
+
+
+/* dynamic structures used by the parser */
+typedef struct ktap_dyndata {
+       struct {  /* list of active local variables */
+               ktap_vardesc *arr;
+               int n;
+               int size;
+       } actvar;
+       ktap_labellist gt;  /* list of pending gotos */
+       ktap_labellist label;   /* list of active labels */
+} ktap_dyndata;
+
+
+/* control of blocks */
+struct ktap_blockcnt;  /* defined in lparser.c */
+
+
+/* state needed to generate code for a given function */
+typedef struct ktap_funcstate {
+       ktap_proto *f;  /* current function header */
+       ktap_tab *h;  /* table to find (and reuse) elements in `k' */
+       struct ktap_funcstate *prev;  /* enclosing function */
+       struct ktap_lexstate *ls;  /* lexical state */
+       struct ktap_blockcnt *bl;  /* chain of current blocks */
+       int pc;  /* next position to code (equivalent to `ncode') */
+       int lasttarget;   /* 'label' of last 'jump label' */
+       int jpc;  /* list of pending jumps to `pc' */
+       int nk;  /* number of elements in `k' */
+       int np;  /* number of elements in `p' */
+       int firstlocal;  /* index of first local var (in ktap_dyndata array) */
+       short nlocvars;  /* number of elements in 'f->locvars' */
+       u8 nactvar;  /* number of active local variables */
+       u8 nups;  /* number of upvalues */
+       u8 freereg;  /* first free register */
+} ktap_funcstate;
+
+
+/*
+ * Marks the end of a patch list. It is an invalid value both as an absolute
+ * address, and as a list link (would link an element to itself).
+ */
+#define NO_JUMP (-1)
+
+
+/*
+ * grep "ORDER OPR" if you change these enums  (ORDER OP)
+ */
+typedef enum BinOpr {
+       OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW,
+       OPR_CONCAT,
+       OPR_EQ, OPR_LT, OPR_LE,
+       OPR_NE, OPR_GT, OPR_GE,
+       OPR_AND, OPR_OR,
+       OPR_NOBINOPR
+} BinOpr;
+
+
+typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
+
+
+#define getcode(fs,e)   ((fs)->f->code[(e)->u.info])
+
+#define codegen_codeAsBx(fs,o,A,sBx)       codegen_codeABx(fs,o,A,(sBx)+MAXARG_sBx)
+
+#define codegen_setmultret(fs,e)   codegen_setreturns(fs, e, KTAP_MULTRET)
+
+#define codegen_jumpto(fs,t)       codegen_patchlist(fs, codegen_jump(fs), t)
+
+
+#define ktapc_realloc(v, osize, nsize, t) \
+        ((v) = (t *)ktapc_reallocv(v, osize * sizeof(t), nsize * sizeof(t)))
+
+#define ktapc_reallocvector(v,oldn,n,t)        ktapc_realloc(v,oldn,n,t)
+
+
+#define ktapc_growvector(v,nelems,size,t,limit,e) \
+          if ((nelems)+1 > (size)) \
+            ((v)=(t *)ktapc_growaux(v,&(size),sizeof(t),limit,e))
+
+
+void lex_init();
+ktap_string *lex_newstring(ktap_lexstate *ls, const char *str, size_t l);
+const char *lex_token2str(ktap_lexstate *ls, int token);
+void lex_syntaxerror(ktap_lexstate *ls, const char *msg);
+void lex_setinput(ktap_lexstate *ls, char *ptr, ktap_string *source, int firstchar);
+void lex_next(ktap_lexstate *ls);
+int lex_lookahead(ktap_lexstate *ls);
+void lex_read_string_until(ktap_lexstate *ls, int c);
+ktap_closure *ktapc_parser(char *pos, const char *name);
+ktap_string *ktapc_ts_new(const char *str);
+int ktapc_ts_eqstr(ktap_string *a, ktap_string *b);
+ktap_string *ktapc_ts_newlstr(const char *str, size_t l);
+ktap_proto *ktapc_newproto();
+ktap_tab *ktapc_table_new();
+const ktap_value *ktapc_table_get(ktap_tab *t, const ktap_value *key);
+void ktapc_table_setvalue(ktap_tab *t, const ktap_value *key, ktap_value *val);
+ktap_closure *ktapc_newclosure(int n);
+char *ktapc_sprintf(const char *fmt, ...);
+
+void *ktapc_reallocv(void *block, size_t osize, size_t nsize);
+void *ktapc_growaux(void *block, int *size, size_t size_elems, int limit,
+                   const char *what);
+
+void ktapio_exit(void);
+int ktapio_create(const char *output_filename);
+
+ktap_eventdef_info *ktapc_parse_eventdef(const char *eventdef);
+void cleanup_event_resources(void);
+
+extern int verbose;
+#define verbose_printf(...) \
+       if (verbose)    \
+               printf("[verbose] " __VA_ARGS__);
+
+#define ktapc_equalobj(t1, t2) kp_equalobjv(NULL, t1, t2)
+
+int codegen_stringK(ktap_funcstate *fs, ktap_string *s);
+void codegen_indexed(ktap_funcstate *fs, ktap_expdesc *t, ktap_expdesc *k);
+void codegen_setreturns(ktap_funcstate *fs, ktap_expdesc *e, int nresults);
+void codegen_reserveregs(ktap_funcstate *fs, int n);
+void codegen_exp2nextreg(ktap_funcstate *fs, ktap_expdesc *e);
+void codegen_nil(ktap_funcstate *fs, int from, int n);
+void codegen_patchlist(ktap_funcstate *fs, int list, int target);
+void codegen_patchclose(ktap_funcstate *fs, int list, int level);
+int codegen_jump(ktap_funcstate *fs);
+void codegen_patchtohere(ktap_funcstate *fs, int list);
+int codegen_codeABx(ktap_funcstate *fs, OpCode o, int a, unsigned int bc);
+void codegen_ret(ktap_funcstate *fs, int first, int nret);
+void codegen_exp2anyregup(ktap_funcstate *fs, ktap_expdesc *e);
+void codegen_exp2val(ktap_funcstate *fs, ktap_expdesc *e);
+int codegen_exp2RK(ktap_funcstate *fs, ktap_expdesc *e);
+int codegen_codeABC(ktap_funcstate *fs, OpCode o, int a, int b, int c);
+void codegen_setlist(ktap_funcstate *fs, int base, int nelems, int tostore);
+void codegen_fixline (ktap_funcstate *fs, int line);
+void codegen_dischargevars(ktap_funcstate *fs, ktap_expdesc *e);
+void codegen_self(ktap_funcstate *fs, ktap_expdesc *e, ktap_expdesc *key);
+void codegen_prefix(ktap_funcstate *fs, UnOpr op, ktap_expdesc *e, int line);
+void codegen_infix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *v);
+void codegen_posfix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *e1, ktap_expdesc *e2, int line);
+void codegen_setoneret(ktap_funcstate *fs, ktap_expdesc *e);
+void codegen_storevar(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex);
+void codegen_storeincr(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex);
+void codegen_store_aggr(ktap_funcstate *fs, ktap_expdesc *var,
+                                           ktap_expdesc *ex);
+void codegen_goiftrue(ktap_funcstate *fs, ktap_expdesc *e);
+int codegen_getlabel(ktap_funcstate *fs);
+int codegen_codek(ktap_funcstate *fs, int reg, int k);
+int codegen_numberK(ktap_funcstate *fs, ktap_number r);
+void codegen_checkstack(ktap_funcstate *fs, int n);
+void codegen_goiffalse(ktap_funcstate *fs, ktap_expdesc *e);
+void codegen_concat(ktap_funcstate *fs, int *l1, int l2);
+int codegen_exp2anyreg(ktap_funcstate *fs, ktap_expdesc *e);
+
+typedef int (*ktap_writer)(const void* p, size_t sz, void* ud);
+int ktapc_dump(const ktap_proto *f, ktap_writer w, void *data, int strip);
+
+void ktapc_chunkid(char *out, const char *source, size_t bufflen);
+int ktapc_str2d(const char *s, size_t len, ktap_number *result);
+int ktapc_hexavalue(int c);
+ktap_number ktapc_arith(int op, ktap_number v1, ktap_number v2);
+int ktapc_int2fb(unsigned int x);
+bool strglobmatch(const char *str, const char *pat);
+int kallsyms_parse(void *arg,
+                  int(*process_symbol)(void *arg, const char *name,
+                  char type, unsigned long start));
+
+unsigned long find_kernel_symbol(const char *symbol);
+void list_available_events(const char *match);
+
+
+#ifdef CONFIG_KTAP_FFI
+#include "../include/ktap_ffi.h"
+
+typedef struct cp_csymbol_state {
+       int cs_nr; /* number of c symbols */
+       int cs_arr_size; /* size of current symbol arrays */
+       csymbol *cs_arr;
+} cp_csymbol_state;
+
+cp_csymbol_state *ctype_get_csym_state(void);
+#endif
diff --git a/drivers/staging/ktap/userspace/ktapio.c b/drivers/staging/ktap/userspace/ktapio.c
new file mode 100644 (file)
index 0000000..7bef6e8
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * ktapio.c - ring buffer transport in userspace
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <sys/signal.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#define MAX_BUFLEN  131072
+#define PATH_MAX 128
+
+#define handle_error(str) do { perror(str); exit(-1); } while(0)
+
+void sigfunc(int signo)
+{
+       /* should not not reach here */
+}
+
+static void block_sigint()
+{
+       sigset_t mask;
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+
+       pthread_sigmask(SIG_BLOCK, &mask, NULL);
+}
+
+static void *reader_thread(void *data)
+{
+       char buf[MAX_BUFLEN];
+       char filename[PATH_MAX];
+       const char *output = data;
+       int failed = 0, fd, out_fd, len;
+
+       block_sigint();
+
+       if (output) {
+               out_fd = open(output, O_CREAT | O_WRONLY | O_TRUNC,
+                                       S_IRUSR|S_IWUSR);
+               if (out_fd < 0) {
+                       fprintf(stderr, "Cannot open output file %s\n", output);
+                       return NULL;
+               }
+       } else
+               out_fd = 2;
+
+       sprintf(filename, "/sys/kernel/debug/ktap/trace_pipe_%d", getpid());
+
+ open_again:
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               usleep(10000);
+
+               if (failed++ == 10) {
+                       fprintf(stderr, "Cannot open file %s\n", filename);
+                       return NULL;
+               }
+               goto open_again;
+       }
+
+       while ((len = read(fd, buf, sizeof(buf))) > 0)
+               write(out_fd, buf, len);
+
+       close(fd);
+       close(out_fd);
+
+       return NULL;
+}
+
+int ktapio_create(const char *output)
+{
+       pthread_t reader;
+
+       signal(SIGINT, sigfunc);
+
+       if (pthread_create(&reader, NULL, reader_thread, (void *)output) < 0)
+               handle_error("pthread_create reader_thread failed\n");
+
+       return 0;
+}
+
diff --git a/drivers/staging/ktap/userspace/lex.c b/drivers/staging/ktap/userspace/lex.c
new file mode 100644 (file)
index 0000000..2cfcd92
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ * lex.c - ktap lexical analyzer
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+ *  - The part of code in this file is copied from lua initially.
+ *  - lua's MIT license is compatible with GPL.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include "../include/ktap_types.h"
+#include "../include/ktap_opcodes.h"
+#include "ktapc.h"
+
+#define next(ls) (ls->current = *ls->ptr++)
+
+#define currIsNewline(ls)      (ls->current == '\n' || ls->current == '\r')
+
+#define KTAP_MINBUFFER   32
+
+/* ORDER RESERVED */
+static const char *const ktap_tokens [] = {
+       "trace", "trace_end", "argevent", "argname", "cdef",
+       "arg1", "arg2", "arg3", "arg4", "arg5", "arg6", "arg7", "arg9", "arg9",
+       "profile", "tick", "<<<",
+       "and", "break", "do", "else", "elseif",
+       "end", "false", "for", "function", "goto", "if",
+       "in", "local", "nil", "not", "or", "repeat",
+       "return", "then", "true", "until", "while",
+       "..", "...", "==", ">=", "<=", "!=", "+=", "::", "<eof>",
+       "<number>", "<name>", "<string>", "<symbol>"
+};
+
+#define save_and_next(ls) (save(ls, ls->current), next(ls))
+
+static void lexerror(ktap_lexstate *ls, const char *msg, int token);
+
+static void save(ktap_lexstate *ls, int c)
+{
+       ktap_mbuffer *b = ls->buff;
+       if (mbuff_len(b) + 1 > mbuff_size(b)) {
+               size_t newsize;
+               if (mbuff_size(b) >= MAX_SIZET / 2)
+                       lexerror(ls, "lexical element too long", 0);
+               newsize = mbuff_size(b) * 2;
+               mbuff_resize(b, newsize);
+       }
+       b->buffer[mbuff_len(b)++] = (char)c;
+}
+
+void lex_init()
+{
+       int i;
+       for (i = 0; i < NUM_RESERVED; i++) {
+               ktap_string *ts = ktapc_ts_new(ktap_tokens[i]);
+               ts->tsv.extra = (u8)(i+1);  /* reserved word */
+       }
+}
+
+const char *lex_token2str(ktap_lexstate *ls, int token)
+{
+       if (token < FIRST_RESERVED) {
+               ktap_assert(token == (unsigned char)token);
+               return (isprint(token)) ? ktapc_sprintf(KTAP_QL("%c"), token) :
+                       ktapc_sprintf("char(%d)", token);
+       } else {
+               const char *s = ktap_tokens[token - FIRST_RESERVED];
+               if (token < TK_EOS)
+                       return ktapc_sprintf(KTAP_QS, s);
+               else
+                       return s;
+       }
+}
+
+static const char *txtToken(ktap_lexstate *ls, int token)
+{
+       switch (token) {
+       case TK_NAME:
+       case TK_STRING:
+       case TK_NUMBER:
+               save(ls, '\0');
+               return ktapc_sprintf(KTAP_QS, mbuff(ls->buff));
+       default:
+               return lex_token2str(ls, token);
+       }
+}
+
+static void lexerror(ktap_lexstate *ls, const char *msg, int token)
+{
+       char buff[KTAP_IDSIZE];
+       char *newmsg;
+
+       ktapc_chunkid(buff, getstr(ls->source), KTAP_IDSIZE);
+       newmsg = ktapc_sprintf("%s:%d: %s", buff, ls->linenumber, msg);
+       if (token)
+               newmsg = ktapc_sprintf("%s near %s", newmsg, txtToken(ls, token));
+       printf("lexerror: %s\n", newmsg);
+       exit(EXIT_FAILURE);
+}
+
+void lex_syntaxerror(ktap_lexstate *ls, const char *msg)
+{
+       lexerror(ls, msg, ls->t.token);
+}
+
+/*
+ * creates a new string and anchors it in function's table so that
+ * it will not be collected until the end of the function's compilation
+ * (by that time it should be anchored in function's prototype)
+ */
+ktap_string *lex_newstring(ktap_lexstate *ls, const char *str, size_t l)
+{
+       const ktap_value *o;  /* entry for `str' */
+       ktap_value val;  /* entry for `str' */
+       ktap_value tsv;
+       ktap_string *ts = ktapc_ts_newlstr(str, l);  /* create new string */
+       set_string(&tsv, ts);
+       o = ktapc_table_get(ls->fs->h, &tsv);
+       if (is_nil(o)) {  /* not in use yet? (see 'addK') */
+               /* boolean value does not need GC barrier;
+               table has no metatable, so it does not need to invalidate cache */
+               set_boolean(&val, 1);  /* t[string] = true */
+               ktapc_table_setvalue(ls->fs->h, &tsv, &val);
+       }
+       return ts;
+}
+
+/*
+ * increment line number and skips newline sequence (any of
+ * \n, \r, \n\r, or \r\n)
+ */
+static void inclinenumber(ktap_lexstate *ls)
+{
+       int old = ls->current;
+       ktap_assert(currIsNewline(ls));
+       next(ls);  /* skip `\n' or `\r' */
+       if (currIsNewline(ls) && ls->current != old)
+               next(ls);  /* skip `\n\r' or `\r\n' */
+       if (++ls->linenumber >= MAX_INT)
+               lex_syntaxerror(ls, "chunk has too many lines");
+}
+
+void lex_setinput(ktap_lexstate *ls, char *ptr, ktap_string *source, int firstchar)
+{
+       ls->decpoint = '.';
+       ls->current = firstchar;
+       ls->lookahead.token = TK_EOS;  /* no look-ahead token */
+       ls->ptr = ptr;
+       ls->fs = NULL;
+       ls->linenumber = 1;
+       ls->lastline = 1;
+       ls->source = source;
+       ls->envn = ktapc_ts_new(KTAP_ENV);  /* create env name */
+       mbuff_resize(ls->buff, KTAP_MINBUFFER);  /* initialize buffer */
+}
+
+/*
+ * =======================================================
+ * LEXICAL ANALYZER
+ * =======================================================
+ */
+static int check_next(ktap_lexstate *ls, const char *set)
+{
+       if (ls->current == '\0' || !strchr(set, ls->current))
+               return 0;
+       save_and_next(ls);
+       return 1;
+}
+
+/*
+ * change all characters 'from' in buffer to 'to'
+ */
+static void buffreplace(ktap_lexstate *ls, char from, char to)
+{
+       size_t n = mbuff_len(ls->buff);
+       char *p = mbuff(ls->buff);
+       while (n--)
+               if (p[n] == from) p[n] = to;
+}
+
+#if !defined(getlocaledecpoint)
+#define getlocaledecpoint()    (localeconv()->decimal_point[0])
+#endif
+
+#define mbuff2d(b,e)   ktapc_str2d(mbuff(b), mbuff_len(b) - 1, e)
+
+/*
+ * in case of format error, try to change decimal point separator to
+ * the one defined in the current locale and check again
+ */
+static void trydecpoint(ktap_lexstate *ls, ktap_seminfo *seminfo)
+{
+       char old = ls->decpoint;
+       ls->decpoint = getlocaledecpoint();
+       buffreplace(ls, old, ls->decpoint);  /* try new decimal separator */
+       if (!mbuff2d(ls->buff, &seminfo->r)) {
+               /* format error with correct decimal point: no more options */
+               buffreplace(ls, ls->decpoint, '.');  /* undo change (for error message) */
+               lexerror(ls, "malformed number", TK_NUMBER);
+       }
+}
+
+/*
+ * this function is quite liberal in what it accepts, as 'ktapc_str2d'
+ * will reject ill-formed numerals.
+ */
+static void read_numeral(ktap_lexstate *ls, ktap_seminfo *seminfo)
+{
+       const char *expo = "Ee";
+       int first = ls->current;
+
+       ktap_assert(isdigit(ls->current));
+       save_and_next(ls);
+       if (first == '0' && check_next(ls, "Xx"))  /* hexadecimal? */
+               expo = "Pp";
+       for (;;) {
+               if (check_next(ls, expo))  /* exponent part? */
+                       check_next(ls, "+-");  /* optional exponent sign */
+               if (isxdigit(ls->current) || ls->current == '.')
+                       save_and_next(ls);
+               else
+                       break;
+       }
+       save(ls, '\0');
+       buffreplace(ls, '.', ls->decpoint);  /* follow locale for decimal point */
+       if (!mbuff2d(ls->buff, &seminfo->r))  /* format error? */
+               trydecpoint(ls, seminfo); /* try to update decimal point separator */
+}
+
+/*
+ * skip a sequence '[=*[' or ']=*]' and return its number of '='s or
+ * -1 if sequence is malformed
+ */
+static int skip_sep(ktap_lexstate *ls)
+{
+       int count = 0;
+       int s = ls->current;
+
+       ktap_assert(s == '[' || s == ']');
+       save_and_next(ls);
+       while (ls->current == '=') {
+               save_and_next(ls);
+               count++;
+       }
+       return (ls->current == s) ? count : (-count) - 1;
+}
+
+static void read_long_string(ktap_lexstate *ls, ktap_seminfo *seminfo, int sep)
+{
+       save_and_next(ls);  /* skip 2nd `[' */
+       if (currIsNewline(ls))  /* string starts with a newline? */
+               inclinenumber(ls);  /* skip it */
+       for (;;) {
+               switch (ls->current) {
+               case EOZ:
+                       lexerror(ls, (seminfo) ? "unfinished long string" :
+                               "unfinished long comment", TK_EOS);
+                       break;  /* to avoid warnings */
+               case ']': {
+                       if (skip_sep(ls) == sep) {
+                               save_and_next(ls);  /* skip 2nd `]' */
+                               goto endloop;
+                       }
+                       break;
+               }
+               case '\n':
+               case '\r': {
+                       save(ls, '\n');
+                       inclinenumber(ls);
+                       /* avoid wasting space */
+                       if (!seminfo)
+                               mbuff_reset(ls->buff);
+                       break;
+               }
+               default: {
+                       if (seminfo)
+                               save_and_next(ls);
+                       else
+                               next(ls);
+               }
+               }
+       }
+
+ endloop:
+       if (seminfo)
+               seminfo->ts = lex_newstring(ls, mbuff(ls->buff) + (2 + sep),
+                       mbuff_len(ls->buff) - 2*(2 + sep));
+}
+
+static void escerror(ktap_lexstate *ls, int *c, int n, const char *msg)
+{
+       int i;
+       mbuff_reset(ls->buff);  /* prepare error message */
+       save(ls, '\\');
+       for (i = 0; i < n && c[i] != EOZ; i++)
+               save(ls, c[i]);
+       lexerror(ls, msg, TK_STRING);
+}
+
+static int readhexaesc(ktap_lexstate *ls)
+{
+       int c[3], i;  /* keep input for error message */
+       int r = 0;  /* result accumulator */
+       c[0] = 'x';  /* for error message */
+       for (i = 1; i < 3; i++) {  /* read two hexa digits */
+               c[i] = next(ls);
+               if (!isxdigit(c[i]))
+                       escerror(ls, c, i + 1, "hexadecimal digit expected");
+               r = (r << 4) + ktapc_hexavalue(c[i]);
+       }
+       return r;
+}
+
+static int readdecesc(ktap_lexstate *ls)
+{
+       int c[3], i;
+       int r = 0;  /* result accumulator */
+       for (i = 0; i < 3 && isdigit(ls->current); i++) {  /* read up to 3 digits */
+               c[i] = ls->current;
+               r = 10*r + c[i] - '0';
+               next(ls);
+       }
+       if (r > UCHAR_MAX)
+               escerror(ls, c, i, "decimal escape too large");
+       return r;
+}
+
+static void read_string(ktap_lexstate *ls, int del, ktap_seminfo *seminfo)
+{
+       save_and_next(ls);  /* keep delimiter (for error messages) */
+       while (ls->current != del) {
+               switch (ls->current) {
+               case EOZ:
+                       lexerror(ls, "unfinished string", TK_EOS);
+                       break;  /* to avoid warnings */
+               case '\n':
+               case '\r':
+                       lexerror(ls, "unfinished string", TK_STRING);
+                       break;  /* to avoid warnings */
+               case '\\': {  /* escape sequences */
+                       int c;  /* final character to be saved */
+                       next(ls);  /* do not save the `\' */
+                       switch (ls->current) {
+                       case 'a': c = '\a'; goto read_save;
+                       case 'b': c = '\b'; goto read_save;
+                       case 'f': c = '\f'; goto read_save;
+                       case 'n': c = '\n'; goto read_save;
+                       case 'r': c = '\r'; goto read_save;
+                       case 't': c = '\t'; goto read_save;
+                       case 'v': c = '\v'; goto read_save;
+                       case 'x': c = readhexaesc(ls); goto read_save;
+                       case '\n': case '\r':
+                               inclinenumber(ls); c = '\n'; goto only_save;
+                       case '\\': case '\"': case '\'':
+                               c = ls->current; goto read_save;
+                       case EOZ: goto no_save;  /* will raise an error next loop */
+                       case 'z': {  /* zap following span of spaces */
+                               next(ls);  /* skip the 'z' */
+                               while (isspace(ls->current)) {
+                                       if (currIsNewline(ls))
+                                               inclinenumber(ls);
+                                       else
+                                               next(ls);
+                               }
+                               goto no_save;
+                       }
+                       default: {
+                               if (!isdigit(ls->current))
+                                       escerror(ls, &ls->current, 1, "invalid escape sequence");
+                               /* digital escape \ddd */
+                               c = readdecesc(ls);
+                               goto only_save;
+                       }
+                       }
+ read_save:
+                       next(ls);  /* read next character */
+ only_save:
+                       save(ls, c);  /* save 'c' */
+ no_save:
+                       break;
+               }
+               default:
+                       save_and_next(ls);
+               }
+       }
+       save_and_next(ls);  /* skip delimiter */
+       seminfo->ts = lex_newstring(ls, mbuff(ls->buff) + 1, mbuff_len(ls->buff) - 2);
+}
+
+static int llex(ktap_lexstate *ls, ktap_seminfo *seminfo)
+{
+       mbuff_reset(ls->buff);
+
+       for (;;) {
+               switch (ls->current) {
+               case '\n': case '\r': {  /* line breaks */
+                       inclinenumber(ls);
+                       break;
+               }
+               case ' ': case '\f': case '\t': case '\v': {  /* spaces */
+                       next(ls);
+                       break;
+               }
+               case '#': {
+                       while (!currIsNewline(ls) && ls->current != EOZ)
+                               next(ls);  /* skip until end of line (or end of file) */
+                       break;
+               }
+               #if 0
+               case '-': {  /* '-' or '--' (comment) */
+                       next(ls);
+                       if (ls->current != '-')
+                               return '-';
+                       /* else is a comment */
+                       next(ls);
+                       if (ls->current == '[') {  /* long comment? */
+                               int sep = skip_sep(ls);
+                               mbuff_reset(ls->buff);  /* `skip_sep' may dirty the buffer */
+                               if (sep >= 0) {
+                                       read_long_string(ls, NULL, sep);  /* skip long comment */
+                                       mbuff_reset(ls->buff);  /* previous call may dirty the buff. */
+                                       break;
+                               }
+                       }
+                       /* else short comment */
+                       while (!currIsNewline(ls) && ls->current != EOZ)
+                               next(ls);  /* skip until end of line (or end of file) */
+                       break;
+               }
+               #endif
+               case '[': {  /* long string or simply '[' */
+                       int sep = skip_sep(ls);
+                       if (sep >= 0) {
+                               read_long_string(ls, seminfo, sep);
+                               return TK_STRING;
+                       }
+                       else if (sep == -1)
+                               return '[';
+                       else
+                               lexerror(ls, "invalid long string delimiter", TK_STRING);
+               }
+               case '+': {
+                       next(ls);
+                       if (ls->current != '=')
+                               return '+';
+                       else {
+                               next(ls);
+                               return TK_INCR;
+                       }
+               }
+               case '=': {
+                       next(ls);
+                       if (ls->current != '=')
+                               return '=';
+                       else {
+                               next(ls);
+                               return TK_EQ;
+                       }
+               }
+               case '<': {
+                       next(ls);
+                       if (ls->current == '=')
+                               return TK_LE;
+                       else if (ls->current == '<') {
+                               next(ls);
+                               if (ls->current == '<') {
+                                       next(ls);
+                                       return TK_AGGR_ASSIGN;
+                               }
+                       } else {
+                               return '<';
+                       }
+               }
+               case '>': {
+                       next(ls);
+                       if (ls->current != '=')
+                               return '>';
+                       else {
+                               next(ls);
+                               return TK_GE;
+                       }
+               }
+               case '!': {
+                       next(ls);
+                       if (ls->current != '=')
+                               return TK_NOT;
+                       else {
+                               next(ls);
+                               return TK_NE;
+                       }
+               }
+               case ':': {
+                       next(ls);
+                       if (ls->current != ':')
+                               return ':';
+                       else {
+                               next(ls);
+                               return TK_DBCOLON;
+                       }
+               }
+               case '"': case '\'': {  /* short literal strings */
+                       read_string(ls, ls->current, seminfo);
+                       return TK_STRING;
+               }
+               case '`': {  /* short literal kernel symbol */
+                       read_string(ls, ls->current, seminfo);
+                       return TK_KSYM;
+               }
+               case '.': {  /* '.', '..', '...', or number */
+                       save_and_next(ls);
+                       if (check_next(ls, ".")) {
+                               if (check_next(ls, "."))
+                                       return TK_DOTS;   /* '...' */
+                               else
+                                       return TK_CONCAT;   /* '..' */
+                       }
+                       else if (!isdigit(ls->current))
+                               return '.';
+                       /* else go through */
+               }
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9': {
+                       read_numeral(ls, seminfo);
+                       return TK_NUMBER;
+               }
+               case EOZ: {
+                       return TK_EOS;
+               }
+               case '&': {
+                       next(ls);
+                       if (ls->current != '&')
+                               return '&';
+                       else {
+                               next(ls);
+                               return TK_AND;
+                       }
+               }
+               case '|': {
+                       next(ls);
+                       if (ls->current != '|')
+                               return '|';
+                       else {
+                               next(ls);
+                               return TK_OR;
+                       }
+               }
+               default: {
+                       if (islalpha(ls->current)) {
+                               /* identifier or reserved word? */
+                               ktap_string *ts;
+                               do {
+                                       save_and_next(ls);
+                               } while (islalnum(ls->current));
+                               ts = lex_newstring(ls, mbuff(ls->buff),
+                                                       mbuff_len(ls->buff));
+                               seminfo->ts = ts;
+                               if (isreserved(ts))  /* reserved word? */
+                                       return ts->tsv.extra - 1 +
+                                               FIRST_RESERVED;
+                               else {
+                                       return TK_NAME;
+                               }
+                       } else {  /* single-char tokens (+ - / ...) */
+                               int c = ls->current;
+                               next(ls);
+                               return c;
+                       }
+               }
+               }
+       }
+}
+
+void lex_read_string_until(ktap_lexstate *ls, int c)
+{
+       ktap_string *ts;
+       char errmsg[32];
+
+       mbuff_reset(ls->buff);
+
+       while (ls->current == ' ')
+               next(ls);
+
+       do {
+               save_and_next(ls);
+       } while (ls->current != c && ls->current != EOZ);
+
+       if (ls->current != c) {
+               sprintf(errmsg, "expect %c", c);
+               lexerror(ls, errmsg, 0);
+       }
+
+       ts = lex_newstring(ls, mbuff(ls->buff), mbuff_len(ls->buff));
+       ls->t.seminfo.ts = ts;
+       ls->t.token = TK_STRING;
+}
+
+void lex_next(ktap_lexstate *ls)
+{
+       ls->lastline = ls->linenumber;
+       if (ls->lookahead.token != TK_EOS) {  /* is there a look-ahead token? */
+               ls->t = ls->lookahead;  /* use this one */
+               ls->lookahead.token = TK_EOS;  /* and discharge it */
+       } else
+               ls->t.token = llex(ls, &ls->t.seminfo);  /* read next token */
+}
+
+int lex_lookahead(ktap_lexstate *ls)
+{
+       ktap_assert(ls->lookahead.token == TK_EOS);
+       ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
+       return ls->lookahead.token;
+}
+
diff --git a/drivers/staging/ktap/userspace/main.c b/drivers/staging/ktap/userspace/main.c
new file mode 100644 (file)
index 0000000..82fbab2
--- /dev/null
@@ -0,0 +1,727 @@
+/*
+ * main.c - ktap compiler and loader entry
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <string.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+
+#include "../include/ktap_types.h"
+#include "../include/ktap_opcodes.h"
+#include "ktapc.h"
+#include "../runtime/kp_obj.h"
+#include "../runtime/kp_str.h"
+#include "../runtime/kp_tab.h"
+#include "symbol.h"
+#include "cparser.h"
+
+
+/*******************************************************************/
+
+void *ktapc_reallocv(void *block, size_t osize, size_t nsize)
+{
+       return kp_reallocv(NULL, block, osize, nsize);
+}
+
+ktap_closure *ktapc_newclosure(int n)
+{
+       return kp_newclosure(NULL, n);
+}
+
+ktap_proto *ktapc_newproto()
+{
+       return kp_newproto(NULL);
+}
+
+const ktap_value *ktapc_table_get(ktap_tab *t, const ktap_value *key)
+{
+       return kp_tab_get(t, key);
+}
+
+void ktapc_table_setvalue(ktap_tab *t, const ktap_value *key, ktap_value *val)
+{
+       kp_tab_setvalue(NULL, t, key, val);
+}
+
+ktap_tab *ktapc_table_new()
+{
+       return kp_tab_new(NULL);
+}
+
+ktap_string *ktapc_ts_newlstr(const char *str, size_t l)
+{
+       return kp_tstring_newlstr(NULL, str, l);
+}
+
+ktap_string *ktapc_ts_new(const char *str)
+{
+       return kp_tstring_new(NULL, str);
+}
+
+int ktapc_ts_eqstr(ktap_string *a, ktap_string *b)
+{
+       return kp_tstring_eqstr(a, b);
+}
+
+static void ktapc_runerror(const char *err_msg_fmt, ...)
+{
+       va_list ap;
+
+       fprintf(stderr, "ktapc_runerror\n");
+
+       va_start(ap, err_msg_fmt);
+       vfprintf(stderr, err_msg_fmt, ap);
+       va_end(ap);
+
+       exit(EXIT_FAILURE);
+}
+
+/*
+ * todo: memory leak here
+ */
+char *ktapc_sprintf(const char *fmt, ...)
+{
+       char *msg = malloc(128);
+
+       va_list argp;
+       va_start(argp, fmt);
+       vsprintf(msg, fmt, argp);
+       va_end(argp);
+       return msg;
+}
+
+
+#define MINSIZEARRAY   4
+
+void *ktapc_growaux(void *block, int *size, size_t size_elems, int limit,
+                   const char *what)
+{
+       void *newblock;
+       int newsize;
+
+       if (*size >= limit/2) {  /* cannot double it? */
+               if (*size >= limit)  /* cannot grow even a little? */
+                       ktapc_runerror("too many %s (limit is %d)\n",
+                                       what, limit);
+               newsize = limit;  /* still have at least one free place */
+       } else {
+               newsize = (*size) * 2;
+               if (newsize < MINSIZEARRAY)
+                       newsize = MINSIZEARRAY;  /* minimum size */
+       }
+
+       newblock = ktapc_reallocv(block, (*size) * size_elems, newsize * size_elems);
+       *size = newsize;  /* update only when everything else is OK */
+       return newblock;
+}
+
+/*************************************************************************/
+
+#define print_base(i) \
+       do {    \
+               if (i < f->sizelocvars) /* it's a localvars */ \
+                       printf("%s", getstr(f->locvars[i].varname));  \
+               else \
+                       printf("base + %d", i); \
+       } while (0)
+
+#define print_RK(instr, _field)  \
+       do {    \
+               if (ISK(GETARG_##_field(instr))) \
+                       kp_showobj(NULL, k + INDEXK(GETARG_##_field(instr))); \
+               else \
+                       print_base(GETARG_##_field(instr)); \
+       } while (0)
+
+#define print_RKA(instr) print_RK(instr, A)
+#define print_RKB(instr) print_RK(instr, B)
+#define print_RKC(instr) print_RK(instr, C)
+
+#define print_upvalue(idx) \
+       do {    \
+               if ((idx) == 0) \
+                       printf("global"); \
+               else \
+                       printf("upvalues[%d]", (idx)); \
+       } while (0)
+
+static void decode_instruction(ktap_proto *f, int instr)
+{
+       int opcode = GET_OPCODE(instr);
+       ktap_value *k;
+
+       k = f->k;
+
+       printf("%.8x\t", instr);
+       printf("%s\t", ktap_opnames[opcode]);
+
+       switch (opcode) {
+       case OP_MOVE:
+               printf("\t");
+               print_base(GETARG_A(instr));
+               printf(" <- ");
+               print_base(GETARG_B(instr));
+               break;
+       case OP_GETTABUP:
+               print_base(GETARG_A(instr));
+               printf(" <- ");
+               print_upvalue(GETARG_B(instr));
+               printf("{"); print_RKC(instr); printf("}");
+
+               break;
+       case OP_GETTABLE:
+               print_base(GETARG_A(instr));
+               printf(" <- ");
+
+               print_base(GETARG_B(instr));
+
+               printf("{");
+               print_RKC(instr);
+               printf("}");
+               break;
+       case OP_SETTABLE:
+               print_base(GETARG_A(instr));
+               printf("{");
+               print_RKB(instr);
+               printf("}");
+               printf(" <- ");
+               print_RKC(instr);
+               break;
+       case OP_LOADK:
+               printf("\t");
+               print_base(GETARG_A(instr));
+               printf(" <- ");
+
+               kp_showobj(NULL, k + GETARG_Bx(instr));
+               break;
+       case OP_CALL:
+               printf("\t");
+               print_base(GETARG_A(instr));
+               break;
+       case OP_JMP:
+               printf("\t%d", GETARG_sBx(instr));
+               break;
+       case OP_CLOSURE:
+               printf("\t");
+               print_base(GETARG_A(instr));
+               printf(" <- closure(func starts from line %d)",
+                       f->p[GETARG_Bx(instr)]->lineinfo[0]);
+               break;
+       case OP_SETTABUP:
+               print_upvalue(GETARG_A(instr));
+               printf("{");
+               print_RKB(instr);
+               printf("} <- ");
+
+               print_RKC(instr);
+               break;
+       case OP_GETUPVAL:
+               print_base(GETARG_A(instr));
+               printf(" <- ");
+
+               print_upvalue(GETARG_B(instr));
+               break;
+       case OP_NEWTABLE:
+               print_base(GETARG_A(instr));
+               printf(" <- {}");
+       default:
+               break;
+       }
+
+       printf("\n");
+}
+
+static int function_nr = 0;
+
+/* this is a debug function used for check bytecode chunk file */
+static void dump_function(int level, ktap_proto *f)
+{
+       int i;
+
+       printf("\n----------------------------------------------------\n");
+       printf("function %d [level %d]:\n", function_nr++, level);
+       printf("linedefined: %d\n", f->linedefined);
+       printf("lastlinedefined: %d\n", f->lastlinedefined);
+       printf("numparams: %d\n", f->numparams);
+       printf("is_vararg: %d\n", f->is_vararg);
+       printf("maxstacksize: %d\n", f->maxstacksize);
+       printf("source: %s\n", getstr(f->source));
+       printf("sizelineinfo: %d \t", f->sizelineinfo);
+       for (i = 0; i < f->sizelineinfo; i++)
+               printf("%d ", f->lineinfo[i]);
+       printf("\n");
+
+       printf("sizek: %d\n", f->sizek);
+       for (i = 0; i < f->sizek; i++) {
+               switch(f->k[i].type) {
+               case KTAP_TNIL:
+                       printf("\tNIL\n");
+                       break;
+               case KTAP_TBOOLEAN:
+                       printf("\tBOOLEAN: ");
+                       printf("%d\n", f->k[i].val.b);
+                       break;
+               case KTAP_TNUMBER:
+                       printf("\tTNUMBER: ");
+                       printf("%ld\n", f->k[i].val.n);
+                       break;
+               case KTAP_TSHRSTR:
+               case KTAP_TLNGSTR:
+                       printf("\tTSTRING: ");
+                       printf("%s\n", svalue(&(f->k[i])));
+                       break;
+               default:
+                       printf("\tUnknow constant type %d: ", f->k[i].type);
+                       kp_showobj(NULL, &(f->k[i]));
+                       printf("\n");
+               }
+       }
+
+       printf("sizelocvars: %d\n", f->sizelocvars);
+       for (i = 0; i < f->sizelocvars; i++) {
+               printf("\tlocvars: %s startpc: %d endpc: %d\n",
+                       getstr(f->locvars[i].varname), f->locvars[i].startpc,
+                       f->locvars[i].endpc);
+       }
+
+       printf("sizeupvalues: %d\n", f->sizeupvalues);
+       for (i = 0; i < f->sizeupvalues; i++) {
+               printf("\tname: %s instack: %d idx: %d\n",
+                       getstr(f->upvalues[i].name), f->upvalues[i].instack,
+                       f->upvalues[i].idx);
+       }
+
+       printf("\n");
+       printf("sizecode: %d\n", f->sizecode);
+       for (i = 0; i < f->sizecode; i++)
+               decode_instruction(f, f->code[i]);
+
+       printf("sizep: %d\n", f->sizep);
+       for (i = 0; i < f->sizep; i++)
+               dump_function(level + 1, f->p[i]);
+
+}
+
+static void usage(const char *msg_fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, msg_fmt);
+       vfprintf(stderr, msg_fmt, ap);
+       va_end(ap);
+
+       fprintf(stderr,
+"Usage: ktap [options] file [script args] -- cmd [args]\n"
+"   or: ktap [options] -e one-liner  -- cmd [args]\n"
+"\n"
+"Options and arguments:\n"
+"  -o file        : send script output to file, instead of stderr\n"
+"  -p pid         : specific tracing pid\n"
+"  -C cpu         : cpu to monitor in system-wide\n"
+"  -T             : show timestamp for event\n"
+"  -V             : show version\n"
+"  -v             : enable verbose mode\n"
+"  -q             : suppress start tracing message\n"
+"  -s             : simple event tracing\n"
+"  -b             : list byte codes\n"
+"  -le [glob]     : list pre-defined events in system\n"
+#ifndef NO_LIBELF
+"  -lf DSO        : list available functions from DSO\n"
+"  -lm DSO        : list available sdt notes from DSO\n"
+#endif
+"  file           : program read from script file\n"
+"  -- cmd [args]  : workload to tracing\n");
+
+       exit(EXIT_FAILURE);
+}
+
+ktap_global_state dummy_global_state;
+
+static void init_dummy_global_state()
+{
+       memset(&dummy_global_state, 0, sizeof(ktap_global_state));
+       dummy_global_state.seed = 201236;
+
+        kp_tstring_resize(NULL, 32); /* set inital string hashtable size */
+}
+
+#define handle_error(str) do { perror(str); exit(-1); } while(0)
+
+static struct ktap_parm uparm;
+static int ktap_trunk_mem_size = 1024;
+
+static int ktapc_writer(const void* p, size_t sz, void* ud)
+{
+       if (uparm.trunk_len + sz > ktap_trunk_mem_size) {
+               int new_size = (uparm.trunk_len + sz) * 2;
+               uparm.trunk = realloc(uparm.trunk, new_size);
+               ktap_trunk_mem_size = new_size;
+       }
+
+       memcpy(uparm.trunk + uparm.trunk_len, p, sz);
+       uparm.trunk_len += sz;
+
+       return 0;
+}
+
+
+static int forks;
+static char **workload_argv;
+
+static int fork_workload(int ktap_fd)
+{
+       int pid;
+
+       pid = fork();
+       if (pid < 0)
+               handle_error("failed to fork");
+
+       if (pid > 0)
+               return pid;
+
+       signal(SIGTERM, SIG_DFL);
+
+       execvp("", workload_argv);
+
+       /*
+        * waiting ktapvm prepare all tracing event
+        * make it more robust in future.
+        */
+       pause();
+
+       execvp(workload_argv[0], workload_argv);
+
+       perror(workload_argv[0]);
+       exit(-1);
+
+       return -1;
+}
+
+#define KTAPVM_PATH "/sys/kernel/debug/ktap/ktapvm"
+
+static char *output_filename;
+
+static int run_ktapvm()
+{
+        int ktapvm_fd, ktap_fd;
+       int ret;
+
+       ktapvm_fd = open(KTAPVM_PATH, O_RDONLY);
+       if (ktapvm_fd < 0)
+               handle_error("open " KTAPVM_PATH " failed");
+
+       ktap_fd = ioctl(ktapvm_fd, 0, NULL);
+       if (ktap_fd < 0)
+               handle_error("ioctl ktapvm failed");
+
+       ktapio_create(output_filename);
+
+       if (forks) {
+               uparm.trace_pid = fork_workload(ktap_fd);
+               uparm.workload = 1;
+       }
+
+       ret = ioctl(ktap_fd, KTAP_CMD_IOC_RUN, &uparm);
+
+       close(ktap_fd);
+       close(ktapvm_fd);
+
+       return ret;
+}
+
+int verbose;
+static int quiet;
+static int dump_bytecode;
+static char oneline_src[1024];
+static int trace_pid = -1;
+static int trace_cpu = -1;
+static int print_timestamp;
+
+#define SIMPLE_ONE_LINER_FMT   \
+       "trace %s { print(cpu(), tid(), execname(), argevent) }"
+
+static const char *script_file;
+static int script_args_start;
+static int script_args_end;
+
+#ifndef NO_LIBELF
+struct binary_base
+{
+       int type;
+       const char *binary;
+};
+static int print_symbol(const char *name, vaddr_t addr, void *arg)
+{
+       struct binary_base *base = (struct binary_base *)arg;
+       const char *type = base->type == FIND_SYMBOL ?
+               "probe" : "sdt";
+
+       printf("%s:%s:%s\n", type, base->binary, name);
+       return 0;
+}
+#endif
+
+static void parse_option(int argc, char **argv)
+{
+       char pid[32] = {0};
+       char cpu_str[32] = {0};
+       char *next_arg;
+       int i, j;
+
+       for (i = 1; i < argc; i++) {
+               if (argv[i][0] != '-') {
+                       script_file = argv[i];
+                       if (!script_file)
+                               usage("");
+
+                       script_args_start = i + 1;
+                       script_args_end = argc;
+
+                       for (j = i + 1; j < argc; j++) {
+                               if (argv[j][0] == '-' && argv[j][1] == '-')
+                                       goto found_cmd;
+                       }
+
+                       return;
+               }
+
+               if (argv[i][0] == '-' && argv[i][1] == '-') {
+                       j = i;
+                       goto found_cmd;
+               }
+
+               next_arg = argv[i + 1];
+
+               switch (argv[i][1]) {
+               case 'o':
+                       output_filename = malloc(strlen(next_arg) + 1);
+                       if (!output_filename)
+                               return;
+
+                       strncpy(output_filename, next_arg, strlen(next_arg));
+                       i++;
+                       break;
+               case 'e':
+                       strncpy(oneline_src, next_arg, strlen(next_arg));
+                       i++;
+                       break;
+               case 'p':
+                       strncpy(pid, next_arg, strlen(next_arg));
+                       trace_pid = atoi(pid);
+                       i++;
+                       break;
+               case 'C':
+                       strncpy(cpu_str, next_arg, strlen(next_arg));
+                       trace_cpu = atoi(cpu_str);
+                       i++;
+                       break;
+               case 'T':
+                       print_timestamp = 1;
+                       break;
+               case 'v':
+                       verbose = 1;
+                       break;
+               case 'q':
+                       quiet = 1;
+                       break;
+               case 's':
+                       sprintf(oneline_src, SIMPLE_ONE_LINER_FMT, next_arg);
+                       i++;
+                       break;
+               case 'b':
+                       dump_bytecode = 1;
+                       break;
+               case 'l': /* list available events */
+                       switch (argv[i][2]) {
+                       case 'e': /* tracepoints */
+                               list_available_events(next_arg);
+                               exit(EXIT_SUCCESS);
+#ifndef NO_LIBELF
+                       case 'f': /* functions in DSO */
+                       case 'm': /* static marks in DSO */ {
+                               const char *binary = next_arg;
+                               int type = argv[i][2] == 'f' ?
+                                               FIND_SYMBOL : FIND_STAPSDT_NOTE;
+                               struct binary_base base = {
+                                       .type = type,
+                                       .binary = binary,
+                               };
+                               int ret;
+
+                               ret = parse_dso_symbols(binary, type,
+                                                       print_symbol,
+                                                       (void *)&base);
+                               if (ret <= 0) {
+                                       fprintf(stderr,
+                                       "error: no symbols in binary %s\n",
+                                               binary);
+                                       exit(EXIT_FAILURE);
+                               }
+                               exit(EXIT_SUCCESS);
+                       }
+#endif
+                       default:
+                               exit(EXIT_FAILURE);
+                       }
+                       break;
+               case 'V':
+#ifdef CONFIG_KTAP_FFI
+                       usage("%s (with FFI)\n\n", KTAP_VERSION);
+#else
+                       usage("%s\n\n", KTAP_VERSION);
+#endif
+                       break;
+               case '?':
+               case 'h':
+                       usage("");
+                       break;
+               default:
+                       usage("wrong argument\n");
+                       break;
+               }
+       }
+
+       return;
+
+ found_cmd:
+       script_args_end = j;
+       forks = 1;
+       workload_argv = &argv[j + 1];
+}
+
+static void compile(const char *input)
+{
+       ktap_closure *cl;
+       char *buff;
+       struct stat sb;
+       int fdin;
+
+       if (oneline_src[0] != '\0') {
+               init_dummy_global_state();
+               ffi_cparser_init();
+               cl = ktapc_parser(oneline_src, input);
+               goto dump;
+       }
+
+       fdin = open(input, O_RDONLY);
+       if (fdin < 0) {
+               fprintf(stderr, "open file %s failed\n", input);
+               exit(-1);
+       }
+
+       if (fstat(fdin, &sb) == -1)
+               handle_error("fstat failed");
+
+       buff = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fdin, 0);
+       if (buff == MAP_FAILED)
+               handle_error("mmap failed");
+
+       init_dummy_global_state();
+       ffi_cparser_init();
+       cl = ktapc_parser(buff, input);
+
+       munmap(buff, sb.st_size);
+       close(fdin);
+
+ dump:
+       if (dump_bytecode) {
+               dump_function(1, cl->p);
+               exit(0);
+       }
+
+       /* ktapc output */
+       uparm.trunk = malloc(ktap_trunk_mem_size);
+       if (!uparm.trunk)
+               handle_error("malloc failed");
+
+       ktapc_dump(cl->p, ktapc_writer, NULL, 0);
+       ffi_cparser_free();
+}
+
+int main(int argc, char **argv)
+{
+       char **ktapvm_argv;
+       int new_index, i;
+       int ret;
+
+       if (argc == 1)
+               usage("");
+
+       parse_option(argc, argv);
+
+       if (oneline_src[0] != '\0')
+               script_file = "one-liner";
+
+       compile(script_file);
+
+       ktapvm_argv = (char **)malloc(sizeof(char *)*(script_args_end -
+                                       script_args_start + 1));
+       if (!ktapvm_argv) {
+               fprintf(stderr, "canno allocate ktapvm_argv\n");
+               return -1;
+       }
+
+       ktapvm_argv[0] = malloc(strlen(script_file) + 1);
+       if (!ktapvm_argv[0]) {
+               fprintf(stderr, "canno allocate memory\n");
+               return -1;
+       }
+       strcpy(ktapvm_argv[0], script_file);
+       ktapvm_argv[0][strlen(script_file)] = '\0';
+
+       /* pass rest argv into ktapvm */
+       new_index = 1;
+       for (i = script_args_start; i < script_args_end; i++) {
+               ktapvm_argv[new_index] = malloc(strlen(argv[i]) + 1);
+               if (!ktapvm_argv[new_index]) {
+                       fprintf(stderr, "canno allocate memory\n");
+                       return -1;
+               }
+               strcpy(ktapvm_argv[new_index], argv[i]);
+               ktapvm_argv[new_index][strlen(argv[i])] = '\0';
+               new_index++;
+       }
+
+       uparm.argv = ktapvm_argv;
+       uparm.argc = new_index;
+       uparm.verbose = verbose;
+       uparm.trace_pid = trace_pid;
+       uparm.trace_cpu = trace_cpu;
+       uparm.print_timestamp = print_timestamp;
+       uparm.quiet = quiet;
+
+       /* start running into kernel ktapvm */
+       ret = run_ktapvm();
+
+       cleanup_event_resources();
+       return ret;
+}
+
+
diff --git a/drivers/staging/ktap/userspace/parser.c b/drivers/staging/ktap/userspace/parser.c
new file mode 100644 (file)
index 0000000..f47c0c0
--- /dev/null
@@ -0,0 +1,1963 @@
+/*
+ * parser.c - ktap parser
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+ *  - The part of code in this file is copied from lua initially.
+ *  - lua's MIT license is compatible with GPL.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/ktap_types.h"
+#include "../include/ktap_opcodes.h"
+#include "ktapc.h"
+#include "cparser.h"
+
+/* maximum number of local variables per function (must be smaller
+   than 250, due to the bytecode format) */
+#define MAXVARS                200
+
+#define hasmultret(k)          ((k) == VCALL || (k) == VVARARG)
+
+/*
+ * nodes for block list (list of active blocks)
+ */
+typedef struct ktap_blockcnt {
+       struct ktap_blockcnt *previous;  /* chain */
+       short firstlabel;  /* index of first label in this block */
+       short firstgoto;  /* index of first pending goto in this block */
+       u8 nactvar;  /* # active locals outside the block */
+       u8 upval;  /* true if some variable in the block is an upvalue */
+        u8 isloop;  /* true if `block' is a loop */
+} ktap_blockcnt;
+
+/*
+ * prototypes for recursive non-terminal functions
+ */
+static void statement (ktap_lexstate *ls);
+static void expr (ktap_lexstate *ls, ktap_expdesc *v);
+
+static void anchor_token(ktap_lexstate *ls)
+{
+       /* last token from outer function must be EOS */
+       ktap_assert((int)(ls->fs != NULL) || ls->t.token == TK_EOS);
+       if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) {
+               ktap_string *ts = ls->t.seminfo.ts;
+               lex_newstring(ls, getstr(ts), ts->tsv.len);
+       }
+}
+
+/* semantic error */
+static void semerror(ktap_lexstate *ls, const char *msg)
+{
+       ls->t.token = 0;  /* remove 'near to' from final message */
+       lex_syntaxerror(ls, msg);
+}
+
+static void error_expected(ktap_lexstate *ls, int token)
+{
+       lex_syntaxerror(ls,
+               ktapc_sprintf("%s expected", lex_token2str(ls, token)));
+}
+
+static void errorlimit(ktap_funcstate *fs, int limit, const char *what)
+{
+       const char *msg;
+       int line = fs->f->linedefined;
+       const char *where = (line == 0) ? "main function"
+                               : ktapc_sprintf("function at line %d", line);
+
+       msg = ktapc_sprintf("too many %s (limit is %d) in %s",
+                               what, limit, where);
+       lex_syntaxerror(fs->ls, msg);
+}
+
+static void checklimit(ktap_funcstate *fs, int v, int l, const char *what)
+{
+       if (v > l)
+               errorlimit(fs, l, what);
+}
+
+static int testnext(ktap_lexstate *ls, int c)
+{
+       if (ls->t.token == c) {
+               lex_next(ls);
+               return 1;
+       }
+       else
+               return 0;
+}
+
+static void check(ktap_lexstate *ls, int c)
+{
+       if (ls->t.token != c)
+               error_expected(ls, c);
+}
+
+static void checknext(ktap_lexstate *ls, int c)
+{
+       check(ls, c);
+       lex_next(ls);
+}
+
+#define check_condition(ls,c,msg)      { if (!(c)) lex_syntaxerror(ls, msg); }
+
+static void check_match(ktap_lexstate *ls, int what, int who, int where)
+{
+       if (!testnext(ls, what)) {
+               if (where == ls->linenumber)
+                       error_expected(ls, what);
+               else {
+                       lex_syntaxerror(ls, ktapc_sprintf(
+                                       "%s expected (to close %s at line %d)",
+                                       lex_token2str(ls, what),
+                                       lex_token2str(ls, who), where));
+               }
+       }
+}
+
+static ktap_string *str_checkname(ktap_lexstate *ls)
+{
+       ktap_string *ts;
+
+       check(ls, TK_NAME);
+       ts = ls->t.seminfo.ts;
+       lex_next(ls);
+       return ts;
+}
+
+static void init_exp(ktap_expdesc *e, expkind k, int i)
+{
+       e->f = e->t = NO_JUMP;
+       e->k = k;
+       e->u.info = i;
+}
+
+static void codestring(ktap_lexstate *ls, ktap_expdesc *e, ktap_string *s)
+{
+       init_exp(e, VK, codegen_stringK(ls->fs, s));
+}
+
+static void codenumber(ktap_lexstate *ls, ktap_expdesc *e, ktap_number n)
+{
+       init_exp(e, VK, codegen_numberK(ls->fs, n));
+}
+
+static void checkname(ktap_lexstate *ls, ktap_expdesc *e)
+{
+       codestring(ls, e, str_checkname(ls));
+}
+
+static int registerlocalvar(ktap_lexstate *ls, ktap_string *varname)
+{
+       ktap_funcstate *fs = ls->fs;
+       ktap_proto *f = fs->f;
+       int oldsize = f->sizelocvars;
+
+       ktapc_growvector(f->locvars, fs->nlocvars, f->sizelocvars,
+                        ktap_locvar, SHRT_MAX, "local variables");
+
+       while (oldsize < f->sizelocvars)
+               f->locvars[oldsize++].varname = NULL;
+
+       f->locvars[fs->nlocvars].varname = varname;
+       return fs->nlocvars++;
+}
+
+static void new_localvar(ktap_lexstate *ls, ktap_string *name)
+{
+       ktap_funcstate *fs = ls->fs;
+       ktap_dyndata *dyd = ls->dyd;
+       int reg = registerlocalvar(ls, name);
+
+       checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
+                  MAXVARS, "local variables");
+       ktapc_growvector(dyd->actvar.arr, dyd->actvar.n + 1,
+                        dyd->actvar.size, ktap_vardesc, MAX_INT, "local variables");
+       dyd->actvar.arr[dyd->actvar.n++].idx = (short)reg;
+}
+
+static void new_localvarliteral_(ktap_lexstate *ls, const char *name, size_t sz)
+{
+       new_localvar(ls, lex_newstring(ls, name, sz));
+}
+
+#define new_localvarliteral(ls,v) \
+       new_localvarliteral_(ls, "" v, (sizeof(v)/sizeof(char))-1)
+
+static ktap_locvar *getlocvar(ktap_funcstate *fs, int i)
+{
+       int idx = fs->ls->dyd->actvar.arr[fs->firstlocal + i].idx;
+
+       ktap_assert(idx < fs->nlocvars);
+       return &fs->f->locvars[idx];
+}
+
+static void adjustlocalvars(ktap_lexstate *ls, int nvars)
+{
+       ktap_funcstate *fs = ls->fs;
+
+       fs->nactvar = (u8)(fs->nactvar + nvars);
+       for (; nvars; nvars--) {
+               getlocvar(fs, fs->nactvar - nvars)->startpc = fs->pc;
+       }
+}
+
+static void removevars(ktap_funcstate *fs, int tolevel)
+{
+       fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel);
+
+       while (fs->nactvar > tolevel)
+               getlocvar(fs, --fs->nactvar)->endpc = fs->pc;
+}
+
+static int searchupvalue(ktap_funcstate *fs, ktap_string *name)
+{
+       int i;
+       ktap_upvaldesc *up = fs->f->upvalues;
+
+       for (i = 0; i < fs->nups; i++) {
+               if (ktapc_ts_eqstr(up[i].name, name))
+                       return i;
+       }
+       return -1;  /* not found */
+}
+
+static int newupvalue(ktap_funcstate *fs, ktap_string *name, ktap_expdesc *v)
+{
+       ktap_proto *f = fs->f;
+       int oldsize = f->sizeupvalues;
+
+       checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues");
+       ktapc_growvector(f->upvalues, fs->nups, f->sizeupvalues,
+                        ktap_upvaldesc, MAXUPVAL, "upvalues");
+
+       while (oldsize < f->sizeupvalues)
+               f->upvalues[oldsize++].name = NULL;
+       f->upvalues[(int)fs->nups].instack = (v->k == VLOCAL);
+       f->upvalues[(int)fs->nups].idx = (u8)(v->u.info);
+       f->upvalues[(int)fs->nups].name = name;
+       return fs->nups++;
+}
+
+static int searchvar(ktap_funcstate *fs, ktap_string *n)
+{
+       int i;
+
+       for (i = fs->nactvar-1; i >= 0; i--) {
+               if (ktapc_ts_eqstr(n, getlocvar(fs, i)->varname))
+                       return i;
+       }
+       return -1;  /* not found */
+}
+
+/*
+ * Mark block where variable at given level was defined
+ * (to emit close instructions later).
+ */
+static void markupval(ktap_funcstate *fs, int level)
+{
+       ktap_blockcnt *bl = fs->bl;
+
+       while (bl->nactvar > level)
+               bl = bl->previous;
+       bl->upval = 1;
+}
+
+/*
+ * Find variable with given name 'n'. If it is an upvalue, add this
+ * upvalue into all intermediate functions.
+ */
+static int singlevaraux(ktap_funcstate *fs, ktap_string *n, ktap_expdesc *var, int base)
+{
+       if (fs == NULL)  /* no more levels? */
+               return VVOID;  /* default is global */
+       else {
+               int v = searchvar(fs, n);  /* look up locals at current level */
+               if (v >= 0) {  /* found? */
+                       init_exp(var, VLOCAL, v);  /* variable is local */
+                       if (!base)
+                               markupval(fs, v);  /* local will be used as an upval */
+                       return VLOCAL;
+               } else {  /* not found as local at current level; try upvalues */
+                       int idx = searchupvalue(fs, n);  /* try existing upvalues */
+                       if (idx < 0) {  /* not found? */
+                               if (singlevaraux(fs->prev, n, var, 0) == VVOID) /* try upper levels */
+                                       return VVOID;  /* not found; is a global */
+                               /* else was LOCAL or UPVAL */
+                               idx  = newupvalue(fs, n, var);  /* will be a new upvalue */
+                       }
+                       init_exp(var, VUPVAL, idx);
+                       return VUPVAL;
+               }
+       }
+}
+
+static void singlevar(ktap_lexstate *ls, ktap_expdesc *var)
+{
+       ktap_string *varname = str_checkname(ls);
+       ktap_funcstate *fs = ls->fs;
+
+       if (singlevaraux(fs, varname, var, 1) == VVOID) {  /* global name? */
+               ktap_expdesc key;
+               singlevaraux(fs, ls->envn, var, 1);  /* get environment variable */
+               ktap_assert(var->k == VLOCAL || var->k == VUPVAL);
+               codestring(ls, &key, varname);  /* key is variable name */
+               codegen_indexed(fs, var, &key);  /* env[varname] */
+       }
+}
+
+static void adjust_assign(ktap_lexstate *ls, int nvars, int nexps, ktap_expdesc *e)
+{
+       ktap_funcstate *fs = ls->fs;
+       int extra = nvars - nexps;
+
+       if (hasmultret(e->k)) {
+               extra++;  /* includes call itself */
+               if (extra < 0)
+                       extra = 0;
+               codegen_setreturns(fs, e, extra);  /* last exp. provides the difference */
+               if (extra > 1)
+                       codegen_reserveregs(fs, extra-1);
+       } else {
+               if (e->k != VVOID)
+                       codegen_exp2nextreg(fs, e);  /* close last expression */
+               if (extra > 0) {
+                       int reg = fs->freereg;
+
+                       codegen_reserveregs(fs, extra);
+                       codegen_nil(fs, reg, extra);
+               }
+       }
+}
+
+static void enterlevel(ktap_lexstate *ls)
+{
+       ++ls->nCcalls;
+       checklimit(ls->fs, ls->nCcalls, KTAP_MAXCCALLS, "C levels");
+}
+
+static void closegoto(ktap_lexstate *ls, int g, ktap_labeldesc *label)
+{
+       int i;
+       ktap_funcstate *fs = ls->fs;
+       ktap_labellist *gl = &ls->dyd->gt;
+       ktap_labeldesc *gt = &gl->arr[g];
+
+       ktap_assert(ktapc_ts_eqstr(gt->name, label->name));
+       if (gt->nactvar < label->nactvar) {
+               ktap_string *vname = getlocvar(fs, gt->nactvar)->varname;
+               const char *msg = ktapc_sprintf(
+                       "<goto %s> at line %d jumps into the scope of local " KTAP_QS,
+                       getstr(gt->name), gt->line, getstr(vname));
+               semerror(ls, msg);
+       }
+
+       codegen_patchlist(fs, gt->pc, label->pc);
+       /* remove goto from pending list */
+       for (i = g; i < gl->n - 1; i++)
+               gl->arr[i] = gl->arr[i + 1];
+       gl->n--;
+}
+
+/*
+ * try to close a goto with existing labels; this solves backward jumps
+ */
+static int findlabel(ktap_lexstate *ls, int g)
+{
+       int i;
+       ktap_blockcnt *bl = ls->fs->bl;
+       ktap_dyndata *dyd = ls->dyd;
+       ktap_labeldesc *gt = &dyd->gt.arr[g];
+
+       /* check labels in current block for a match */
+       for (i = bl->firstlabel; i < dyd->label.n; i++) {
+               ktap_labeldesc *lb = &dyd->label.arr[i];
+               if (ktapc_ts_eqstr(lb->name, gt->name)) {  /* correct label? */
+                       if (gt->nactvar > lb->nactvar &&
+                               (bl->upval || dyd->label.n > bl->firstlabel))
+                               codegen_patchclose(ls->fs, gt->pc, lb->nactvar);
+                       closegoto(ls, g, lb);  /* close it */
+                       return 1;
+               }
+       }
+       return 0;  /* label not found; cannot close goto */
+}
+
+static int newlabelentry(ktap_lexstate *ls, ktap_labellist *l, ktap_string *name,
+                        int line, int pc)
+{
+       int n = l->n;
+
+       ktapc_growvector(l->arr, n, l->size,
+                        ktap_labeldesc, SHRT_MAX, "labels/gotos");
+       l->arr[n].name = name;
+       l->arr[n].line = line;
+       l->arr[n].nactvar = ls->fs->nactvar;
+       l->arr[n].pc = pc;
+       l->n++;
+       return n;
+}
+
+
+/*
+ * check whether new label 'lb' matches any pending gotos in current
+ * block; solves forward jumps
+ */
+static void findgotos(ktap_lexstate *ls, ktap_labeldesc *lb)
+{
+       ktap_labellist *gl = &ls->dyd->gt;
+       int i = ls->fs->bl->firstgoto;
+
+       while (i < gl->n) {
+               if (ktapc_ts_eqstr(gl->arr[i].name, lb->name))
+                       closegoto(ls, i, lb);
+               else
+                       i++;
+       }
+}
+
+/*
+ * "export" pending gotos to outer level, to check them against
+ * outer labels; if the block being exited has upvalues, and
+ * the goto exits the scope of any variable (which can be the
+ * upvalue), close those variables being exited.
+ */
+static void movegotosout(ktap_funcstate *fs, ktap_blockcnt *bl)
+{
+       int i = bl->firstgoto;
+       ktap_labellist *gl = &fs->ls->dyd->gt;
+
+       /* correct pending gotos to current block and try to close it
+               with visible labels */
+       while (i < gl->n) {
+               ktap_labeldesc *gt = &gl->arr[i];
+
+               if (gt->nactvar > bl->nactvar) {
+                       if (bl->upval)
+                               codegen_patchclose(fs, gt->pc, bl->nactvar);
+                       gt->nactvar = bl->nactvar;
+               }
+               if (!findlabel(fs->ls, i))
+                       i++;  /* move to next one */
+       }
+}
+
+static void enterblock(ktap_funcstate *fs, ktap_blockcnt *bl, u8 isloop)
+{
+       bl->isloop = isloop;
+       bl->nactvar = fs->nactvar;
+       bl->firstlabel = fs->ls->dyd->label.n;
+       bl->firstgoto = fs->ls->dyd->gt.n;
+       bl->upval = 0;
+       bl->previous = fs->bl;
+       fs->bl = bl;
+       ktap_assert(fs->freereg == fs->nactvar);
+}
+
+
+/*
+ * create a label named "break" to resolve break statements
+ */
+static void breaklabel(ktap_lexstate *ls)
+{
+       ktap_string *n = ktapc_ts_new("break");
+       int l = newlabelentry(ls, &ls->dyd->label, n, 0, ls->fs->pc);
+
+       findgotos(ls, &ls->dyd->label.arr[l]);
+}
+
+/*
+ * generates an error for an undefined 'goto'; choose appropriate
+ * message when label name is a reserved word (which can only be 'break')
+ */
+static void undefgoto(ktap_lexstate *ls, ktap_labeldesc *gt)
+{
+       const char *msg = isreserved(gt->name)
+                       ? "<%s> at line %d not inside a loop"
+                       : "no visible label " KTAP_QS " for <goto> at line %d";
+
+       msg = ktapc_sprintf(msg, getstr(gt->name), gt->line);
+       semerror(ls, msg);
+}
+
+static void leaveblock(ktap_funcstate *fs)
+{
+       ktap_blockcnt *bl = fs->bl;
+       ktap_lexstate *ls = fs->ls;
+       if (bl->previous && bl->upval) {
+               /* create a 'jump to here' to close upvalues */
+               int j = codegen_jump(fs);
+
+               codegen_patchclose(fs, j, bl->nactvar);
+               codegen_patchtohere(fs, j);
+       }
+
+       if (bl->isloop)
+               breaklabel(ls);  /* close pending breaks */
+
+       fs->bl = bl->previous;
+       removevars(fs, bl->nactvar);
+       ktap_assert(bl->nactvar == fs->nactvar);
+       fs->freereg = fs->nactvar;  /* free registers */
+       ls->dyd->label.n = bl->firstlabel;  /* remove local labels */
+       if (bl->previous)  /* inner block? */
+               movegotosout(fs, bl);  /* update pending gotos to outer block */
+       else if (bl->firstgoto < ls->dyd->gt.n)  /* pending gotos in outer block? */
+               undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]);  /* error */
+}
+
+/*
+ * adds a new prototype into list of prototypes
+ */
+static ktap_proto *addprototype(ktap_lexstate *ls)
+{
+       ktap_proto *clp;
+       ktap_funcstate *fs = ls->fs;
+       ktap_proto *f = fs->f;  /* prototype of current function */
+
+       if (fs->np >= f->sizep) {
+               int oldsize = f->sizep;
+               ktapc_growvector(f->p, fs->np, f->sizep, ktap_proto *, MAXARG_Bx, "functions");
+               while (oldsize < f->sizep)
+                       f->p[oldsize++] = NULL;
+       }
+       f->p[fs->np++] = clp = ktapc_newproto();
+       return clp;
+}
+
+/*
+ * codes instruction to create new closure in parent function
+ */
+static void codeclosure(ktap_lexstate *ls, ktap_expdesc *v)
+{
+       ktap_funcstate *fs = ls->fs->prev;
+       init_exp(v, VRELOCABLE, codegen_codeABx(fs, OP_CLOSURE, 0, fs->np - 1));
+       codegen_exp2nextreg(fs, v);  /* fix it at stack top (for GC) */
+}
+
+static void open_func(ktap_lexstate *ls, ktap_funcstate *fs, ktap_blockcnt *bl)
+{
+       ktap_proto *f;
+
+       fs->prev = ls->fs;  /* linked list of funcstates */
+       fs->ls = ls;
+       ls->fs = fs;
+       fs->pc = 0;
+       fs->lasttarget = 0;
+       fs->jpc = NO_JUMP;
+       fs->freereg = 0;
+       fs->nk = 0;
+       fs->np = 0;
+       fs->nups = 0;
+       fs->nlocvars = 0;
+       fs->nactvar = 0;
+       fs->firstlocal = ls->dyd->actvar.n;
+       fs->bl = NULL;
+       f = fs->f;
+       f->source = ls->source;
+       f->maxstacksize = 2;  /* registers 0/1 are always valid */
+       fs->h = ktapc_table_new();
+       //table_resize(NULL, fs->h, 32, 32);
+       enterblock(fs, bl, 0);
+}
+
+static void close_func(ktap_lexstate *ls)
+{
+       ktap_funcstate *fs = ls->fs;
+       ktap_proto *f = fs->f;
+
+       codegen_ret(fs, 0, 0);  /* final return */
+       leaveblock(fs);
+       ktapc_reallocvector(f->code, f->sizecode, fs->pc, ktap_instruction);
+       f->sizecode = fs->pc;
+       ktapc_reallocvector(f->lineinfo, f->sizelineinfo, fs->pc, int);
+       f->sizelineinfo = fs->pc;
+       ktapc_reallocvector(f->k, f->sizek, fs->nk, ktap_value);
+       f->sizek = fs->nk;
+       ktapc_reallocvector(f->p, f->sizep, fs->np, ktap_proto *);
+       f->sizep = fs->np;
+       ktapc_reallocvector(f->locvars, f->sizelocvars, fs->nlocvars, ktap_locvar);
+       f->sizelocvars = fs->nlocvars;
+       ktapc_reallocvector(f->upvalues, f->sizeupvalues, fs->nups, ktap_upvaldesc);
+       f->sizeupvalues = fs->nups;
+       ktap_assert((int)(fs->bl == NULL));
+       ls->fs = fs->prev;
+       /* last token read was anchored in defunct function; must re-anchor it */
+       anchor_token(ls);
+}
+
+
+/*============================================================*/
+/* GRAMMAR RULES */
+/*============================================================*/
+
+/*
+ * check whether current token is in the follow set of a block.
+ * 'until' closes syntactical blocks, but do not close scope,
+ * so it handled in separate.
+ */
+static int block_follow(ktap_lexstate *ls, int withuntil)
+{
+       switch (ls->t.token) {
+       case TK_ELSE: case TK_ELSEIF:
+       case TK_END: case TK_EOS:
+               return 1;
+       case TK_UNTIL:
+               return withuntil;
+       case '}':
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static void statlist(ktap_lexstate *ls)
+{
+       /* statlist -> { stat [`;'] } */
+       while (!block_follow(ls, 1)) {
+               if (ls->t.token == TK_RETURN) {
+                       statement(ls);
+                       return;  /* 'return' must be last statement */
+               }
+               statement(ls);
+       }
+}
+
+static void fieldsel(ktap_lexstate *ls, ktap_expdesc *v)
+{
+       /* fieldsel -> ['.' | ':'] NAME */
+       ktap_funcstate *fs = ls->fs;
+       ktap_expdesc key;
+
+       codegen_exp2anyregup(fs, v);
+       lex_next(ls);  /* skip the dot or colon */
+       checkname(ls, &key);
+       codegen_indexed(fs, v, &key);
+}
+
+static void yindex(ktap_lexstate *ls, ktap_expdesc *v)
+{
+       /* index -> '[' expr ']' */
+       lex_next(ls);  /* skip the '[' */
+       expr(ls, v);
+       codegen_exp2val(ls->fs, v);
+       checknext(ls, ']');
+}
+
+/*
+ * {======================================================================
+ * Rules for Constructors
+ * =======================================================================
+ */
+struct ConsControl {
+       ktap_expdesc v;  /* last list item read */
+       ktap_expdesc *t;  /* table descriptor */
+       int nh;  /* total number of `record' elements */
+       int na;  /* total number of array elements */
+       int tostore;  /* number of array elements pending to be stored */
+};
+
+static void recfield(ktap_lexstate *ls, struct ConsControl *cc)
+{
+       /* recfield -> (NAME | `['exp1`]') = exp1 */
+       ktap_funcstate *fs = ls->fs;
+       int reg = ls->fs->freereg;
+       ktap_expdesc key, val;
+       int rkkey;
+
+       if (ls->t.token == TK_NAME) {
+               checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
+               checkname(ls, &key);
+       } else  /* ls->t.token == '[' */
+               yindex(ls, &key);
+
+       cc->nh++;
+       checknext(ls, '=');
+       rkkey = codegen_exp2RK(fs, &key);
+       expr(ls, &val);
+       codegen_codeABC(fs, OP_SETTABLE, cc->t->u.info, rkkey, codegen_exp2RK(fs, &val));
+       fs->freereg = reg;  /* free registers */
+}
+
+static void closelistfield(ktap_funcstate *fs, struct ConsControl *cc)
+{
+       if (cc->v.k == VVOID)
+               return;  /* there is no list item */
+       codegen_exp2nextreg(fs, &cc->v);
+       cc->v.k = VVOID;
+       if (cc->tostore == LFIELDS_PER_FLUSH) {
+               codegen_setlist(fs, cc->t->u.info, cc->na, cc->tostore);  /* flush */
+               cc->tostore = 0;  /* no more items pending */
+       }
+}
+
+static void lastlistfield(ktap_funcstate *fs, struct ConsControl *cc)
+{
+       if (cc->tostore == 0)
+               return;
+
+       if (hasmultret(cc->v.k)) {
+               codegen_setmultret(fs, &cc->v);
+               codegen_setlist(fs, cc->t->u.info, cc->na, KTAP_MULTRET);
+               cc->na--;  /* do not count last expression (unknown number of elements) */
+       } else {
+               if (cc->v.k != VVOID)
+                       codegen_exp2nextreg(fs, &cc->v);
+               codegen_setlist(fs, cc->t->u.info, cc->na, cc->tostore);
+       }
+}
+
+static void listfield(ktap_lexstate *ls, struct ConsControl *cc)
+{
+       /* listfield -> exp */
+       expr(ls, &cc->v);
+       checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor");
+       cc->na++;
+       cc->tostore++;
+}
+
+static void field(ktap_lexstate *ls, struct ConsControl *cc)
+{
+       /* field -> listfield | recfield */
+       switch(ls->t.token) {
+       case TK_NAME: {  /* may be 'listfield' or 'recfield' */
+               if (lex_lookahead(ls) != '=')  /* expression? */
+                       listfield(ls, cc);
+               else
+                       recfield(ls, cc);
+               break;
+       }
+       case '[': {
+               recfield(ls, cc);
+               break;
+       }
+       default:
+               listfield(ls, cc);
+               break;
+       }
+}
+
+static void constructor(ktap_lexstate *ls, ktap_expdesc *t)
+{
+       /* constructor -> '{' [ field { sep field } [sep] ] '}'
+               sep -> ',' | ';' */
+       ktap_funcstate *fs = ls->fs;
+       int line = ls->linenumber;
+       int pc = codegen_codeABC(fs, OP_NEWTABLE, 0, 0, 0);
+       struct ConsControl cc;
+
+       cc.na = cc.nh = cc.tostore = 0;
+       cc.t = t;
+       init_exp(t, VRELOCABLE, pc);
+       init_exp(&cc.v, VVOID, 0);  /* no value (yet) */
+       codegen_exp2nextreg(ls->fs, t);  /* fix it at stack top */
+       checknext(ls, '{');
+       do {
+               ktap_assert(cc.v.k == VVOID || cc.tostore > 0);
+               if (ls->t.token == '}')
+                       break;
+               closelistfield(fs, &cc);
+               field(ls, &cc);
+       } while (testnext(ls, ',') || testnext(ls, ';'));
+       check_match(ls, '}', '{', line);
+       lastlistfield(fs, &cc);
+       SETARG_B(fs->f->code[pc], ktapc_int2fb(cc.na)); /* set initial array size */
+       SETARG_C(fs->f->code[pc], ktapc_int2fb(cc.nh));  /* set initial table size */
+}
+
+/* }====================================================================== */
+
+static void parlist(ktap_lexstate *ls)
+{
+       /* parlist -> [ param { `,' param } ] */
+       ktap_funcstate *fs = ls->fs;
+       ktap_proto *f = fs->f;
+       int nparams = 0;
+       f->is_vararg = 0;
+
+       if (ls->t.token != ')') {  /* is `parlist' not empty? */
+               do {
+                       switch (ls->t.token) {
+                       case TK_NAME: {  /* param -> NAME */
+                               new_localvar(ls, str_checkname(ls));
+                               nparams++;
+                               break;
+                       }
+                       case TK_DOTS: {  /* param -> `...' */
+                               lex_next(ls);
+                               f->is_vararg = 1;
+                               break;
+                       }
+                       default:
+                               lex_syntaxerror(ls, "<name> or " KTAP_QL("...") " expected");
+                       }
+               } while (!f->is_vararg && testnext(ls, ','));
+       }
+       adjustlocalvars(ls, nparams);
+       f->numparams = (u8)(fs->nactvar);
+       codegen_reserveregs(fs, fs->nactvar);  /* reserve register for parameters */
+}
+
+static void body(ktap_lexstate *ls, ktap_expdesc *e, int ismethod, int line)
+{
+       /* body ->  `(' parlist `)' block END */
+       ktap_funcstate new_fs;
+       ktap_blockcnt bl;
+
+       new_fs.f = addprototype(ls);
+       new_fs.f->linedefined = line;
+       open_func(ls, &new_fs, &bl);
+       checknext(ls, '(');
+       if (ismethod) {
+               new_localvarliteral(ls, "self");  /* create 'self' parameter */
+               adjustlocalvars(ls, 1);
+       }
+       parlist(ls);
+       checknext(ls, ')');
+       checknext(ls, '{');
+       statlist(ls);
+       new_fs.f->lastlinedefined = ls->linenumber;
+       checknext(ls, '}');
+       //check_match(ls, TK_END, TK_FUNCTION, line);
+       codeclosure(ls, e);
+       close_func(ls);
+}
+
+static void func_body_no_args(ktap_lexstate *ls, ktap_expdesc *e, int line)
+{
+       /* body ->  `(' parlist `)' block END */
+       ktap_funcstate new_fs;
+       ktap_blockcnt bl;
+
+       new_fs.f = addprototype(ls);
+       new_fs.f->linedefined = line;
+       open_func(ls, &new_fs, &bl);
+       checknext(ls, '{');
+       statlist(ls);
+       new_fs.f->lastlinedefined = ls->linenumber;
+       checknext(ls, '}');
+       //check_match(ls, TK_END, TK_FUNCTION, line);
+       codeclosure(ls, e);
+       close_func(ls);
+}
+
+static int explist(ktap_lexstate *ls, ktap_expdesc *v)
+{
+       /* explist -> expr { `,' expr } */
+       int n = 1;  /* at least one expression */
+
+       expr(ls, v);
+       while (testnext(ls, ',')) {
+               codegen_exp2nextreg(ls->fs, v);
+               expr(ls, v);
+               n++;
+       }
+       return n;
+}
+
+static void funcargs(ktap_lexstate *ls, ktap_expdesc *f, int line)
+{
+       ktap_funcstate *fs = ls->fs;
+       ktap_expdesc args;
+       int base, nparams;
+
+       switch (ls->t.token) {
+       case '(': {  /* funcargs -> `(' [ explist ] `)' */
+               lex_next(ls);
+               if (ls->t.token == ')')  /* arg list is empty? */
+                       args.k = VVOID;
+               else {
+                       explist(ls, &args);
+                       codegen_setmultret(fs, &args);
+               }
+               check_match(ls, ')', '(', line);
+               break;
+       }
+       case '{': {  /* funcargs -> constructor */
+               constructor(ls, &args);
+               break;
+       }
+       case TK_STRING: {  /* funcargs -> STRING */
+               codestring(ls, &args, ls->t.seminfo.ts);
+               lex_next(ls);  /* must use `seminfo' before `next' */
+               break;
+       }
+       default: {
+               lex_syntaxerror(ls, "function arguments expected");
+       }
+       }
+       ktap_assert(f->k == VNONRELOC);
+       base = f->u.info;  /* base register for call */
+       if (hasmultret(args.k))
+               nparams = KTAP_MULTRET;  /* open call */
+       else {
+               if (args.k != VVOID)
+                       codegen_exp2nextreg(fs, &args);  /* close last argument */
+               nparams = fs->freereg - (base+1);
+       }
+       init_exp(f, VCALL, codegen_codeABC(fs, OP_CALL, base, nparams+1, 2));
+       codegen_fixline(fs, line);
+       fs->freereg = base+1;  /* call remove function and arguments and leaves
+                               (unless changed) one result */
+}
+
+/*
+ * {======================================================================
+ * Expression parsing
+ * =======================================================================
+ */
+static void primaryexp(ktap_lexstate *ls, ktap_expdesc *v)
+{
+       /* primaryexp -> NAME | '(' expr ')' */
+       switch (ls->t.token) {
+       case '(': {
+               int line = ls->linenumber;
+
+               lex_next(ls);
+               expr(ls, v);
+               check_match(ls, ')', '(', line);
+               codegen_dischargevars(ls->fs, v);
+               return;
+       }
+       case TK_NAME:
+               singlevar(ls, v);
+               return;
+       default:
+               lex_syntaxerror(ls, "unexpected symbol");
+       }
+}
+
+static void suffixedexp(ktap_lexstate *ls, ktap_expdesc *v)
+{
+       /* suffixedexp ->
+               primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
+       ktap_funcstate *fs = ls->fs;
+       int line = ls->linenumber;
+
+       primaryexp(ls, v);
+       for (;;) {
+               switch (ls->t.token) {
+               case '.': {  /* fieldsel */
+                       fieldsel(ls, v);
+                       break;
+               }
+               case '[': {  /* `[' exp1 `]' */
+                       ktap_expdesc key;
+                       codegen_exp2anyregup(fs, v);
+                       yindex(ls, &key);
+                       codegen_indexed(fs, v, &key);
+                       break;
+               }
+               case ':': {  /* `:' NAME funcargs */
+                       ktap_expdesc key;
+                       lex_next(ls);
+                       checkname(ls, &key);
+                       codegen_self(fs, v, &key);
+                       funcargs(ls, v, line);
+                       break;
+               }
+               case '(': case TK_STRING: case '{': {  /* funcargs */
+                       codegen_exp2nextreg(fs, v);
+                       funcargs(ls, v, line);
+                       break;
+               }
+               default:
+                       return;
+               }
+       }
+}
+
+static void simpleexp(ktap_lexstate *ls, ktap_expdesc *v)
+{
+       /* simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... |
+               constructor | FUNCTION body | suffixedexp */
+       switch (ls->t.token) {
+       case TK_NUMBER: {
+               init_exp(v, VKNUM, 0);
+               v->u.nval = ls->t.seminfo.r;
+               break;
+       }
+       case TK_STRING: {
+               codestring(ls, v, ls->t.seminfo.ts);
+               break;
+       }
+       case TK_KSYM: {
+               init_exp(v, VKNUM, 0);
+               v->u.nval =
+               (ktap_number)find_kernel_symbol(getstr(ls->t.seminfo.ts));
+               break;
+       }
+       case TK_NIL: {
+               init_exp(v, VNIL, 0);
+               break;
+       }
+       case TK_TRUE: {
+               init_exp(v, VTRUE, 0);
+               break;
+       }
+       case TK_FALSE: {
+               init_exp(v, VFALSE, 0);
+               break;
+       }
+       case TK_DOTS: {  /* vararg */
+               ktap_funcstate *fs = ls->fs;
+               check_condition(ls, fs->f->is_vararg,
+                      "cannot use " KTAP_QL("...") " outside a vararg function");
+               init_exp(v, VVARARG, codegen_codeABC(fs, OP_VARARG, 0, 1, 0));
+               break;
+       }
+       case '{': {  /* constructor */
+               constructor(ls, v);
+               return;
+       }
+       case TK_FUNCTION: {
+               lex_next(ls);
+               body(ls, v, 0, ls->linenumber);
+               return;
+       }
+       case TK_ARGEVENT:
+               init_exp(v, VEVENT, 0);
+               break;
+
+       case TK_ARGNAME:
+               init_exp(v, VEVENTNAME, 0);
+               break;
+       case TK_ARG1:
+       case TK_ARG2:
+       case TK_ARG3:
+       case TK_ARG4:
+       case TK_ARG5:
+       case TK_ARG6:
+       case TK_ARG7:
+       case TK_ARG8:
+       case TK_ARG9:
+               init_exp(v, VEVENTARG, ls->t.token - TK_ARG1 + 1);
+               break;
+       default: {
+               suffixedexp(ls, v);
+               return;
+       }
+       }
+       lex_next(ls);
+}
+
+static UnOpr getunopr(int op)
+{
+       switch (op) {
+       case TK_NOT: return OPR_NOT;
+       case '-': return OPR_MINUS;
+       case '#': return OPR_LEN;
+       default: return OPR_NOUNOPR;
+       }
+}
+
+static BinOpr getbinopr(int op)
+{
+       switch (op) {
+       case '+': return OPR_ADD;
+       case '-': return OPR_SUB;
+       case '*': return OPR_MUL;
+       case '/': return OPR_DIV;
+       case '%': return OPR_MOD;
+       case '^': return OPR_POW;
+       case TK_CONCAT: return OPR_CONCAT;
+       case TK_NE: return OPR_NE;
+       case TK_EQ: return OPR_EQ;
+       case '<': return OPR_LT;
+       case TK_LE: return OPR_LE;
+       case '>': return OPR_GT;
+       case TK_GE: return OPR_GE;
+       case TK_AND: return OPR_AND;
+       case TK_OR: return OPR_OR;
+       default: return OPR_NOBINOPR;
+       }
+}
+
+static const struct {
+       u8 left;  /* left priority for each binary operator */
+       u8 right; /* right priority */
+} priority[] = {  /* ORDER OPR */
+       {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7},  /* `+' `-' `*' `/' `%' */
+       {10, 9}, {5, 4},                 /* ^, .. (right associative) */
+       {3, 3}, {3, 3}, {3, 3},          /* ==, <, <= */
+       {3, 3}, {3, 3}, {3, 3},          /* !=, >, >= */
+       {2, 2}, {1, 1}                   /* and, or */
+};
+
+#define UNARY_PRIORITY 8  /* priority for unary operators */
+
+#define leavelevel(ls) (ls->nCcalls--)
+
+/*
+ * subexpr -> (simpleexp | unop subexpr) { binop subexpr }
+ * where `binop' is any binary operator with a priority higher than `limit'
+ */
+static BinOpr subexpr(ktap_lexstate *ls, ktap_expdesc *v, int limit)
+{
+       BinOpr op;
+       UnOpr uop;
+
+       enterlevel(ls);
+       uop = getunopr(ls->t.token);
+       if (uop != OPR_NOUNOPR) {
+               int line = ls->linenumber;
+
+               lex_next(ls);
+               subexpr(ls, v, UNARY_PRIORITY);
+               codegen_prefix(ls->fs, uop, v, line);
+       } else
+               simpleexp(ls, v);
+
+       /* expand while operators have priorities higher than `limit' */
+       op = getbinopr(ls->t.token);
+       while (op != OPR_NOBINOPR && priority[op].left > limit) {
+               ktap_expdesc v2;
+               BinOpr nextop;
+               int line = ls->linenumber;
+
+               lex_next(ls);
+               codegen_infix(ls->fs, op, v);
+               /* read sub-expression with higher priority */
+               nextop = subexpr(ls, &v2, priority[op].right);
+               codegen_posfix(ls->fs, op, v, &v2, line);
+               op = nextop;
+       }
+       leavelevel(ls);
+       return op;  /* return first untreated operator */
+}
+
+static void expr(ktap_lexstate *ls, ktap_expdesc *v)
+{
+       subexpr(ls, v, 0);
+}
+
+/* }==================================================================== */
+
+/*
+ * {======================================================================
+ * Rules for Statements
+ * =======================================================================
+ */
+static void block(ktap_lexstate *ls)
+{
+       /* block -> statlist */
+       ktap_funcstate *fs = ls->fs;
+       ktap_blockcnt bl;
+
+       enterblock(fs, &bl, 0);
+       statlist(ls);
+       leaveblock(fs);
+}
+
+/*
+ * structure to chain all variables in the left-hand side of an
+ * assignment
+ */
+struct LHS_assign {
+       struct LHS_assign *prev;
+       ktap_expdesc v;  /* variable (global, local, upvalue, or indexed) */
+};
+
+/*
+ * check whether, in an assignment to an upvalue/local variable, the
+ * upvalue/local variable is begin used in a previous assignment to a
+ * table. If so, save original upvalue/local value in a safe place and
+ * use this safe copy in the previous assignment.
+ */
+static void check_conflict(ktap_lexstate *ls, struct LHS_assign *lh, ktap_expdesc *v)
+{
+       ktap_funcstate *fs = ls->fs;
+       int extra = fs->freereg;  /* eventual position to save local variable */
+       int conflict = 0;
+
+       for (; lh; lh = lh->prev) {  /* check all previous assignments */
+               if (lh->v.k == VINDEXED) {  /* assigning to a table? */
+                       /* table is the upvalue/local being assigned now? */
+                       if (lh->v.u.ind.vt == v->k && lh->v.u.ind.t == v->u.info) {
+                               conflict = 1;
+                               lh->v.u.ind.vt = VLOCAL;
+                               lh->v.u.ind.t = extra;  /* previous assignment will use safe copy */
+                       }
+                       /* index is the local being assigned? (index cannot be upvalue) */
+                       if (v->k == VLOCAL && lh->v.u.ind.idx == v->u.info) {
+                               conflict = 1;
+                               lh->v.u.ind.idx = extra;  /* previous assignment will use safe copy */
+                       }
+               }
+       }
+       if (conflict) {
+               /* copy upvalue/local value to a temporary (in position 'extra') */
+               OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;
+               codegen_codeABC(fs, op, extra, v->u.info, 0);
+               codegen_reserveregs(fs, 1);
+       }
+}
+
+static void assignment(ktap_lexstate *ls, struct LHS_assign *lh, int nvars)
+{
+       ktap_expdesc e;
+
+       check_condition(ls, vkisvar(lh->v.k), "syntax error");
+       if (testnext(ls, ',')) {  /* assignment -> ',' suffixedexp assignment */
+               struct LHS_assign nv;
+
+               nv.prev = lh;
+               suffixedexp(ls, &nv.v);
+               if (nv.v.k != VINDEXED)
+                       check_conflict(ls, lh, &nv.v);
+               checklimit(ls->fs, nvars + ls->nCcalls, KTAP_MAXCCALLS,
+                               "C levels");
+               assignment(ls, &nv, nvars+1);
+       } else if (testnext(ls, '=')) {  /* assignment -> '=' explist */
+               int nexps;
+
+               nexps = explist(ls, &e);
+               if (nexps != nvars) {
+                       adjust_assign(ls, nvars, nexps, &e);
+                       /* remove extra values */
+                       if (nexps > nvars)
+                               ls->fs->freereg -= nexps - nvars;
+               } else {
+                       /* close last expression */
+                       codegen_setoneret(ls->fs, &e);
+                       codegen_storevar(ls->fs, &lh->v, &e);
+                       return;  /* avoid default */
+               }
+       } else if (testnext(ls, TK_INCR)) { /* assignment -> '+=' explist */
+               int nexps;
+
+               nexps = explist(ls, &e);
+               if (nexps != nvars) {
+                       lex_syntaxerror(ls, "don't allow multi-assign for +=");
+               } else {
+                       /* close last expression */
+                       codegen_setoneret(ls->fs, &e);
+                       codegen_storeincr(ls->fs, &lh->v, &e);
+                       return;  /* avoid default */
+               }
+       } else if (testnext(ls, TK_AGGR_ASSIGN)) { /* assignment -> '<<<' explist */
+               int nexps;
+
+               nexps = explist(ls, &e);
+               if (nexps != nvars) {
+                       lex_syntaxerror(ls, "don't allow multi-assign for <<<");
+               } else {
+                       /* close last expression */
+                       codegen_setoneret(ls->fs, &e);
+                       codegen_store_aggr(ls->fs, &lh->v, &e);
+                       return;  /* avoid default */
+               }
+       }
+
+       init_exp(&e, VNONRELOC, ls->fs->freereg-1);  /* default assignment */
+       codegen_storevar(ls->fs, &lh->v, &e);
+}
+
+static int cond(ktap_lexstate *ls)
+{
+       /* cond -> exp */
+       ktap_expdesc v;
+       expr(ls, &v);  /* read condition */
+       if (v.k == VNIL)
+               v.k = VFALSE;  /* `falses' are all equal here */
+       codegen_goiftrue(ls->fs, &v);
+       return v.f;
+}
+
+static void gotostat(ktap_lexstate *ls, int pc)
+{
+       int line = ls->linenumber;
+       ktap_string *label;
+       int g;
+
+       if (testnext(ls, TK_GOTO))
+               label = str_checkname(ls);
+       else {
+               lex_next(ls);  /* skip break */
+               label = ktapc_ts_new("break");
+       }
+       g = newlabelentry(ls, &ls->dyd->gt, label, line, pc);
+       findlabel(ls, g);  /* close it if label already defined */
+}
+
+/* check for repeated labels on the same block */
+static void checkrepeated(ktap_funcstate *fs, ktap_labellist *ll, ktap_string *label)
+{
+       int i;
+       for (i = fs->bl->firstlabel; i < ll->n; i++) {
+               if (ktapc_ts_eqstr(label, ll->arr[i].name)) {
+                       const char *msg = ktapc_sprintf(
+                               "label " KTAP_QS " already defined on line %d",
+                               getstr(label), ll->arr[i].line);
+                       semerror(fs->ls, msg);
+               }
+       }
+}
+
+/* skip no-op statements */
+static void skipnoopstat(ktap_lexstate *ls)
+{
+       while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
+               statement(ls);
+}
+
+static void labelstat (ktap_lexstate *ls, ktap_string *label, int line)
+{
+       /* label -> '::' NAME '::' */
+       ktap_funcstate *fs = ls->fs;
+       ktap_labellist *ll = &ls->dyd->label;
+       int l;  /* index of new label being created */
+
+       checkrepeated(fs, ll, label);  /* check for repeated labels */
+       checknext(ls, TK_DBCOLON);  /* skip double colon */
+       /* create new entry for this label */
+       l = newlabelentry(ls, ll, label, line, fs->pc);
+       skipnoopstat(ls);  /* skip other no-op statements */
+       if (block_follow(ls, 0)) {  /* label is last no-op statement in the block? */
+               /* assume that locals are already out of scope */
+               ll->arr[l].nactvar = fs->bl->nactvar;
+       }
+       findgotos(ls, &ll->arr[l]);
+}
+
+static void whilestat(ktap_lexstate *ls, int line)
+{
+       /* whilestat -> WHILE cond DO block END */
+       ktap_funcstate *fs = ls->fs;
+       int whileinit;
+       int condexit;
+       ktap_blockcnt bl;
+
+       lex_next(ls);  /* skip WHILE */
+       whileinit = codegen_getlabel(fs);
+       checknext(ls, '(');
+       condexit = cond(ls);
+       checknext(ls, ')');
+
+       enterblock(fs, &bl, 1);
+       //checknext(ls, TK_DO);
+       checknext(ls, '{');
+       block(ls);
+       codegen_jumpto(fs, whileinit);
+       checknext(ls, '}');
+       //check_match(ls, TK_END, TK_WHILE, line);
+       leaveblock(fs);
+       codegen_patchtohere(fs, condexit);  /* false conditions finish the loop */
+}
+
+static void repeatstat(ktap_lexstate *ls, int line)
+{
+       /* repeatstat -> REPEAT block UNTIL cond */
+       int condexit;
+       ktap_funcstate *fs = ls->fs;
+       int repeat_init = codegen_getlabel(fs);
+       ktap_blockcnt bl1, bl2;
+
+       enterblock(fs, &bl1, 1);  /* loop block */
+       enterblock(fs, &bl2, 0);  /* scope block */
+       lex_next(ls);  /* skip REPEAT */
+       statlist(ls);
+       check_match(ls, TK_UNTIL, TK_REPEAT, line);
+       condexit = cond(ls);  /* read condition (inside scope block) */
+       if (bl2.upval)  /* upvalues? */
+               codegen_patchclose(fs, condexit, bl2.nactvar);
+       leaveblock(fs);  /* finish scope */
+       codegen_patchlist(fs, condexit, repeat_init);  /* close the loop */
+       leaveblock(fs);  /* finish loop */
+}
+
+static int exp1(ktap_lexstate *ls)
+{
+       ktap_expdesc e;
+       int reg;
+
+       expr(ls, &e);
+       codegen_exp2nextreg(ls->fs, &e);
+       ktap_assert(e.k == VNONRELOC);
+       reg = e.u.info;
+       return reg;
+}
+
+static void forbody(ktap_lexstate *ls, int base, int line, int nvars, int isnum)
+{
+       /* forbody -> DO block */
+       ktap_blockcnt bl;
+       ktap_funcstate *fs = ls->fs;
+       int prep, endfor;
+
+       checknext(ls, ')');
+
+       adjustlocalvars(ls, 3);  /* control variables */
+       //checknext(ls, TK_DO);
+       checknext(ls, '{');
+       prep = isnum ? codegen_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : codegen_jump(fs);
+       enterblock(fs, &bl, 0);  /* scope for declared variables */
+       adjustlocalvars(ls, nvars);
+       codegen_reserveregs(fs, nvars);
+       block(ls);
+       leaveblock(fs);  /* end of scope for declared variables */
+       codegen_patchtohere(fs, prep);
+       if (isnum)  /* numeric for? */
+               endfor = codegen_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP);
+       else {  /* generic for */
+               codegen_codeABC(fs, OP_TFORCALL, base, 0, nvars);
+               codegen_fixline(fs, line);
+               endfor = codegen_codeAsBx(fs, OP_TFORLOOP, base + 2, NO_JUMP);
+       }
+       codegen_patchlist(fs, endfor, prep + 1);
+       codegen_fixline(fs, line);
+}
+
+static void fornum(ktap_lexstate *ls, ktap_string *varname, int line)
+{
+       /* fornum -> NAME = exp1,exp1[,exp1] forbody */
+       ktap_funcstate *fs = ls->fs;
+       int base = fs->freereg;
+
+       new_localvarliteral(ls, "(for index)");
+       new_localvarliteral(ls, "(for limit)");
+       new_localvarliteral(ls, "(for step)");
+       new_localvar(ls, varname);
+       checknext(ls, '=');
+       exp1(ls);  /* initial value */
+       checknext(ls, ',');
+       exp1(ls);  /* limit */
+       if (testnext(ls, ','))
+               exp1(ls);  /* optional step */
+       else {  /* default step = 1 */
+               codegen_codek(fs, fs->freereg, codegen_numberK(fs, 1));
+               codegen_reserveregs(fs, 1);
+       }
+       forbody(ls, base, line, 1, 1);
+}
+
+static void forlist(ktap_lexstate *ls, ktap_string *indexname)
+{
+       /* forlist -> NAME {,NAME} IN explist forbody */
+       ktap_funcstate *fs = ls->fs;
+       ktap_expdesc e;
+       int nvars = 4;  /* gen, state, control, plus at least one declared var */
+       int line;
+       int base = fs->freereg;
+
+       /* create control variables */
+       new_localvarliteral(ls, "(for generator)");
+       new_localvarliteral(ls, "(for state)");
+       new_localvarliteral(ls, "(for control)");
+       /* create declared variables */
+       new_localvar(ls, indexname);
+       while (testnext(ls, ',')) {
+               new_localvar(ls, str_checkname(ls));
+               nvars++;
+       }
+       checknext(ls, TK_IN);
+       line = ls->linenumber;
+       adjust_assign(ls, 3, explist(ls, &e), &e);
+       codegen_checkstack(fs, 3);  /* extra space to call generator */
+       forbody(ls, base, line, nvars - 3, 0);
+}
+
+static void forstat(ktap_lexstate *ls, int line)
+{
+       /* forstat -> FOR (fornum | forlist) END */
+       ktap_funcstate *fs = ls->fs;
+       ktap_string *varname;
+       ktap_blockcnt bl;
+
+       enterblock(fs, &bl, 1);  /* scope for loop and control variables */
+       lex_next(ls);  /* skip `for' */
+
+       checknext(ls, '(');
+       varname = str_checkname(ls);  /* first variable name */
+       switch (ls->t.token) {
+       case '=':
+               fornum(ls, varname, line);
+               break;
+       case ',': case TK_IN:
+               forlist(ls, varname);
+               break;
+       default:
+               lex_syntaxerror(ls, KTAP_QL("=") " or " KTAP_QL("in") " expected");
+       }
+       //check_match(ls, TK_END, TK_FOR, line);
+       checknext(ls, '}');
+       leaveblock(fs);  /* loop scope (`break' jumps to this point) */
+}
+
+static void test_then_block(ktap_lexstate *ls, int *escapelist)
+{
+       /* test_then_block -> [IF | ELSEIF] cond THEN block */
+       ktap_blockcnt bl;
+       ktap_funcstate *fs = ls->fs;
+       ktap_expdesc v;
+       int jf;  /* instruction to skip 'then' code (if condition is false) */
+
+       lex_next(ls);  /* skip IF or ELSEIF */
+       checknext(ls, '(');
+       expr(ls, &v);  /* read condition */
+       checknext(ls, ')');
+       //checknext(ls, TK_THEN);
+       checknext(ls, '{');
+       if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) {
+               codegen_goiffalse(ls->fs, &v);  /* will jump to label if condition is true */
+               enterblock(fs, &bl, 0);  /* must enter block before 'goto' */
+               gotostat(ls, v.t);  /* handle goto/break */
+               skipnoopstat(ls);  /* skip other no-op statements */
+               if (block_follow(ls, 0)) {  /* 'goto' is the entire block? */
+                       leaveblock(fs);
+                       checknext(ls, '}');
+                       return;  /* and that is it */
+               } else  /* must skip over 'then' part if condition is false */
+                       jf = codegen_jump(fs);
+       } else {  /* regular case (not goto/break) */
+               codegen_goiftrue(ls->fs, &v);  /* skip over block if condition is false */
+               enterblock(fs, &bl, 0);
+               jf = v.f;
+       }
+       statlist(ls);  /* `then' part */
+       checknext(ls, '}');
+       leaveblock(fs);
+       if (ls->t.token == TK_ELSE || ls->t.token == TK_ELSEIF)  /* followed by 'else'/'elseif'? */
+               codegen_concat(fs, escapelist, codegen_jump(fs));  /* must jump over it */
+       codegen_patchtohere(fs, jf);
+}
+
+static void ifstat(ktap_lexstate *ls, int line)
+{
+       /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
+       ktap_funcstate *fs = ls->fs;
+       int escapelist = NO_JUMP;  /* exit list for finished parts */
+
+       test_then_block(ls, &escapelist);  /* IF cond THEN block */
+       while (ls->t.token == TK_ELSEIF)
+               test_then_block(ls, &escapelist);  /* ELSEIF cond THEN block */
+       if (testnext(ls, TK_ELSE)) {
+               checknext(ls, '{');
+               block(ls);  /* `else' part */
+               checknext(ls, '}');
+       }
+       //check_match(ls, TK_END, TK_IF, line);
+       codegen_patchtohere(fs, escapelist);  /* patch escape list to 'if' end */
+}
+
+static void localfunc(ktap_lexstate *ls)
+{
+       ktap_expdesc b;
+       ktap_funcstate *fs = ls->fs;
+
+       new_localvar(ls, str_checkname(ls));  /* new local variable */
+       adjustlocalvars(ls, 1);  /* enter its scope */
+       body(ls, &b, 0, ls->linenumber);  /* function created in next register */
+       /* debug information will only see the variable after this point! */
+       getlocvar(fs, b.u.info)->startpc = fs->pc;
+}
+
+static void localstat(ktap_lexstate *ls)
+{
+       /* stat -> LOCAL NAME {`,' NAME} [`=' explist] */
+       int nvars = 0;
+       int nexps;
+       ktap_expdesc e;
+
+       do {
+               new_localvar(ls, str_checkname(ls));
+               nvars++;
+       } while (testnext(ls, ','));
+       if (testnext(ls, '='))
+               nexps = explist(ls, &e);
+       else {
+               e.k = VVOID;
+               nexps = 0;
+       }
+       adjust_assign(ls, nvars, nexps, &e);
+       adjustlocalvars(ls, nvars);
+}
+
+static int funcname(ktap_lexstate *ls, ktap_expdesc *v)
+{
+       /* funcname -> NAME {fieldsel} [`:' NAME] */
+       int ismethod = 0;
+
+       singlevar(ls, v);
+       while (ls->t.token == '.')
+               fieldsel(ls, v);
+               if (ls->t.token == ':') {
+                       ismethod = 1;
+                       fieldsel(ls, v);
+       }
+       return ismethod;
+}
+
+static void funcstat(ktap_lexstate *ls, int line)
+{
+       /* funcstat -> FUNCTION funcname body */
+       int ismethod;
+       ktap_expdesc v, b;
+
+       lex_next(ls);  /* skip FUNCTION */
+       ismethod = funcname(ls, &v);
+       body(ls, &b, ismethod, line);
+       codegen_storevar(ls->fs, &v, &b);
+       codegen_fixline(ls->fs, line);  /* definition `happens' in the first line */
+}
+
+static void exprstat(ktap_lexstate *ls)
+{
+       /* stat -> func | assignment */
+       ktap_funcstate *fs = ls->fs;
+       struct LHS_assign v;
+
+       suffixedexp(ls, &v.v);
+       /* stat -> assignment ? */
+       if (ls->t.token == '=' || ls->t.token == ',' ||
+           ls->t.token == TK_INCR || ls->t.token == TK_AGGR_ASSIGN) {
+               v.prev = NULL;
+               assignment(ls, &v, 1);
+       } else {  /* stat -> func */
+               check_condition(ls, v.v.k == VCALL, "syntax error");
+               SETARG_C(getcode(fs, &v.v), 1);  /* call statement uses no results */
+       }
+}
+
+static void retstat(ktap_lexstate *ls)
+{
+       /* stat -> RETURN [explist] [';'] */
+       ktap_funcstate *fs = ls->fs;
+       ktap_expdesc e;
+       int first, nret;  /* registers with returned values */
+
+       if (block_follow(ls, 1) || ls->t.token == ';')
+               first = nret = 0;  /* return no values */
+       else {
+               nret = explist(ls, &e);  /* optional return values */
+               if (hasmultret(e.k)) {
+                       codegen_setmultret(fs, &e);
+                       if (e.k == VCALL && nret == 1) {  /* tail call? */
+                               SET_OPCODE(getcode(fs,&e), OP_TAILCALL);
+                               ktap_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar);
+                       }
+                       first = fs->nactvar;
+                       nret = KTAP_MULTRET;  /* return all values */
+               } else {
+                       if (nret == 1)  /* only one single value? */
+                               first = codegen_exp2anyreg(fs, &e);
+                       else {
+                               codegen_exp2nextreg(fs, &e);  /* values must go to the `stack' */
+                               first = fs->nactvar;  /* return all `active' values */
+                               ktap_assert(nret == fs->freereg - first);
+                       }
+               }
+       }
+       codegen_ret(fs, first, nret);
+       testnext(ls, ';');  /* skip optional semicolon */
+}
+
+static void tracestat(ktap_lexstate *ls)
+{
+       ktap_expdesc v0, key, args;
+       ktap_expdesc *v = &v0;
+       ktap_string *kdebug_str = ktapc_ts_new("kdebug");
+       ktap_string *probe_str = ktapc_ts_new("probe_by_id");
+       ktap_string *probe_end_str = ktapc_ts_new("probe_end");
+       ktap_funcstate *fs = ls->fs;
+       int token = ls->t.token;
+       int line = ls->linenumber;
+       int base, nparams;
+
+       if (token == TK_TRACE)
+               lex_read_string_until(ls, '{');
+       else
+               lex_next(ls);  /* skip "trace_end" keyword */
+
+       /* kdebug */
+       singlevaraux(fs, ls->envn, v, 1);  /* get environment variable */
+       codestring(ls, &key, kdebug_str);  /* key is variable name */
+       codegen_indexed(fs, v, &key);  /* env[varname] */
+
+       /* fieldsel: kdebug.probe */
+       codegen_exp2anyregup(fs, v);
+       if (token == TK_TRACE)
+               codestring(ls, &key, probe_str);
+       else if (token == TK_TRACE_END)
+               codestring(ls, &key, probe_end_str);
+       codegen_indexed(fs, v, &key);
+
+       /* funcargs*/
+       codegen_exp2nextreg(fs, v);
+
+       if (token == TK_TRACE) {
+               ktap_eventdef_info *evdef_info;
+
+               /* argument: EVENTDEF string */
+               check(ls, TK_STRING);
+               enterlevel(ls);
+               evdef_info = ktapc_parse_eventdef(getstr(ls->t.seminfo.ts));
+               check_condition(ls, evdef_info != NULL, "Cannot parse eventdef");
+
+               /* pass a userspace pointer to kernel */
+               codenumber(ls, &args, (ktap_number)evdef_info);
+               lex_next(ls);  /* skip EVENTDEF string */
+               leavelevel(ls);
+
+               codegen_exp2nextreg(fs, &args); /* for next argument */
+       }
+
+       /* argument: callback function */
+       enterlevel(ls);
+       func_body_no_args(ls, &args, ls->linenumber);
+       leavelevel(ls);
+
+       codegen_setmultret(fs, &args);
+
+       base = v->u.info;  /* base register for call */
+       if (hasmultret(args.k))
+               nparams = KTAP_MULTRET;  /* open call */
+       else {
+               codegen_exp2nextreg(fs, &args);  /* close last argument */
+               nparams = fs->freereg - (base+1);
+       }
+       init_exp(v, VCALL, codegen_codeABC(fs, OP_CALL, base, nparams+1, 2));
+       codegen_fixline(fs, line);
+       fs->freereg = base+1;
+
+       check_condition(ls, v->k == VCALL, "syntax error");
+       SETARG_C(getcode(fs, v), 1);  /* call statement uses no results */
+}
+
+static void timerstat(ktap_lexstate *ls)
+{
+       ktap_expdesc v0, key, args;
+       ktap_expdesc *v = &v0;
+       ktap_funcstate *fs = ls->fs;
+       ktap_string *token_str = ls->t.seminfo.ts;
+       ktap_string *interval_str;
+       int line = ls->linenumber;
+       int base, nparams;
+
+       lex_next(ls);  /* skip profile/tick keyword */
+       check(ls, '-');
+
+       lex_read_string_until(ls, '{');
+       interval_str = ls->t.seminfo.ts;
+
+       //printf("timerstat str: %s\n", getstr(interval_str));
+       //exit(0);
+
+       /* timer */
+       singlevaraux(fs, ls->envn, v, 1);  /* get environment variable */
+       codestring(ls, &key, ktapc_ts_new("timer"));  /* key is variable name */
+       codegen_indexed(fs, v, &key);  /* env[varname] */
+
+       /* fieldsel: timer.profile, timer.tick */
+       codegen_exp2anyregup(fs, v);
+       codestring(ls, &key, token_str);
+       codegen_indexed(fs, v, &key);
+
+       /* funcargs*/
+       codegen_exp2nextreg(fs, v);
+
+       /* argument: interval string */
+       check(ls, TK_STRING);
+       enterlevel(ls);
+       codestring(ls, &args, interval_str);
+       lex_next(ls);  /* skip interval string */
+       leavelevel(ls);
+
+       codegen_exp2nextreg(fs, &args); /* for next argument */
+
+       /* argument: callback function */
+       enterlevel(ls);
+       func_body_no_args(ls, &args, ls->linenumber);
+       leavelevel(ls);
+
+       codegen_setmultret(fs, &args);
+
+       base = v->u.info;  /* base register for call */
+       if (hasmultret(args.k))
+               nparams = KTAP_MULTRET;  /* open call */
+       else {
+               codegen_exp2nextreg(fs, &args);  /* close last argument */
+               nparams = fs->freereg - (base+1);
+       }
+       init_exp(v, VCALL, codegen_codeABC(fs, OP_CALL, base, nparams+1, 2));
+       codegen_fixline(fs, line);
+       fs->freereg = base+1;
+
+       check_condition(ls, v->k == VCALL, "syntax error");
+       SETARG_C(getcode(fs, v), 1);  /* call statement uses no results */
+}
+
+/* we still keep cdef keyword even FFI feature is disabled, it just does
+ * nothing and prints out a warning */
+#ifdef CONFIG_KTAP_FFI
+static void parsecdef(ktap_lexstate *ls)
+{
+       /* read long string cdef */
+       lex_next(ls);
+
+       check(ls, TK_STRING);
+       ffi_cdef(getstr(ls->t.seminfo.ts));
+
+       /* consume newline */
+       lex_next(ls);
+}
+#else
+static void parsecdef(ktap_lexstate *ls)
+{
+       printf("Please compile with FFI support to use cdef!\n");
+       exit(EXIT_SUCCESS);
+}
+#endif
+
+
+static void statement(ktap_lexstate *ls)
+{
+       int line = ls->linenumber;  /* may be needed for error messages */
+
+       enterlevel(ls);
+       switch (ls->t.token) {
+       case ';': {  /* stat -> ';' (empty statement) */
+               lex_next(ls);  /* skip ';' */
+               break;
+       }
+       case TK_IF: {  /* stat -> ifstat */
+               ifstat(ls, line);
+               break;
+       }
+       case TK_WHILE: {  /* stat -> whilestat */
+               whilestat(ls, line);
+               break;
+       }
+       case TK_DO: {  /* stat -> DO block END */
+               lex_next(ls);  /* skip DO */
+               block(ls);
+               check_match(ls, TK_END, TK_DO, line);
+               break;
+       }
+       case TK_FOR: {  /* stat -> forstat */
+               forstat(ls, line);
+               break;
+       }
+       case TK_REPEAT: {  /* stat -> repeatstat */
+               repeatstat(ls, line);
+               break;
+       }
+       case TK_FUNCTION: {  /* stat -> funcstat */
+               funcstat(ls, line);
+               break;
+       }
+       case TK_LOCAL: {  /* stat -> localstat */
+               lex_next(ls);  /* skip LOCAL */
+               if (testnext(ls, TK_FUNCTION))  /* local function? */
+                       localfunc(ls);
+               else
+                       localstat(ls);
+               break;
+       }
+       case TK_DBCOLON: {  /* stat -> label */
+               lex_next(ls);  /* skip double colon */
+               labelstat(ls, str_checkname(ls), line);
+               break;
+       }
+       case TK_RETURN: {  /* stat -> retstat */
+               lex_next(ls);  /* skip RETURN */
+               retstat(ls);
+               break;
+       }
+       case TK_BREAK:   /* stat -> breakstat */
+       case TK_GOTO: {  /* stat -> 'goto' NAME */
+               gotostat(ls, codegen_jump(ls->fs));
+               break;
+       }
+
+       case TK_TRACE:
+       case TK_TRACE_END:
+               tracestat(ls);
+               break;
+       case TK_PROFILE:
+       case TK_TICK:
+               timerstat(ls);
+               break;
+       case TK_FFI_CDEF:
+               parsecdef(ls);
+               break;
+       default: {  /* stat -> func | assignment */
+               exprstat(ls);
+               break;
+       }
+       }
+       //ktap_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
+       //      ls->fs->freereg >= ls->fs->nactvar);
+       ls->fs->freereg = ls->fs->nactvar;  /* free registers */
+       leavelevel(ls);
+}
+/* }====================================================================== */
+
+/*
+ * compiles the main function, which is a regular vararg function with an upvalue
+ */
+static void mainfunc(ktap_lexstate *ls, ktap_funcstate *fs)
+{
+       ktap_blockcnt bl;
+       ktap_expdesc v;
+
+       open_func(ls, fs, &bl);
+       fs->f->is_vararg = 1;  /* main function is always vararg */
+       init_exp(&v, VLOCAL, 0);  /* create and... */
+       newupvalue(fs, ls->envn, &v);  /* ...set environment upvalue */
+       lex_next(ls);  /* read first token */
+       statlist(ls);  /* parse main body */
+       check(ls, TK_EOS);
+       close_func(ls);
+}
+
+ktap_closure *ktapc_parser(char *ptr, const char *name)
+{
+       ktap_lexstate lexstate;
+       ktap_funcstate funcstate;
+       ktap_dyndata dyd;
+       ktap_mbuffer buff;
+       int firstchar = *ptr++;
+       ktap_closure *cl = ktapc_newclosure(1);  /* create main closure */
+
+       memset(&lexstate, 0, sizeof(ktap_lexstate));
+       memset(&funcstate, 0, sizeof(ktap_funcstate));
+       funcstate.f = cl->p = ktapc_newproto();
+       funcstate.f->source = ktapc_ts_new(name);  /* create and anchor ktap_string */
+
+       lex_init();
+
+       mbuff_init(&buff);
+       memset(&dyd, 0, sizeof(ktap_dyndata));
+       lexstate.buff = &buff;
+       lexstate.dyd = &dyd;
+       lex_setinput(&lexstate, ptr, funcstate.f->source, firstchar);
+
+       mainfunc(&lexstate, &funcstate);
+
+       ktap_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
+
+       /* all scopes should be correctly finished */
+       ktap_assert(dyd.actvar.n == 0 && dyd.gt.n == 0 && dyd.label.n == 0);
+       return cl;
+}
diff --git a/drivers/staging/ktap/userspace/symbol.c b/drivers/staging/ktap/userspace/symbol.c
new file mode 100644 (file)
index 0000000..74129e2
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * symbol.c
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2013 Azat Khuzhin <a3at.mail@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "symbol.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <libelf.h>
+
+static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
+                                   GElf_Shdr *shp, const char *name)
+{
+       Elf_Scn *scn = NULL;
+
+       /* Elf is corrupted/truncated, avoid calling elf_strptr. */
+       if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL))
+               return NULL;
+
+       while ((scn = elf_nextscn(elf, scn)) != NULL) {
+               char *str;
+
+               gelf_getshdr(scn, shp);
+               str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
+               if (!strcmp(name, str))
+                       break;
+       }
+
+       return scn;
+}
+
+/**
+ * @return v_addr of "LOAD" program header, that have zero offset.
+ */
+static int find_load_address(Elf *elf, vaddr_t *load_address)
+{
+       GElf_Phdr phdr;
+       size_t i, phdrnum;
+
+       if (elf_getphdrnum(elf, &phdrnum))
+               return -1;
+
+       for (i = 0; i < phdrnum; i++) {
+               if (gelf_getphdr(elf, i, &phdr) == NULL)
+                       return -1;
+
+               if (phdr.p_type != PT_LOAD || phdr.p_offset != 0)
+                       continue;
+
+               *load_address = phdr.p_vaddr;
+               return 0;
+       }
+
+       /* cannot found load address */
+       return -1;
+}
+
+static size_t elf_symbols(GElf_Shdr shdr)
+{
+       return shdr.sh_size / shdr.sh_entsize;
+}
+
+static int dso_symbols(Elf *elf, symbol_actor actor, void *arg)
+{
+       Elf_Data *elf_data = NULL;
+       Elf_Scn *scn = NULL;
+       GElf_Sym sym;
+       GElf_Shdr shdr;
+       int symbols_count = 0;
+       vaddr_t load_address;
+
+       if (find_load_address(elf, &load_address))
+               return -1;
+
+       while ((scn = elf_nextscn(elf, scn))) {
+               int i;
+
+               gelf_getshdr(scn, &shdr);
+
+               if (shdr.sh_type != SHT_SYMTAB)
+                       continue;
+
+               elf_data = elf_getdata(scn, elf_data);
+
+               for (i = 0; i < elf_symbols(shdr); i++) {
+                       char *name;
+                       vaddr_t addr;
+                       int ret;
+
+                       gelf_getsym(elf_data, i, &sym);
+
+                       if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
+                               continue;
+
+                       name = elf_strptr(elf, shdr.sh_link, sym.st_name);
+                       addr = sym.st_value - load_address;
+
+                       ret = actor(name, addr, arg);
+                       if (ret)
+                               return ret;
+
+                       ++symbols_count;
+               }
+       }
+
+       return symbols_count;
+}
+
+#define SDT_NOTE_TYPE 3
+#define SDT_NOTE_COUNT 3
+#define SDT_NOTE_SCN ".note.stapsdt"
+#define SDT_NOTE_NAME "stapsdt"
+
+static vaddr_t sdt_note_addr(Elf *elf, const char *data, size_t len, int type)
+{
+       vaddr_t vaddr;
+
+       /*
+        * Three addresses need to be obtained :
+        * Marker location, address of base section and semaphore location
+        */
+       union {
+               Elf64_Addr a64[3];
+               Elf32_Addr a32[3];
+       } buf;
+
+       /*
+        * dst and src are required for translation from file to memory
+        * representation
+        */
+       Elf_Data dst = {
+               .d_buf = &buf, .d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
+               .d_size = gelf_fsize(elf, ELF_T_ADDR, SDT_NOTE_COUNT, EV_CURRENT),
+               .d_off = 0, .d_align = 0
+       };
+
+       Elf_Data src = {
+               .d_buf = (void *) data, .d_type = ELF_T_ADDR,
+               .d_version = EV_CURRENT, .d_size = dst.d_size, .d_off = 0,
+               .d_align = 0
+       };
+
+       /* Check the type of each of the notes */
+       if (type != SDT_NOTE_TYPE)
+               return 0;
+
+       if (len < dst.d_size + SDT_NOTE_COUNT)
+               return 0;
+
+       /* Translation from file representation to memory representation */
+       if (gelf_xlatetom(elf, &dst, &src,
+                         elf_getident(elf, NULL)[EI_DATA]) == NULL)
+               return 0; /* TODO */
+
+       memcpy(&vaddr, &buf, sizeof(vaddr));
+
+       return vaddr;
+}
+
+static const char *sdt_note_name(Elf *elf, GElf_Nhdr *nhdr, const char *data)
+{
+       const char *provider = data + gelf_fsize(elf,
+               ELF_T_ADDR, SDT_NOTE_COUNT, EV_CURRENT);
+       const char *name = (const char *)memchr(provider, '\0',
+               data + nhdr->n_descsz - provider);
+
+       if (name++ == NULL)
+               return NULL;
+
+       return name;
+}
+
+static const char *sdt_note_data(const Elf_Data *data, size_t off)
+{
+       return ((data->d_buf) + off);
+}
+
+static int dso_sdt_notes(Elf *elf, symbol_actor actor, void *arg)
+{
+       GElf_Ehdr ehdr;
+       Elf_Scn *scn = NULL;
+       Elf_Data *data;
+       GElf_Shdr shdr;
+       size_t shstrndx;
+       size_t next;
+       GElf_Nhdr nhdr;
+       size_t name_off, desc_off, offset;
+       vaddr_t vaddr = 0;
+       int symbols_count = 0;
+
+       if (gelf_getehdr(elf, &ehdr) == NULL)
+               return 0;
+       if (elf_getshdrstrndx(elf, &shstrndx) != 0)
+               return 0;
+
+       /*
+        * Look for section type = SHT_NOTE, flags = no SHF_ALLOC
+        * and name = .note.stapsdt
+        */
+       scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN);
+       if (!scn)
+               return 0;
+       if (!(shdr.sh_type == SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC))
+               return 0;
+
+       data = elf_getdata(scn, NULL);
+
+       for (offset = 0;
+               (next = gelf_getnote(data, offset, &nhdr, &name_off, &desc_off)) > 0;
+               offset = next) {
+               const char *name;
+               int ret;
+
+               if (nhdr.n_namesz != sizeof(SDT_NOTE_NAME) ||
+                   memcmp(data->d_buf + name_off, SDT_NOTE_NAME,
+                           sizeof(SDT_NOTE_NAME)))
+                       continue;
+
+               name = sdt_note_name(elf, &nhdr, sdt_note_data(data, desc_off));
+               if (!name)
+                       continue;
+
+               vaddr = sdt_note_addr(elf, sdt_note_data(data, desc_off),
+                                       nhdr.n_descsz, nhdr.n_type);
+               if (!vaddr)
+                       continue;
+
+               ret = actor(name, vaddr, arg);
+               if (ret)
+                       return ret;
+
+               ++symbols_count;
+       }
+
+       return symbols_count;
+}
+
+int parse_dso_symbols(const char *exec, int type, symbol_actor actor, void *arg)
+{
+       int symbols_count = 0;
+       Elf *elf;
+       int fd;
+
+       if (elf_version(EV_CURRENT) == EV_NONE)
+               return -1;
+
+       fd = open(exec, O_RDONLY);
+       if (fd < 0)
+               return -1;
+
+       elf = elf_begin(fd, ELF_C_READ, NULL);
+       if (elf) {
+               switch (type) {
+               case FIND_SYMBOL:
+                       symbols_count = dso_symbols(elf, actor, arg);
+                       break;
+               case FIND_STAPSDT_NOTE:
+                       symbols_count = dso_sdt_notes(elf, actor, arg);
+                       break;
+               }
+
+               elf_end(elf);
+       }
+
+       close(fd);
+       return symbols_count;
+}
diff --git a/drivers/staging/ktap/userspace/symbol.h b/drivers/staging/ktap/userspace/symbol.h
new file mode 100644 (file)
index 0000000..650e785
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * symbol.h - extract symbols from DSO.
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2013 Azat Khuzhin <a3at.mail@gmail.com>.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#define FIND_SYMBOL 1
+#define FIND_STAPSDT_NOTE 2
+
+#ifndef NO_LIBELF
+
+#include <gelf.h>
+#include <sys/queue.h>
+
+typedef GElf_Addr vaddr_t;
+typedef int (*symbol_actor)(const char *name, vaddr_t addr, void *arg);
+
+/**
+ * Parse all DSO symbols/sdt notes and all for every of them
+ * an actor.
+ *
+ * @exec - path to DSO
+ * @type - see FIND_*
+ * @symbol_actor - actor to call (callback)
+ * @arg - argument for @actor
+ *
+ * @return
+ * If there have errors, return negative value;
+ * No symbols found, return 0;
+ * Otherwise return number of dso symbols found
+ */
+int
+parse_dso_symbols(const char *exec, int type, symbol_actor actor, void *arg);
+#endif
diff --git a/drivers/staging/ktap/userspace/util.c b/drivers/staging/ktap/userspace/util.c
new file mode 100644 (file)
index 0000000..c0e6545
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * util.c
+ *
+ * This file is part of ktap by Jovi Zhangwei.
+ *
+ * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+ *
+ * Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+ *  - The part of code in this file is copied from lua initially.
+ *  - lua's MIT license is compatible with GPL.
+ *
+ * ktap is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * ktap is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "../include/ktap_types.h"
+#include "../include/ktap_opcodes.h"
+#include "ktapc.h"
+
+/*
+ * converts an integer to a "floating point byte", represented as
+ * (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
+ * eeeee != 0 and (xxx) otherwise.
+ */
+int ktapc_int2fb(unsigned int x)
+{
+       int e = 0;  /* exponent */
+
+       if (x < 8)
+               return x;
+       while (x >= 0x10) {
+               x = (x+1) >> 1;
+               e++;
+       }
+       return ((e+1) << 3) | ((int)x - 8);
+}
+
+/* converts back */
+int ktapc_fb2int(int x)
+{
+       int e = (x >> 3) & 0x1f;
+
+       if (e == 0)
+               return x;
+       else
+               return ((x & 7) + 8) << (e - 1);
+}
+
+int ktapc_ceillog2(unsigned int x)
+{
+       static const u8 log_2[256] = {
+       0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+       6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+       7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+       7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+       8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+       8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+       8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+       8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+       };
+       int l = 0;
+
+       x--;
+       while (x >= 256) {
+               l += 8;
+               x >>= 8;
+       }
+       return l + log_2[x];
+}
+
+ktap_number ktapc_arith(int op, ktap_number v1, ktap_number v2)
+{
+       switch (op) {
+       case KTAP_OPADD: return NUMADD(v1, v2);
+       case KTAP_OPSUB: return NUMSUB(v1, v2);
+       case KTAP_OPMUL: return NUMMUL(v1, v2);
+       case KTAP_OPDIV: return NUMDIV(v1, v2);
+       case KTAP_OPMOD: return NUMMOD(v1, v2);
+       //case KTAP_OPPOW: return NUMPOW(v1, v2);
+       case KTAP_OPUNM: return NUMUNM(v1);
+       default: ktap_assert(0); return 0;
+       }
+}
+
+int ktapc_hexavalue(int c)
+{
+       if (isdigit(c))
+               return c - '0';
+       else
+               return tolower(c) - 'a' + 10;
+}
+
+int ktapc_str2d(const char *s, size_t len, ktap_number *result)
+{
+       char *endptr;
+
+       if (strpbrk(s, "nN"))  /* reject 'inf' and 'nan' */
+               return 0;
+       else
+               *result = (long)strtoul(s, &endptr, 0);
+
+       if (endptr == s)
+               return 0;  /* nothing recognized */
+       while (isspace((unsigned char)(*endptr)))
+               endptr++;
+       return (endptr == s + len);  /* OK if no trailing characters */
+}
+
+/* number of chars of a literal string without the ending \0 */
+#define LL(x)  (sizeof(x)/sizeof(char) - 1)
+
+#define RETS   "..."
+#define PRE    "[string \""
+#define POS    "\"]"
+
+#define addstr(a,b,l)  ( memcpy(a,b,(l) * sizeof(char)), a += (l) )
+
+void ktapc_chunkid(char *out, const char *source, size_t bufflen)
+{
+       size_t l = strlen(source);
+
+       if (*source == '=') {  /* 'literal' source */
+               if (l <= bufflen)  /* small enough? */
+                       memcpy(out, source + 1, l * sizeof(char));
+               else {  /* truncate it */
+                       addstr(out, source + 1, bufflen - 1);
+                       *out = '\0';
+               }
+       } else if (*source == '@') {  /* file name */
+               if (l <= bufflen)  /* small enough? */
+                       memcpy(out, source + 1, l * sizeof(char));
+               else {  /* add '...' before rest of name */
+                       addstr(out, RETS, LL(RETS));
+                       bufflen -= LL(RETS);
+                       memcpy(out, source + 1 + l - bufflen, bufflen * sizeof(char));
+               }
+       } else {  /* string; format as [string "source"] */
+               const char *nl = strchr(source, '\n');  /* find first new line (if any) */
+               addstr(out, PRE, LL(PRE));  /* add prefix */
+               bufflen -= LL(PRE RETS POS) + 1;  /* save space for prefix+suffix+'\0' */
+               if (l < bufflen && nl == NULL) {  /* small one-line source? */
+                       addstr(out, source, l);  /* keep it */
+               } else {
+                       if (nl != NULL)
+                               l = nl - source;  /* stop at first newline */
+                       if (l > bufflen)
+                               l = bufflen;
+                       addstr(out, source, l);
+                       addstr(out, RETS, LL(RETS));
+               }
+               memcpy(out, POS, (LL(POS) + 1) * sizeof(char));
+       }
+}
+
+
+/*
+ * strglobmatch is copyed from perf(linux/tools/perf/util/string.c)
+ */
+
+/* Character class matching */
+static bool __match_charclass(const char *pat, char c, const char **npat)
+{
+       bool complement = false, ret = true;
+
+       if (*pat == '!') {
+               complement = true;
+               pat++;
+       }
+       if (*pat++ == c)        /* First character is special */
+               goto end;
+
+       while (*pat && *pat != ']') {   /* Matching */
+               if (*pat == '-' && *(pat + 1) != ']') { /* Range */
+                       if (*(pat - 1) <= c && c <= *(pat + 1))
+                               goto end;
+                       if (*(pat - 1) > *(pat + 1))
+                               goto error;
+                       pat += 2;
+               } else if (*pat++ == c)
+                       goto end;
+       }
+       if (!*pat)
+               goto error;
+       ret = false;
+
+end:
+       while (*pat && *pat != ']')     /* Searching closing */
+               pat++;
+       if (!*pat)
+               goto error;
+       *npat = pat + 1;
+       return complement ? !ret : ret;
+
+error:
+       return false;
+}
+
+/* Glob/lazy pattern matching */
+static bool __match_glob(const char *str, const char *pat, bool ignore_space)
+{
+       while (*str && *pat && *pat != '*') {
+               if (ignore_space) {
+                       /* Ignore spaces for lazy matching */
+                       if (isspace(*str)) {
+                               str++;
+                               continue;
+                       }
+                       if (isspace(*pat)) {
+                               pat++;
+                               continue;
+                       }
+               }
+               if (*pat == '?') {      /* Matches any single character */
+                       str++;
+                       pat++;
+                       continue;
+               } else if (*pat == '[') /* Character classes/Ranges */
+                       if (__match_charclass(pat + 1, *str, &pat)) {
+                               str++;
+                               continue;
+                       } else
+                               return false;
+               else if (*pat == '\\') /* Escaped char match as normal char */
+                       pat++;
+               if (*str++ != *pat++)
+                       return false;
+       }
+       /* Check wild card */
+       if (*pat == '*') {
+               while (*pat == '*')
+                       pat++;
+               if (!*pat)      /* Tail wild card matches all */
+                       return true;
+               while (*str)
+                       if (__match_glob(str++, pat, ignore_space))
+                               return true;
+       }
+       return !*str && !*pat;
+}
+
+/**
+ * strglobmatch - glob expression pattern matching
+ * @str: the target string to match
+ * @pat: the pattern string to match
+ *
+ * This returns true if the @str matches @pat. @pat can includes wildcards
+ * ('*','?') and character classes ([CHARS], complementation and ranges are
+ * also supported). Also, this supports escape character ('\') to use special
+ * characters as normal character.
+ *
+ * Note: if @pat syntax is broken, this always returns false.
+ */
+bool strglobmatch(const char *str, const char *pat)
+{
+       return __match_glob(str, pat, false);
+}
+
+#define handle_error(str) do { perror(str); exit(-1); } while(0)
+
+#define KALLSYMS_PATH "/proc/kallsyms"
+/*
+ * read kernel symbol from /proc/kallsyms
+ */
+int kallsyms_parse(void *arg,
+                  int(*process_symbol)(void *arg, const char *name,
+                  char type, unsigned long start))
+{
+       int ret = 0;
+       FILE *file;
+       char *line = NULL;
+
+       file = fopen(KALLSYMS_PATH, "r");
+       if (file == NULL)
+               handle_error("open " KALLSYMS_PATH " failed");
+
+       while (!feof(file)) {
+               char *symbol_addr, *symbol_name;
+               char symbol_type;
+               unsigned long start;
+               int line_len;
+               size_t n;
+
+               line_len = getline(&line, &n, file);
+               if (line_len < 0 || !line)
+                       break;
+
+               line[--line_len] = '\0'; /* \n */
+
+               symbol_addr = strtok(line, " \t");
+               start = strtoul(symbol_addr, NULL, 16);
+
+               symbol_type = *strtok(NULL, " \t");
+               symbol_name = strtok(NULL, " \t");
+
+               ret = process_symbol(arg, symbol_name, symbol_type, start);
+               if (ret)
+                       break;
+       }
+
+       free(line);
+       fclose(file);
+
+       return ret;
+}
+
+struct ksym_addr_t {
+       const char *name;
+       unsigned long addr;
+};
+
+static int symbol_cmp(void *arg, const char *name, char type,
+                     unsigned long start)
+{
+       struct ksym_addr_t *base = arg;
+
+       if (strcmp(base->name, name) == 0) {
+               base->addr = start;
+               return 1;
+       }
+
+       return 0;
+}
+
+unsigned long find_kernel_symbol(const char *symbol)
+{
+       int ret;
+       struct ksym_addr_t arg = {
+               .name = symbol,
+               .addr = 0
+       };
+
+       ret = kallsyms_parse(&arg, symbol_cmp);
+       if (ret < 0 || arg.addr == 0) {
+               fprintf(stderr, "cannot read kernel symbol \"%s\" in %s\n",
+                       symbol, KALLSYMS_PATH);
+               exit(EXIT_FAILURE);
+       }
+
+       return arg.addr;
+}
+
+
+#define AVAILABLE_EVENTS_PATH "/sys/kernel/debug/tracing/available_events"
+
+void list_available_events(const char *match)
+{
+       FILE *file;
+       char *line = NULL;
+
+       file = fopen(AVAILABLE_EVENTS_PATH, "r");
+       if (file == NULL)
+               handle_error("open " AVAILABLE_EVENTS_PATH " failed");
+
+       while (!feof(file)) {
+               size_t n;
+
+               getline(&line, &n, file);
+
+               if (!match || strglobmatch(line, match))
+                       printf("%s", line);
+       }
+
+       free(line);
+       fclose(file);
+}
+