| 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 |
| @@ -146,4 +146,6 @@ source "drivers/staging/dgnc/Kconfig" |
| |
| source "drivers/staging/dgap/Kconfig" |
| |
| +source "drivers/staging/ktap/Kconfig" |
| + |
| endif # STAGING |
| --- a/drivers/staging/Makefile |
| +++ b/drivers/staging/Makefile |
| @@ -65,3 +65,4 @@ obj-$(CONFIG_XILLYBUS) += xillybus/ |
| obj-$(CONFIG_DGNC) += dgnc/ |
| obj-$(CONFIG_DGAP) += dgap/ |
| obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/ |
| +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); |
| +} |
| + |