summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/staging/Kconfig2
-rw-r--r--drivers/staging/Makefile1
-rw-r--r--drivers/staging/ktap/Kconfig21
-rw-r--r--drivers/staging/ktap/Makefile101
-rw-r--r--drivers/staging/ktap/README.md144
-rw-r--r--drivers/staging/ktap/doc/tutorial.md552
-rw-r--r--drivers/staging/ktap/include/ktap.h169
-rw-r--r--drivers/staging/ktap/include/ktap_opcodes.h240
-rw-r--r--drivers/staging/ktap/include/ktap_types.h674
-rw-r--r--drivers/staging/ktap/interpreter/ktap.c235
-rw-r--r--drivers/staging/ktap/interpreter/library/ansilib.c153
-rw-r--r--drivers/staging/ktap/interpreter/library/baselib.c455
-rw-r--r--drivers/staging/ktap/interpreter/library/kdebug.c450
-rw-r--r--drivers/staging/ktap/interpreter/library/timer.c192
-rw-r--r--drivers/staging/ktap/interpreter/loader.c310
-rw-r--r--drivers/staging/ktap/interpreter/object.c483
-rw-r--r--drivers/staging/ktap/interpreter/opcode.c127
-rw-r--r--drivers/staging/ktap/interpreter/strfmt.c196
-rw-r--r--drivers/staging/ktap/interpreter/table.c1292
-rw-r--r--drivers/staging/ktap/interpreter/transport.c636
-rw-r--r--drivers/staging/ktap/interpreter/tstring.c287
-rw-r--r--drivers/staging/ktap/interpreter/vm.c1369
-rw-r--r--drivers/staging/ktap/scripts/basic/backtrace.kp6
-rw-r--r--drivers/staging/ktap/scripts/basic/event_trigger.kp24
-rw-r--r--drivers/staging/ktap/scripts/basic/event_trigger_ftrace.kp28
-rw-r--r--drivers/staging/ktap/scripts/basic/ftrace.kp6
-rw-r--r--drivers/staging/ktap/scripts/basic/function_time.kp57
-rw-r--r--drivers/staging/ktap/scripts/basic/kretprobe.kp6
-rw-r--r--drivers/staging/ktap/scripts/game/tetris.kp328
-rw-r--r--drivers/staging/ktap/scripts/helloworld.kp3
-rw-r--r--drivers/staging/ktap/scripts/interrupt/hardirq_time.kp25
-rw-r--r--drivers/staging/ktap/scripts/interrupt/softirq_time.kp25
-rw-r--r--drivers/staging/ktap/scripts/io/kprobes-do-sys-open.kp20
-rw-r--r--drivers/staging/ktap/scripts/io/traceio.kp56
-rw-r--r--drivers/staging/ktap/scripts/mem/kmalloc-top.kp17
-rw-r--r--drivers/staging/ktap/scripts/mem/kmem.kp30
-rw-r--r--drivers/staging/ktap/scripts/profiling/function_profiler.kp41
-rw-r--r--drivers/staging/ktap/scripts/profiling/stack_profile.kp26
-rw-r--r--drivers/staging/ktap/scripts/schedule/sched_transition.kp5
-rw-r--r--drivers/staging/ktap/scripts/schedule/schedtimes.kp125
-rw-r--r--drivers/staging/ktap/scripts/syscalls/errinfo.kp145
-rw-r--r--drivers/staging/ktap/scripts/syscalls/sctop.kp14
-rw-r--r--drivers/staging/ktap/scripts/syscalls/syscalls.kp6
-rw-r--r--drivers/staging/ktap/scripts/syscalls/syscalls_count.kp56
-rw-r--r--drivers/staging/ktap/scripts/syscalls/syscalls_count_by_proc.kp24
-rw-r--r--drivers/staging/ktap/scripts/tracepoints/eventcount.kp212
-rw-r--r--drivers/staging/ktap/scripts/tracepoints/eventcount_by_proc.kp59
-rw-r--r--drivers/staging/ktap/scripts/tracepoints/tracepoints.kp6
-rw-r--r--drivers/staging/ktap/scripts/userspace/uprobes-malloc.kp9
-rw-r--r--drivers/staging/ktap/test/aggr_table.kp50
-rw-r--r--drivers/staging/ktap/test/ansi.kp20
-rw-r--r--drivers/staging/ktap/test/arg.kp24
-rw-r--r--drivers/staging/ktap/test/arith.kp27
-rw-r--r--drivers/staging/ktap/test/bench/sembench.c556
-rw-r--r--drivers/staging/ktap/test/bench/test.sh25
-rw-r--r--drivers/staging/ktap/test/concat.kp15
-rw-r--r--drivers/staging/ktap/test/count.kp20
-rw-r--r--drivers/staging/ktap/test/fibonacci.kp36
-rw-r--r--drivers/staging/ktap/test/function.kp88
-rw-r--r--drivers/staging/ktap/test/if.kp24
-rw-r--r--drivers/staging/ktap/test/kprobe.kp14
-rw-r--r--drivers/staging/ktap/test/kretprobe.kp14
-rw-r--r--drivers/staging/ktap/test/len.kp25
-rw-r--r--drivers/staging/ktap/test/looping.kp40
-rw-r--r--drivers/staging/ktap/test/pairs.kp45
-rw-r--r--drivers/staging/ktap/test/run_test.sh58
-rw-r--r--drivers/staging/ktap/test/table.kp71
-rw-r--r--drivers/staging/ktap/test/timer.kp28
-rw-r--r--drivers/staging/ktap/test/tracepoint.kp22
-rw-r--r--drivers/staging/ktap/test/zerodivide.kp5
-rw-r--r--drivers/staging/ktap/userspace/code.c968
-rw-r--r--drivers/staging/ktap/userspace/dump.c187
-rw-r--r--drivers/staging/ktap/userspace/eventdef.c588
-rw-r--r--drivers/staging/ktap/userspace/ktapc.h376
-rw-r--r--drivers/staging/ktap/userspace/ktapio.c108
-rw-r--r--drivers/staging/ktap/userspace/lex.c622
-rw-r--r--drivers/staging/ktap/userspace/main.c609
-rw-r--r--drivers/staging/ktap/userspace/parser.c1910
-rw-r--r--drivers/staging/ktap/userspace/util.c346
80 files changed, 16376 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 0ad4692c2820..500c8fa86133 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4748,6 +4748,13 @@ S: Maintained
F: Documentation/hwmon/k8temp
F: drivers/hwmon/k8temp.c
+KTAP
+M: zhangwei(Jovi) <jovi.zhangwei@gmail.com>
+W: http://www.ktap.org
+L: ktap@freelists.org
+S: Maintained
+F: drivers/staging/ktap/
+
KCONFIG
M: Michal Marek <mmarek@suse.cz>
L: linux-kbuild@vger.kernel.org
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 3bfdaa8d80a9..3b1501b7d894 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -150,4 +150,6 @@ source "drivers/staging/dgnc/Kconfig"
source "drivers/staging/dgap/Kconfig"
+source "drivers/staging/ktap/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index b0d3303b4680..2270ed077bde 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -67,3 +67,4 @@ obj-$(CONFIG_XILLYBUS) += xillybus/
obj-$(CONFIG_DGNC) += dgnc/
obj-$(CONFIG_DGAP) += dgap/
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
+obj-$(CONFIG_KTAP) += ktap/
diff --git a/drivers/staging/ktap/Kconfig b/drivers/staging/ktap/Kconfig
new file mode 100644
index 000000000000..21f8d2ed03b9
--- /dev/null
+++ b/drivers/staging/ktap/Kconfig
@@ -0,0 +1,21 @@
+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
+
diff --git a/drivers/staging/ktap/Makefile b/drivers/staging/ktap/Makefile
new file mode 100644
index 000000000000..e2e54baf36d9
--- /dev/null
+++ b/drivers/staging/ktap/Makefile
@@ -0,0 +1,101 @@
+
+# Do not instrument the tracer itself:
+ifdef CONFIG_FUNCTION_TRACER
+ORIG_CFLAGS := $(KBUILD_CFLAGS)
+KBUILD_CFLAGS = $(subst -pg,,$(ORIG_CFLAGS))
+endif
+
+all: mod ktap
+
+INTP = interpreter
+
+LIBDIR = $(INTP)/library
+
+LIB_OBJS += $(LIBDIR)/baselib.o $(LIBDIR)/kdebug.o $(LIBDIR)/timer.o \
+ $(LIBDIR)/ansilib.o
+
+INTP_OBJS += $(INTP)/ktap.o $(INTP)/loader.o $(INTP)/object.o \
+ $(INTP)/tstring.o $(INTP)/table.o $(INTP)/vm.o \
+ $(INTP)/opcode.o $(INTP)/strfmt.o $(INTP)/transport.o \
+ $(LIB_OBJS)
+
+obj-m += ktapvm.o
+ktapvm-y := $(INTP_OBJS)
+
+KVERSION ?= $(shell uname -r)
+KERNEL_SRC ?= /lib/modules/$(KVERSION)/build
+mod:
+ $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules
+
+modules_install:
+ $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules_install
+
+KTAPC_CFLAGS = -Wall -O2
+
+UDIR = userspace
+
+$(UDIR)/lex.o: $(UDIR)/lex.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/parser.o: $(UDIR)/parser.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/code.o: $(UDIR)/code.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/dump.o: $(UDIR)/dump.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/main.o: $(UDIR)/main.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/util.o: $(UDIR)/util.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/ktapio.o: $(UDIR)/ktapio.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/eventdef.o: $(UDIR)/eventdef.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/opcode.o: $(INTP)/opcode.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/table.o: $(INTP)/table.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/tstring.o: $(INTP)/tstring.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+$(UDIR)/object.o: $(INTP)/object.c
+ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
+
+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)/opcode.o
+KTAPOBJS += $(UDIR)/table.o
+KTAPOBJS += $(UDIR)/tstring.o
+KTAPOBJS += $(UDIR)/object.o
+
+ktap: $(KTAPOBJS)
+ $(QUIET_LINK)$(CC) $(KTAPC_CFLAGS) -o $@ $(KTAPOBJS) -lpthread
+
+KMISC := /lib/modules/$(KVERSION)/ktapvm/
+
+install: mod ktap
+ install -d $(KMISC)
+ install -m 644 -c *.ko /lib/modules/$(KVERSION)/ktapvm/
+ /sbin/depmod -a
+
+load:
+ insmod ktapvm.ko
+
+unload:
+ rmmod ktapvm
+
+test: FORCE
+ cd test; sh ./run_test.sh; cd -
+
+clean:
+ $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
+ $(RM) ktap
+
+PHONY += FORCE
+FORCE:
+
diff --git a/drivers/staging/ktap/README.md b/drivers/staging/ktap/README.md
new file mode 100644
index 000000000000..c8ddd5fa66c6
--- /dev/null
+++ b/drivers/staging/ktap/README.md
@@ -0,0 +1,144 @@
+# 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 scripts/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. function tracing
+
+ 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:0x000773c0 {
+ print("entry:", execname(), argevent)
+ }
+
+ trace probe:/lib/libc.so.6:0x000773c0%return {
+ print("exit:", execname(), argevent)
+ }
+
+8. timer
+
+ tick-1ms {
+ printf("time fired on one cpu\n");
+ }
+
+ profile-2s {
+ printf("time fired on every cpu\n");
+ }
+
+More sample scripts can be found at scripts/ directory.
+
+## Mailing list
+
+ktap@freelists.org
+You can subscribe to ktap mailing list at link (subscribe before posting):
+http://www.freelists.org/list/ktap
+
+
+## Copyright and License
+
+ktap is licensed under GPL v2
+
+Copyright (C) 2012-2013, Jovi Zhangwei <jovi.zhangwei@gmail.com>.
+All rights reserved.
+
+
+## Contribution
+
+ktap is still under active development, so contributions are welcome.
+You are encouraged to report bugs, provide feedback, send feature request,
+or hack on it.
+
+
+## See More
+
+More info can be found at [documentation][tutorial]
+[tutorial]: http://www.ktap.org/doc/tutorial.html
+
diff --git a/drivers/staging/ktap/doc/tutorial.md b/drivers/staging/ktap/doc/tutorial.md
new file mode 100644
index 000000000000..3c32ce77a731
--- /dev/null
+++ b/drivers/staging/ktap/doc/tutorial.md
@@ -0,0 +1,552 @@
+% The ktap Tutorial
+
+# Introduction
+
+ktap is a new scripting dynamic tracing tool for linux
+
+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.
+
+Highlights features:
+
+* 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
+
+
+# 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/)
+
+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
+
+**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 (event_ids, eventfun)**
+
+This function is underly representation of high level tracing primitive.
+event_ids is the id of all events, it's read from
+/sys/kernel/debug/tracing/events/$SYS/$EVENT/id
+
+for multi-events tracing, the event_ids is concatenation of all id, for example:
+ "2 3 4", seperated by blank space.
+
+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.
+
+
+### 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.
+
+EVENTDEF is compatible with perf(see perf-list), with glob match, for example:
+
+ 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
+ *:* trace all tracepoints in system.
+
+All events are based on: /sys/kernel/debug/tracing/events/$SYS/$EVENT
+
+**trace_end { ACTION }**
+
+This is based on kdebug.probe_end function.
+
+## Tracing built-in variable
+
+**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.
+
+
+## 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 then 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][lwn]
+* [ktap introduction in LinuxCon Japan 2013][lcj]
+
+[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
+[lwn]: http://lwn.net/Articles/551314/
+[lcj]: http://events.linuxfoundation.org/sites/events/files/lcjpcojp13_zhangwei.pdf
+
+
+# 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
+
+For more release info, please look at RELEASES.txt in project root directory.
+
+# Sample scripts
+
+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. function tracing
+
+ 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:0x000773c0 {
+ print("entry:", execname(), argevent)
+ }
+
+ trace probe:/lib/libc.so.6:0x000773c0%return {
+ print("exit:", execname(), argevent)
+ }
+
+8. timer
+
+ tick-1ms {
+ printf("time fired on one cpu\n");
+ }
+
+ profile-2s {
+ printf("time fired on every cpu\n");
+ }
+
+More sample scripts can be found at scripts/ directory.
+
+
+# Appendix
+
+Here is the complete syntax of ktap in extended BNF.
+(based on lua syntax: http://www.lua.org/manual/5.1/manual.html#5.1)
+
+ chunk ::= {stat [';']} [laststat [';']
+
+ block ::= chunk
+
+ stat ::= varlist '=' explist |
+ functioncall |
+ { block } |
+ while exp { block } |
+ repeat block until exp |
+ if exp { block {elseif exp { block }} [else block] } |
+ for Name '=' exp ',' exp [',' exp] { block } |
+ for namelist in explist { block } |
+ function funcname funcbody |
+ local function Name funcbody |
+ local namelist ['=' explist]
+
+ laststat ::= return [explist] | break
+
+ funcname ::= Name {'.' Name} [':' Name]
+
+ varlist ::= var {',' var}
+
+ var ::= Name | prefixexp '[' exp ']'| prefixexp '.' Name
+
+ namelist ::= Name {',' Name}
+
+ explist ::= {exp ',' exp
+
+ exp ::= nil | false | true | Number | String | '...' | function |
+ prefixexp | tableconstructor | exp binop exp | unop exp
+
+ prefixexp ::= var | functioncall | '(' exp ')'
+
+ functioncall ::= prefixexp args | prefixexp ':' Name args
+
+ args ::= '(' [explist] ')' | tableconstructor | String
+
+ function ::= function funcbody
+
+ funcbody ::= '(' [parlist] ')' { block }
+
+ parlist ::= namelist [',' '...'] | '...'
+
+ tableconstructor ::= '{' [fieldlist] '}'
+
+ fieldlist ::= field {fieldsep field} [fieldsep]
+
+ field ::= '[' exp ']' '=' exp | Name '=' exp | exp
+
+ fieldsep ::= ',' | ';'
+
+ binop ::= '+' | '-' | '*' | '/' | '^' | '%' | '..' |
+ '<' | '<=' | '>' | '>=' | '==' | '!=' |
+ and | or
+
+ unop ::= '-'
+
diff --git a/drivers/staging/ktap/include/ktap.h b/drivers/staging/ktap/include/ktap.h
new file mode 100644
index 000000000000..076dd4aa6477
--- /dev/null
+++ b/drivers/staging/ktap/include/ktap.h
@@ -0,0 +1,169 @@
+#ifndef __KTAP_H__
+#define __KTAP_H__
+
+#include "ktap_types.h"
+#include "ktap_opcodes.h"
+
+#include <linux/version.h>
+#include <linux/hardirq.h>
+#include <linux/perf_event.h>
+#include <linux/trace_seq.h>
+
+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;
+};
+
+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
+};
+
+#define KTAP_PERCPU_BUFFER_SIZE (3 * PAGE_SIZE)
+
+int gettimeofday_us(void);
+ktap_state *kp_newstate(struct ktap_parm *parm, struct dentry *dir);
+void kp_exit(ktap_state *ks);
+void kp_final_exit(ktap_state *ks);
+ktap_state *kp_newthread(ktap_state *mainthread);
+void kp_exitthread(ktap_state *ks);
+ktap_closure *kp_load(ktap_state *ks, unsigned char *buff);
+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);
+void *kp_percpu_data(int type);
+
+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);
+
+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);
+
+int kp_strfmt(ktap_state *ks, struct trace_seq *seq);
+
+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);
+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);
+
+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;
+}
+
+/* use a special timer context kp_state instead use this recursion approach? */
+DECLARE_PER_CPU(int, kp_recursion_context[PERF_NR_CONTEXTS]);
+
+static __always_inline int get_recursion_context(void)
+{
+ int rctx = trace_get_context_bit();
+
+ if (__this_cpu_read(kp_recursion_context[rctx]))
+ return -1;
+
+ __this_cpu_write(kp_recursion_context[rctx], true);
+ barrier();
+
+ return rctx;
+}
+
+static inline void put_recursion_context(int rctx)
+{
+ barrier();
+ __this_cpu_write(kp_recursion_context[rctx], false);
+}
+
+
+extern 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;
+ }
+}
+
+#define kp_verbose_printf(ks, ...) \
+ if (G(ks)->parm->verbose) \
+ kp_printf(ks, "[verbose] "__VA_ARGS__);
+
+/* get argument operation macro */
+#define kp_arg(ks, n) ((ks)->ci->func + (n))
+#define kp_arg_nr(ks) ((int)(ks->top - (ks->ci->func + 1)))
+
+#define kp_arg_check(ks, narg, type) \
+ do { \
+ if (unlikely(ttypenv(kp_arg(ks, narg)) != type)) { \
+ kp_error(ks, "wrong type of argument %d\n", narg);\
+ return -1; \
+ } \
+ } while (0)
+
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 5, 0)
+#define SPRINT_SYMBOL sprint_symbol_no_offset
+#else
+#define SPRINT_SYMBOL sprint_symbol
+#endif
+
+#endif /* __KTAP_H__ */
diff --git a/drivers/staging/ktap/include/ktap_opcodes.h b/drivers/staging/ktap/include/ktap_opcodes.h
new file mode 100644
index 000000000000..31c558b680f4
--- /dev/null
+++ b/drivers/staging/ktap/include/ktap_opcodes.h
@@ -0,0 +1,240 @@
+#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_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_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_A(i) getarg(i, POS_A, SIZE_A)
+#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
+
+#define GETARG_B(i) getarg(i, POS_B, SIZE_B)
+#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B)
+
+#define GETARG_C(i) getarg(i, POS_C, SIZE_C)
+#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C)
+
+#define GETARG_Bx(i) getarg(i, POS_Bx, SIZE_Bx)
+#define SETARG_Bx(i,v) setarg(i, v, POS_Bx, SIZE_Bx)
+
+#define GETARG_Ax(i) getarg(i, POS_Ax, SIZE_Ax)
+#define SETARG_Ax(i,v) setarg(i, v, POS_Ax, SIZE_Ax)
+
+#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx)
+#define SETARG_sBx(i,b) SETARG_Bx((i), (unsigned int)(b)+MAXARG_sBx)
+
+#define CREATE_ABC(o,a,b,c) (((ktap_instruction)(o))<<POS_OP) \
+ | (((ktap_instruction)(a))<<POS_A) \
+ | (((ktap_instruction)(b))<<POS_B) \
+ | (((ktap_instruction)(c))<<POS_C)
+
+#define CREATE_ABx(o,a,bc) (((ktap_instruction)(o))<<POS_OP) \
+ | (((ktap_instruction)(a))<<POS_A) \
+ | (((ktap_instruction)(bc))<<POS_Bx)
+
+#define CREATE_Ax(o,a) (((ktap_instruction)(o))<<POS_OP) \
+ | (((ktap_instruction)(a))<<POS_Ax)
+
+
+
+/*
+ * ** Macros to operate RK indices
+ * */
+
+/* this bit 1 means constant (0 means register) */
+#define BITRK (1 << (SIZE_B - 1))
+
+/* test whether value is a constant */
+#define ISK(x) ((x) & BITRK)
+
+/* gets the index of the constant */
+#define INDEXK(r) ((int)(r) & ~BITRK)
+
+#define MAXINDEXRK (BITRK - 1)
+
+/* code a constant index as a RK value */
+#define RKASK(x) ((x) | BITRK)
+
+
+/*
+ * ** invalid register that fits in 8 bits
+ * */
+#define NO_REG MAXARG_A
+
+
+/*
+ * ** R(x) - register
+ * ** Kst(x) - constant (in constant table)
+ * ** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
+ * */
+
+
+
+/*
+ * ** masks for instruction properties. The format is:
+ * ** bits 0-1: op mode
+ * ** bits 2-3: C arg mode
+ * ** bits 4-5: B arg mode
+ * ** bit 6: instruction set register A
+ * ** bit 7: operator is a test (next instruction must be a jump)
+ * */
+
+enum OpArgMask {
+ OpArgN, /* argument is not used */
+ OpArgU, /* argument is used */
+ OpArgR, /* argument is a register or a jump offset */
+ OpArgK /* argument is a constant or register/constant */
+};
+
+extern const u8 ktap_opmodes[NUM_OPCODES];
+
+#define getOpMode(m) ((enum OpMode)ktap_opmodes[m] & 3)
+#define getBMode(m) ((enum OpArgMask)(ktap_opmodes[m] >> 4) & 3)
+#define getCMode(m) ((enum OpArgMask)(ktap_opmodes[m] >> 2) & 3)
+#define testAMode(m) (ktap_opmodes[m] & (1 << 6))
+#define testTMode(m) (ktap_opmodes[m] & (1 << 7))
+
+
+/* number of list items to accumulate before a SETLIST instruction */
+#define LFIELDS_PER_FLUSH 50
+
+extern const char *const ktap_opnames[NUM_OPCODES + 1];
+
+#endif /* __KTAP_BYTECODE_H__ */
diff --git a/drivers/staging/ktap/include/ktap_types.h b/drivers/staging/ktap/include/ktap_types.h
new file mode 100644
index 000000000000..cf1cef41b584
--- /dev/null
+++ b/drivers/staging/ktap/include/ktap_types.h
@@ -0,0 +1,674 @@
+#ifndef __KTAP_TYPES_H__
+#define __KTAP_TYPES_H__
+
+/* opcode is copied from lua initially */
+
+#ifdef __KERNEL__
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/semaphore.h>
+#include <linux/wait.h>
+#else
+typedef char u8;
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#endif
+
+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;
+} 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 "2"
+
+#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 int ktap_instruction;
+
+typedef union ktap_gcobject ktap_gcobject;
+
+#define CommonHeader ktap_gcobject *next; u8 tt;
+
+struct ktap_state;
+typedef int (*ktap_cfunction) (struct ktap_state *ks);
+
+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;
+} ktap_string;
+
+#define getstr(ts) (const char *)((ts) + 1)
+#define eqshrstr(a,b) ((a) == (b))
+
+#define svalue(o) getstr(rawtsvalue(o))
+
+
+union _ktap_value {
+ ktap_gcobject *gc; /* collectable objects */
+ void *p; /* light userdata */
+ int b; /* booleans */
+ ktap_cfunction f; /* light C functions */
+ long n; /* numbers */
+};
+
+
+typedef struct ktap_value {
+ union _ktap_value val;
+ int type;
+} ktap_value;
+
+typedef ktap_value * StkId;
+
+
+
+typedef union ktap_udata {
+ struct {
+ CommonHeader;
+ size_t len; /* number of bytes */
+ } uv;
+} ktap_udata;
+
+/*
+ * 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_STACK_MAX_ENTRIES 10
+
+typedef struct ktap_btrace {
+ CommonHeader;
+ unsigned int nr_entries;
+ unsigned long entries[KTAP_STACK_MAX_ENTRIES];
+} ktap_btrace;
+
+#define ktap_closure_header \
+ CommonHeader; u8 nupvalues; ktap_gcobject *gclist
+
+typedef struct ktap_cclosure {
+ ktap_closure_header;
+ ktap_cfunction f;
+ ktap_value upvalue[1]; /* list of upvalues */
+} ktap_cclosure;
+
+
+typedef struct ktap_lclosure {
+ ktap_closure_header;
+ struct ktap_proto *p;
+ struct ktap_upval *upvals[1]; /* list of upvalues */
+} ktap_lclosure;
+
+
+typedef struct ktap_closure {
+ struct ktap_cclosure c;
+ struct ktap_lclosure l;
+} 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 Lua 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_tables
+ */
+typedef union ktap_tkey {
+ struct {
+ union _ktap_value value_;
+ int tt_;
+ struct ktap_tnode *next; /* for chaining */
+ } nk;
+ ktap_value tvk;
+} ktap_tkey;
+
+
+typedef struct ktap_tnode {
+ ktap_value i_val;
+ ktap_tkey i_key;
+} ktap_tnode;
+
+
+typedef struct ktap_table {
+ 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 */
+ ktap_gcobject *gclist;
+} ktap_table;
+
+#define lmod(s,size) ((int)((s) & ((size)-1)))
+
+enum AGGREGATION_TYPE {
+ AGGREGATION_TYPE_COUNT,
+ AGGREGATION_TYPE_MAX,
+ AGGREGATION_TYPE_MIN,
+ AGGREGATION_TYPE_SUM,
+ AGGREGATION_TYPE_AVG
+};
+
+typedef struct ktap_aggrtable {
+ CommonHeader;
+ ktap_table **pcpu_tbl;
+ ktap_gcobject *gclist;
+} ktap_aggrtable;
+
+typedef struct ktap_aggraccval {
+ CommonHeader;
+ int type;
+ int val;
+ int more;
+} ktap_aggraccval;
+
+typedef struct ktap_stringtable {
+ ktap_gcobject **hash;
+ int nuse;
+ int size;
+} ktap_stringtable;
+
+typedef struct ktap_global_state {
+ ktap_stringtable strt; /* hash table for strings */
+ ktap_value registry;
+ unsigned int seed; /* randonized seed for hashes */
+ u8 gcstate; /* state of garbage collector */
+ u8 gckind; /* kind of GC running */
+ u8 gcrunning; /* true if GC is running */
+
+ 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__
+ 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;
+#endif
+ int error;
+} ktap_global_state;
+
+typedef struct ktap_state {
+ CommonHeader;
+ u8 status;
+ ktap_global_state *g;
+ int stop;
+ StkId top;
+ ktap_callinfo *ci;
+ const unsigned long *oldpc;
+ StkId stack_last;
+ StkId stack;
+ int stacksize;
+ ktap_gcobject *openupval;
+ ktap_callinfo baseci;
+
+ int debug;
+ int version;
+ int gcrunning;
+
+ /* list of temp collectable objects, free when thread exit */
+ ktap_gcobject *gclist;
+
+#ifdef __KERNEL__
+ struct ktap_event *current_event;
+ int aggr_accval; /* for temp value storage */
+#endif
+} ktap_state;
+
+
+typedef struct gcheader {
+ CommonHeader;
+} gcheader;
+
+/*
+ * Union of all collectable objects
+ */
+union ktap_gcobject {
+ gcheader gch; /* common header */
+ union ktap_string ts;
+ union ktap_udata u;
+ struct ktap_closure cl;
+ struct ktap_table h;
+ struct ktap_aggrtable ah;
+ struct ktap_aggraccval acc;
+ struct ktap_proto p;
+ struct ktap_upval uv;
+ struct ktap_state th; /* thread */
+ struct ktap_btrace bt; /* thread */
+};
+
+#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))
+
+
+#ifdef __KERNEL__
+#define ktap_assert(s)
+#else
+#define ktap_assert(s)
+#if 0
+#define ktap_assert(s) \
+ do { \
+ if (!s) { \
+ printf("assert failed %s, %d\n", __func__, __LINE__);\
+ exit(0); \
+ } \
+ } while(0)
+#endif
+#endif
+
+#define check_exp(c,e) (e)
+
+
+typedef int ktap_number;
+
+
+#define ktap_number2int(i,n) ((i)=(int)(n))
+
+
+/* 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_TLCL (KTAP_TFUNCTION | (0 << 4)) /* closure */
+#define KTAP_TLCF (KTAP_TFUNCTION | (1 << 4)) /* light C function */
+#define KTAP_TCCL (KTAP_TFUNCTION | (2 << 4)) /* C closure */
+#define KTAP_TUSERDATA 7
+#define KTAP_TTHREAD 8
+
+#define KTAP_NUMTAGS 9
+
+#define KTAP_TPROTO 11
+#define KTAP_TUPVAL 12
+
+#define KTAP_TEVENT 13
+
+#define KTAP_TBTRACE 14
+
+#define KTAP_TAGGRTABLE 15
+#define KTAP_TAGGRACCVAL 16
+#define KTAP_TAGGRVAL 17
+
+#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 bvalue(o) (val_(o).b)
+#define nvalue(o) (val_(o).n)
+#define hvalue(o) (&val_(o).gc->h)
+#define ahvalue(o) (&val_(o).gc->ah)
+#define aggraccvalue(o) (&val_(o).gc->acc)
+#define CLVALUE(o) (&val_(o).gc->cl.l)
+#define clcvalue(o) (&val_(o).gc->cl.c)
+#define clvalue(o) (&val_(o).gc->cl)
+#define rawtsvalue(o) (&val_(o).gc->ts)
+#define pvalue(o) (&val_(o).p)
+#define fvalue(o) (val_(o).f)
+#define rawuvalue(o) (&val_(o).gc->u)
+#define uvalue(o) (&rawuvalue(o)->uv)
+#define evalue(o) (val_(o).p)
+#define btvalue(o) (&val_(o).gc->bt)
+
+#define gcvalue(o) (val_(o).gc)
+
+#define isnil(o) (o->type == KTAP_TNIL)
+#define isboolean(o) (o->type == KTAP_TBOOLEAN)
+#define isfalse(o) (isnil(o) || (isboolean(o) && bvalue(o) == 0))
+
+#define ttisshrstring(o) ((o)->type == KTAP_TSHRSTR)
+#define ttisstring(o) (((o)->type & 0x0F) == KTAP_TSTRING)
+#define ttisnumber(o) ((o)->type == KTAP_TNUMBER)
+#define ttisfunc(o) ((o)->type == KTAP_TFUNCTION)
+#define ttistable(o) ((o)->type == KTAP_TTABLE)
+#define ttisaggrtable(o) ((o)->type == KTAP_TAGGRTABLE)
+#define ttisaggrval(o) ((o)->type == KTAP_TAGGRVAL)
+#define ttisaggracc(o) ((o)->type == KTAP_TAGGRACCVAL)
+#define ttisnil(o) ((o)->type == KTAP_TNIL)
+#define ttisboolean(o) ((o)->type == KTAP_TBOOLEAN)
+#define ttisequal(o1,o2) ((o1)->type == (o2)->type)
+#define ttisevent(o) ((o)->type == KTAP_TEVENT)
+#define ttisbtrace(o) ((o)->type == KTAP_TBTRACE)
+
+#define ttisclone(o) ttisbtrace(o)
+
+
+#define setnilvalue(obj) \
+ { ktap_value *io = (obj); io->val.n = 0; settype(io, KTAP_TNIL); }
+
+#define setbvalue(obj, x) \
+ { ktap_value *io = (obj); io->val.b = (x); settype(io, KTAP_TBOOLEAN); }
+
+#define setnvalue(obj, x) \
+ { ktap_value *io = (obj); io->val.n = (x); settype(io, KTAP_TNUMBER); }
+
+#define setaggrvalue(obj, x) \
+ { ktap_value *io = (obj); io->val.n = (x); settype(io, KTAP_TAGGRVAL); }
+
+#define setaggraccvalue(obj,x) \
+ { ktap_value *io=(obj); \
+ val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TAGGRACCVAL); }
+
+#define setsvalue(obj, x) \
+ { ktap_value *io = (obj); \
+ ktap_string *x_ = (x); \
+ io->val.gc = (ktap_gcobject *)x_; settype(io, x_->tsv.tt); }
+
+#define setcllvalue(obj, x) \
+ { ktap_value *io = (obj); \
+ io->val.gc = (ktap_gcobject *)x; settype(io, KTAP_TLCL); }
+
+#define sethvalue(obj,x) \
+ { ktap_value *io=(obj); \
+ val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TTABLE); }
+
+#define setahvalue(obj,x) \
+ { ktap_value *io=(obj); \
+ val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TAGGRTABLE); }
+
+#define setfvalue(obj,x) \
+ { ktap_value *io=(obj); val_(io).f=(x); settype(io, KTAP_TLCF); }
+
+#define setthvalue(L,obj,x) \
+ { ktap_value *io=(obj); \
+ val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TTHREAD); }
+
+#define setevalue(obj, x) \
+ { ktap_value *io=(obj); val_(io).p = (x); settype(io, KTAP_TEVENT); }
+
+#define setbtvalue(obj,x) \
+ { ktap_value *io=(obj); \
+ val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TBTRACE); }
+
+#define setobj(obj1,obj2) \
+ { const ktap_value *io2=(obj2); ktap_value *io1=(obj1); \
+ io1->val = io2->val; io1->type = io2->type; }
+
+#define rawequalobj(t1, t2) \
+ (ttisequal(t1, t2) && kp_equalobjv(NULL, t1, t2))
+
+#define equalobj(ks, t1, t2) rawequalobj(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))
+
+
+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);
+
+ktap_value *kp_table_set(ktap_state *ks, ktap_table *t, const ktap_value *key);
+ktap_table *kp_table_new(ktap_state *ks);
+const ktap_value *kp_table_getint(ktap_table *t, int key);
+void kp_table_setint(ktap_state *ks, ktap_table *t, int key, ktap_value *v);
+const ktap_value *kp_table_get(ktap_table *t, const ktap_value *key);
+void kp_table_setvalue(ktap_state *ks, ktap_table *t, const ktap_value *key, ktap_value *val);
+void kp_table_resize(ktap_state *ks, ktap_table *t, int nasize, int nhsize);
+void kp_table_resizearray(ktap_state *ks, ktap_table *t, int nasize);
+void kp_table_free(ktap_state *ks, ktap_table *t);
+int kp_table_length(ktap_state *ks, ktap_table *t);
+void kp_table_dump(ktap_state *ks, ktap_table *t);
+void kp_table_clear(ktap_state *ks, ktap_table *t);
+void kp_table_histogram(ktap_state *ks, ktap_table *t);
+int kp_table_next(ktap_state *ks, ktap_table *t, StkId key);
+void kp_table_atomic_inc(ktap_state *ks, ktap_table *t, ktap_value *key, int n);
+void kp_aggraccval_dump(ktap_state *ks, ktap_aggraccval *acc);
+ktap_aggrtable *kp_aggrtable_new(ktap_state *ks);
+ktap_table *kp_aggrtable_synthesis(ktap_state *ks, ktap_aggrtable *ah);
+void kp_aggrtable_dump(ktap_state *ks, ktap_aggrtable *ah);
+void kp_aggrtable_free(ktap_state *ks, ktap_aggrtable *ah);
+void kp_aggrtable_set(ktap_state *ks, ktap_aggrtable *ah,
+ ktap_value *key, ktap_value *val);
+void kp_aggrtable_get(ktap_state *ks, ktap_aggrtable *ah,
+ ktap_value *key, ktap_value *val);
+void kp_aggrtable_histogram(ktap_state *ks, ktap_aggrtable *ah);
+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_newlclosure(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);
+
+int kp_str2d(const char *s, size_t len, ktap_number *result);
+
+#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_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);
+
+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_malloc(ks, size) malloc(size)
+#define kp_free(ks, block) free(block)
+#define kp_reallocv(ks, block, osize, nsize) realloc(block, nsize)
+#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")
+
+#endif /* __KTAP_TYPES_H__ */
+
diff --git a/drivers/staging/ktap/interpreter/ktap.c b/drivers/staging/ktap/interpreter/ktap.c
new file mode 100644
index 000000000000..18d8fc8ec63d
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/ktap.c
@@ -0,0 +1,235 @@
+/*
+ * 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/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.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;
+}
+
+int gettimeofday_us(void)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ return tv.tv_sec * USEC_PER_SEC + tv.tv_usec;
+}
+
+struct dentry *kp_dir_dentry;
+static atomic_t kp_is_running = ATOMIC_INIT(0);
+
+/* 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;
+
+ if (atomic_inc_return(&kp_is_running) != 1) {
+ atomic_dec(&kp_is_running);
+ pr_info("only one ktap thread allow to run\n");
+ return -EBUSY;
+ }
+
+ start_time = gettimeofday_us();
+
+ ks = kp_newstate(parm, kp_dir_dentry);
+ if (unlikely(!ks)) {
+ ret = -ENOEXEC;
+ goto out;
+ }
+
+ file->private_data = ks;
+
+ ret = load_trunk(parm, &buff);
+ if (ret) {
+ pr_err("cannot load file\n");
+ goto out;
+ }
+
+ cl = kp_load(ks, (unsigned char *)buff);
+
+ vfree(buff);
+
+ if (cl) {
+ /* optimize bytecode before excuting */
+ kp_optimize_code(ks, 0, cl->l.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);
+
+ out:
+ atomic_dec(&kp_is_running);
+ 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,
+};
+
+unsigned int kp_stub_exit_instr;
+
+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;
+ }
+
+ SET_OPCODE(kp_stub_exit_instr, OP_EXIT);
+
+ return 0;
+}
+
+static void __exit exit_ktap(void)
+{
+ debugfs_remove_recursive(kp_dir_dentry);
+}
+
+module_init(init_ktap);
+module_exit(exit_ktap);
+
+MODULE_AUTHOR("Jovi Zhangwei <jovi.zhangwei@gmail.com>");
+MODULE_DESCRIPTION("ktap");
+MODULE_LICENSE("GPL");
+
+int kp_max_exec_count = 10000;
+module_param_named(max_exec_count, kp_max_exec_count, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_exec_count, "non-mainthread max instruction execution count");
+
diff --git a/drivers/staging/ktap/interpreter/library/ansilib.c b/drivers/staging/ktap/interpreter/library/ansilib.c
new file mode 100644
index 000000000000..d058373e5f30
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/library/ansilib.c
@@ -0,0 +1,153 @@
+/*
+ * 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.h"
+
+/**
+ * function ansi.clear_screen - Move cursor to top left and clear screen.
+ *
+ * Description: Sends ansi code for moving cursor to top left and then the
+ * ansi code for clearing the screen from the cursor position to the end.
+ */
+
+static int ktap_lib_clear_screen(ktap_state *ks)
+{
+ kp_printf(ks, "\033[1;1H\033[J");
+ return 0;
+}
+
+/**
+ * function ansi.set_color - Set the ansi Select Graphic Rendition mode.
+ * @fg: Foreground color to set.
+ *
+ * Description: Sends ansi code for Select Graphic Rendition mode for the
+ * given forground color. Black (30), Blue (34), Green (32), Cyan (36),
+ * Red (31), Purple (35), Brown (33), Light Gray (37).
+ */
+
+static int ktap_lib_set_color(ktap_state *ks)
+{
+ int fg;
+
+ kp_arg_check(ks, 1, KTAP_TNUMBER);
+
+ fg = nvalue(kp_arg(ks, 1));
+ kp_printf(ks, "\033[%dm", fg);
+ return 0;
+}
+
+/**
+ * function ansi.set_color2 - Set the ansi Select Graphic Rendition mode.
+ * @fg: Foreground color to set.
+ * @bg: Background color to set.
+ *
+ * Description: Sends ansi code for Select Graphic Rendition mode for the
+ * given forground color, Black (30), Blue (34), Green (32), Cyan (36),
+ * Red (31), Purple (35), Brown (33), Light Gray (37) and the given
+ * background color, Black (40), Red (41), Green (42), Yellow (43),
+ * Blue (44), Magenta (45), Cyan (46), White (47).
+ */
+static int ktap_lib_set_color2(ktap_state *ks)
+{
+ int fg, bg;
+
+ kp_arg_check(ks, 1, KTAP_TNUMBER);
+ kp_arg_check(ks, 2, KTAP_TNUMBER);
+
+ fg = nvalue(kp_arg(ks, 1));
+ bg = nvalue(kp_arg(ks, 2));
+ kp_printf(ks, "\033[%d;%dm", fg, bg);
+ return 0;
+}
+
+/**
+ * function ansi.set_color3 - Set the ansi Select Graphic Rendition mode.
+ * @fg: Foreground color to set.
+ * @bg: Background color to set.
+ * @attr: Color attribute to set.
+ *
+ * Description: Sends ansi code for Select Graphic Rendition mode for the
+ * given forground color, Black (30), Blue (34), Green (32), Cyan (36),
+ * Red (31), Purple (35), Brown (33), Light Gray (37), the given
+ * background color, Black (40), Red (41), Green (42), Yellow (43),
+ * Blue (44), Magenta (45), Cyan (46), White (47) and the color attribute
+ * All attributes off (0), Intensity Bold (1), Underline Single (4),
+ * Blink Slow (5), Blink Rapid (6), Image Negative (7).
+ */
+static int ktap_lib_set_color3(ktap_state *ks)
+{
+ int fg, bg, attr;
+
+ kp_arg_check(ks, 1, KTAP_TNUMBER);
+ kp_arg_check(ks, 2, KTAP_TNUMBER);
+ kp_arg_check(ks, 3, KTAP_TNUMBER);
+
+ fg = nvalue(kp_arg(ks, 1));
+ bg = nvalue(kp_arg(ks, 2));
+ attr = nvalue(kp_arg(ks, 3));
+
+ if (attr)
+ kp_printf(ks, "\033[%d;%d;%dm", fg, bg, attr);
+ else
+ kp_printf(ks, "\033[%d;%dm", fg, bg);
+
+ return 0;
+}
+
+/**
+ * function ansi.reset_color - Resets Select Graphic Rendition mode.
+ *
+ * Description: Sends ansi code to reset foreground, background and color
+ * attribute to default values.
+ */
+static int ktap_lib_reset_color(ktap_state *ks)
+{
+ kp_printf(ks, "\033[0;0m");
+ return 0;
+}
+
+/**
+ * function ansi.new_line - Move cursor to new line.
+ *
+ * Description: Sends ansi code new line.
+ */
+static int ktap_lib_new_line (ktap_state *ks)
+{
+ kp_printf(ks, "\12");
+ return 0;
+}
+
+static const ktap_Reg ansi_funcs[] = {
+ {"clear_screen", ktap_lib_clear_screen},
+ {"set_color", ktap_lib_set_color},
+ {"set_color2", ktap_lib_set_color2},
+ {"set_color3", ktap_lib_set_color3},
+ {"reset_color", ktap_lib_reset_color},
+ {"new_line", ktap_lib_new_line},
+ {NULL}
+};
+
+void kp_init_ansilib(ktap_state *ks)
+{
+ kp_register_lib(ks, "ansi", ansi_funcs);
+}
diff --git a/drivers/staging/ktap/interpreter/library/baselib.c b/drivers/staging/ktap/interpreter/library/baselib.c
new file mode 100644
index 000000000000..4bcdc620f9d9
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/library/baselib.c
@@ -0,0 +1,455 @@
+/*
+ * 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/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 "../../include/ktap.h"
+
+static int ktap_lib_next(ktap_state *ks)
+{
+ ktap_table *t = hvalue(ks->top - 2);
+
+ if (kp_table_next(ks, t, ks->top-1)) {
+ ks->top += 1;
+ return 2;
+ } else {
+ ks->top -= 1;
+ setnilvalue(ks->top++);
+ return 1;
+ }
+}
+
+static int ktap_lib_pairs(ktap_state *ks)
+{
+ ktap_value *v = kp_arg(ks, 1);
+ ktap_table *t;
+
+ if (G(ks)->mainthread != ks) {
+ kp_error(ks, "only mainthread can call table pairs\n");
+ return -1;
+ }
+
+ if (ttistable(v)) {
+ t = hvalue(v);
+ } else if (ttisaggrtable(v)) {
+ t = kp_aggrtable_synthesis(ks, ahvalue(v));
+ } else if (isnil(v)) {
+ kp_error(ks, "table is nil in pairs\n");
+ return 0;
+ } else {
+ kp_error(ks, "wrong argument for pairs\n");
+ return 0;
+ }
+
+ setfvalue(ks->top++, ktap_lib_next);
+ sethvalue(ks->top++, t);
+ setnilvalue(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;
+
+ setnvalue(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(KTAP_PERCPU_DATA_BUFFER);
+ trace_seq_init(seq);
+
+ if (kp_strfmt(ks, seq))
+ return 0;
+
+ seq->buffer[seq->len] = '\0';
+ kp_transport_write(ks, seq->buffer, seq->len + 1);
+
+ preempt_enable_notrace();
+ return 0;
+}
+
+#ifdef CONFIG_STACKTRACE
+static int ktap_lib_print_backtrace(ktap_state *ks)
+{
+ kp_transport_print_backtrace(ks);
+ 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;
+ ktap_btrace *bt;
+
+ bt = kp_percpu_data(KTAP_PERCPU_DATA_BTRACE);
+
+ trace.nr_entries = 0;
+ trace.skip = 10;
+ trace.max_entries = KTAP_STACK_MAX_ENTRIES;
+ trace.entries = &bt->entries[0];
+ save_stack_trace(&trace);
+
+ bt->nr_entries = trace.nr_entries;
+ setbtvalue(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)
+{
+ pid_t pid = task_tgid_vnr(current);
+
+ setnvalue(ks->top, (int)pid);
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_tid(ktap_state *ks)
+{
+ pid_t pid = task_pid_vnr(current);
+
+ setnvalue(ks->top, (int)pid);
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_execname(ktap_state *ks)
+{
+ ktap_string *ts = kp_tstring_new(ks, current->comm);
+ setsvalue(ks->top, ts);
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_cpu(ktap_state *ks)
+{
+ setnvalue(ks->top, smp_processor_id());
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_num_cpus(ktap_state *ks)
+{
+ setnvalue(ks->top, num_online_cpus());
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_in_interrupt(ktap_state *ks)
+{
+ int ret = in_interrupt();
+
+ setnvalue(ks->top, ret);
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_arch(ktap_state *ks)
+{
+ setsvalue(ks->top, kp_tstring_new(ks, utsname()->machine));
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_kernel_v(ktap_state *ks)
+{
+ setsvalue(ks->top, kp_tstring_new(ks, utsname()->release));
+ 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';
+ setsvalue(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 (G(ks)->mainthread != ks) {
+ kp_error(ks, "only mainthread can call table historgram\n");
+ return -1;
+ }
+
+ if (ttistable(v))
+ kp_table_histogram(ks, hvalue(v));
+ else if (ttisaggrtable(v))
+ kp_aggrtable_histogram(ks, ahvalue(v));
+
+ return 0;
+}
+
+static int ktap_lib_aggr_table(ktap_state *ks)
+{
+ ktap_aggrtable *ah;
+
+ ah = kp_aggrtable_new(ks);
+ setahvalue(ks->top, ah);
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_aggr_count(ktap_state *ks)
+{
+ setaggrvalue(ks->top, AGGREGATION_TYPE_COUNT);
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_aggr_max(ktap_state *ks)
+{
+ kp_arg_check(ks, 1, KTAP_TNUMBER);
+
+ ks->aggr_accval = nvalue(kp_arg(ks, 1));
+ setaggrvalue(ks->top, AGGREGATION_TYPE_MAX);
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_aggr_min(ktap_state *ks)
+{
+ kp_arg_check(ks, 1, KTAP_TNUMBER);
+
+ ks->aggr_accval = nvalue(kp_arg(ks, 1));
+ setaggrvalue(ks->top, AGGREGATION_TYPE_MIN);
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_aggr_sum(ktap_state *ks)
+{
+ kp_arg_check(ks, 1, KTAP_TNUMBER);
+
+ ks->aggr_accval = nvalue(kp_arg(ks, 1));
+ setaggrvalue(ks->top, AGGREGATION_TYPE_SUM);
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_aggr_avg(ktap_state *ks)
+{
+ kp_arg_check(ks, 1, KTAP_TNUMBER);
+
+ ks->aggr_accval = nvalue(kp_arg(ks, 1));
+ setaggrvalue(ks->top, AGGREGATION_TYPE_AVG);
+ incr_top(ks);
+ return 1;
+}
+
+static int ktap_lib_delete(ktap_state *ks)
+{
+ kp_arg_check(ks, 1, KTAP_TTABLE);
+
+ kp_table_clear(ks, hvalue(kp_arg(ks, 1)));
+ return 0;
+}
+
+static int ktap_lib_gettimeofday_us(ktap_state *ks)
+{
+ setnvalue(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)) {
+ setnilvalue(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:
+ setnvalue(ks->top, *(unsigned int *)RET_VALUE);
+ break;
+ case 8:
+ setnvalue(ks->top, *(unsigned long *)RET_VALUE);
+ break;
+ default:
+ kp_error(ks, "unsupported fetch bytes in curr_task_info\n");
+ setnilvalue(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)
+{
+ setnvalue(ks->top, current->in_iowait);
+ incr_top(ks);
+
+ return 1;
+}
+
+static const ktap_Reg base_funcs[] = {
+ {"pairs", ktap_lib_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},
+ {"execname", ktap_lib_execname},
+ {"cpu", ktap_lib_cpu},
+ {"num_cpus", ktap_lib_num_cpus},
+ {"arch", ktap_lib_arch},
+ {"kernel_v", ktap_lib_kernel_v},
+ {"user_string", ktap_lib_user_string},
+ {"histogram", ktap_lib_histogram},
+ {"aggr_table", ktap_lib_aggr_table},
+ {"count", ktap_lib_aggr_count},
+ {"max", ktap_lib_aggr_max},
+ {"min", ktap_lib_aggr_min},
+ {"sum", ktap_lib_aggr_sum},
+ {"avg", ktap_lib_aggr_avg},
+
+ {"delete", ktap_lib_delete},
+ {"gettimeofday_us", ktap_lib_gettimeofday_us},
+ {"curr_taskinfo", ktap_lib_curr_task_info},
+ {"in_iowait", ktap_lib_in_iowait},
+ {NULL}
+};
+
+void kp_init_baselib(ktap_state *ks)
+{
+ kp_register_lib(ks, NULL, base_funcs);
+}
diff --git a/drivers/staging/ktap/interpreter/library/kdebug.c b/drivers/staging/ktap/interpreter/library/kdebug.c
new file mode 100644
index 000000000000..8bbbc491b6c7
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/library/kdebug.c
@@ -0,0 +1,450 @@
+/*
+ * 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 <asm/syscall.h> //syscall_set_return_value defined here
+#include "../../include/ktap.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);
+ setcllvalue(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(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);
+ }
+}
+
+#if 0
+/* check pt_regs defintion in linux/arch/x86/include/asm/ptrace.h */
+/* support other architecture pt_regs showing */
+static void event_regstr(ktap_state *ks, struct ktap_event *e, StkId ra)
+{
+ struct pt_regs *regs = e->regs;
+ char str[256] = {0};
+
+#if defined(CONFIG_X86_32)
+ snprintf(str, sizeof(str),
+ "{ax: 0x%lx, orig_ax: 0x%lx, bx: 0x%lx, cx: 0x%lx, dx: 0x%lx, "
+ "si: 0x%lx, di: 0x%lx, bp: 0x%lx, ds: 0x%lx, es: 0x%lx, fs: 0x%lx, "
+ "gs: 0x%lx, ip: 0x%lx, cs: 0x%lx, flags: 0x%lx, sp: 0x%lx, ss: 0x%lx}\n",
+ regs->ax, regs->orig_ax, regs->bx, regs->cx, regs->dx,
+ regs->si, regs->di, regs->bp, regs->ds, regs->es, regs->fs,
+ regs->gs, regs->ip, regs->cs, regs->flags, regs->sp, regs->ss);
+#elif defined(CONFIG_X86_64)
+ /* x86_64 pt_regs doesn't have ds, es, fs or gs. */
+ snprintf(str, sizeof(str),
+ "{ax: 0x%lx, orig_ax: 0x%lx, bx: 0x%lx, cx: 0x%lx, dx: 0x%lx, "
+ "si: 0x%lx, di: 0x%lx, r8: 0x%lx, r9: 0x%lx, r10: 0x%lx, r11: 0x%lx, "
+ "r12: 0x%lx, r13: 0x%lx, r14: 0x%lx, r15: 0x%lx, bp: 0x%lx, ip: 0x%lx, "
+ "cs: 0x%lx, flags: 0x%lx, sp: 0x%lx, ss: 0x%lx}\n",
+ regs->ax, regs->orig_ax, regs->bx, regs->cx, regs->dx,
+ regs->si, regs->di, regs->r8, regs->r9, regs->r10, regs->r11,
+ regs->r12, regs->r13, regs->r14, regs->r15, regs->bp, regs->ip,
+ regs->cs, regs->flags, regs->sp, regs->ss);
+#endif
+ setsvalue(ra, kp_tstring_new_local(ks, str));
+}
+#endif
+
+/***************************/
+/* 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;
+ setnvalue(ra, n);
+ return;
+ } else if (field->size == 8) {
+ long n = *(long *)value;
+ setnvalue(ra, n);
+ return;
+ }
+
+ if (!strncmp(field->type, "char", 4)) {
+ setsvalue(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;
+ }
+ }
+
+ setnilvalue(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)
+ *
+ * perf callback already consider on the recursion issue,
+ * so ktap don't need to check again in here.
+ *
+ * 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();
+ if (rctx < 0)
+ return;
+
+ /* 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(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 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 = kp_zalloc(ks, sizeof(*ktap_pevent));
+ 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)
+{
+ const char *ids_str;
+ char *start;
+ ktap_closure *cl;
+ struct task_struct *task = G(ks)->trace_task;
+ char filter_str[128] = {0};
+ char *filter, *ptr1, *sep, *ptr;
+
+ kp_arg_check(ks, 1, KTAP_TSTRING);
+ kp_arg_check(ks, 2, KTAP_TFUNCTION);
+
+ ids_str = svalue(kp_arg(ks, 1));
+ cl = clvalue(kp_arg(ks, 2));
+
+ start = (char *)ids_str;
+
+ again:
+ filter = NULL;
+
+ sep = strchr(start, ',');
+ if (!sep)
+ ptr1 = strchr(start, '/');
+ else
+ ptr1 = strnchr(start, sep - start, '/');
+
+ if (ptr1) {
+ char *ptr2 = strrchr(ptr1 + 1, '/');
+
+ if (ptr2) {
+ memset(filter_str, 0, sizeof(filter_str));
+ strncpy(filter_str, ptr1 + 1, ptr2 - ptr1 - 1);
+ filter = &filter_str[0];
+ } else {
+ kp_printf(ks, "cannot parse ids_str: %s\n", ids_str);
+ return -1;
+ }
+ }
+
+ for (ptr = start; *ptr != ',' && *ptr != '\0' && *ptr != '/'; ptr++) {
+ char token[32] = {0};
+ int id;
+ int i = 0;
+
+ if (*ptr == ' ')
+ continue;
+
+ while (isdigit(*ptr)) {
+ token[i++] = *ptr++;
+ }
+
+ if (!kstrtoint(token, 10, &id)) {
+ struct perf_event_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.type = PERF_TYPE_TRACEPOINT;
+ attr.config = id;
+ 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);
+ }
+ }
+
+ if (sep && (*(sep + 1) != '\0')) {
+ start = sep + 1;
+ goto again;
+ }
+
+ 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) {
+ setcllvalue(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) {
+ setcllvalue(ks->top, G(ks)->trace_end_closure);
+ incr_top(ks);
+ kp_call(ks, ks->top - 1, 0);
+ G(ks)->trace_end_closure = NULL;
+ }
+
+ G(ks)->trace_enabled = 0;
+}
+
+int kp_probe_init(ktap_state *ks)
+{
+ 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) {
+ printk("ktap: cannot lookup ftrace_profile_set_filter "
+ "in kallsyms\n");
+ return;
+ }
+
+ kp_register_lib(ks, "kdebug", kdebuglib_funcs);
+}
+
diff --git a/drivers/staging/ktap/interpreter/library/timer.c b/drivers/staging/ktap/interpreter/library/timer.c
new file mode 100644
index 000000000000..759f917b3658
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/library/timer.c
@@ -0,0 +1,192 @@
+/*
+ * 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.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.
+ *
+ * option: export perf_swevent_put_recursion_context to slove this issue.
+ */
+static enum hrtimer_restart hrtimer_ktap_fn(struct hrtimer *timer)
+{
+ struct hrtimer_ktap *t;
+ ktap_state *ks;
+ int rctx;
+
+ rcu_read_lock_sched_notrace();
+ rctx = get_recursion_context();
+
+ t = container_of(timer, struct hrtimer_ktap, timer);
+
+ ks = kp_newthread(t->ks);
+ setcllvalue(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(rctx);
+ rcu_read_unlock_sched_notrace();
+
+ return HRTIMER_RESTART;
+}
+
+static void set_tick_timer(ktap_state *ks, u64 period, ktap_closure *cl)
+{
+ struct hrtimer_ktap *t;
+
+ t = kp_malloc(ks, sizeof(*t));
+ t->ks = ks;
+ t->cl = cl;
+ t->ns = period;
+
+ INIT_LIST_HEAD(&t->list);
+ list_add(&t->list, &(G(ks)->timers));
+
+ hrtimer_init(&t->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ t->timer.function = hrtimer_ktap_fn;
+ hrtimer_start(&t->timer, ns_to_ktime(period), HRTIMER_MODE_REL);
+}
+
+static void set_profile_timer(ktap_state *ks, u64 period, ktap_closure *cl)
+{
+ struct perf_event_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.type = PERF_TYPE_SOFTWARE;
+ attr.config = PERF_COUNT_SW_CPU_CLOCK;
+ attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
+ PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD;
+ attr.sample_period = period;
+ attr.size = sizeof(attr);
+ attr.disabled = 0;
+
+ kp_perf_event_register(ks, &attr, NULL, NULL, cl);
+}
+
+static int do_tick_profile(ktap_state *ks, int is_tick)
+{
+ const char *str, *tmp;
+ char interval_str[32] = {0};
+ char suffix[10] = {0};
+ int n, i = 0;
+ int factor;
+
+ kp_arg_check(ks, 1, KTAP_TSTRING);
+ kp_arg_check(ks, 2, KTAP_TFUNCTION);
+
+ str = svalue(kp_arg(ks, 1));
+ tmp = str;
+ while (isdigit(*tmp))
+ tmp++;
+
+ strncpy(interval_str, str, tmp - str);
+ if (kstrtoint(interval_str, 10, &n))
+ goto error;
+
+ strncpy(suffix, tmp, 9);
+ while (suffix[i] != ' ' && suffix[i] != '\0')
+ i++;
+
+ suffix[i] = '\0';
+
+ if (!strcmp(suffix, "s") || !strcmp(suffix, "sec"))
+ factor = NSEC_PER_SEC;
+ else if (!strcmp(suffix, "ms") || !strcmp(suffix, "msec"))
+ factor = NSEC_PER_MSEC;
+ else if (!strcmp(suffix, "us") || !strcmp(suffix, "usec"))
+ factor = NSEC_PER_USEC;
+ else
+ goto error;
+
+ if (is_tick)
+ set_tick_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2)));
+ else
+ set_profile_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2)));
+
+ return 0;
+
+ error:
+ kp_error(ks, "cannot parse timer interval: %s\n", str);
+ return -1;
+}
+
+/*
+ * tick-n probes fire on only one CPU per interval.
+ * valid time suffixes: sec/s, msec/ms, usec/us
+ */
+static int ktap_lib_tick(ktap_state *ks)
+{
+ return do_tick_profile(ks, 1);
+}
+
+/*
+ * A profile-n probe fires every fixed interval on every CPU
+ * valid time suffixes: sec/s, msec/ms, usec/us
+ */
+static int ktap_lib_profile(ktap_state *ks)
+{
+ return do_tick_profile(ks, 0);
+}
+
+void kp_exit_timers(ktap_state *ks)
+{
+ struct hrtimer_ktap *t, *tmp;
+ struct list_head *timers_list = &(G(ks)->timers);
+
+ list_for_each_entry_safe(t, tmp, timers_list, list) {
+ hrtimer_cancel(&t->timer);
+ kp_free(ks, t);
+ }
+}
+
+static const ktap_Reg timerlib_funcs[] = {
+ {"profile", ktap_lib_profile},
+ {"tick", ktap_lib_tick},
+ {NULL}
+};
+
+void kp_init_timerlib(ktap_state *ks)
+{
+ kp_register_lib(ks, "timer", timerlib_funcs);
+}
+
diff --git a/drivers/staging/ktap/interpreter/loader.c b/drivers/staging/ktap/interpreter/loader.c
new file mode 100644
index 000000000000..0da54d7ef146
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/loader.c
@@ -0,0 +1,310 @@
+/*
+ * loader.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.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 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 int load_number(struct load_state *S)
+{
+ int 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++)
+ setnilvalue(&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:
+ setnilvalue(o);
+ break;
+ case KTAP_TBOOLEAN:
+ setbvalue(o, READ_CHAR(S));
+ break;
+ case KTAP_TNUMBER:
+ /*
+ * todo: kernel not support fp, check double when
+ * loading
+ */
+ setnvalue(o, READ_NUMBER(S));
+ break;
+ case KTAP_TSTRING:
+ setsvalue(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;
+}
+
+
+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;
+ ktap_lclosure *f;
+ int ret, i;
+
+ S.ks = ks;
+ S.buff = buff;
+ S.pos = 0;
+
+ ret = load_header(&S);
+ if (ret)
+ return NULL;
+
+ cl = kp_newlclosure(ks, 1);
+ if (!cl)
+ return cl;
+
+ /* put closure on the top, prepare to run with this closure */
+ setcllvalue(ks->top, cl);
+ incr_top(ks);
+
+ cl->l.p = kp_newproto(ks);
+ if (load_function(&S, cl->l.p))
+ return NULL;
+
+ if (cl->l.p->sizeupvalues != 1) {
+ ktap_proto *p = cl->l.p;
+ cl = kp_newlclosure(ks, cl->l.p->sizeupvalues);
+ cl->l.p = p;
+ setcllvalue(ks->top - 1, cl);
+ }
+
+ f = &cl->l;
+ for (i = 0; i < f->nupvalues; i++) { /* initialize upvalues */
+ ktap_upval *up = kp_newupval(ks);
+ f->upvals[i] = up;
+ }
+
+ /* set global table as 1st upvalue of 'f' */
+ if (f->nupvalues == 1) {
+ ktap_table *reg = hvalue(&G(ks)->registry);
+ const ktap_value *gt = kp_table_getint(reg, KTAP_RIDX_GLOBALS);
+ setobj(f->upvals[0]->v, gt);
+ }
+
+ verify_code(&S, cl->l.p);
+ return cl;
+}
+
diff --git a/drivers/staging/ktap/interpreter/object.c b/drivers/staging/ktap/interpreter/object.c
new file mode 100644
index 000000000000..6538167a2b87
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/object.c
@@ -0,0 +1,483 @@
+/*
+ * object.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.
+ */
+
+#ifdef __KERNEL__
+#include "../include/ktap.h"
+#else
+#include "../include/ktap_types.h"
+#endif
+
+#ifdef __KERNEL__
+
+#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");
+ }
+
+ return addr;
+}
+
+void kp_free(ktap_state *ks, void *addr)
+{
+ 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");
+ }
+
+ 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");
+ }
+
+ 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_TLCF:
+ 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_TUSERDATA:
+ kp_printf(ks, "USERDATA 0x%lx", (unsigned long)uvalue(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/kallsyms.h>
+
+static void kp_btrace_dump(ktap_state *ks, ktap_btrace *bt)
+{
+ char str[KSYM_SYMBOL_LEN];
+ int i;
+
+ for (i = 0; i < bt->nr_entries; i++) {
+ unsigned long p = bt->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)
+{
+ int i;
+
+ if (bt1->nr_entries != bt2->nr_entries)
+ return 0;
+
+ for (i = 0; i < bt1->nr_entries; i++) {
+ if (bt1->entries[i] != bt2->entries[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_TLCF:
+ kp_printf(ks, "0x%lx", (unsigned long)fvalue(v));
+ break;
+ case KTAP_TSHRSTR:
+ case KTAP_TLNGSTR:
+ kp_puts(ks, svalue(v));
+ break;
+ case KTAP_TUSERDATA:
+ kp_printf(ks, "0x%lx", (unsigned long)uvalue(v));
+ break;
+ case KTAP_TTABLE:
+ kp_table_dump(ks, hvalue(v));
+ break;
+#ifdef __KERNEL__
+ case KTAP_TEVENT:
+ kp_transport_event_write(ks, evalue(v));
+ break;
+ case KTAP_TBTRACE:
+ kp_btrace_dump(ks, btvalue(v));
+ break;
+ case KTAP_TAGGRTABLE:
+ kp_aggrtable_dump(ks, ahvalue(v));
+ break;
+ case KTAP_TAGGRACCVAL:
+ kp_aggraccval_dump(ks, aggraccvalue(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_TLCF:
+ 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_TUSERDATA:
+ if (uvalue(t1) == uvalue(t2))
+ return 1;
+ else if (ks == NULL)
+ return 0;
+ 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.
+ *
+ * Quote from lua mannal:
+ * 2.5.5 - The Length Operator
+ *
+ * The length operator is denoted by the unary operator #.
+ * The length of a string is its number of bytes(that is,
+ * the usual meaning of string length when each character is one byte).
+ *
+ * The length of a table t is defined to be any integer index n
+ * such that t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil,
+ * n can be zero. For a regular array, with non-nil values from 1 to a given n,
+ * its length is exactly that n, the index of its last value. If the array has
+ * "holes" (that is, nil values between other non-nil values), then #t can be
+ * any of the indices that directly precedes a nil value
+ * (that is, it may consider any such nil value as the end of the array).
+ */
+int kp_objlen(ktap_state *ks, const ktap_value *v)
+{
+ switch(v->type) {
+ case KTAP_TTABLE:
+ return kp_table_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;
+ setnilvalue(uv->v);
+ return uv;
+}
+
+static ktap_btrace *kp_newbacktrace(ktap_state *ks, ktap_gcobject **list)
+{
+ ktap_btrace *bt;
+
+ bt = &kp_newobject(ks, KTAP_TBTRACE, sizeof(ktap_btrace), list)->bt;
+ return bt;
+}
+
+void kp_objclone(ktap_state *ks, const ktap_value *o, ktap_value *newo,
+ ktap_gcobject **list)
+{
+ if (ttisbtrace(o)) {
+ ktap_btrace *bt;
+ bt = kp_newbacktrace(ks, list);
+ bt->nr_entries = btvalue(o)->nr_entries;
+ memcpy(&bt->entries[0], &btvalue(o)->entries[0],
+ sizeof(bt->entries));
+ setbtvalue(newo, bt);
+ } else {
+ kp_error(ks, "cannot clone ktap value type %d\n", ttype(o));
+ setnilvalue(newo);
+ }
+}
+
+ktap_closure *kp_newlclosure(ktap_state *ks, int n)
+{
+ ktap_closure *cl;
+
+ cl = (ktap_closure *)kp_newobject(ks, KTAP_TLCL, sizeof(*cl), NULL);
+ cl->l.p = NULL;
+ cl->l.nupvalues = n;
+ while (n--)
+ cl->l.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;
+}
+
+static ktap_udata *newudata(ktap_state *ks, size_t s)
+{
+ ktap_udata *u;
+
+ u = &kp_newobject(ks, KTAP_TUSERDATA, sizeof(ktap_udata) + s, NULL)->u;
+ u->uv.len = s;
+ return u;
+}
+
+void *kp_newuserdata(ktap_state *ks, size_t size)
+{
+ ktap_udata *u;
+
+ u = newudata(ks, size);
+ return u + 1;
+}
+
+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_table_free(ks, (ktap_table *)o);
+ break;
+ case KTAP_TPROTO:
+ free_proto(ks, (ktap_proto *)o);
+ break;
+#ifdef __KERNEL__
+ case KTAP_TAGGRTABLE:
+ kp_aggrtable_free(ks, (ktap_aggrtable *)o);
+ break;
+#endif
+ default:
+ kp_free(ks, o);
+ }
+ o = next;
+ }
+}
+
+void kp_free_all_gcobject(ktap_state *ks)
+{
+ kp_free_gclist(ks, G(ks)->allgc);
+ G(ks)->allgc = NULL;
+}
+
+/******************************************************************************/
+
+/*
+ * make header for precompiled chunks
+ * if you change the code below be sure to update load_header and FORMAT above
+ * and KTAPC_HEADERSIZE in ktap_types.h
+ */
+void kp_header(u8 *h)
+{
+ int x = 1;
+
+ memcpy(h, KTAP_SIGNATURE, sizeof(KTAP_SIGNATURE) - sizeof(char));
+ h += sizeof(KTAP_SIGNATURE) - sizeof(char);
+ *h++ = (u8)VERSION;
+ *h++ = (u8)FORMAT;
+ *h++ = (u8)(*(char*)&x); /* endianness */
+ *h++ = (u8)(sizeof(int));
+ *h++ = (u8)(sizeof(size_t));
+ *h++ = (u8)(sizeof(ktap_instruction));
+ *h++ = (u8)(sizeof(ktap_number));
+ *h++ = (u8)(((ktap_number)0.5) == 0); /* is ktap_number integral? */
+ memcpy(h, KTAPC_TAIL, sizeof(KTAPC_TAIL) - sizeof(char));
+}
+
+
diff --git a/drivers/staging/ktap/interpreter/opcode.c b/drivers/staging/ktap/interpreter/opcode.c
new file mode 100644
index 000000000000..2f44855fcad2
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/opcode.c
@@ -0,0 +1,127 @@
+/*
+ * 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",
+ "SETUPVAL",
+ "SETTABLE",
+ "SETTABLE_INCR",
+ "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, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */
+ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */
+ ,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 */
+};
+
+
diff --git a/drivers/staging/ktap/interpreter/strfmt.c b/drivers/staging/ktap/interpreter/strfmt.c
new file mode 100644
index 000000000000..2c1e7618fef4
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/strfmt.c
@@ -0,0 +1,196 @@
+/*
+ * strfmt.c - printf implementation
+ *
+ * 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/ctype.h>
+#include <linux/kallsyms.h>
+#include "../include/ktap.h"
+
+/* 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_strfmt(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 (isnil(v)) {
+ trace_seq_puts(seq, "nil");
+ return 0;
+ }
+
+ if (ttisevent(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;
+}
+
diff --git a/drivers/staging/ktap/interpreter/table.c b/drivers/staging/ktap/interpreter/table.c
new file mode 100644
index 000000000000..4e83947ba02d
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/table.c
@@ -0,0 +1,1292 @@
+/*
+ * table.c - ktap table data structure manipulation function
+ *
+ * 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.
+ */
+
+#ifdef __KERNEL__
+#include "../include/ktap.h"
+#include <linux/spinlock.h>
+#include <linux/kallsyms.h>
+#include <linux/sort.h>
+#else
+#include "../include/ktap_types.h"
+
+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
+
+
+#ifdef __KERNEL__
+#define kp_table_lock_init(t) \
+ do { \
+ t->lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; \
+ } while (0)
+#define kp_table_lock(t) \
+ do { \
+ local_irq_save(flags); \
+ arch_spin_lock(&t->lock); \
+ } while (0)
+#define kp_table_unlock(t) \
+ do { \
+ arch_spin_unlock(&t->lock); \
+ local_irq_restore(flags); \
+ } while (0)
+
+#else
+#define kp_table_lock_init(t)
+#define kp_table_lock(t)
+#define kp_table_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 */
+ {{NILCONSTANT, NULL}}, /* 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.nk.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_table *t, int key, ktap_value *v);
+static ktap_value *table_set(ktap_state *ks, ktap_table *t,
+ const ktap_value *key);
+static void setnodevector(ktap_state *ks, ktap_table *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];
+}
+
+
+ktap_table *kp_table_new(ktap_state *ks)
+{
+ ktap_table *t = &kp_newobject(ks, KTAP_TTABLE, sizeof(ktap_table),
+ NULL)->h;
+ t->flags = (u8)(~0);
+ t->array = NULL;
+ t->sizearray = 0;
+ t->node = (ktap_tnode *)dummynode;
+ t->gclist = NULL;
+ setnodevector(ks, t, 0);
+
+ kp_table_lock_init(t);
+ return t;
+}
+
+static const ktap_value *table_getint(ktap_table *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 (ttisnumber(gkey(n)) && nvalue(gkey(n)) == key)
+ return gval(n);
+ else
+ n = gnext(n);
+ } while (n);
+
+ return ktap_nilobject;
+}
+
+const ktap_value *kp_table_getint(ktap_table *t, int key)
+{
+ const ktap_value *val;
+ unsigned long __maybe_unused flags;
+
+ kp_table_lock(t);
+ val = table_getint(t, key);
+ kp_table_unlock(t);
+
+ return val;
+}
+
+static ktap_tnode *mainposition (const ktap_table *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_TLCF:
+ return hashpointer(t, fvalue(key));
+ case KTAP_TBTRACE:
+ /* use first entry as hash key, cannot use gcvalue as key */
+ return hashpointer(t, btvalue(key)->entries[0]);
+ default:
+ return hashpointer(t, gcvalue(key));
+ }
+}
+
+static int arrayindex(const ktap_value *key)
+{
+ if (ttisnumber(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_table *t, StkId key)
+{
+ int i;
+
+ if (ttisnil(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_table_next(ktap_state *ks, ktap_table *t, StkId key)
+{
+ unsigned long __maybe_unused flags;
+ int i;
+
+ kp_table_lock(t);
+
+ i = findindex(ks, t, key); /* find original element */
+
+ for (i++; i < t->sizearray; i++) { /* try first array part */
+ if (!ttisnil(&t->array[i])) { /* a non-nil value? */
+ setnvalue(key, i+1);
+ setobj(key+1, &t->array[i]);
+ kp_table_unlock(t);
+ return 1;
+ }
+ }
+
+ for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */
+ if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */
+ setobj(key, gkey(gnode(t, i)));
+ setobj(key+1, gval(gnode(t, i)));
+ kp_table_unlock(t);
+ return 1;
+ }
+ }
+
+ kp_table_unlock(t);
+ return 0; /* no more elements */
+}
+
+
+
+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_table *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 (!ttisnil(&t->array[i-1]))
+ lc++;
+ }
+ nums[lg] += lc;
+ ause += lc;
+ }
+ return ause;
+}
+
+static int numusehash(const ktap_table *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 (!isnil(gval(n))) {
+ ause += countint(gkey(n), nums);
+ totaluse++;
+ }
+ }
+
+ *pnasize += ause;
+ return totaluse;
+}
+
+
+static void setarrayvector(ktap_state *ks, ktap_table *t, int size)
+{
+ int i;
+
+ kp_realloc(ks, t->array, t->sizearray, size, ktap_value);
+ for (i = t->sizearray; i < size; i++)
+ setnilvalue(&t->array[i]);
+
+ t->sizearray = size;
+}
+
+static void setnodevector(ktap_state *ks, ktap_table *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));
+ for (i = 0; i < size; i++) {
+ ktap_tnode *n = gnode(t, i);
+ gnext(n) = NULL;
+ setnilvalue(gkey(n));
+ setnilvalue(gval(n));
+ }
+ }
+
+ t->lsizenode = (u8)lsize;
+ t->lastfree = gnode(t, size); /* all positions are free */
+}
+
+static void table_resize(ktap_state *ks, ktap_table *t, int nasize, int nhsize)
+{
+ int i;
+ int oldasize = t->sizearray;
+ int oldhsize = t->lsizenode;
+ ktap_tnode *nold = t->node; /* save old hash ... */
+
+#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 (!ttisnil(&t->array[i]))
+ table_setint(ks, t, i + 1, &t->array[i]);
+ }
+
+ /* shrink array */
+ kp_realloc(ks, t->array, oldasize, nasize, ktap_value);
+ }
+
+ /* re-insert elements from hash part */
+ for (i = twoto(oldhsize) - 1; i >= 0; i--) {
+ ktap_tnode *old = nold+i;
+ if (!ttisnil(gval(old))) {
+ /*
+ * doesn't need barrier/invalidate cache, as entry was
+ * already present in the table
+ */
+ setobj(table_set(ks, t, gkey(old)), gval(old));
+ }
+ }
+
+ if (!isdummy(nold))
+ kp_free(ks, nold); /* free old array */
+}
+
+void kp_table_resize(ktap_state *ks, ktap_table *t, int nasize, int nhsize)
+{
+ unsigned long __maybe_unused flags;
+
+ kp_table_lock(t);
+ table_resize(ks, t, nasize, nhsize);
+ kp_table_unlock(t);
+}
+
+void kp_table_resizearray(ktap_state *ks, ktap_table *t, int nasize)
+{
+ unsigned long __maybe_unused flags;
+ int nsize;
+
+ kp_table_lock(t);
+
+ nsize = isdummy(t->node) ? 0 : sizenode(t);
+ table_resize(ks, t, nasize, nsize);
+
+ kp_table_unlock(t);
+}
+
+static void rehash(ktap_state *ks, ktap_table *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_table *t)
+{
+ while (t->lastfree > t->node) {
+ t->lastfree--;
+ if (isnil(gkey(t->lastfree)))
+ return t->lastfree;
+ }
+ return NULL; /* could not find a free place */
+}
+
+
+static ktap_value *table_newkey(ktap_state *ks, ktap_table *t,
+ const ktap_value *key)
+{
+ ktap_tnode *mp;
+ ktap_value newkey;
+
+ mp = mainposition(t, key);
+ if (!isnil(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 */
+ /* whatever called 'newkey' take care of TM cache and GC barrier */
+ return table_set(ks, t, key); /* insert key into grown table */
+ }
+
+ othern = mainposition(t, gkey(mp));
+ if (othern != mp) { /* is colliding node out of its main position? */
+ /* yes; move colliding node into free position */
+ while (gnext(othern) != mp)
+ othern = gnext(othern); /* find previous */
+ gnext(othern) = n; /* redo the chain with `n' in place of `mp' */
+ *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */
+ gnext(mp) = NULL; /* now `mp' is free */
+ setnilvalue(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 (ttisclone(key))
+ kp_objclone(ks, key, &newkey, &t->gclist);
+ else
+ newkey = *key;
+
+ setobj(gkey(mp), &newkey);
+ return gval(mp);
+}
+
+
+/*
+ * search function for short strings
+ */
+static const ktap_value *table_getstr(ktap_table *t, ktap_string *key)
+{
+ ktap_tnode *n = hashstr(t, key);
+
+ do { /* check whether `key' is somewhere in the chain */
+ if (ttisshrstring(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_table *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_table_get(ktap_table *t, const ktap_value *key)
+{
+ const ktap_value *val;
+ unsigned long __maybe_unused flags;
+
+ kp_table_lock(t);
+ val = table_get(t, key);
+ kp_table_unlock(t);
+
+ return val;
+}
+
+static ktap_value *table_set(ktap_state *ks, ktap_table *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_table_setvalue(ktap_state *ks, ktap_table *t,
+ const ktap_value *key, ktap_value *val)
+{
+ unsigned long __maybe_unused flags;
+
+ if (isnil(key)) {
+ kp_printf(ks, "table index is nil\n");
+ kp_exit(ks);
+ return;
+ }
+
+ kp_table_lock(t);
+ setobj(table_set(ks, t, key), val);
+ kp_table_unlock(t);
+}
+
+static void table_setint(ktap_state *ks, ktap_table *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;
+ setnvalue(&k, key);
+ cell = table_newkey(ks, t, &k);
+ }
+
+ setobj(cell, v);
+}
+
+void kp_table_setint(ktap_state *ks, ktap_table *t, int key, ktap_value *val)
+{
+ unsigned long __maybe_unused flags;
+
+ kp_table_lock(t);
+ table_setint(ks, t, key, val);
+ kp_table_unlock(t);
+}
+
+void kp_table_atomic_inc(ktap_state *ks, ktap_table *t, ktap_value *key, int n)
+{
+ unsigned long __maybe_unused flags;
+ ktap_value *v;
+
+ if (isnil(key)) {
+ kp_printf(ks, "table index is nil\n");
+ kp_exit(ks);
+ return;
+ }
+
+ kp_table_lock(t);
+
+ v = table_set(ks, t, key);
+ if (isnil(v)) {
+ setnvalue(v, n);
+ } else
+ setnvalue(v, nvalue(v) + n);
+
+ kp_table_unlock(t);
+}
+
+int kp_table_length(ktap_state *ks, ktap_table *t)
+{
+ unsigned long __maybe_unused flags;
+ int i, len = 0;
+
+ kp_table_lock(t);
+
+ for (i = 0; i < t->sizearray; i++) {
+ ktap_value *v = &t->array[i];
+
+ if (isnil(v))
+ continue;
+ len++;
+ }
+
+ for (i = 0; i < sizenode(t); i++) {
+ ktap_tnode *n = &t->node[i];
+
+ if (isnil(gkey(n)))
+ continue;
+
+ len++;
+ }
+
+ kp_table_unlock(t);
+ return len;
+}
+
+void kp_table_free(ktap_state *ks, ktap_table *t)
+{
+ if (t->sizearray > 0)
+ kp_free(ks, t->array);
+ if (!isdummy(t->node))
+ kp_free(ks, t->node);
+
+ kp_free_gclist(ks, t->gclist);
+ kp_free(ks, t);
+}
+
+void kp_table_dump(ktap_state *ks, ktap_table *t)
+{
+ int i, count = 0;
+
+ kp_puts(ks, "{");
+ for (i = 0; i < t->sizearray; i++) {
+ ktap_value *v = &t->array[i];
+
+ if (isnil(v))
+ continue;
+
+ if (count)
+ kp_puts(ks, ", ");
+
+ kp_printf(ks, "(%d: ", i + 1);
+ kp_showobj(ks, v);
+ kp_puts(ks, ")");
+ count++;
+ }
+
+ for (i = 0; i < sizenode(t); i++) {
+ ktap_tnode *n = &t->node[i];
+
+ if (isnil(gkey(n)))
+ continue;
+
+ if (count)
+ kp_puts(ks, ", ");
+
+ kp_puts(ks, "(");
+ kp_showobj(ks, gkey(n));
+ kp_puts(ks, ": ");
+ kp_showobj(ks, gval(n));
+ kp_puts(ks, ")");
+ count++;
+ }
+ kp_puts(ks, "}");
+}
+
+/*
+ * 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_table_clear(ktap_state *ks, ktap_table *t)
+{
+ unsigned long __maybe_unused flags;
+ int i;
+
+ kp_table_lock(t);
+
+ for (i = 0; i < t->sizearray; i++) {
+ ktap_value *v = &t->array[i];
+
+ if (isnil(v))
+ continue;
+
+ setnilvalue(v);
+ }
+
+ for (i = 0; i < sizenode(t); i++) {
+ ktap_tnode *n = &t->node[i];
+
+ if (isnil(gkey(n)))
+ continue;
+
+ setnilvalue(gkey(n));
+ setnilvalue(gval(n));
+ }
+
+ kp_table_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;
+}
+
+static int kp_aggracc_read(ktap_aggraccval *acc);
+
+/* histogram: key should be number or string, value must be number */
+static void table_histdump(ktap_state *ks, ktap_table *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_table_lock(t);
+
+ for (i = 0; i < t->sizearray; i++) {
+ ktap_value *v = &t->array[i];
+
+ if (isnil(v))
+ continue;
+
+ if (ttisnumber(v))
+ num = nvalue(v);
+ else if (ttisaggracc(v))
+ num = kp_aggracc_read(aggraccvalue(v));
+ else {
+ kp_table_unlock(t);
+ goto error;
+ }
+
+ setnvalue(&thr[count].key, i + 1);
+ setnvalue(&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 (isnil(gkey(n)))
+ continue;
+
+ if (ttisnumber(v))
+ num = nvalue(v);
+ else if (ttisaggracc(v))
+ num = kp_aggracc_read(aggraccvalue(v));
+ else {
+ kp_table_unlock(t);
+ goto error;
+ }
+
+ setobj(&thr[count].key, gkey(n));
+ setnvalue(&thr[count].val, num);
+ count++;
+ total += num;
+ }
+
+ kp_table_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 (ttisnumber(&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 (ttisstring(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 (ttisnumber(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_table_histogram(ktap_state *ks, ktap_table *t)
+{
+ kp_printf(ks, "%32s%s%s\n", "value ", DISTRIBUTION_STR, " count");
+ table_histdump(ks, t, HISTOGRAM_DEFAULT_TOP_NUM);
+}
+
+/*
+ * Aggregation Table
+ */
+
+static ktap_table *table_new2(ktap_state *ks, ktap_gcobject **list)
+{
+ ktap_table *t = &kp_newobject(ks, KTAP_TTABLE, sizeof(ktap_table),
+ list)->h;
+ t->flags = (u8)(~0);
+ t->array = NULL;
+ t->sizearray = 0;
+ t->node = (ktap_tnode *)dummynode;
+ t->gclist = NULL;
+ setnodevector(ks, t, 0);
+
+ kp_table_lock_init(t);
+ return t;
+}
+
+static int kp_aggracc_read(ktap_aggraccval *acc)
+{
+ switch (acc->type) {
+ case AGGREGATION_TYPE_COUNT:
+ case AGGREGATION_TYPE_MAX:
+ case AGGREGATION_TYPE_MIN:
+ case AGGREGATION_TYPE_SUM:
+ return acc->val;
+ case AGGREGATION_TYPE_AVG:
+ return acc->val / acc->more;
+ default:
+ return 0;
+ }
+
+}
+
+void kp_aggraccval_dump(ktap_state *ks, ktap_aggraccval *acc)
+{
+ switch (acc->type) {
+ case AGGREGATION_TYPE_COUNT:
+ case AGGREGATION_TYPE_MAX:
+ case AGGREGATION_TYPE_MIN:
+ case AGGREGATION_TYPE_SUM:
+ kp_printf(ks, "%d", acc->val);
+ break;
+ case AGGREGATION_TYPE_AVG:
+ kp_printf(ks, "%d", acc->val / acc->more);
+ break;
+ default:
+ break;
+ }
+}
+
+static void synth_acc(ktap_aggraccval *acc1, ktap_aggraccval *acc2)
+{
+ switch (acc1->type) {
+ case AGGREGATION_TYPE_COUNT:
+ acc2->val += acc1->val;
+ break;
+ case AGGREGATION_TYPE_MAX:
+ acc2->val = max(acc1->val, acc2->val);
+ break;
+ case AGGREGATION_TYPE_MIN:
+ acc2->val = min(acc1->val, acc2->val);
+ break;
+ case AGGREGATION_TYPE_SUM:
+ acc2->val += acc1->val;
+ break;
+ case AGGREGATION_TYPE_AVG:
+ acc2->val += acc1->val;
+ acc2->more += acc1->more;
+ break;
+ default:
+ break;
+ }
+}
+
+static ktap_aggraccval *get_accval(ktap_state *ks, int type,
+ ktap_gcobject **list)
+{
+ ktap_aggraccval *acc;
+
+ acc = &kp_newobject(ks, KTAP_TAGGRACCVAL, sizeof(ktap_aggraccval),
+ list)->acc;
+ acc->type = type;
+ acc->val = 0;
+ acc->more = 0;
+ return acc;
+}
+
+static void synth_accval(ktap_state *ks, ktap_value *o1, ktap_value *o2,
+ ktap_gcobject **list)
+{
+ ktap_aggraccval *acc;
+
+ if (isnil(o2)) {
+ acc = get_accval(ks, aggraccvalue(o1)->type, list);
+ acc->val = aggraccvalue(o1)->val;
+ acc->more = aggraccvalue(o1)->more;
+ setaggraccvalue(o2, acc);
+ return;
+ }
+
+ synth_acc(aggraccvalue(o1), aggraccvalue(o2));
+}
+
+static void move_table(ktap_state *ks, ktap_table *t1, ktap_table *t2)
+{
+ ktap_value *newv;
+ ktap_value n;
+ int i;
+
+ for (i = 0; i < t1->sizearray; i++) {
+ ktap_value *v = &t1->array[i];
+
+ if (isnil(v))
+ continue;
+
+ setnvalue(&n, i);
+
+ newv = table_set(ks, t2, &n);
+ synth_accval(ks, v, newv, &t2->gclist);
+ }
+
+ for (i = 0; i < sizenode(t1); i++) {
+ ktap_tnode *node = &t1->node[i];
+
+ if (isnil(gkey(node)))
+ continue;
+
+ newv = table_set(ks, t2, gkey(node));
+ synth_accval(ks, gval(node), newv, &t2->gclist);
+ }
+}
+
+ktap_table *kp_aggrtable_synthesis(ktap_state *ks, ktap_aggrtable *ah)
+{
+ ktap_table *synth_tbl;
+ int cpu;
+
+ synth_tbl = table_new2(ks, &ah->gclist);
+
+ for_each_possible_cpu(cpu) {
+ ktap_table **t = per_cpu_ptr(ah->pcpu_tbl, cpu);
+ move_table(ks, *t, synth_tbl);
+ }
+
+ return synth_tbl;
+}
+
+void kp_aggrtable_dump(ktap_state *ks, ktap_aggrtable *ah)
+{
+ kp_table_dump(ks, kp_aggrtable_synthesis(ks, ah));
+}
+
+ktap_aggrtable *kp_aggrtable_new(ktap_state *ks)
+{
+ ktap_aggrtable *ah;
+ int cpu;
+
+ ah = &kp_newobject(ks, KTAP_TAGGRTABLE, sizeof(ktap_aggrtable),
+ NULL)->ah;
+ ah->pcpu_tbl = alloc_percpu(ktap_table *);
+ ah->gclist = NULL;
+
+ for_each_possible_cpu(cpu) {
+ ktap_table **t = per_cpu_ptr(ah->pcpu_tbl, cpu);
+ *t = table_new2(ks, &ah->gclist);
+ }
+
+ return ah;
+}
+
+void kp_aggrtable_free(ktap_state *ks, ktap_aggrtable *ah)
+{
+ free_percpu(ah->pcpu_tbl);
+ kp_free_gclist(ks, ah->gclist);
+ kp_free(ks, ah);
+}
+
+static
+void handle_aggr_count(ktap_state *ks, ktap_aggrtable *ah, ktap_value *key)
+{
+ ktap_table *t = *__this_cpu_ptr(ah->pcpu_tbl);
+ ktap_value *v = table_set(ks, t, key);
+ ktap_aggraccval *acc;
+
+ if (isnil(v)) {
+ acc = get_accval(ks, AGGREGATION_TYPE_COUNT, &t->gclist);
+ acc->val = 1;
+ setaggraccvalue(v, acc);
+ return;
+ }
+
+ acc = aggraccvalue(v);
+ acc->val += 1;
+}
+
+static
+void handle_aggr_max(ktap_state *ks, ktap_aggrtable *ah, ktap_value *key)
+{
+ ktap_table *t = *__this_cpu_ptr(ah->pcpu_tbl);
+ ktap_value *v = table_set(ks, t, key);
+ ktap_aggraccval *acc;
+
+ if (isnil(v)) {
+ acc = get_accval(ks, AGGREGATION_TYPE_MAX, &t->gclist);
+ acc->val = ks->aggr_accval;
+ setaggraccvalue(v, acc);
+ return;
+ }
+
+ acc = aggraccvalue(v);
+ acc->val = max(acc->val, ks->aggr_accval);
+}
+
+static
+void handle_aggr_min(ktap_state *ks, ktap_aggrtable *ah, ktap_value *key)
+{
+ ktap_table *t = *__this_cpu_ptr(ah->pcpu_tbl);
+ ktap_value *v = table_set(ks, t, key);
+ ktap_aggraccval *acc;
+
+ if (isnil(v)) {
+ acc = get_accval(ks, AGGREGATION_TYPE_MIN, &t->gclist);
+ acc->val = ks->aggr_accval;
+ setaggraccvalue(v, acc);
+ return;
+ }
+
+ acc = aggraccvalue(v);
+ acc->val = min(acc->val, ks->aggr_accval);
+}
+
+static
+void handle_aggr_sum(ktap_state *ks, ktap_aggrtable *ah, ktap_value *key)
+{
+ ktap_table *t = *__this_cpu_ptr(ah->pcpu_tbl);
+ ktap_value *v = table_set(ks, t, key);
+ ktap_aggraccval *acc;
+
+ if (isnil(v)) {
+ acc = get_accval(ks, AGGREGATION_TYPE_SUM, &t->gclist);
+ acc->val = ks->aggr_accval;
+ setaggraccvalue(v, acc);
+ return;
+ }
+
+ acc = aggraccvalue(v);
+ acc->val += ks->aggr_accval;
+}
+
+static
+void handle_aggr_avg(ktap_state *ks, ktap_aggrtable *ah, ktap_value *key)
+{
+ ktap_table *t = *__this_cpu_ptr(ah->pcpu_tbl);
+ ktap_value *v = table_set(ks, t, key);
+ ktap_aggraccval *acc;
+
+ if (isnil(v)) {
+ acc = get_accval(ks, AGGREGATION_TYPE_AVG, &t->gclist);
+ acc->val = ks->aggr_accval;
+ acc->more = 1;
+ setaggraccvalue(v, acc);
+ return;
+ }
+
+ acc = aggraccvalue(v);
+ acc->val += ks->aggr_accval;
+ acc->more++;
+}
+
+typedef void (*aggr_func_t)(ktap_state *ks, ktap_aggrtable *ah, ktap_value *k);
+static aggr_func_t kp_aggregation_handler[] = {
+ handle_aggr_count,
+ handle_aggr_max,
+ handle_aggr_min,
+ handle_aggr_sum,
+ handle_aggr_avg
+};
+
+void kp_aggrtable_set(ktap_state *ks, ktap_aggrtable *ah,
+ ktap_value *key, ktap_value *val)
+{
+ if (unlikely(!ttisaggrval(val))) {
+ kp_error(ks, "set invalid value to aggregation table\n");
+ return;
+ }
+
+ kp_aggregation_handler[nvalue(val)](ks, ah, key);
+}
+
+
+void kp_aggrtable_get(ktap_state *ks, ktap_aggrtable *ah, ktap_value *key,
+ ktap_value *val)
+{
+ ktap_aggraccval acc; /* in stack */
+ const ktap_value *v;
+ int cpu;
+
+ acc.val = -1;
+ acc.more = -1;
+
+ for_each_possible_cpu(cpu) {
+ ktap_table **t = per_cpu_ptr(ah->pcpu_tbl, cpu);
+
+ v = table_get(*t, key);
+ if (isnil(v))
+ continue;
+
+ if (acc.more == -1) {
+ acc = *aggraccvalue(v);
+ continue;
+ }
+
+ synth_acc(aggraccvalue(v), &acc);
+ }
+
+ if (acc.more == -1) {
+ setnilvalue(val);
+ } else {
+ setnvalue(val, kp_aggracc_read(&acc));
+ }
+}
+
+void kp_aggrtable_histogram(ktap_state *ks, ktap_aggrtable *ah)
+{
+ kp_table_histogram(ks, kp_aggrtable_synthesis(ks, ah));
+}
+#endif
diff --git a/drivers/staging/ktap/interpreter/transport.c b/drivers/staging/ktap/interpreter/transport.c
new file mode 100644
index 000000000000..4cd3662b792a
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/transport.c
@@ -0,0 +1,636 @@
+/*
+ * 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 "../include/ktap.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)
+
+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,
+};
+
+/*
+ * print_backtrace maybe called from ktap mainthread, so be
+ * care on race with event closure thread.
+ *
+ * preempt disabled in ring_buffer_lock_reserve
+ *
+ * The implementation is similar with funtion __ftrace_trace_stack.
+ */
+void kp_transport_print_backtrace(ktap_state *ks)
+{
+ struct ring_buffer *buffer = G(ks)->buffer;
+ struct ring_buffer_event *event;
+ struct trace_entry *entry;
+ int size;
+
+ size = KTAP_STACK_MAX_ENTRIES * sizeof(unsigned long);
+ event = ring_buffer_lock_reserve(buffer, sizeof(*entry) + size);
+ if (!event) {
+ 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 = 10;
+ trace.max_entries = KTAP_STACK_MAX_ENTRIES;
+ trace.entries = (unsigned long *)(entry + 1);
+ save_stack_trace(&trace);
+
+ ring_buffer_unlock_commit(buffer, event);
+ }
+
+ return;
+}
+
+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) {
+ 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) {
+ 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) {
+ return;
+ } else {
+ entry = ring_buffer_event_data(event);
+
+ tracing_generic_entry_update(entry, 0, 0);
+ entry->type = TRACE_BPUTS;
+ *(unsigned long *)(entry + 1) = (unsigned long)str;
+
+ ring_buffer_unlock_commit(buffer, event);
+ }
+}
+
+void kp_transport_exit(ktap_state *ks)
+{
+ ring_buffer_free(G(ks)->buffer);
+ debugfs_remove(G(ks)->trace_pipe_dentry);
+}
+
+#define TRACE_BUF_SIZE_DEFAULT 1441792UL /* 16384 * 88 (sizeof(entry)) */
+
+int kp_transport_init(ktap_state *ks, struct dentry *dir)
+{
+ struct ring_buffer *buffer;
+ struct dentry *dentry;
+ char filename[32] = {0};
+
+ ftrace_find_event = (void *)kallsyms_lookup_name("ftrace_find_event");
+ if (!ftrace_find_event) {
+ printk("ktap: cannot lookup ftrace_find_event in kallsyms\n");
+ return -EINVAL;
+ }
+
+ buffer = ring_buffer_alloc(TRACE_BUF_SIZE_DEFAULT, RB_FL_OVERWRITE);
+ if (!buffer)
+ return -ENOMEM;
+
+ sprintf(filename, "trace_pipe_%d", (int)task_tgid_vnr(current));
+
+ dentry = debugfs_create_file(filename, 0444, dir,
+ ks, &tracing_pipe_fops);
+ if (!dentry) {
+ pr_err("ktapvm: cannot create trace_pipe file in debugfs\n");
+ ring_buffer_free(buffer);
+ return -1;
+ }
+
+ G(ks)->buffer = buffer;
+ G(ks)->trace_pipe_dentry = dentry;
+
+ return 0;
+}
+
diff --git a/drivers/staging/ktap/interpreter/tstring.c b/drivers/staging/ktap/interpreter/tstring.c
new file mode 100644
index 000000000000..ce4c88df2cc8
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/tstring.c
@@ -0,0 +1,287 @@
+/*
+ * tstring.c - ktap tstring data struction manipulation function
+ *
+ * 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.
+ */
+
+#ifdef __KERNEL__
+#include "../include/ktap.h"
+#else
+#include "../include/ktap_types.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;
+}
+
+#ifdef __KERNEL__
+static arch_spinlock_t tstring_lock =
+ (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
+#endif
+
+/*
+ * 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(&tstring_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(&tstring_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);
+ }
+ }
+}
+
diff --git a/drivers/staging/ktap/interpreter/vm.c b/drivers/staging/ktap/interpreter/vm.c
new file mode 100644
index 000000000000..bc7b9518a88f
--- /dev/null
+++ b/drivers/staging/ktap/interpreter/vm.c
@@ -0,0 +1,1369 @@
+/*
+ * 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 "../include/ktap.h"
+
+#define KTAP_MINSTACK 20
+
+/* todo: enlarge maxstack for big system like 64-bit */
+#define KTAP_MAXSTACK 15000
+
+#define KTAP_STACK_SIZE (BASIC_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)
+
+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 (!ttisstring(top + i)) {
+ kp_error(ks, "cannot concat non-string\n");
+ setnilvalue(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(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);
+ setsvalue(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 (ttisnumber(l) && ttisnumber(r))
+ return NUMLT(nvalue(l), nvalue(r));
+ else if (ttisstring(l) && ttisstring(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 (ttisnumber(l) && ttisnumber(r))
+ return NUMLE(nvalue(l), nvalue(r));
+ else if (ttisstring(l) && ttisstring(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 (ttisnumber(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_newlclosure(ks, nup);
+
+ ncl->l.p = p;
+ setcllvalue(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->l.upvals[i] = findupval(ks, base + uv[i].idx);
+ } else {
+ /* get upvalue from enclosing function */
+ ncl->l.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 (ttistable(t)) {
+ setobj(val, kp_table_get(hvalue(t), key));
+ } else if (ttisaggrtable(t)) {
+ kp_aggrtable_get(ks, ahvalue(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 (ttistable(t)) {
+ kp_table_setvalue(ks, hvalue(t), key, val);
+ } else if (ttisaggrtable(t)) {
+ kp_aggrtable_set(ks, ahvalue(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(!ttistable(t))) {
+ kp_error(ks, "use += operator for non-table\n");
+ return;
+ }
+
+ if (unlikely(!ttisnumber(val))) {
+ kp_error(ks, "use non-number to += operator\n");
+ return;
+ }
+
+ kp_table_atomic_inc(ks, hvalue(t), key, nvalue(val));
+}
+
+
+static void growstack(ktap_state *ks, int n)
+{
+ ktap_value *oldstack;
+ int lim;
+ ktap_callinfo *ci;
+ ktap_gcobject *up;
+ int size = ks->stacksize;
+ int needed = (int)(ks->top - ks->stack) + n;
+ int newsize = 2 * size;
+
+ if (newsize > KTAP_MAXSTACK)
+ newsize = KTAP_MAXSTACK;
+
+ if (newsize < needed)
+ newsize = needed;
+
+ if (newsize > KTAP_MAXSTACK) { /* stack overflow? */
+ kp_error(ks, "stack overflow\n");
+ return;
+ }
+
+ /* realloc stack */
+ oldstack = ks->stack;
+ lim = ks->stacksize;
+ kp_realloc(ks, ks->stack, ks->stacksize, newsize, ktap_value);
+
+ for (; lim < newsize; lim++)
+ setnilvalue(ks->stack + lim);
+ ks->stacksize = newsize;
+ ks->stack_last = ks->stack + newsize;
+
+ /* correct stack */
+ ks->top = (ks->top - oldstack) + ks->stack;
+ for (up = ks->openupval; up != NULL; up = up->gch.next)
+ gco2uv(up)->v = (gco2uv(up)->v - oldstack) + ks->stack;
+
+ for (ci = ks->ci; ci != NULL; ci = ci->prev) {
+ ci->top = (ci->top - oldstack) + ks->stack;
+ ci->func = (ci->func - oldstack) + ks->stack;
+ if (isktapfunc(ci))
+ ci->u.l.base = (ci->u.l.base - oldstack) + ks->stack;
+ }
+
+}
+
+static inline void checkstack(ktap_state *ks, int n)
+{
+ if (ks->stack_last - ks->top <= n)
+ growstack(ks, n);
+}
+
+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++) {
+ setobj(ks->top++, fixed + i);
+ setnilvalue(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--)
+ setobj(res++, first_result++);
+
+ while(i-- > 0)
+ setnilvalue(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;
+ StkId base;
+ ptrdiff_t funcr = savestack(ks, func);
+ int n;
+
+ switch (ttype(func)) {
+ case KTAP_TLCF: /* light C function */
+ f = fvalue(func);
+ goto CFUNC;
+ case KTAP_TCCL: /* C closure */
+ f = clcvalue(func)->f;
+ CFUNC:
+ checkstack(ks, KTAP_MINSTACK);
+ ci = next_ci(ks);
+ ci->nresults = nresults;
+ ci->func = restorestack(ks, funcr);
+ ci->top = ks->top + KTAP_MINSTACK;
+ ci->callstatus = 0;
+ n = (*f)(ks);
+ poscall(ks, ks->top - n);
+ return 1;
+ case KTAP_TLCL:
+ p = CLVALUE(func)->p;
+ checkstack(ks, p->maxstacksize);
+ func = restorestack(ks, funcr);
+ n = (int)(ks->top - func) - 1; /* number of real arguments */
+
+ /* complete missing arguments */
+ for (; n < p->numparams; n++)
+ setnilvalue(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;
+ 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 (ttisnumber(rb) && ttisnumber(rc)) { \
+ ktap_number nb = nvalue(rb), nc = nvalue(rc); \
+ setnvalue(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_lclosure *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:
+ setobj(ra, base + GETARG_B(instr));
+ break;
+ case OP_LOADK:
+ setobj(ra, k + GETARG_Bx(instr));
+ break;
+ case OP_LOADKX:
+ setobj(ra, k + GETARG_Ax(*ci->u.l.savedpc++));
+ break;
+ case OP_LOADBOOL:
+ setbvalue(ra, GETARG_B(instr));
+ if (GETARG_C(instr))
+ ci->u.l.savedpc++;
+ break;
+ case OP_LOADNIL: {
+ int b = GETARG_B(instr);
+ do {
+ setnilvalue(ra++);
+ } while (b--);
+ break;
+ }
+ case OP_GETUPVAL: {
+ int b = GETARG_B(instr);
+ setobj(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_SETUPVAL: {
+ ktap_upval *uv = cl->upvals[GETARG_B(instr)];
+ setobj(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_NEWTABLE: {
+ int b = GETARG_B(instr);
+ int c = GETARG_C(instr);
+ ktap_table *t = kp_table_new(ks);
+ sethvalue(ra, t);
+ if (b != 0 || c != 0)
+ kp_table_resize(ks, t, fb2int(b), fb2int(c));
+ break;
+ }
+ case OP_SELF: {
+ StkId rb = RB(instr);
+ setobj(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 (ttisnumber(rb)) {
+ ktap_number nb = nvalue(rb);
+ setnvalue(ra, NUMUNM(nb));
+ }
+ break;
+ }
+ case OP_NOT:
+ res = isfalse(RB(instr));
+ setbvalue(ra, res);
+ break;
+ case OP_LEN: {
+ int len = kp_objlen(ks, RB(instr));
+ if (len < 0)
+ return;
+ setnvalue(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)equalobj(ks, 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) ? isfalse(ra) : !isfalse(ra))
+ ci->u.l.savedpc++;
+ else
+ donextjump(ci);
+ break;
+ case OP_TESTSET: {
+ ktap_value *rb = RB(instr);
+ if (GETARG_C(instr) ? isfalse(rb) : !isfalse(rb))
+ ci->u.l.savedpc++;
+ else {
+ setobj(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++)
+ setobj(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 */
+ setnvalue(ra, idx); /* update internal index... */
+ setnvalue(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;
+ }
+
+ setnvalue(ra, NUMSUB(nvalue(ra), nvalue(pstep)));
+ ci->u.l.savedpc += GETARG_sBx(instr);
+ break;
+ }
+ case OP_TFORCALL: {
+ StkId cb = ra + 3; /* call base */
+ setobj(cb + 2, ra + 2);
+ setobj(cb + 1, ra + 1);
+ setobj(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 (!ttisnil(ra + 1)) { /* continue loop? */
+ setobj(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_table *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_table_resizearray(ks, h, last);
+
+ for (; n > 0; n--) {
+ ktap_value *val = ra+n;
+ kp_table_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 */
+ checkstack(ks, n);
+ /* previous call may change the stack */
+ ra = RA(instr);
+ ks->top = ra + n;
+ }
+ for (j = 0; j < b; j++) {
+ if (j < n) {
+ setobj(ra + j, base - n + j);
+ } else
+ setnilvalue(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;
+ }
+ setevalue(ra, e);
+ break;
+ }
+
+ case OP_EVENTNAME: {
+ struct ktap_event *e = ks->current_event;
+
+ if (unlikely(!e)) {
+ kp_error(ks, "invalid event context\n");
+ return;
+ }
+ setsvalue(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));
+ setobj(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_table_getint(hvalue(&G(ks)->registry),
+ KTAP_RIDX_GLOBALS);
+ const ktap_value *cfunc;
+ int nr, i;
+
+ nr = G(ks)->nr_builtin_cfunction;
+ cfunc = kp_table_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;
+ setobj(&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_table *target_tbl;
+ const ktap_value *gt = kp_table_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_table_new(ks);
+ kp_table_resize(ks, target_tbl, 0,
+ sizeof(*funcs) / sizeof(ktap_Reg));
+
+ setsvalue(&key, kp_tstring_new(ks, libname));
+ sethvalue(&val, target_tbl);
+ kp_table_setvalue(ks, hvalue(gt), &key, &val);
+ }
+
+ for (i = 0; funcs[i].name != NULL; i++) {
+ ktap_value func_name, cl;
+
+ setsvalue(&func_name, kp_tstring_new(ks, funcs[i].name));
+ setfvalue(&cl, funcs[i].func);
+ kp_table_setvalue(ks, target_tbl, &func_name, &cl);
+
+ cfunction_cache_add(ks, &cl);
+ }
+}
+
+#define BASIC_STACK_SIZE (2 * KTAP_MINSTACK)
+
+static void kp_init_registry(ktap_state *ks)
+{
+ ktap_value mt;
+ ktap_table *registry = kp_table_new(ks);
+
+ sethvalue(&G(ks)->registry, registry);
+ kp_table_resize(ks, registry, KTAP_RIDX_LAST, 0);
+ setthvalue(ks, &mt, ks);
+ kp_table_setint(ks, registry, KTAP_RIDX_MAINTHREAD, &mt);
+ sethvalue(&mt, kp_table_new(ks));
+ kp_table_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_table_getint(hvalue(&G(ks)->registry),
+ KTAP_RIDX_GLOBALS);
+ ktap_table *global_tbl = hvalue(gt);
+ ktap_table *arg_tbl = kp_table_new(ks);
+ ktap_value arg_tblval;
+ ktap_value arg_tsval;
+ char **argv;
+ int i, ret;
+
+ setsvalue(&arg_tsval, kp_tstring_new(ks, "arg"));
+ sethvalue(&arg_tblval, arg_tbl);
+ kp_table_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_table_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)) {
+ setnvalue(&val, res);
+ } else
+ setsvalue(&val, kp_tstring_new(ks, kstr));
+
+ kp_table_setint(ks, arg_tbl, i, &val);
+
+ kfree(kstr);
+ }
+
+ kfree(argv);
+ return ret;
+}
+
+DEFINE_PER_CPU(int, kp_recursion_context[PERF_NR_CONTEXTS]);
+
+/* todo: make this per-session aware */
+static void __percpu *kp_pcpu_data[KTAP_PERCPU_DATA_MAX][PERF_NR_CONTEXTS];
+
+void *kp_percpu_data(int type)
+{
+ return this_cpu_ptr(kp_pcpu_data[type][trace_get_context_bit()]);
+}
+
+static void free_kp_percpu_data(void)
+{
+ int i, j;
+
+ for (i = 0; i < KTAP_PERCPU_DATA_MAX; i++) {
+ for (j = 0; j < PERF_NR_CONTEXTS; j++) {
+ free_percpu(kp_pcpu_data[i][j]);
+ kp_pcpu_data[i][j] = NULL;
+ }
+ }
+}
+
+static int alloc_kp_percpu_data(void)
+{
+ int data_size[KTAP_PERCPU_DATA_MAX] = {
+ sizeof(ktap_state), KTAP_STACK_SIZE, KTAP_PERCPU_BUFFER_SIZE,
+ KTAP_PERCPU_BUFFER_SIZE, sizeof(ktap_btrace)};
+ 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;
+ kp_pcpu_data[i][j] = data;
+ }
+ }
+
+ return 0;
+
+ fail:
+ free_kp_percpu_data();
+ return -ENOMEM;
+}
+
+static void kp_init_state(ktap_state *ks)
+{
+ ktap_callinfo *ci;
+ int i;
+
+ ks->stacksize = BASIC_STACK_SIZE;
+
+ for (i = 0; i < BASIC_STACK_SIZE; i++)
+ setnilvalue(ks->stack + i);
+
+ ks->top = ks->stack;
+ ks->stack_last = ks->stack + ks->stacksize;
+
+ ci = &ks->baseci;
+ ci->callstatus = 0;
+ ci->func = ks->top;
+ setnilvalue(ks->top++);
+ ci->top = ks->top + KTAP_MINSTACK;
+ ks->ci = ci;
+}
+
+static void free_all_ci(ktap_state *ks)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ ktap_state *ks;
+ int j;
+
+ for (j = 0; j < PERF_NR_CONTEXTS; j++) {
+ if (!kp_pcpu_data[KTAP_PERCPU_DATA_STATE][j])
+ break;
+
+ ks = per_cpu_ptr(kp_pcpu_data[KTAP_PERCPU_DATA_STATE][j], 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(KTAP_PERCPU_DATA_STATE);
+ ks->stack = kp_percpu_data(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;
+ }
+}
+
+/* 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;
+
+ /* tell workload process to start executing */
+ if (G(ks)->parm->workload)
+ send_sig(SIGINT, G(ks)->trace_task, 0);
+
+ while (!ks->stop) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ /* sleep for 100 msecs, and try again. */
+ schedule_timeout(HZ / 10);
+
+ if (signal_pending(current)) {
+ flush_signals(current);
+
+ /* newline for handle CTRL+C display as ^C */
+ kp_puts(ks, "\n");
+ break;
+ }
+
+ /* stop waiting if target pid is exited */
+ if (task && task->state == TASK_DEAD)
+ break;
+ }
+
+}
+
+void kp_exit(ktap_state *ks)
+{
+ set_next_as_exit(ks);
+
+ G(ks)->mainthread->stop = 1;
+ G(ks)->exit = 1;
+}
+
+void kp_final_exit(ktap_state *ks)
+{
+ if (!list_empty(&G(ks)->probe_events_head) ||
+ !list_empty(&G(ks)->timers))
+ kp_wait(ks);
+
+ if (G(ks)->trace_task)
+ put_task_struct(G(ks)->trace_task);
+
+ kp_exit_timers(ks);
+ kp_probe_exit(ks);
+
+ /* free all resources got by ktap */
+ kp_tstring_freeall(ks);
+ kp_free_all_gcobject(ks);
+ cfunction_cache_exit(ks);
+
+ wait_user_completion(ks);
+
+ kp_transport_exit(ks);
+
+ kp_exitthread(ks);
+ kp_free(ks, ks->stack);
+ free_all_ci(ks);
+
+ free_kp_percpu_data();
+
+ free_cpumask_var(G(ks)->cpumask);
+ 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;
+
+ ks->stack = kp_malloc(ks, KTAP_STACK_SIZE);
+ 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;
+ INIT_LIST_HEAD(&(G(ks)->timers));
+ INIT_LIST_HEAD(&(G(ks)->probe_events_head));
+ G(ks)->exit = 0;
+
+ if (kp_transport_init(ks, dir))
+ goto out;
+
+ 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)) {
+ printk(KERN_INFO "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);
+
+ if (alloc_kp_percpu_data())
+ goto out;
+
+ if (kp_probe_init(ks))
+ goto out;
+
+ return ks;
+
+ out:
+ G(ks)->exit = 1;
+ kp_final_exit(ks);
+ return NULL;
+}
+
diff --git a/drivers/staging/ktap/scripts/basic/backtrace.kp b/drivers/staging/ktap/scripts/basic/backtrace.kp
new file mode 100644
index 000000000000..39b8c3989e7f
--- /dev/null
+++ b/drivers/staging/ktap/scripts/basic/backtrace.kp
@@ -0,0 +1,6 @@
+#!/usr/bin/env ktap
+
+trace sched:sched_switch {
+ print_backtrace()
+}
+
diff --git a/drivers/staging/ktap/scripts/basic/event_trigger.kp b/drivers/staging/ktap/scripts/basic/event_trigger.kp
new file mode 100644
index 000000000000..3cc8b04f361e
--- /dev/null
+++ b/drivers/staging/ktap/scripts/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()
+ }
+}
+
diff --git a/drivers/staging/ktap/scripts/basic/event_trigger_ftrace.kp b/drivers/staging/ktap/scripts/basic/event_trigger_ftrace.kp
new file mode 100644
index 000000000000..7e0d7d315619
--- /dev/null
+++ b/drivers/staging/ktap/scripts/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()
+ }
+}
+
diff --git a/drivers/staging/ktap/scripts/basic/ftrace.kp b/drivers/staging/ktap/scripts/basic/ftrace.kp
new file mode 100644
index 000000000000..9feca2ba2318
--- /dev/null
+++ b/drivers/staging/ktap/scripts/basic/ftrace.kp
@@ -0,0 +1,6 @@
+#!/usr/bin/env ktap
+
+trace ftrace:function /ip==mutex*/ {
+ print(cpu(), pid(), execname(), argevent)
+}
+
diff --git a/drivers/staging/ktap/scripts/basic/function_time.kp b/drivers/staging/ktap/scripts/basic/function_time.kp
new file mode 100644
index 000000000000..e7859a396d95
--- /dev/null
+++ b/drivers/staging/ktap/scripts/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)
+}
+
+
diff --git a/drivers/staging/ktap/scripts/basic/kretprobe.kp b/drivers/staging/ktap/scripts/basic/kretprobe.kp
new file mode 100644
index 000000000000..82de3cffa276
--- /dev/null
+++ b/drivers/staging/ktap/scripts/basic/kretprobe.kp
@@ -0,0 +1,6 @@
+#!/usr/bin/env ktap
+
+trace probe:vfs_read%return fd=$retval {
+ print(execname(), argevent);
+}
+
diff --git a/drivers/staging/ktap/scripts/game/tetris.kp b/drivers/staging/ktap/scripts/game/tetris.kp
new file mode 100644
index 000000000000..7125cd4e46ce
--- /dev/null
+++ b/drivers/staging/ktap/scripts/game/tetris.kp
@@ -0,0 +1,328 @@
+#!/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 scripts/game/tetris.kp
+#
+
+
+#
+# utils
+#
+
+function rand(max) {
+ r = gettimeofday_us()
+ if (r < 0) {
+ r = r * -1
+ }
+ return r % max
+}
+
+color_table = {}
+color_table["Black"] = 40
+color_table["Red"] = 41
+color_table["Green"] = 42
+color_table["Yellow"] = 43
+color_table["Blue"] = 44
+color_table["Purple"] = 45
+color_table["Cyan"] = 46
+color_table["White"] = 47
+
+function get_color_number(txt) {
+ return color_table[txt]
+}
+
+color_table_text = {}
+color_table_text[40] = "Black"
+color_table_text[41] = "Red"
+color_table_text[42] = "Green"
+color_table_text[43] = "Yellow"
+color_table_text[44] = "Blue"
+color_table_text[45] = "Purple"
+color_table_text[46] = "Cyan"
+color_table_text[47] = "White"
+
+function get_color_text(num) {
+ return color_table_text[num]
+}
+
+function update_display() {
+ for (i = 0, 239, 1) {
+ if ((i % 12 - 11) != 0) {
+ tmp = ""
+ } else {
+ tmp = "\n"
+ }
+
+ if (display_buffer[240 + i] == back_text) {
+ printf("%s%s", back_text, tmp)
+ } else {
+ ctext = display_buffer[240 + i]
+ ansi.set_color2(get_color_number(ctext),
+ get_color_number(ctext))
+ printf(" %s", tmp)
+ ansi.reset_color()
+ }
+
+ # clear the display buffer
+ display_buffer[240 + i] = display_buffer[i]
+ }
+
+ printf("%d\n",point)
+}
+
+
+#
+# global value
+#
+
+key_code = 0
+point = 0
+block_number = 0
+height = 0
+height_update = 0
+
+destination_position = {}
+back_text = {}
+block_color = {}
+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)) {
+ block_color = "White"
+ tmp = block_color
+ } else {
+ back_text = " "
+ tmp = back_text
+ }
+ display_buffer[i - 1] = tmp
+ display_buffer[240 + i - 1] = tmp
+}
+
+block_number = rand(len(color_table) - 1)
+block_color = get_color_text(block_number + 40)
+
+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]
+ != back_text) {
+ 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]] != back_text) {
+ 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] != back_text) {
+ f = 1
+ }
+
+ # copy the moving block to display buffer
+ display_buffer[240 + p] = block_color
+ }
+
+ 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_color
+ }
+ # determin the next block
+ block_number = rand(len(color_table) - 1)
+
+ block_color = get_color_text(block_number + 40)
+
+ 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] != back_text)) {
+ j = j - 1
+ }
+
+ if (j == 0) { # filled!
+ # add a point: 1 line - 1 point, ..., tetris - 10points
+ point = point + k
+ k = k + 1
+
+ # drop every upper block
+ j = (i + 1) * 12
+ i = i + 1
+ while (j > 2 * 12) {
+ j = j - 1
+ display_buffer[j] = display_buffer[j - 12]
+ }
+ }
+ }
+
+ update_display()
+}
diff --git a/drivers/staging/ktap/scripts/helloworld.kp b/drivers/staging/ktap/scripts/helloworld.kp
new file mode 100644
index 000000000000..5673c15b0226
--- /dev/null
+++ b/drivers/staging/ktap/scripts/helloworld.kp
@@ -0,0 +1,3 @@
+#!/usr/bin/env ktap
+
+print("Hello World! I am ktap")
diff --git a/drivers/staging/ktap/scripts/interrupt/hardirq_time.kp b/drivers/staging/ktap/scripts/interrupt/hardirq_time.kp
new file mode 100644
index 000000000000..1d3c33e859d1
--- /dev/null
+++ b/drivers/staging/ktap/scripts/interrupt/hardirq_time.kp
@@ -0,0 +1,25 @@
+#!/usr/bin/env ktap
+
+#this script output each average consumimg time of each hardirq
+s = aggr_table()
+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] = avg(gettimeofday_us() - entry_time)
+ map[cpu()] = nil
+}
+
+trace_end {
+ print("hardirq average executing time (us)")
+ histogram(s)
+}
+
diff --git a/drivers/staging/ktap/scripts/interrupt/softirq_time.kp b/drivers/staging/ktap/scripts/interrupt/softirq_time.kp
new file mode 100644
index 000000000000..7e1a9d8a9172
--- /dev/null
+++ b/drivers/staging/ktap/scripts/interrupt/softirq_time.kp
@@ -0,0 +1,25 @@
+#!/usr/bin/env ktap
+
+#this script output each average consumimg time of each softirq line
+s = aggr_table()
+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] = avg(gettimeofday_us() - entry_time)
+ map[cpu()] = nil
+}
+
+trace_end {
+ print("softirq average executing time (us)")
+ histogram(s)
+}
+
diff --git a/drivers/staging/ktap/scripts/io/kprobes-do-sys-open.kp b/drivers/staging/ktap/scripts/io/kprobes-do-sys-open.kp
new file mode 100644
index 000000000000..a15f911a1804
--- /dev/null
+++ b/drivers/staging/ktap/scripts/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)
+}
diff --git a/drivers/staging/ktap/scripts/io/traceio.kp b/drivers/staging/ktap/scripts/io/traceio.kp
new file mode 100644
index 000000000000..8a95a2534384
--- /dev/null
+++ b/drivers/staging/ktap/scripts/io/traceio.kp
@@ -0,0 +1,56 @@
+#! /usr/bin/env ktap
+
+# Based on systemtap traceio.stp
+
+#this script is broken, fix it soon.
+
+reads = aggr_table()
+writes = aggr_table()
+total_io = aggr_table()
+
+trace syscalls:sys_exit_read {
+ reads[execname()] = sum(arg2)
+ total_io[execname()] = sum(arg2)
+}
+
+trace syscalls:sys_exit_write {
+ writes[execname()] = sum(arg2)
+ total_io[execname()] = sum(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, count in pairs(total_io)) {
+ local readnum = reads[exec]
+ local writenum = writes[exec]
+ printf("%15s r: %12d%s w: %12d%s\n", exec,
+ humanread_digit(readnum), humanread_x(readnum),
+ humanread_digit(writenum), humanread_x(writenum))
+ }
+ printf("\n")
+}
+
diff --git a/drivers/staging/ktap/scripts/mem/kmalloc-top.kp b/drivers/staging/ktap/scripts/mem/kmalloc-top.kp
new file mode 100644
index 000000000000..f18ed9fc64cf
--- /dev/null
+++ b/drivers/staging/ktap/scripts/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()
+}
+
diff --git a/drivers/staging/ktap/scripts/mem/kmem.kp b/drivers/staging/ktap/scripts/mem/kmem.kp
new file mode 100644
index 000000000000..c6f2c99c54cd
--- /dev/null
+++ b/drivers/staging/ktap/scripts/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")
+}
diff --git a/drivers/staging/ktap/scripts/profiling/function_profiler.kp b/drivers/staging/ktap/scripts/profiling/function_profiler.kp
new file mode 100644
index 000000000000..589017fe83b4
--- /dev/null
+++ b/drivers/staging/ktap/scripts/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 = aggr_table()
+
+trace ftrace:function {
+ s[arg1] = count()
+}
+
+trace_end {
+ histogram(s)
+}
+
+#sample output
+#^C
+# value ------------- Distribution ------------- count
+# sub_preempt_count | @@@@@ 34904
+# add_preempt_count | @@@@@ 33435
+# nsecs_to_jiffies64 | @@@ 19919
+# irqtime_account_process_tick... | @ 9970
+# account_idle_time | @ 9880
+# _raw_spin_lock | 5100
+# _raw_spin_unlock | 5021
+# _raw_spin_unlock_irqrestore | 4235
+# _raw_spin_lock_irqsave | 4232
+# __rcu_read_lock | 3373
+# __rcu_read_unlock | 3373
+# lookup_address | 2392
+# pfn_range_is_mapped | 2384
+# update_cfs_rq_blocked_load | 1983
+# idle_cpu | 1808
+# ktime_get | 1394
+# _raw_spin_unlock_irq | 1270
+# _raw_spin_lock_irq | 1091
+# update_curr | 950
+# irqtime_account_irq | 950
+# ... |
+#
diff --git a/drivers/staging/ktap/scripts/profiling/stack_profile.kp b/drivers/staging/ktap/scripts/profiling/stack_profile.kp
new file mode 100644
index 000000000000..97945602fcea
--- /dev/null
+++ b/drivers/staging/ktap/scripts/profiling/stack_profile.kp
@@ -0,0 +1,26 @@
+#!/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 = aggr_table()
+
+profile-10us {
+ s[backtrace()] = count()
+}
+
+tick-60s {
+ exit()
+}
+
+trace_end {
+ for (k, v in pairs(s)) {
+ print(k)
+ print(v)
+ print()
+ }
+}
+
diff --git a/drivers/staging/ktap/scripts/schedule/sched_transition.kp b/drivers/staging/ktap/scripts/schedule/sched_transition.kp
new file mode 100644
index 000000000000..eb54b09c3248
--- /dev/null
+++ b/drivers/staging/ktap/scripts/schedule/sched_transition.kp
@@ -0,0 +1,5 @@
+#!/usr/bin/env ktap
+
+trace sched:sched_switch {
+ printf("%s ... ", arg1)
+}
diff --git a/drivers/staging/ktap/scripts/schedule/schedtimes.kp b/drivers/staging/ktap/scripts/schedule/schedtimes.kp
new file mode 100644
index 000000000000..acef904a2cae
--- /dev/null
+++ b/drivers/staging/ktap/scripts/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]);
+ }
+}
diff --git a/drivers/staging/ktap/scripts/syscalls/errinfo.kp b/drivers/staging/ktap/scripts/syscalls/errinfo.kp
new file mode 100644
index 000000000000..049031b3e374
--- /dev/null
+++ b/drivers/staging/ktap/scripts/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])
+ }
+}
diff --git a/drivers/staging/ktap/scripts/syscalls/sctop.kp b/drivers/staging/ktap/scripts/syscalls/sctop.kp
new file mode 100644
index 000000000000..6df45cb98178
--- /dev/null
+++ b/drivers/staging/ktap/scripts/syscalls/sctop.kp
@@ -0,0 +1,14 @@
+#! /usr/bin/env ktap
+
+#this script is broken, fix it soon.
+s = {}
+
+trace syscalls:sys_enter_* {
+ s[argname] += 1
+}
+
+tick-5s {
+ ansi.clear_screen()
+ histogram(s)
+ delete(s)
+}
diff --git a/drivers/staging/ktap/scripts/syscalls/syscalls.kp b/drivers/staging/ktap/scripts/syscalls/syscalls.kp
new file mode 100644
index 000000000000..8bbaacad68ac
--- /dev/null
+++ b/drivers/staging/ktap/scripts/syscalls/syscalls.kp
@@ -0,0 +1,6 @@
+#!/usr/bin/env ktap
+
+trace syscalls:* {
+ print(cpu(), pid(), execname(), argevent)
+}
+
diff --git a/drivers/staging/ktap/scripts/syscalls/syscalls_count.kp b/drivers/staging/ktap/scripts/syscalls/syscalls_count.kp
new file mode 100644
index 000000000000..363c6226e8a9
--- /dev/null
+++ b/drivers/staging/ktap/scripts/syscalls/syscalls_count.kp
@@ -0,0 +1,56 @@
+#!/usr/bin/env ktap
+
+s = aggr_table()
+
+trace syscalls:sys_enter_* {
+ s[argname] = count()
+}
+
+trace_end {
+ histogram(s)
+}
+
+print("Press Control-C to stop.")
+
+#Result:
+#
+#[root@jovi ktap]# ./ktap scripts/syscalls_histogram.kp
+#^C
+# value ------------- Distribution ------------- count
+# sys_enter_rt_sigprocmask |@@@@@@ 326
+# sys_enter_read |@@@@@ 287
+# sys_enter_close |@@@@ 236
+# sys_enter_open |@@@@ 222
+# sys_enter_stat64 |@@ 132
+# sys_enter_select |@@ 123
+# sys_enter_rt_sigaction |@@ 107
+# sys_enter_poll |@ 72
+# sys_enter_write |@ 70
+# sys_enter_mmap_pgoff |@ 58
+# sys_enter_fstat64 | 41
+# sys_enter_nanosleep | 23
+# sys_enter_access | 20
+# sys_enter_mprotect | 18
+# sys_enter_geteuid | 17
+# sys_enter_getegid | 16
+# sys_enter_getuid | 16
+# sys_enter_getgid | 16
+# sys_enter_brk | 15
+# sys_enter_waitpid | 11
+# sys_enter_time | 10
+# sys_enter_ioctl | 9
+# sys_enter_munmap | 9
+# sys_enter_fcntl64 | 7
+# sys_enter_dup2 | 7
+# sys_enter_clone | 6
+# sys_enter_exit_group | 6
+# sys_enter_execve | 4
+# sys_enter_pipe | 3
+# sys_enter_gettimeofday | 3
+# sys_enter_getdents | 2
+# sys_enter_getgroups | 2
+# sys_enter_statfs64 | 2
+# sys_enter_lseek | 2
+# sys_enter_openat | 1
+# sys_enter_newuname | 1
+
diff --git a/drivers/staging/ktap/scripts/syscalls/syscalls_count_by_proc.kp b/drivers/staging/ktap/scripts/syscalls/syscalls_count_by_proc.kp
new file mode 100644
index 000000000000..7b7d722b893d
--- /dev/null
+++ b/drivers/staging/ktap/scripts/syscalls/syscalls_count_by_proc.kp
@@ -0,0 +1,24 @@
+#!/usr/bin/env ktap
+
+s = aggr_table()
+
+trace syscalls:sys_enter_* {
+ s[execname()] = count()
+}
+
+trace_end {
+ histogram(s)
+}
+
+print("Press Control-C to stop.")
+
+#Result:
+#
+#[root@jovi ktap]# ./ktap scripts/syscalls_histogram2.kp
+#^C
+# value ------------- Distribution ------------- count
+# sshd |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 196
+# iscsid |@@@@ 24
+# sendmail |@ 9
+
+
diff --git a/drivers/staging/ktap/scripts/tracepoints/eventcount.kp b/drivers/staging/ktap/scripts/tracepoints/eventcount.kp
new file mode 100644
index 000000000000..9bc357fca56b
--- /dev/null
+++ b/drivers/staging/ktap/scripts/tracepoints/eventcount.kp
@@ -0,0 +1,212 @@
+#!/usr/bin/env ktap
+
+# showing all tracepoints in histogram style
+
+s = {}
+
+trace *:* {
+ s[argname] += 1
+}
+
+trace_end {
+ histogram(s)
+}
+
+print("Press Control-C to stop.")
+
+#Results:
+#^C
+#
+# value ------------- Distribution ------------- count
+# rcu_utilization |@@@@@ 225289
+# cpu_idle |@@@ 120168
+# sched_wakeup |@@ 91950
+# timer_cancel |@@ 91232
+# timer_start |@@ 91201
+# sched_stat_sleep |@@ 90981
+# timer_expire_exit |@@ 90634
+# timer_expire_entry |@@ 90625
+# hrtimer_cancel |@ 75411
+# hrtimer_start |@ 74946
+# softirq_raise |@ 63117
+# softirq_exit |@ 63109
+# softirq_entry |@ 63094
+# sched_switch |@ 62331
+# sched_stat_wait |@ 60491
+# hrtimer_expire_exit |@ 47538
+# hrtimer_expire_entry |@ 47530
+# sched_stat_runtime | 2780
+# kmem_cache_free | 2684
+# kmem_cache_alloc | 2415
+# kfree | 2288
+# sys_exit | 2145
+# sys_enter | 2145
+# sys_exit_rt_sigprocmask | 1000
+# sys_enter_rt_sigprocmask | 1000
+# timer_init | 912
+# sched_stat_blocked | 685
+# kmalloc | 667
+# workqueue_execute_end | 621
+# workqueue_execute_start | 621
+# sys_enter_select | 566
+# sys_exit_select | 566
+# sys_enter_read | 526
+# sys_exit_read | 526
+# mm_page_free | 478
+# mm_page_alloc | 427
+# mm_page_free_batched | 382
+# net_dev_queue | 296
+# net_dev_xmit | 296
+# consume_skb | 296
+# sys_exit_write | 290
+# sys_enter_write | 290
+# kfree_skb | 289
+# kmem_cache_alloc_node | 269
+# kmalloc_node | 263
+# sys_enter_close | 249
+# sys_exit_close | 249
+# hrtimer_init | 248
+# netif_receive_skb | 242
+# sys_enter_open | 237
+# sys_exit_open | 237
+# napi_poll | 226
+# sched_migrate_task | 207
+# sys_exit_poll | 173
+# sys_enter_poll | 173
+# workqueue_queue_work | 152
+# workqueue_activate_work | 152
+# sys_enter_stat64 | 133
+# sys_exit_stat64 | 133
+# sys_exit_rt_sigaction | 133
+# sys_enter_rt_sigaction | 133
+# irq_handler_entry | 125
+# irq_handler_exit | 125
+# mm_page_alloc_zone_locked | 99
+# sys_exit_mmap_pgoff | 66
+# sys_enter_mmap_pgoff | 66
+# sys_exit_fstat64 | 54
+# sys_enter_fstat64 | 54
+# sys_enter_nanosleep | 51
+# sys_exit_nanosleep | 51
+# block_bio_queue | 46
+# block_bio_remap | 46
+# block_bio_complete | 46
+# mix_pool_bytes | 44
+# mm_page_pcpu_drain | 31
+# sys_exit_time | 23
+# sys_enter_time | 23
+# sys_exit_access | 20
+# sys_enter_access | 20
+# mix_pool_bytes_nolock | 18
+# sys_enter_mprotect | 18
+# sys_exit_mprotect | 18
+# sys_enter_geteuid | 17
+# sys_exit_geteuid | 17
+# sys_enter_munmap | 17
+# sys_exit_munmap | 17
+# block_getrq | 16
+# sys_enter_getuid | 16
+# sys_enter_getgid | 16
+# sys_exit_getgid | 16
+# sys_exit_getuid | 16
+# block_rq_issue | 16
+# scsi_dispatch_cmd_start | 16
+# block_rq_complete | 16
+# scsi_dispatch_cmd_done | 16
+# sys_enter_getegid | 16
+# sys_exit_getegid | 16
+# block_rq_insert | 16
+# skb_copy_datagram_iovec | 15
+# sys_enter_brk | 15
+# sys_exit_brk | 15
+# credit_entropy_bits | 14
+# wbc_writepage | 14
+# sys_exit_clone | 12
+# block_touch_buffer | 12
+# sched_process_wait | 11
+# sys_enter_waitpid | 11
+# sys_exit_waitpid | 11
+# writeback_written | 10
+# writeback_start | 10
+# writeback_queue_io | 10
+# ext4_es_lookup_extent_enter | 9
+# sys_enter_ioctl | 9
+# sys_exit_ioctl | 9
+# ext4_ext_map_blocks_enter | 9
+# ext4_ext_map_blocks_exit | 9
+# ext4_es_lookup_extent_exit | 9
+# ext4_es_insert_extent | 9
+# ext4_ext_show_extent | 8
+# extract_entropy | 8
+#ext4_es_find_delayed_extent_exit | 8
+# ext4_es_find_delayed_extent_... | 8
+# writeback_pages_written | 7
+# sys_exit_dup2 | 7
+# sys_enter_dup2 | 7
+# signal_generate | 7
+# sys_enter_fcntl64 | 7
+# sys_exit_fcntl64 | 7
+# global_dirty_state | 7
+# writeback_dirty_inode_start | 7
+# block_bio_backmerge | 7
+# writeback_dirty_inode | 7
+# sched_wakeup_new | 6
+# sched_process_free | 6
+# sys_enter_exit_group | 6
+# task_newtask | 6
+# sys_enter_clone | 6
+# sched_process_fork | 6
+# sched_process_exit | 6
+# sys_exit_gettimeofday | 5
+# signal_deliver | 5
+# sys_enter_gettimeofday | 5
+# writeback_single_inode | 4
+# sys_enter_execve | 4
+# task_rename | 4
+# sched_process_exec | 4
+# block_dirty_buffer | 4
+# sys_exit_execve | 4
+# block_unplug | 4
+# sched_stat_iowait | 4
+# writeback_single_inode_start | 4
+# block_plug | 4
+# writeback_write_inode | 3
+# sys_enter_pipe | 3
+# writeback_dirty_page | 3
+# writeback_write_inode_start | 3
+# ext4_mark_inode_dirty | 3
+# ext4_journal_start | 3
+# sys_exit_pipe | 3
+# jbd2_drop_transaction | 2
+# jbd2_commit_locking | 2
+# jbd2_commit_flushing | 2
+# jbd2_handle_start | 2
+# jbd2_run_stats | 2
+# sys_exit_getdents | 2
+# jbd2_checkpoint_stats | 2
+# sys_enter_getgroups | 2
+# jbd2_start_commit | 2
+# jbd2_end_commit | 2
+# ext4_da_writepages | 2
+# jbd2_handle_stats | 2
+# sys_enter_statfs64 | 2
+# sys_exit_statfs64 | 2
+# sys_exit_getgroups | 2
+# sys_exit_lseek | 2
+# sys_enter_lseek | 2
+# sys_enter_getdents | 2
+# ext4_da_write_pages | 2
+# jbd2_commit_logging | 2
+# ext4_request_blocks | 1
+# sys_exit_openat | 1
+# ext4_discard_preallocations | 1
+# ext4_mballoc_alloc | 1
+# sys_enter_openat | 1
+# ext4_da_writepages_result | 1
+# ext4_allocate_blocks | 1
+# sys_enter_newuname | 1
+# ext4_da_update_reserve_space | 1
+# ext4_get_reserved_cluster_alloc | 1
+# sys_exit_newuname | 1
+# writeback_wake_thread | 1
+
diff --git a/drivers/staging/ktap/scripts/tracepoints/eventcount_by_proc.kp b/drivers/staging/ktap/scripts/tracepoints/eventcount_by_proc.kp
new file mode 100644
index 000000000000..8706f73d5b40
--- /dev/null
+++ b/drivers/staging/ktap/scripts/tracepoints/eventcount_by_proc.kp
@@ -0,0 +1,59 @@
+#!/usr/bin/env ktap
+
+# showing all tracepoints in histogram style
+
+s = aggr_table()
+
+trace *:* {
+ s[execname()] = count()
+}
+
+trace_end {
+ histogram(s)
+}
+
+print("Press Control-C to stop.")
+
+#Results:
+#^C
+# value ------------- Distribution ------------- count
+# swapper/0 |@@@@@@@@@@@@ 354378
+# swapper/1 |@@@@@@@@@@ 284984
+# ps |@@@@ 115697
+# ksmtuned |@@@ 95857
+# iscsid |@@ 80008
+# awk |@ 30354
+# irqbalance | 16530
+# rcu_sched | 15892
+# sendmail | 14463
+# kworker/0:1 | 10540
+# kworker/u4:2 | 9250
+# kworker/1:2 | 7943
+# sleep | 7555
+# crond | 3911
+# ksoftirqd/0 | 3817
+# sshd | 2849
+# systemd-journal | 2209
+# migration/1 | 1601
+# migration/0 | 1350
+# dhclient | 1343
+# nm-dhcp-client. | 1208
+# ksoftirqd/1 | 1064
+# watchdog/1 | 966
+# watchdog/0 | 964
+# khugepaged | 776
+# dbus-daemon | 611
+# rpcbind | 607
+# gdbus | 529
+# NetworkManager | 399
+# jbd2/dm-1-8 | 378
+# modem-manager | 184
+# abrt-watch-log | 157
+# polkitd | 156
+# rs:main Q:Reg | 153
+# avahi-daemon | 151
+# rsyslogd | 102
+# systemd | 96
+# kworker/0:1H | 45
+# smartd | 30
+
diff --git a/drivers/staging/ktap/scripts/tracepoints/tracepoints.kp b/drivers/staging/ktap/scripts/tracepoints/tracepoints.kp
new file mode 100644
index 000000000000..5d088695bbf2
--- /dev/null
+++ b/drivers/staging/ktap/scripts/tracepoints/tracepoints.kp
@@ -0,0 +1,6 @@
+#!/usr/bin/env ktap
+
+trace *:* {
+ print(cpu(), pid(), execname(), argevent)
+}
+
diff --git a/drivers/staging/ktap/scripts/userspace/uprobes-malloc.kp b/drivers/staging/ktap/scripts/userspace/uprobes-malloc.kp
new file mode 100644
index 000000000000..14c172f8f327
--- /dev/null
+++ b/drivers/staging/ktap/scripts/userspace/uprobes-malloc.kp
@@ -0,0 +1,9 @@
+#!/usr/bin/env ktap
+
+trace probe:/lib/libc.so.6:0x000773c0 {
+ print("entry:", execname(), argevent)
+}
+
+trace probe:/lib/libc.so.6:0x000773c0%return {
+ print("exit:", execname(), argevent)
+}
diff --git a/drivers/staging/ktap/test/aggr_table.kp b/drivers/staging/ktap/test/aggr_table.kp
new file mode 100644
index 000000000000..985b634952c1
--- /dev/null
+++ b/drivers/staging/ktap/test/aggr_table.kp
@@ -0,0 +1,50 @@
+#!/usr/bin/env ktap
+
+function failed() {
+ printf("failed\n");
+ exit(-1);
+}
+
+#---------------------------------#
+
+s = aggr_table()
+
+s["count"] = count()
+s["count"] = count()
+s["count"] = count()
+
+s["max"] = max(1)
+s["max"] = max(0)
+s["max"] = max(100)
+
+s["min"] = min(50)
+s["min"] = min(2)
+s["min"] = min(100)
+
+s["sum"] = sum(10)
+s["sum"] = sum(20)
+s["sum"] = sum(30)
+
+s["avg"] = avg(10)
+s["avg"] = avg(20)
+s["avg"] = avg(30)
+
+if (s["count"] != 3) {
+ failed()
+}
+
+if (s["max"] != 100) {
+ failed()
+}
+
+if (s["min"] != 2) {
+ failed()
+}
+
+if (s["sum"] != 60) {
+ failed()
+}
+
+if (s["avg"] != 20) {
+ failed()
+}
diff --git a/drivers/staging/ktap/test/ansi.kp b/drivers/staging/ktap/test/ansi.kp
new file mode 100644
index 000000000000..e8a27835ce75
--- /dev/null
+++ b/drivers/staging/ktap/test/ansi.kp
@@ -0,0 +1,20 @@
+#!/usr/bin/env ktap
+
+ansi.clear_screen()
+
+ansi.set_color(32)
+printf("this line should be Green color\n")
+
+ansi.set_color(31)
+printf("this line should be Red color\n")
+
+ansi.set_color2(34, 43)
+printf("this line should be Blue color, with Yellow background\n")
+
+ansi.reset_color()
+ansi.set_color3(34, 46, 4)
+printf("this line should be Blue color, with Cyan background, underline single attribute\n")
+
+ansi.reset_color()
+ansi.new_line()
+
diff --git a/drivers/staging/ktap/test/arg.kp b/drivers/staging/ktap/test/arg.kp
new file mode 100644
index 000000000000..0cf16ebf3227
--- /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()
+}
diff --git a/drivers/staging/ktap/test/arith.kp b/drivers/staging/ktap/test/arith.kp
new file mode 100644
index 000000000000..32880a140c69
--- /dev/null
+++ b/drivers/staging/ktap/test/arith.kp
@@ -0,0 +1,27 @@
+#!/usr/bin/env ktap
+
+function failed() {
+ printf("failed\n");
+ exit(-1);
+}
+
+#-----------------------------------------#
+
+a = 4
+b = 5
+
+if ((a + b) != 9) {
+ failed()
+}
+
+if ((a - b) != -1) {
+ failed()
+}
+
+if ((a % b) != 4) {
+ failed()
+}
+
+if ((a / b) != 0) {
+ failed()
+}
diff --git a/drivers/staging/ktap/test/bench/sembench.c b/drivers/staging/ktap/test/bench/sembench.c
new file mode 100644
index 000000000000..08119341c91d
--- /dev/null
+++ b/drivers/staging/ktap/test/bench/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;
+}
+
diff --git a/drivers/staging/ktap/test/bench/test.sh b/drivers/staging/ktap/test/bench/test.sh
new file mode 100644
index 000000000000..9f77969b6f2b
--- /dev/null
+++ b/drivers/staging/ktap/test/bench/test.sh
@@ -0,0 +1,25 @@
+#!/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_ONE_LINER="trace syscalls:sys_*_futex {}"
+
+echo -e "\n\t\tPass 1 with tracing"
+../../ktap -e "$KTAP_ONE_LINER" -- $COMMAND
+echo -e "\n\t\tPass 2 with tracing"
+../../ktap -e "$KTAP_ONE_LINER" -- $COMMAND
+echo -e "\n\t\tPass 3 with tracing"
+../../ktap -e "$KTAP_ONE_LINER" -- $COMMAND
+
+rm -rf ./sembench
diff --git a/drivers/staging/ktap/test/concat.kp b/drivers/staging/ktap/test/concat.kp
new file mode 100644
index 000000000000..be77bb70ea8e
--- /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()
+}
diff --git a/drivers/staging/ktap/test/count.kp b/drivers/staging/ktap/test/count.kp
new file mode 100644
index 000000000000..26f962c227a4
--- /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()
+}
diff --git a/drivers/staging/ktap/test/fibonacci.kp b/drivers/staging/ktap/test/fibonacci.kp
new file mode 100644
index 000000000000..7e141da0de42
--- /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()
+}
diff --git a/drivers/staging/ktap/test/function.kp b/drivers/staging/ktap/test/function.kp
new file mode 100644
index 000000000000..bfbff2614104
--- /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();
+}
+
+
diff --git a/drivers/staging/ktap/test/if.kp b/drivers/staging/ktap/test/if.kp
new file mode 100644
index 000000000000..3122084af5ee
--- /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()
+}
+
diff --git a/drivers/staging/ktap/test/kprobe.kp b/drivers/staging/ktap/test/kprobe.kp
new file mode 100644
index 000000000000..022d4a70b31f
--- /dev/null
+++ b/drivers/staging/ktap/test/kprobe.kp
@@ -0,0 +1,14 @@
+#!/usr/bin/env ktap
+
+n = 0
+trace probe:schedule {
+ n = n + 1
+}
+
+tick-1s {
+ if (n == 0) {
+ printf("failed\n");
+ }
+ exit()
+}
+
diff --git a/drivers/staging/ktap/test/kretprobe.kp b/drivers/staging/ktap/test/kretprobe.kp
new file mode 100644
index 000000000000..e311e84c292f
--- /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()
+}
+
diff --git a/drivers/staging/ktap/test/len.kp b/drivers/staging/ktap/test/len.kp
new file mode 100644
index 000000000000..697d91553ba9
--- /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()
+}
+
+
diff --git a/drivers/staging/ktap/test/looping.kp b/drivers/staging/ktap/test/looping.kp
new file mode 100644
index 000000000000..fe48051a29c2
--- /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()
+}
diff --git a/drivers/staging/ktap/test/pairs.kp b/drivers/staging/ktap/test/pairs.kp
new file mode 100644
index 000000000000..cdf3825951dd
--- /dev/null
+++ b/drivers/staging/ktap/test/pairs.kp
@@ -0,0 +1,45 @@
+#!/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()
+}
diff --git a/drivers/staging/ktap/test/run_test.sh b/drivers/staging/ktap/test/run_test.sh
new file mode 100644
index 000000000000..b98af2659123
--- /dev/null
+++ b/drivers/staging/ktap/test/run_test.sh
@@ -0,0 +1,58 @@
+#!/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
+function 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 > /devnull
+
+$KTAP -o /dev/null -e 'trace syscalls:* { print(argevent) }' \
+ -- $KTAP -e 'while (1) {}'
+
+ktaprun arith.kp
+ktaprun concat.kp
+ktaprun count.kp
+ktaprun fibonacci.kp
+ktaprun function.kp
+ktaprun if.kp
+ktaprun kprobe.kp
+ktaprun kretprobe.kp
+ktaprun len.kp
+ktaprun looping.kp
+ktaprun pairs.kp
+ktaprun table.kp
+ktaprun aggr_table.kp
+ktaprun timer.kp
+ktaprun tracepoint.kp
+ktaprun -o /dev/null zerodivide.kp
+ktaprun ansi.kp
+
+echo "testing kill deadloop ktap script"
+$KTAP -e 'while (1) {}' &
+pkill ktap
+sleep 1
+
+#####################################################
+rmmod ktapvm
+if test $? -ne 0; then
+ echo "Error in rmmod ../ktapvm.ko, leak module refcount?"
+ exit -1
+fi
+
diff --git a/drivers/staging/ktap/test/table.kp b/drivers/staging/ktap/test/table.kp
new file mode 100644
index 000000000000..1f6d6e43302f
--- /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()
+}
+
+
diff --git a/drivers/staging/ktap/test/timer.kp b/drivers/staging/ktap/test/timer.kp
new file mode 100644
index 000000000000..02e8a61da664
--- /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()
+}
+
+
diff --git a/drivers/staging/ktap/test/tracepoint.kp b/drivers/staging/ktap/test/tracepoint.kp
new file mode 100644
index 000000000000..fb036e675a75
--- /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()
+}
+
diff --git a/drivers/staging/ktap/test/zerodivide.kp b/drivers/staging/ktap/test/zerodivide.kp
new file mode 100644
index 000000000000..abb1eae4fdab
--- /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")
diff --git a/drivers/staging/ktap/userspace/code.c b/drivers/staging/ktap/userspace/code.c
new file mode 100644
index 000000000000..1427fd518dec
--- /dev/null
+++ b/drivers/staging/ktap/userspace/code.c
@@ -0,0 +1,968 @@
+/*
+ * 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"
+
+
+#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 (ttisnumber(idx)) {
+ ktap_number n = nvalue(idx);
+ ktap_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 */
+ setnvalue(&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)
+ setnilvalue(&f->k[oldsize++]);
+ setobj(&f->k[k], v);
+ fs->nk++;
+ return k;
+}
+
+int codegen_stringK(ktap_funcstate *fs, ktap_string *s)
+{
+ ktap_value o;
+
+ setsvalue(&o, s);
+ return addk(fs, &o, &o);
+}
+
+int codegen_numberK(ktap_funcstate *fs, ktap_number r)
+{
+ int n;
+ ktap_value o, s;
+
+ setnvalue(&o, r);
+ if (r == 0 || ktap_numisnan(NULL, r)) { /* handle -0 and NaN */
+ /* use raw representation as key to avoid numeric problems */
+ setsvalue(&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;
+ setbvalue(&o, b);
+ return addk(fs, &o, &o);
+}
+
+static int nilK(ktap_funcstate *fs)
+{
+ ktap_value k, v;
+ setnilvalue(&v);
+ /* cannot use nil as key; instead use table itself to represent nil */
+ sethvalue(&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_self(ktap_funcstate *fs, ktap_expdesc *e, ktap_expdesc *key)
+{
+ int ereg;
+
+ codegen_exp2anyreg(fs, e);
+ ereg = e->u.info; /* register where 'e' was placed */
+ freeexp(fs, e);
+ e->u.info = fs->freereg; /* base register for op_self */
+ e->k = VNONRELOC;
+ codegen_reserveregs(fs, 2); /* function and 'self' produced by op_self */
+ codegen_codeABC(fs, OP_SELF, e->u.info, ereg, codegen_exp2RK(fs, key));
+ freeexp(fs, key);
+}
+
+static void invertjump(ktap_funcstate *fs, ktap_expdesc *e)
+{
+ ktap_instruction *pc = getjumpcontrol(fs, e->u.info);
+ ktap_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
+ GET_OPCODE(*pc) != OP_TEST);
+ SETARG_A(*pc, !(GETARG_A(*pc)));
+}
+
+static int jumponcond(ktap_funcstate *fs, ktap_expdesc *e, int cond)
+{
+ if (e->k == VRELOCABLE) {
+ ktap_instruction ie = getcode(fs, e);
+ if (GET_OPCODE(ie) == OP_NOT) {
+ fs->pc--; /* remove previous OP_NOT */
+ return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
+ }
+ /* else go through */
+ }
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ return condjump(fs, OP_TESTSET, NO_REG, e->u.info, cond);
+}
+
+void codegen_goiftrue(ktap_funcstate *fs, ktap_expdesc *e)
+{
+ int pc; /* pc of last jump */
+
+ codegen_dischargevars(fs, e);
+ switch (e->k) {
+ case VJMP: {
+ invertjump(fs, e);
+ pc = e->u.info;
+ break;
+ }
+ case VK: case VKNUM: case VTRUE: {
+ pc = NO_JUMP; /* always true; do nothing */
+ break;
+ }
+ default:
+ pc = jumponcond(fs, e, 0);
+ break;
+ }
+
+ codegen_concat(fs, &e->f, pc); /* insert last jump in `f' list */
+ codegen_patchtohere(fs, e->t);
+ e->t = NO_JUMP;
+}
+
+void codegen_goiffalse(ktap_funcstate *fs, ktap_expdesc *e)
+{
+ int pc; /* pc of last jump */
+ codegen_dischargevars(fs, e);
+
+ switch (e->k) {
+ case VJMP: {
+ pc = e->u.info;
+ break;
+ }
+ case VNIL: case VFALSE: {
+ pc = NO_JUMP; /* always false; do nothing */
+ break;
+ }
+ default:
+ pc = jumponcond(fs, e, 1);
+ break;
+ }
+ codegen_concat(fs, &e->t, pc); /* insert last jump in `t' list */
+ codegen_patchtohere(fs, e->f);
+ e->f = NO_JUMP;
+}
+
+static void codenot(ktap_funcstate *fs, ktap_expdesc *e)
+{
+ codegen_dischargevars(fs, e);
+ switch (e->k) {
+ case VNIL: case VFALSE: {
+ e->k = VTRUE;
+ break;
+ }
+ case VK: case VKNUM: case VTRUE: {
+ e->k = VFALSE;
+ break;
+ }
+ case VJMP: {
+ invertjump(fs, e);
+ break;
+ }
+ case VRELOCABLE:
+ case VNONRELOC: {
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ e->u.info = codegen_codeABC(fs, OP_NOT, 0, e->u.info, 0);
+ e->k = VRELOCABLE;
+ break;
+ }
+ default:
+ ktap_assert(0); /* cannot happen */
+ break;
+ }
+
+ /* interchange true and false lists */
+ { int temp = e->f; e->f = e->t; e->t = temp; }
+ removevalues(fs, e->f);
+ removevalues(fs, e->t);
+}
+
+void codegen_indexed(ktap_funcstate *fs, ktap_expdesc *t, ktap_expdesc *k)
+{
+ ktap_assert(!hasjumps(t));
+ t->u.ind.t = t->u.info;
+ t->u.ind.idx = codegen_exp2RK(fs, k);
+ t->u.ind.vt = (t->k == VUPVAL) ? VUPVAL
+ : check_exp(vkisinreg(t->k), VLOCAL);
+ t->k = VINDEXED;
+}
+
+static int constfolding(OpCode op, ktap_expdesc *e1, ktap_expdesc *e2)
+{
+ ktap_number r;
+
+ if (!isnumeral(e1) || !isnumeral(e2))
+ return 0;
+
+ if ((op == OP_DIV || op == OP_MOD) && e2->u.nval == 0)
+ return 0; /* do not attempt to divide by 0 */
+
+ if (op == OP_POW)
+ return 0; /* ktap current do not suppor pow arith */
+
+ r = ktapc_arith(op - OP_ADD + KTAP_OPADD, e1->u.nval, e2->u.nval);
+ e1->u.nval = r;
+ return 1;
+}
+
+static void codearith(ktap_funcstate *fs, OpCode op,
+ ktap_expdesc *e1, ktap_expdesc *e2, int line)
+{
+ if (constfolding(op, e1, e2))
+ return;
+ else {
+ int o2 = (op != OP_UNM && op != OP_LEN) ? codegen_exp2RK(fs, e2) : 0;
+ int o1 = codegen_exp2RK(fs, e1);
+
+ if (o1 > o2) {
+ freeexp(fs, e1);
+ freeexp(fs, e2);
+ } else {
+ freeexp(fs, e2);
+ freeexp(fs, e1);
+ }
+ e1->u.info = codegen_codeABC(fs, op, 0, o1, o2);
+ e1->k = VRELOCABLE;
+ codegen_fixline(fs, line);
+ }
+}
+
+static void codecomp(ktap_funcstate *fs, OpCode op, int cond, ktap_expdesc *e1,
+ ktap_expdesc *e2)
+{
+ int o1 = codegen_exp2RK(fs, e1);
+ int o2 = codegen_exp2RK(fs, e2);
+
+ freeexp(fs, e2);
+ freeexp(fs, e1);
+ if (cond == 0 && op != OP_EQ) {
+ int temp; /* exchange args to replace by `<' or `<=' */
+ temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */
+ cond = 1;
+ }
+ e1->u.info = condjump(fs, op, cond, o1, o2);
+ e1->k = VJMP;
+}
+
+void codegen_prefix(ktap_funcstate *fs, UnOpr op, ktap_expdesc *e, int line)
+{
+ ktap_expdesc e2;
+
+ e2.t = e2.f = NO_JUMP;
+ e2.k = VKNUM;
+ e2.u.nval = 0;
+
+ switch (op) {
+ case OPR_MINUS: {
+ if (isnumeral(e)) /* minus constant? */
+ e->u.nval = ktap_numunm(e->u.nval); /* fold it */
+ else {
+ codegen_exp2anyreg(fs, e);
+ codearith(fs, OP_UNM, e, &e2, line);
+ }
+ break;
+ }
+ case OPR_NOT:
+ codenot(fs, e);
+ break;
+ case OPR_LEN: {
+ codegen_exp2anyreg(fs, e); /* cannot operate on constants */
+ codearith(fs, OP_LEN, e, &e2, line);
+ break;
+ }
+ default:
+ ktap_assert(0);
+ }
+}
+
+void codegen_infix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *v)
+{
+ switch (op) {
+ case OPR_AND: {
+ codegen_goiftrue(fs, v);
+ break;
+ }
+ case OPR_OR: {
+ codegen_goiffalse(fs, v);
+ break;
+ }
+ case OPR_CONCAT: {
+ codegen_exp2nextreg(fs, v); /* operand must be on the `stack' */
+ break;
+ }
+ case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
+ case OPR_MOD: case OPR_POW: {
+ if (!isnumeral(v)) codegen_exp2RK(fs, v);
+ break;
+ }
+ default:
+ codegen_exp2RK(fs, v);
+ break;
+ }
+}
+
+void codegen_posfix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *e1, ktap_expdesc *e2, int line)
+{
+ switch (op) {
+ case OPR_AND: {
+ ktap_assert(e1->t == NO_JUMP); /* list must be closed */
+ codegen_dischargevars(fs, e2);
+ codegen_concat(fs, &e2->f, e1->f);
+ *e1 = *e2;
+ break;
+ }
+ case OPR_OR: {
+ ktap_assert(e1->f == NO_JUMP); /* list must be closed */
+ codegen_dischargevars(fs, e2);
+ codegen_concat(fs, &e2->t, e1->t);
+ *e1 = *e2;
+ break;
+ }
+ case OPR_CONCAT: {
+ codegen_exp2val(fs, e2);
+ if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) {
+ ktap_assert(e1->u.info == GETARG_B(getcode(fs, e2))-1);
+ freeexp(fs, e1);
+ SETARG_B(getcode(fs, e2), e1->u.info);
+ e1->k = VRELOCABLE; e1->u.info = e2->u.info;
+ } else {
+ codegen_exp2nextreg(fs, e2); /* operand must be on the 'stack' */
+ codearith(fs, OP_CONCAT, e1, e2, line);
+ }
+ break;
+ }
+ case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
+ case OPR_MOD: case OPR_POW: {
+ codearith(fs, (OpCode)(op - OPR_ADD + OP_ADD), e1, e2, line);
+ break;
+ }
+ case OPR_EQ: case OPR_LT: case OPR_LE: {
+ codecomp(fs, (OpCode)(op - OPR_EQ + OP_EQ), 1, e1, e2);
+ break;
+ }
+ case OPR_NE: case OPR_GT: case OPR_GE: {
+ codecomp(fs, (OpCode)(op - OPR_NE + OP_EQ), 0, e1, e2);
+ break;
+ }
+ default:
+ ktap_assert(0);
+ }
+}
+
+void codegen_fixline(ktap_funcstate *fs, int line)
+{
+ fs->f->lineinfo[fs->pc - 1] = line;
+}
+
+void codegen_setlist(ktap_funcstate *fs, int base, int nelems, int tostore)
+{
+ int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1;
+ int b = (tostore == KTAP_MULTRET) ? 0 : tostore;
+
+ ktap_assert(tostore != 0);
+ if (c <= MAXARG_C)
+ codegen_codeABC(fs, OP_SETLIST, base, b, c);
+ else if (c <= MAXARG_Ax) {
+ codegen_codeABC(fs, OP_SETLIST, base, b, 0);
+ codeextraarg(fs, c);
+ } else
+ lex_syntaxerror(fs->ls, "constructor too long");
+ fs->freereg = base + 1; /* free registers with list values */
+}
+
diff --git a/drivers/staging/ktap/userspace/dump.c b/drivers/staging/ktap/userspace/dump.c
new file mode 100644
index 000000000000..088b08d0fa8e
--- /dev/null
+++ b/drivers/staging/ktap/userspace/dump.c
@@ -0,0 +1,187 @@
+/*
+ * 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"
+
+
+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);
+}
+
+/*
+ * 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);
+ DumpFunction(f, &D);
+ return D.status;
+}
diff --git a/drivers/staging/ktap/userspace/eventdef.c b/drivers/staging/ktap/userspace/eventdef.c
new file mode 100644
index 000000000000..76a68ac8aabc
--- /dev/null
+++ b/drivers/staging/ktap/userspace/eventdef.c
@@ -0,0 +1,588 @@
+/*
+ * 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"
+
+static char tracing_events_path[] = "/sys/kernel/debug/tracing/events";
+
+#define IDS_ARRAY_SIZE 4096
+static u8 *ids_array;
+
+#define set_id(id) \
+ do { \
+ ids_array[id/8] = ids_array[id/8] | (1 << (id%8)); \
+ } while(0)
+
+#define clear_id(id) \
+ do { \
+ ids_array[id/8] = ids_array[id/8] & ~ (1 << (id%8)); \
+ } while(0)
+
+
+static int get_digit_len(int id)
+{
+ int len = -1;
+
+ if (id < 10)
+ len = 1;
+ else if (id < 100)
+ len = 2;
+ else if (id < 1000)
+ len = 3;
+ else if (id < 10000)
+ len = 4;
+ else if (id < 100000)
+ len = 5;
+
+ return len;
+}
+
+static char *get_idstr(char *filter)
+{
+ char *idstr, *ptr;
+ int total_len = 0;
+ int filter_len;
+ int i;
+
+ filter_len = filter ? strlen(filter) : 0;
+
+ for (i = 0; i < IDS_ARRAY_SIZE*8; i++) {
+ if (ids_array[i/8] & (1 << (i%8)))
+ total_len += get_digit_len(i) + 1;
+ }
+
+ if (!total_len)
+ return NULL;
+
+ idstr = malloc(total_len + filter_len + 1);
+ if (!idstr)
+ return NULL;
+
+ memset(idstr, 0, total_len + filter_len + 1);
+ ptr = idstr;
+ for (i = 0; i < IDS_ARRAY_SIZE*8; i++) {
+ if (ids_array[i/8] & (1 << (i%8))) {
+ char digits[32] = {0};
+ int len;
+
+ sprintf(digits, "%d ", i);
+ len = strlen(digits);
+ strncpy(ptr, digits, len);
+ ptr += len;
+ }
+ }
+
+ if (filter)
+ memcpy(ptr, filter, strlen(filter));
+
+ return idstr;
+}
+
+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);
+
+ if (id >= IDS_ARRAY_SIZE * 8) {
+ fprintf(stderr, "tracepoint id(%d) is bigger than %d\n", id,
+ IDS_ARRAY_SIZE * 8);
+ close(fd);
+ return -1;
+ }
+
+ set_id(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_path,
+ 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_path, 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_path);
+ 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;
+ int kp_seq;
+ char *probe_event;
+};
+
+static struct probe_list *probe_list_head;
+
+#define KPROBE_EVENTS_PATH "/sys/kernel/debug/tracing/kprobe_events"
+
+static int parse_events_add_kprobe(char *old_event)
+{
+ static int event_seq = 0;
+ struct probe_list *pl;
+ char probe_event[128] = {0};
+ char event_id_path[128] = {0};
+ char *event;
+ char *r;
+ int fd;
+ int ret;
+
+ fd = open(KPROBE_EVENTS_PATH, O_WRONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Cannot open %s\n", KPROBE_EVENTS_PATH);
+ return -1;
+ }
+
+ event = strdup(old_event);
+ r = strstr(event, "%return");
+ if (r) {
+ memset(r, ' ', 7);
+ snprintf(probe_event, 128, "r:kprobes/kp%d %s",
+ event_seq, event);
+ } else
+ snprintf(probe_event, 128, "p:kprobes/kp%d %s",
+ event_seq, event);
+
+ free(event);
+
+ verbose_printf("kprobe event %s\n", probe_event);
+ ret = write(fd, probe_event, strlen(probe_event));
+ if (ret <= 0) {
+ fprintf(stderr, "Cannot write %s to %s\n", probe_event,
+ KPROBE_EVENTS_PATH);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ pl = malloc(sizeof(struct probe_list));
+ if (!pl)
+ return -1;
+
+ pl->type = KPROBE_EVENT;
+ pl->kp_seq = event_seq;
+ pl->next = probe_list_head;
+ probe_list_head = pl;
+
+ sprintf(event_id_path, "/sys/kernel/debug/tracing/events/kprobes/kp%d/id",
+ event_seq);
+ ret = add_event(event_id_path);
+ if (ret < 0)
+ return -1;
+
+ event_seq++;
+ return 0;
+}
+
+#define UPROBE_EVENTS_PATH "/sys/kernel/debug/tracing/uprobe_events"
+
+static int parse_events_add_uprobe(char *old_event)
+{
+ static int event_seq = 0;
+ struct probe_list *pl;
+ char probe_event[128] = {0};
+ char event_id_path[128] = {0};
+ char *event;
+ char *r;
+ int fd;
+ int ret;
+
+ fd = open(UPROBE_EVENTS_PATH, O_WRONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Cannot open %s\n", UPROBE_EVENTS_PATH);
+ return -1;
+ }
+
+ event = strdup(old_event);
+ r = strstr(event, "%return");
+ if (r) {
+ memset(r, ' ', 7);
+ snprintf(probe_event, 128, "r:uprobes/kp%d %s",
+ event_seq, event);
+ } else
+ snprintf(probe_event, 128, "p:uprobes/kp%d %s",
+ event_seq, event);
+
+ free(event);
+
+ verbose_printf("uprobe event %s\n", probe_event);
+ ret = write(fd, probe_event, strlen(probe_event));
+ if (ret <= 0) {
+ fprintf(stderr, "Cannot write %s to %s\n", probe_event,
+ UPROBE_EVENTS_PATH);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ pl = malloc(sizeof(struct probe_list));
+ if (!pl)
+ return -1;
+
+ pl->type = UPROBE_EVENT;
+ pl->kp_seq = event_seq;
+ pl->next = probe_list_head;
+ probe_list_head = pl;
+
+ sprintf(event_id_path, "/sys/kernel/debug/tracing/events/uprobes/kp%d/id",
+ event_seq);
+ ret = add_event(event_id_path);
+ if (ret < 0)
+ return -1;
+
+ event_seq++;
+ return 0;
+}
+
+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);
+}
+
+static int parse_events_add_stapsdt(char *old_event)
+{
+ printf("Currently ktap don't support stapsdt, please waiting\n");
+
+ return -1;
+}
+
+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_string *ktapc_parse_eventdef(ktap_string *eventdef)
+{
+ const char *def_str = getstr(eventdef);
+ char *str = strdup(def_str);
+ char *sys, *event, *filter, *idstr, *g_idstr, *next;
+ ktap_string *ts;
+ int ret;
+
+ if (!ids_array) {
+ ids_array = malloc(IDS_ARRAY_SIZE);
+ if (!ids_array)
+ return NULL;
+ }
+
+ g_idstr = malloc(4096);
+ if (!g_idstr)
+ return NULL;
+
+ memset(g_idstr, 0, 4096);
+
+ parse_next_eventdef:
+ memset(ids_array, 0, IDS_ARRAY_SIZE);
+
+ 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, "stapsdt"))
+ ret = parse_events_add_stapsdt(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, "*"))
+ clear_id(1);
+
+ idstr = get_idstr(filter);
+ if (!idstr)
+ goto error;
+
+ str = next;
+
+ g_idstr = strcat(g_idstr, idstr);
+ g_idstr = strcat(g_idstr, ",");
+
+ if (*next != '\0')
+ goto parse_next_eventdef;
+
+ ts = ktapc_ts_new(g_idstr);
+ free(g_idstr);
+
+ return ts;
+ error:
+ cleanup_event_resources();
+ return NULL;
+}
+
+void cleanup_event_resources(void)
+{
+ struct probe_list *pl;
+ const char *path;
+ char probe_event[32] = {0};
+ int fd, ret;
+
+ for (pl = probe_list_head; pl; pl = pl->next) {
+ if (pl->type == KPROBE_EVENT) {
+ path = KPROBE_EVENTS_PATH;
+ snprintf(probe_event, 32, "-:kprobes/kp%d", pl->kp_seq);
+ } else if (pl->type == UPROBE_EVENT) {
+ path = UPROBE_EVENTS_PATH;
+ snprintf(probe_event, 32, "-:uprobes/kp%d", pl->kp_seq);
+ } else {
+ fprintf(stderr, "Cannot cleanup event type %d\n", pl->type);
+ continue;
+ }
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Cannot open %s\n", UPROBE_EVENTS_PATH);
+ continue;
+ }
+
+ ret = write(fd, probe_event, strlen(probe_event));
+ if (ret <= 0) {
+ fprintf(stderr, "Cannot write %s to %s\n", probe_event,
+ path);
+ close(fd);
+ continue;
+ }
+
+ close(fd);
+ }
+}
+
diff --git a/drivers/staging/ktap/userspace/ktapc.h b/drivers/staging/ktap/userspace/ktapc.h
new file mode 100644
index 000000000000..879cd29d8c94
--- /dev/null
+++ b/drivers/staging/ktap/userspace/ktapc.h
@@ -0,0 +1,376 @@
+/*
+ * 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_ARG1, TK_ARG2, TK_ARG3, TK_ARG4, TK_ARG5, TK_ARG6, TK_ARG7, TK_ARG8,
+ TK_ARG9, TK_PROFILE, TK_TICK,
+ 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
+};
+
+/* 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_table *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_table *ktapc_table_new();
+const ktap_value *ktapc_table_get(ktap_table *t, const ktap_value *key);
+void ktapc_table_setvalue(ktap_table *t, const ktap_value *key, ktap_value *val);
+ktap_closure *ktapc_newlclosure(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_string *ktapc_parse_eventdef(ktap_string *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)
+
+
+#include "../include/ktap_opcodes.h"
+
+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_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);
+
diff --git a/drivers/staging/ktap/userspace/ktapio.c b/drivers/staging/ktap/userspace/ktapio.c
new file mode 100644
index 000000000000..f9bcab461f2a
--- /dev/null
+++ b/drivers/staging/ktap/userspace/ktapio.c
@@ -0,0 +1,108 @@
+/*
+ * 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)
+
+extern pid_t ktap_pid;
+
+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", ktap_pid);
+
+ open_again:
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ usleep(10000);
+
+ if (failed++ == 10) {
+ fprintf(stderr, "Cannot open file %s\n", filename);
+ return NULL;
+ }
+ goto open_again;
+ }
+
+ while ((len = read(fd, buf, sizeof(buf))) > 0)
+ write(out_fd, buf, len);
+
+ close(fd);
+ close(out_fd);
+
+ return NULL;
+}
+
+int ktapio_create(const char *output)
+{
+ pthread_t reader;
+
+ signal(SIGINT, sigfunc);
+
+ if (pthread_create(&reader, NULL, reader_thread, (void *)output) < 0)
+ handle_error("pthread_create reader_thread failed\n");
+
+ return 0;
+}
+
diff --git a/drivers/staging/ktap/userspace/lex.c b/drivers/staging/ktap/userspace/lex.c
new file mode 100644
index 000000000000..484dd7fdafc3
--- /dev/null
+++ b/drivers/staging/ktap/userspace/lex.c
@@ -0,0 +1,622 @@
+/*
+ * 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 "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",
+ "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>"
+};
+
+#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 */
+ setsvalue(&tsv, ts);
+ o = ktapc_table_get(ls->fs->h, &tsv);
+ if (ttisnil(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 */
+ setbvalue(&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 '<';
+ else {
+ next(ls);
+ return TK_LE;
+ }
+ }
+ 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 '.': { /* '.', '..', '...', or number */
+ save_and_next(ls);
+ if (check_next(ls, ".")) {
+ if (check_next(ls, "."))
+ return TK_DOTS; /* '...' */
+ else
+ return TK_CONCAT; /* '..' */
+ }
+ else if (!isdigit(ls->current))
+ return '.';
+ /* else go through */
+ }
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': {
+ read_numeral(ls, seminfo);
+ return TK_NUMBER;
+ }
+ case EOZ: {
+ return TK_EOS;
+ }
+ case '&': {
+ next(ls);
+ if (ls->current != '&')
+ return '&';
+ else {
+ next(ls);
+ return TK_AND;
+ }
+ }
+ case '|': {
+ next(ls);
+ if (ls->current != '|')
+ return '|';
+ else {
+ next(ls);
+ return TK_OR;
+ }
+ }
+ default: {
+ if (islalpha(ls->current)) {
+ /* identifier or reserved word? */
+ ktap_string *ts;
+ do {
+ save_and_next(ls);
+ } while (islalnum(ls->current));
+ ts = lex_newstring(ls, mbuff(ls->buff),
+ mbuff_len(ls->buff));
+ seminfo->ts = ts;
+ if (isreserved(ts)) /* reserved word? */
+ return ts->tsv.extra - 1 +
+ FIRST_RESERVED;
+ else {
+ return TK_NAME;
+ }
+ } else { /* single-char tokens (+ - / ...) */
+ int c = ls->current;
+ next(ls);
+ return c;
+ }
+ }
+ }
+ }
+}
+
+void lex_read_string_until(ktap_lexstate *ls, int c)
+{
+ ktap_string *ts;
+ char errmsg[32];
+
+ mbuff_reset(ls->buff);
+
+ while (ls->current == ' ')
+ next(ls);
+
+ do {
+ save_and_next(ls);
+ } while (ls->current != c && ls->current != EOZ);
+
+ if (ls->current != c) {
+ sprintf(errmsg, "expect %c", c);
+ lexerror(ls, errmsg, 0);
+ }
+
+ ts = lex_newstring(ls, mbuff(ls->buff), mbuff_len(ls->buff));
+ ls->t.seminfo.ts = ts;
+ ls->t.token = TK_STRING;
+}
+
+void lex_next(ktap_lexstate *ls)
+{
+ ls->lastline = ls->linenumber;
+ if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
+ ls->t = ls->lookahead; /* use this one */
+ ls->lookahead.token = TK_EOS; /* and discharge it */
+ } else
+ ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
+}
+
+int lex_lookahead(ktap_lexstate *ls)
+{
+ ktap_assert(ls->lookahead.token == TK_EOS);
+ ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
+ return ls->lookahead.token;
+}
+
diff --git a/drivers/staging/ktap/userspace/main.c b/drivers/staging/ktap/userspace/main.c
new file mode 100644
index 000000000000..2aa686a99914
--- /dev/null
+++ b/drivers/staging/ktap/userspace/main.c
@@ -0,0 +1,609 @@
+/*
+ * 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"
+
+
+/*******************************************************************/
+
+void *ktapc_reallocv(void *block, size_t osize, size_t nsize)
+{
+ return kp_reallocv(NULL, block, osize, nsize);
+}
+
+ktap_closure *ktapc_newlclosure(int n)
+{
+ return kp_newlclosure(NULL, n);
+}
+
+ktap_proto *ktapc_newproto()
+{
+ return kp_newproto(NULL);
+}
+
+const ktap_value *ktapc_table_get(ktap_table *t, const ktap_value *key)
+{
+ return kp_table_get(t, key);
+}
+
+void ktapc_table_setvalue(ktap_table *t, const ktap_value *key, ktap_value *val)
+{
+ kp_table_setvalue(NULL, t, key, val);
+}
+
+ktap_table *ktapc_table_new()
+{
+ return kp_table_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_RKC(instr) \
+ do { \
+ if (ISK(GETARG_C(instr))) \
+ kp_showobj(NULL, k + INDEXK(GETARG_C(instr))); \
+ else \
+ print_base(GETARG_C(instr)); \
+ } 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_GETTABUP:
+ print_base(GETARG_A(instr));
+ printf(" <- ");
+
+ if (GETARG_B(instr) == 0)
+ printf("global");
+ else
+ printf("upvalues[%d]", 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_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;
+ 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_TSTRING:
+ printf("\tTSTRING: ");
+ printf("%s\n", svalue(&(f->k[i])));
+
+ break;
+ default:
+ printf("\terror: unknow constant type\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);
+ fprintf(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"
+" -s : simple event tracing\n"
+" -b : list byte codes\n"
+" 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;
+pid_t ktap_pid;
+
+static int run_ktapvm()
+{
+ int ktapvm_fd, ktap_fd;
+ int ret;
+
+ ktap_pid = getpid();
+
+ 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 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;
+
+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 's':
+ sprintf(oneline_src, SIMPLE_ONE_LINER_FMT, next_arg);
+ i++;
+ break;
+ case 'b':
+ dump_bytecode = 1;
+ break;
+ case 'V':
+ 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();
+ 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();
+ cl = ktapc_parser(buff, input);
+
+ munmap(buff, sb.st_size);
+ close(fdin);
+
+ dump:
+ if (dump_bytecode) {
+ dump_function(1, cl->l.p);
+ exit(0);
+ }
+
+ /* ktapc output */
+ uparm.trunk = malloc(ktap_trunk_mem_size);
+ if (!uparm.trunk)
+ handle_error("malloc failed");
+
+ ktapc_dump(cl->l.p, ktapc_writer, NULL, 0);
+}
+
+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;
+
+ /* start running into kernel ktapvm */
+ ret = run_ktapvm();
+
+ cleanup_event_resources();
+ return ret;
+}
+
+
diff --git a/drivers/staging/ktap/userspace/parser.c b/drivers/staging/ktap/userspace/parser.c
new file mode 100644
index 000000000000..dd9e2de692ad
--- /dev/null
+++ b/drivers/staging/ktap/userspace/parser.c
@@ -0,0 +1,1910 @@
+/*
+ * 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"
+
+/* 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 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_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 */
+ }
+ }
+
+ 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) {
+ 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) {
+ /* argument: EVENTDEF string */
+ check(ls, TK_STRING);
+ enterlevel(ls);
+ ktap_string *ts = ktapc_parse_eventdef(ls->t.seminfo.ts);
+ check_condition(ls, ts != NULL, "Cannot parse eventdef");
+ codestring(ls, &args, ts);
+ 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 */
+}
+
+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;
+ 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_newlclosure(1); /* create main closure */
+
+ memset(&lexstate, 0, sizeof(ktap_lexstate));
+ memset(&funcstate, 0, sizeof(ktap_funcstate));
+ funcstate.f = cl->l.p = ktapc_newproto();
+ funcstate.f->source = ktapc_ts_new(name); /* create and anchor ktap_string */
+
+ lex_init();
+
+ mbuff_init(&buff);
+ memset(&dyd, 0, sizeof(ktap_dyndata));
+ lexstate.buff = &buff;
+ lexstate.dyd = &dyd;
+ lex_setinput(&lexstate, ptr, funcstate.f->source, firstchar);
+
+ mainfunc(&lexstate, &funcstate);
+
+ ktap_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
+
+ /* all scopes should be correctly finished */
+ ktap_assert(dyd.actvar.n == 0 && dyd.gt.n == 0 && dyd.label.n == 0);
+ return cl;
+}
+
diff --git a/drivers/staging/ktap/userspace/util.c b/drivers/staging/ktap/userspace/util.c
new file mode 100644
index 000000000000..8124de995be7
--- /dev/null
+++ b/drivers/staging/ktap/userspace/util.c
@@ -0,0 +1,346 @@
+/*
+ * 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 "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;
+}
+
+static int isneg(const char **s)
+{
+ if (**s == '-') {
+ (*s)++;
+ return 1;
+ } else if (**s == '+')
+ (*s)++;
+
+ return 0;
+}
+
+static ktap_number readhexa(const char **s, ktap_number r, int *count)
+{
+ for (; isxdigit((unsigned char)(**s)); (*s)++) { /* read integer part */
+ r = (r * 16.0) + (ktap_number)(ktapc_hexavalue((unsigned char)(**s)));
+ (*count)++;
+ }
+
+ return r;
+}
+
+/*
+ * convert an hexadecimal numeric string to a number, following
+ * C99 specification for 'strtod'
+ */
+static ktap_number strx2number(const char *s, char **endptr)
+{
+ ktap_number r = 0.0;
+ int e = 0, i = 0;
+ int neg = 0; /* 1 if number is negative */
+
+ *endptr = (char *)s; /* nothing is valid yet */
+ while (isspace((unsigned char)(*s)))
+ s++; /* skip initial spaces */
+
+ neg = isneg(&s); /* check signal */
+ if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */
+ return 0.0; /* invalid format (no '0x') */
+
+ s += 2; /* skip '0x' */
+ r = readhexa(&s, r, &i); /* read integer part */
+ if (*s == '.') {
+ s++; /* skip dot */
+ r = readhexa(&s, r, &e); /* read fractional part */
+ }
+
+ if (i == 0 && e == 0)
+ return 0.0; /* invalid format (no digit) */
+ e *= -4; /* each fractional digit divides value by 2^-4 */
+ *endptr = (char *)s; /* valid up to here */
+
+ if (*s == 'p' || *s == 'P') { /* exponent part? */
+ int exp1 = 0;
+ int neg1;
+
+ s++; /* skip 'p' */
+ neg1 = isneg(&s); /* signal */
+ if (!isdigit((unsigned char)(*s)))
+ goto ret; /* must have at least one digit */
+ while (isdigit((unsigned char)(*s))) /* read exponent */
+ exp1 = exp1 * 10 + *(s++) - '0';
+ if (neg1) exp1 = -exp1;
+ e += exp1;
+ }
+
+ *endptr = (char *)s; /* valid up to here */
+ ret:
+ if (neg)
+ r = -r;
+
+ return ldexp(r, e);
+}
+
+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 if (strpbrk(s, "xX")) /* hexa? */
+ *result = strx2number(s, &endptr);
+ else
+ *result = strtod(s, &endptr);
+
+ 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);
+}
+