aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-11-10 09:56:02 +0900
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-11-10 09:56:02 +0900
commitfd27e437793a269e9f1ee2c7137084725b5f2a30 (patch)
treed1e1c8d2659d284e7ae9842d1f8579a2cc9d2047
parent56bb878043c6cbec798eed5270804b2c26bb522f (diff)
downloadltsi-kernel-fd27e437793a269e9f1ee2c7137084725b5f2a30.tar.gz
proper add patches.ktap/
-rw-r--r--patches.ktap/ktap-0.4.patch23278
1 files changed, 23278 insertions, 0 deletions
diff --git a/patches.ktap/ktap-0.4.patch b/patches.ktap/ktap-0.4.patch
new file mode 100644
index 0000000000000..dda586c366b14
--- /dev/null
+++ b/patches.ktap/ktap-0.4.patch
@@ -0,0 +1,23278 @@
+From 982d59f0bfdb7a85fd64fcebf4d18b49dfdf5120 Mon Sep 17 00:00:00 2001
+From: Li xin <lixin.fnst@cn.fujitsu.com>
+Date: Fri, 17 Oct 2014 15:06:28 +0300
+Subject: [PATCH] ktap-0.4
+
+---
+ drivers/staging/Kconfig | 2
+ drivers/staging/Makefile | 1
+ drivers/staging/ktap/Kconfig | 32
+ drivers/staging/ktap/Makefile | 203 +
+ drivers/staging/ktap/README.md | 167
+ drivers/staging/ktap/RELEASES.txt | 155
+ drivers/staging/ktap/doc/tutorial.md | 691 +++
+ drivers/staging/ktap/include/ktap_ffi.h | 180
+ drivers/staging/ktap/include/ktap_opcodes.h | 239 +
+ drivers/staging/ktap/include/ktap_types.h | 609 +++
+ drivers/staging/ktap/runtime/ffi/call_x86_64.S | 143
+ drivers/staging/ktap/runtime/ffi/cdata.c | 67
+ drivers/staging/ktap/runtime/ffi/ffi_call.c | 427 ++
+ drivers/staging/ktap/runtime/ffi/ffi_symbol.c | 174
+ drivers/staging/ktap/runtime/ffi/ffi_type.c | 51
+ drivers/staging/ktap/runtime/ffi/ffi_util.c | 92
+ drivers/staging/ktap/runtime/kp_amalg.c | 43
+ drivers/staging/ktap/runtime/kp_load.c | 401 ++
+ drivers/staging/ktap/runtime/kp_load.h | 6
+ drivers/staging/ktap/runtime/kp_obj.c | 478 ++
+ drivers/staging/ktap/runtime/kp_obj.h | 29
+ drivers/staging/ktap/runtime/kp_opcode.c | 134
+ drivers/staging/ktap/runtime/kp_str.c | 460 ++
+ drivers/staging/ktap/runtime/kp_str.h | 20
+ drivers/staging/ktap/runtime/kp_tab.c | 1396 +++++++
+ drivers/staging/ktap/runtime/kp_tab.h | 32
+ drivers/staging/ktap/runtime/kp_transport.c | 641 +++
+ drivers/staging/ktap/runtime/kp_transport.h | 13
+ drivers/staging/ktap/runtime/kp_vm.c | 1496 +++++++
+ drivers/staging/ktap/runtime/kp_vm.h | 16
+ drivers/staging/ktap/runtime/ktap.c | 217 +
+ drivers/staging/ktap/runtime/ktap.h | 130
+ drivers/staging/ktap/runtime/lib_ansi.c | 155
+ drivers/staging/ktap/runtime/lib_base.c | 607 +++
+ drivers/staging/ktap/runtime/lib_ffi.c | 50
+ drivers/staging/ktap/runtime/lib_kdebug.c | 426 ++
+ drivers/staging/ktap/runtime/lib_timer.c | 193
+ drivers/staging/ktap/samples/ansi/ansi_color_demo.kp | 22
+ drivers/staging/ktap/samples/basic/backtrace.kp | 6
+ drivers/staging/ktap/samples/basic/event_trigger.kp | 24
+ drivers/staging/ktap/samples/basic/event_trigger_ftrace.kp | 28
+ drivers/staging/ktap/samples/basic/ftrace.kp | 6
+ drivers/staging/ktap/samples/basic/function_time.kp | 57
+ drivers/staging/ktap/samples/basic/kretprobe.kp | 6
+ drivers/staging/ktap/samples/ffi/ffi_kmalloc.kp | 19
+ drivers/staging/ktap/samples/ffi/printk.kp | 10
+ drivers/staging/ktap/samples/ffi/sched_clock.kp | 6
+ drivers/staging/ktap/samples/game/tetris.kp | 293 +
+ drivers/staging/ktap/samples/helloworld.kp | 3
+ drivers/staging/ktap/samples/interrupt/hardirq_time.kp | 24
+ drivers/staging/ktap/samples/interrupt/softirq_time.kp | 24
+ drivers/staging/ktap/samples/io/kprobes-do-sys-open.kp | 20
+ drivers/staging/ktap/samples/io/traceio.kp | 54
+ drivers/staging/ktap/samples/mem/kmalloc-top.kp | 17
+ drivers/staging/ktap/samples/mem/kmem.kp | 30
+ drivers/staging/ktap/samples/profiling/function_profiler.kp | 41
+ drivers/staging/ktap/samples/profiling/stack_profile.kp | 30
+ drivers/staging/ktap/samples/schedule/sched_transition.kp | 5
+ drivers/staging/ktap/samples/schedule/schedtimes.kp | 125
+ drivers/staging/ktap/samples/syscalls/errinfo.kp | 145
+ drivers/staging/ktap/samples/syscalls/execve.kp | 8
+ drivers/staging/ktap/samples/syscalls/opensnoop.kp | 31
+ drivers/staging/ktap/samples/syscalls/sctop.kp | 13
+ drivers/staging/ktap/samples/syscalls/syscalls.kp | 6
+ drivers/staging/ktap/samples/syscalls/syscalls_count.kp | 54
+ drivers/staging/ktap/samples/syscalls/syscalls_count_by_proc.kp | 22
+ drivers/staging/ktap/samples/syscalls/syslatl.kp | 30
+ drivers/staging/ktap/samples/syscalls/syslist.kp | 31
+ drivers/staging/ktap/samples/tracepoints/eventcount.kp | 210 +
+ drivers/staging/ktap/samples/tracepoints/eventcount_by_proc.kp | 57
+ drivers/staging/ktap/samples/tracepoints/tracepoints.kp | 6
+ drivers/staging/ktap/samples/userspace/gcc_unwind.kp | 9
+ drivers/staging/ktap/samples/userspace/glibc_func_hist.kp | 44
+ drivers/staging/ktap/samples/userspace/glibc_sdt.kp | 11
+ drivers/staging/ktap/samples/userspace/glibc_trace.kp | 11
+ drivers/staging/ktap/samples/userspace/malloc_free.kp | 20
+ drivers/staging/ktap/samples/userspace/malloc_size_hist.kp | 22
+ drivers/staging/ktap/test/arg.kp | 24
+ drivers/staging/ktap/test/arithmetic.kp | 50
+ drivers/staging/ktap/test/benchmark/sembench.c | 556 ++
+ drivers/staging/ktap/test/benchmark/test.sh | 26
+ drivers/staging/ktap/test/concat.kp | 15
+ drivers/staging/ktap/test/count.kp | 20
+ drivers/staging/ktap/test/ffi/.gitignore | 2
+ drivers/staging/ktap/test/ffi/Makefile | 46
+ drivers/staging/ktap/test/ffi/cparser_test.c | 322 +
+ drivers/staging/ktap/test/ffi/ffi_test.kp | 47
+ drivers/staging/ktap/test/ffi/ktap_ffi_test.c | 64
+ drivers/staging/ktap/test/fibonacci.kp | 36
+ drivers/staging/ktap/test/function.kp | 88
+ drivers/staging/ktap/test/if.kp | 24
+ drivers/staging/ktap/test/kprobe.kp | 19
+ drivers/staging/ktap/test/kretprobe.kp | 14
+ drivers/staging/ktap/test/ksym.kp | 17
+ drivers/staging/ktap/test/len.kp | 25
+ drivers/staging/ktap/test/looping.kp | 40
+ drivers/staging/ktap/test/pairs.kp | 84
+ drivers/staging/ktap/test/ptable.kp | 46
+ drivers/staging/ktap/test/run_test.sh | 62
+ drivers/staging/ktap/test/stack_overflow.kp | 9
+ drivers/staging/ktap/test/table.kp | 71
+ drivers/staging/ktap/test/timer.kp | 28
+ drivers/staging/ktap/test/tracepoint.kp | 22
+ drivers/staging/ktap/test/zerodivide.kp | 5
+ drivers/staging/ktap/userspace/code.c | 998 +++++
+ drivers/staging/ktap/userspace/cparser.h | 202 +
+ drivers/staging/ktap/userspace/dump.c | 251 +
+ drivers/staging/ktap/userspace/eventdef.c | 857 ++++
+ drivers/staging/ktap/userspace/ffi/cparser.c | 1755 ++++++++
+ drivers/staging/ktap/userspace/ffi/ctype.c | 551 ++
+ drivers/staging/ktap/userspace/ktapc.h | 393 ++
+ drivers/staging/ktap/userspace/ktapio.c | 106
+ drivers/staging/ktap/userspace/lex.c | 632 +++
+ drivers/staging/ktap/userspace/main.c | 727 +++
+ drivers/staging/ktap/userspace/parser.c | 1963 ++++++++++
+ drivers/staging/ktap/userspace/symbol.c | 291 +
+ drivers/staging/ktap/userspace/symbol.h | 50
+ drivers/staging/ktap/userspace/util.c | 381 +
+ 118 files changed, 22675 insertions(+)
+ create mode 100644 drivers/staging/ktap/Kconfig
+ create mode 100644 drivers/staging/ktap/Makefile
+ create mode 100644 drivers/staging/ktap/README.md
+ create mode 100644 drivers/staging/ktap/RELEASES.txt
+ create mode 100644 drivers/staging/ktap/doc/tutorial.md
+ create mode 100644 drivers/staging/ktap/include/ktap_ffi.h
+ create mode 100644 drivers/staging/ktap/include/ktap_opcodes.h
+ create mode 100644 drivers/staging/ktap/include/ktap_types.h
+ create mode 100644 drivers/staging/ktap/runtime/ffi/call_x86_64.S
+ create mode 100644 drivers/staging/ktap/runtime/ffi/cdata.c
+ create mode 100644 drivers/staging/ktap/runtime/ffi/ffi_call.c
+ create mode 100644 drivers/staging/ktap/runtime/ffi/ffi_symbol.c
+ create mode 100644 drivers/staging/ktap/runtime/ffi/ffi_type.c
+ create mode 100644 drivers/staging/ktap/runtime/ffi/ffi_util.c
+ create mode 100644 drivers/staging/ktap/runtime/kp_amalg.c
+ create mode 100644 drivers/staging/ktap/runtime/kp_load.c
+ create mode 100644 drivers/staging/ktap/runtime/kp_load.h
+ create mode 100644 drivers/staging/ktap/runtime/kp_obj.c
+ create mode 100644 drivers/staging/ktap/runtime/kp_obj.h
+ create mode 100644 drivers/staging/ktap/runtime/kp_opcode.c
+ create mode 100644 drivers/staging/ktap/runtime/kp_str.c
+ create mode 100644 drivers/staging/ktap/runtime/kp_str.h
+ create mode 100644 drivers/staging/ktap/runtime/kp_tab.c
+ create mode 100644 drivers/staging/ktap/runtime/kp_tab.h
+ create mode 100644 drivers/staging/ktap/runtime/kp_transport.c
+ create mode 100644 drivers/staging/ktap/runtime/kp_transport.h
+ create mode 100644 drivers/staging/ktap/runtime/kp_vm.c
+ create mode 100644 drivers/staging/ktap/runtime/kp_vm.h
+ create mode 100644 drivers/staging/ktap/runtime/ktap.c
+ create mode 100644 drivers/staging/ktap/runtime/ktap.h
+ create mode 100644 drivers/staging/ktap/runtime/lib_ansi.c
+ create mode 100644 drivers/staging/ktap/runtime/lib_base.c
+ create mode 100644 drivers/staging/ktap/runtime/lib_ffi.c
+ create mode 100644 drivers/staging/ktap/runtime/lib_kdebug.c
+ create mode 100644 drivers/staging/ktap/runtime/lib_timer.c
+ create mode 100644 drivers/staging/ktap/samples/ansi/ansi_color_demo.kp
+ create mode 100644 drivers/staging/ktap/samples/basic/backtrace.kp
+ create mode 100644 drivers/staging/ktap/samples/basic/event_trigger.kp
+ create mode 100644 drivers/staging/ktap/samples/basic/event_trigger_ftrace.kp
+ create mode 100644 drivers/staging/ktap/samples/basic/ftrace.kp
+ create mode 100644 drivers/staging/ktap/samples/basic/function_time.kp
+ create mode 100644 drivers/staging/ktap/samples/basic/kretprobe.kp
+ create mode 100644 drivers/staging/ktap/samples/ffi/ffi_kmalloc.kp
+ create mode 100644 drivers/staging/ktap/samples/ffi/printk.kp
+ create mode 100644 drivers/staging/ktap/samples/ffi/sched_clock.kp
+ create mode 100644 drivers/staging/ktap/samples/game/tetris.kp
+ create mode 100644 drivers/staging/ktap/samples/helloworld.kp
+ create mode 100644 drivers/staging/ktap/samples/interrupt/hardirq_time.kp
+ create mode 100644 drivers/staging/ktap/samples/interrupt/softirq_time.kp
+ create mode 100644 drivers/staging/ktap/samples/io/kprobes-do-sys-open.kp
+ create mode 100644 drivers/staging/ktap/samples/io/traceio.kp
+ create mode 100644 drivers/staging/ktap/samples/mem/kmalloc-top.kp
+ create mode 100644 drivers/staging/ktap/samples/mem/kmem.kp
+ create mode 100644 drivers/staging/ktap/samples/profiling/function_profiler.kp
+ create mode 100644 drivers/staging/ktap/samples/profiling/stack_profile.kp
+ create mode 100644 drivers/staging/ktap/samples/schedule/sched_transition.kp
+ create mode 100644 drivers/staging/ktap/samples/schedule/schedtimes.kp
+ create mode 100644 drivers/staging/ktap/samples/syscalls/errinfo.kp
+ create mode 100644 drivers/staging/ktap/samples/syscalls/execve.kp
+ create mode 100644 drivers/staging/ktap/samples/syscalls/opensnoop.kp
+ create mode 100644 drivers/staging/ktap/samples/syscalls/sctop.kp
+ create mode 100644 drivers/staging/ktap/samples/syscalls/syscalls.kp
+ create mode 100644 drivers/staging/ktap/samples/syscalls/syscalls_count.kp
+ create mode 100644 drivers/staging/ktap/samples/syscalls/syscalls_count_by_proc.kp
+ create mode 100644 drivers/staging/ktap/samples/syscalls/syslatl.kp
+ create mode 100644 drivers/staging/ktap/samples/syscalls/syslist.kp
+ create mode 100644 drivers/staging/ktap/samples/tracepoints/eventcount.kp
+ create mode 100644 drivers/staging/ktap/samples/tracepoints/eventcount_by_proc.kp
+ create mode 100644 drivers/staging/ktap/samples/tracepoints/tracepoints.kp
+ create mode 100644 drivers/staging/ktap/samples/userspace/gcc_unwind.kp
+ create mode 100644 drivers/staging/ktap/samples/userspace/glibc_func_hist.kp
+ create mode 100644 drivers/staging/ktap/samples/userspace/glibc_sdt.kp
+ create mode 100644 drivers/staging/ktap/samples/userspace/glibc_trace.kp
+ create mode 100644 drivers/staging/ktap/samples/userspace/malloc_free.kp
+ create mode 100644 drivers/staging/ktap/samples/userspace/malloc_size_hist.kp
+ create mode 100644 drivers/staging/ktap/test/arg.kp
+ create mode 100644 drivers/staging/ktap/test/arithmetic.kp
+ create mode 100644 drivers/staging/ktap/test/benchmark/sembench.c
+ create mode 100644 drivers/staging/ktap/test/benchmark/test.sh
+ create mode 100644 drivers/staging/ktap/test/concat.kp
+ create mode 100644 drivers/staging/ktap/test/count.kp
+ create mode 100644 drivers/staging/ktap/test/ffi/.gitignore
+ create mode 100644 drivers/staging/ktap/test/ffi/Makefile
+ create mode 100644 drivers/staging/ktap/test/ffi/cparser_test.c
+ create mode 100644 drivers/staging/ktap/test/ffi/ffi_test.kp
+ create mode 100644 drivers/staging/ktap/test/ffi/ktap_ffi_test.c
+ create mode 100644 drivers/staging/ktap/test/fibonacci.kp
+ create mode 100644 drivers/staging/ktap/test/function.kp
+ create mode 100644 drivers/staging/ktap/test/if.kp
+ create mode 100644 drivers/staging/ktap/test/kprobe.kp
+ create mode 100644 drivers/staging/ktap/test/kretprobe.kp
+ create mode 100644 drivers/staging/ktap/test/ksym.kp
+ create mode 100644 drivers/staging/ktap/test/len.kp
+ create mode 100644 drivers/staging/ktap/test/looping.kp
+ create mode 100644 drivers/staging/ktap/test/pairs.kp
+ create mode 100644 drivers/staging/ktap/test/ptable.kp
+ create mode 100644 drivers/staging/ktap/test/run_test.sh
+ create mode 100644 drivers/staging/ktap/test/stack_overflow.kp
+ create mode 100644 drivers/staging/ktap/test/table.kp
+ create mode 100644 drivers/staging/ktap/test/timer.kp
+ create mode 100644 drivers/staging/ktap/test/tracepoint.kp
+ create mode 100644 drivers/staging/ktap/test/zerodivide.kp
+ create mode 100644 drivers/staging/ktap/userspace/code.c
+ create mode 100644 drivers/staging/ktap/userspace/cparser.h
+ create mode 100644 drivers/staging/ktap/userspace/dump.c
+ create mode 100644 drivers/staging/ktap/userspace/eventdef.c
+ create mode 100644 drivers/staging/ktap/userspace/ffi/cparser.c
+ create mode 100644 drivers/staging/ktap/userspace/ffi/ctype.c
+ create mode 100644 drivers/staging/ktap/userspace/ktapc.h
+ create mode 100644 drivers/staging/ktap/userspace/ktapio.c
+ create mode 100644 drivers/staging/ktap/userspace/lex.c
+ create mode 100644 drivers/staging/ktap/userspace/main.c
+ create mode 100644 drivers/staging/ktap/userspace/parser.c
+ create mode 100644 drivers/staging/ktap/userspace/symbol.c
+ create mode 100644 drivers/staging/ktap/userspace/symbol.h
+ create mode 100644 drivers/staging/ktap/userspace/util.c
+
+--- a/drivers/staging/Kconfig
++++ b/drivers/staging/Kconfig
+@@ -148,4 +148,6 @@ source "drivers/staging/dgap/Kconfig"
+
+ source "drivers/staging/lttng/Kconfig"
+
++source "drivers/staging/ktap/Kconfig"
++
+ endif # STAGING
+--- a/drivers/staging/Makefile
++++ b/drivers/staging/Makefile
+@@ -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/
+--- /dev/null
++++ b/drivers/staging/ktap/Kconfig
+@@ -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.
+--- /dev/null
++++ b/drivers/staging/ktap/Makefile
+@@ -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:
++
+--- /dev/null
++++ b/drivers/staging/ktap/README.md
+@@ -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
++
+--- /dev/null
++++ b/drivers/staging/ktap/RELEASES.txt
+@@ -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/
++
+--- /dev/null
++++ b/drivers/staging/ktap/doc/tutorial.md
+@@ -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 ::= '-'
++
+--- /dev/null
++++ b/drivers/staging/ktap/include/ktap_ffi.h
+@@ -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__ */
+--- /dev/null
++++ b/drivers/staging/ktap/include/ktap_opcodes.h
+@@ -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__ */
+--- /dev/null
++++ b/drivers/staging/ktap/include/ktap_types.h
+@@ -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__ */
++
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/ffi/call_x86_64.S
+@@ -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 */
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/ffi/cdata.c
+@@ -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));
++ }
++}
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/ffi/ffi_call.c
+@@ -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));
++}
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/ffi/ffi_symbol.c
+@@ -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));
++}
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/ffi/ffi_type.c
+@@ -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"},
++};
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/ffi/ffi_util.c
+@@ -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;
++}
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_amalg.c
+@@ -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
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_load.c
+@@ -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;
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_load.h
+@@ -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__ */
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_obj.c
+@@ -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));
++}
++
++
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_obj.h
+@@ -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__ */
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_opcode.c
+@@ -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 */
++};
++
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_str.c
+@@ -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
++
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_str.h
+@@ -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__ */
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_tab.c
+@@ -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
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_tab.h
+@@ -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__ */
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_transport.c
+@@ -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;
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_transport.h
+@@ -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__ */
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_vm.c
+@@ -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;
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/kp_vm.h
+@@ -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__ */
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/ktap.c
+@@ -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");
++
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/ktap.h
+@@ -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__ */
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/lib_ansi.c
+@@ -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);
++}
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/lib_base.c
+@@ -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);
++}
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/lib_ffi.c
+@@ -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);
++}
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/lib_kdebug.c
+@@ -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);
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/runtime/lib_timer.c
+@@ -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);
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/ansi/ansi_color_demo.kp
+@@ -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()
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/basic/backtrace.kp
+@@ -0,0 +1,6 @@
++#!/usr/bin/env ktap
++
++trace sched:sched_switch {
++ print_backtrace()
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/basic/event_trigger.kp
+@@ -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()
++ }
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/basic/event_trigger_ftrace.kp
+@@ -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()
++ }
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/basic/ftrace.kp
+@@ -0,0 +1,6 @@
++#!/usr/bin/env ktap
++
++trace ftrace:function /ip==mutex*/ {
++ print(cpu(), pid(), execname(), argevent)
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/basic/function_time.kp
+@@ -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)
++}
++
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/basic/kretprobe.kp
+@@ -0,0 +1,6 @@
++#!/usr/bin/env ktap
++
++trace probe:vfs_read%return fd=$retval {
++ print(execname(), argevent);
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/ffi/ffi_kmalloc.kp
+@@ -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)
+--- /dev/null
++++ b/drivers/staging/ktap/samples/ffi/printk.kp
+@@ -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")
+--- /dev/null
++++ b/drivers/staging/ktap/samples/ffi/sched_clock.kp
+@@ -0,0 +1,6 @@
++cdef[[
++ unsigned long long sched_clock();
++]]
++
++ret = C.sched_clock()
++print("C.sched_clock returned, value: ", ret)
+--- /dev/null
++++ b/drivers/staging/ktap/samples/game/tetris.kp
+@@ -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()
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/helloworld.kp
+@@ -0,0 +1,3 @@
++#!/usr/bin/env ktap
++
++print("Hello World! I am ktap")
+--- /dev/null
++++ b/drivers/staging/ktap/samples/interrupt/hardirq_time.kp
+@@ -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)
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/interrupt/softirq_time.kp
+@@ -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)
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/io/kprobes-do-sys-open.kp
+@@ -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)
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/io/traceio.kp
+@@ -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")
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/mem/kmalloc-top.kp
+@@ -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()
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/mem/kmem.kp
+@@ -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")
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/profiling/function_profiler.kp
+@@ -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
++# ... |
++#
+--- /dev/null
++++ b/drivers/staging/ktap/samples/profiling/stack_profile.kp
+@@ -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()
++ }
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/schedule/sched_transition.kp
+@@ -0,0 +1,5 @@
++#!/usr/bin/env ktap
++
++trace sched:sched_switch {
++ printf("%s ... ", arg1)
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/schedule/schedtimes.kp
+@@ -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]);
++ }
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/syscalls/errinfo.kp
+@@ -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])
++ }
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/syscalls/execve.kp
+@@ -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))
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/syscalls/opensnoop.kp
+@@ -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
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/syscalls/sctop.kp
+@@ -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)
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/syscalls/syscalls.kp
+@@ -0,0 +1,6 @@
++#!/usr/bin/env ktap
++
++trace syscalls:* {
++ print(cpu(), pid(), execname(), argevent)
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/syscalls/syscalls_count.kp
+@@ -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
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/syscalls/syscalls_count_by_proc.kp
+@@ -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
++
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/syscalls/syslatl.kp
+@@ -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]);
++ }
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/syscalls/syslist.kp
+@@ -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]);
++ }
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/tracepoints/eventcount.kp
+@@ -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
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/tracepoints/eventcount_by_proc.kp
+@@ -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
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/tracepoints/tracepoints.kp
+@@ -0,0 +1,6 @@
++#!/usr/bin/env ktap
++
++trace *:* {
++ print(cpu(), pid(), execname(), argevent)
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/userspace/gcc_unwind.kp
+@@ -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)
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/userspace/glibc_func_hist.kp
+@@ -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
++# ... |
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/userspace/glibc_sdt.kp
+@@ -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)
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/userspace/glibc_trace.kp
+@@ -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)
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/samples/userspace/malloc_free.kp
+@@ -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())
++}
+--- /dev/null
++++ b/drivers/staging/ktap/samples/userspace/malloc_size_hist.kp
+@@ -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)
++}
+--- /dev/null
++++ b/drivers/staging/ktap/test/arg.kp
+@@ -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()
++}
+--- /dev/null
++++ b/drivers/staging/ktap/test/arithmetic.kp
+@@ -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()
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/benchmark/sembench.c
+@@ -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;
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/benchmark/test.sh
+@@ -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
+--- /dev/null
++++ b/drivers/staging/ktap/test/concat.kp
+@@ -0,0 +1,15 @@
++#!/usr/bin/env ktap
++
++function failed() {
++ printf("failed\n");
++ exit(-1);
++}
++
++#----------------------------------------#
++
++a = "123"
++b = "456"
++
++if (a..b != "123456") {
++ failed()
++}
+--- /dev/null
++++ b/drivers/staging/ktap/test/count.kp
+@@ -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()
++}
+--- /dev/null
++++ b/drivers/staging/ktap/test/ffi/.gitignore
+@@ -0,0 +1,2 @@
++cparser_test
++Module.symvers
+--- /dev/null
++++ b/drivers/staging/ktap/test/ffi/Makefile
+@@ -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
+--- /dev/null
++++ b/drivers/staging/ktap/test/ffi/cparser_test.c
+@@ -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;
++}
+--- /dev/null
++++ b/drivers/staging/ktap/test/ffi/ffi_test.kp
+@@ -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")
++}
+--- /dev/null
++++ b/drivers/staging/ktap/test/ffi/ktap_ffi_test.c
+@@ -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);
+--- /dev/null
++++ b/drivers/staging/ktap/test/fibonacci.kp
+@@ -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()
++}
+--- /dev/null
++++ b/drivers/staging/ktap/test/function.kp
+@@ -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();
++}
++
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/if.kp
+@@ -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()
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/kprobe.kp
+@@ -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()
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/kretprobe.kp
+@@ -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()
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/ksym.kp
+@@ -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`)
+--- /dev/null
++++ b/drivers/staging/ktap/test/len.kp
+@@ -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()
++}
++
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/looping.kp
+@@ -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()
++}
+--- /dev/null
++++ b/drivers/staging/ktap/test/pairs.kp
+@@ -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()
++}
+--- /dev/null
++++ b/drivers/staging/ktap/test/ptable.kp
+@@ -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()
++}
++
++
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/run_test.sh
+@@ -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
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/stack_overflow.kp
+@@ -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))
+--- /dev/null
++++ b/drivers/staging/ktap/test/table.kp
+@@ -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()
++}
++
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/timer.kp
+@@ -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()
++}
++
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/tracepoint.kp
+@@ -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()
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/test/zerodivide.kp
+@@ -0,0 +1,5 @@
++#!/usr/bin/env ktap
++
++a = 1/0
++#should not go here
++printf("Failed\n")
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/code.c
+@@ -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 */
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/cparser.h
+@@ -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__ */
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/dump.c
+@@ -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;
++}
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/eventdef.c
+@@ -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);
++ }
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/ffi/cparser.c
+@@ -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();
++}
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/ffi/ctype.c
+@@ -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;
++}
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/ktapc.h
+@@ -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
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/ktapio.c
+@@ -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;
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/lex.c
+@@ -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;
++}
++
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/main.c
+@@ -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;
++}
++
++
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/parser.c
+@@ -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;
++}
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/symbol.c
+@@ -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;
++}
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/symbol.h
+@@ -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
+--- /dev/null
++++ b/drivers/staging/ktap/userspace/util.c
+@@ -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);
++}
++