ktap testsuite is based on perl-prove framwork.
More info can read from test/README.

The test framework is contributed by Yichun Zhang (agentzh)

ktap run the test suite in parallel defaultly:
        prove -j4 -r test/

There also have several benchmark script to compare performance
between ktap with stap.

The benchmark shows that:
1). ktap number computation and comparsion overhead is bigger than stap,
    nearly 10+%.

2). Perf backend tracing overhead is bigger than raw tracepoint/kprobe.

3). ktap table operation overhead is smaller than stap, nearly 10+%.

(This benchmark result only tell the data in my box)

Signed-off-by: Jovi Zhangwei <jovi.zhang...@gmail.com>
---
 tools/ktap/test/README                   |  69 ++++
 tools/ktap/test/arithmetic.t             | 109 ++++++
 tools/ktap/test/benchmark/cmp_neq.sh     | 158 +++++++++
 tools/ktap/test/benchmark/cmp_profile.sh |  54 +++
 tools/ktap/test/benchmark/cmp_table.sh   | 112 +++++++
 tools/ktap/test/benchmark/sembench.c     | 556 +++++++++++++++++++++++++++++++
 tools/ktap/test/cli-arg.t                |  25 ++
 tools/ktap/test/concat.t                 |  21 ++
 tools/ktap/test/count.t                  |  25 ++
 tools/ktap/test/deadloop.t               |  37 ++
 tools/ktap/test/fibonacci.t              |  42 +++
 tools/ktap/test/function.t               |  78 +++++
 tools/ktap/test/if.t                     |  32 ++
 tools/ktap/test/kprobe.t                 |  82 +++++
 tools/ktap/test/kretprobe.t              |  35 ++
 tools/ktap/test/len.t                    |  27 ++
 tools/ktap/test/lib/Test/ktap.pm         | 128 +++++++
 tools/ktap/test/looping.t                |  46 +++
 tools/ktap/test/one-liner.t              |  48 +++
 tools/ktap/test/pairs.t                  |  52 +++
 tools/ktap/test/stack_overflow.t         |  22 ++
 tools/ktap/test/syntax-err.t             |  19 ++
 tools/ktap/test/table.t                  |  81 +++++
 tools/ktap/test/time.t                   |  59 ++++
 tools/ktap/test/timer.t                  |  65 ++++
 tools/ktap/test/tracepoint.t             |  53 +++
 tools/ktap/test/util/reindex             |  61 ++++
 tools/ktap/test/zerodivide.t             |  21 ++
 28 files changed, 2117 insertions(+)
 create mode 100644 tools/ktap/test/README
 create mode 100644 tools/ktap/test/arithmetic.t
 create mode 100644 tools/ktap/test/benchmark/cmp_neq.sh
 create mode 100644 tools/ktap/test/benchmark/cmp_profile.sh
 create mode 100644 tools/ktap/test/benchmark/cmp_table.sh
 create mode 100644 tools/ktap/test/benchmark/sembench.c
 create mode 100644 tools/ktap/test/cli-arg.t
 create mode 100644 tools/ktap/test/concat.t
 create mode 100644 tools/ktap/test/count.t
 create mode 100644 tools/ktap/test/deadloop.t
 create mode 100644 tools/ktap/test/fibonacci.t
 create mode 100644 tools/ktap/test/function.t
 create mode 100644 tools/ktap/test/if.t
 create mode 100644 tools/ktap/test/kprobe.t
 create mode 100644 tools/ktap/test/kretprobe.t
 create mode 100644 tools/ktap/test/len.t
 create mode 100644 tools/ktap/test/lib/Test/ktap.pm
 create mode 100644 tools/ktap/test/looping.t
 create mode 100644 tools/ktap/test/one-liner.t
 create mode 100644 tools/ktap/test/pairs.t
 create mode 100644 tools/ktap/test/stack_overflow.t
 create mode 100644 tools/ktap/test/syntax-err.t
 create mode 100644 tools/ktap/test/table.t
 create mode 100644 tools/ktap/test/time.t
 create mode 100644 tools/ktap/test/timer.t
 create mode 100644 tools/ktap/test/tracepoint.t
 create mode 100755 tools/ktap/test/util/reindex
 create mode 100644 tools/ktap/test/zerodivide.t

diff --git a/tools/ktap/test/README b/tools/ktap/test/README
new file mode 100644
index 0000000..5a628e1
--- /dev/null
+++ b/tools/ktap/test/README
@@ -0,0 +1,69 @@
+This directory contains the test suite for ktap.
+
+Prerequisites
+-------------
+
+One needs to install perl and CPAN modules Test::Base and IPC::Run
+before running the tests. After perl is installed, the "cpan" utility
+can be used to install the CPAN modules required:
+
+    cpan Test::Base IPC::Run
+
+Alternatively you can just install the pre-built binary packages
+provided by your Linux distribution vendor. For example, on Fedora, you
+just need to run
+
+    yum install perl-Test-Base perl-IPC-Run
+
+Running tests
+-------------
+
+You are required to run the tests from the root directory of this project.
+
+You can run the whole test suite like this:
+
+    prove -r test/
+
+To utilize multiple CPU cores while running the tests, it is also
+supported to spawn multiple processes to run the test files in parallel,
+as in
+
+    prove -j4 -r test/
+
+Then 4 processes will be spawned to run the tests at the same time.
+
+To run individual .t test files, just specify their file paths
+explicitly:
+
+    prove test/cli-args.t test/one-liner.t
+
+If you just want to run an individual test case in a particular .t
+file, then just add the line
+
+    --- ONLY
+
+to the end of the test block you want to run and run that .t file
+normally with the "prove" utility.
+
+Similarly, if you want to skip a particular test block, add the line
+
+    --- SKIP
+
+to that test block.
+
+Test file formatting
+--------------------
+
+We do have a "reindex" tool to automatically re-format
+the .t test files, so that you do not have to manually get the test
+serial numbers exactly right, like "TEST 1: ", "TEST 2: " and etc,
+nor manually keep 3 blank lines between adjacent test blocks. For
+example,
+
+    ./test/util/reindex test/cli-arg.t
+
+or re-format all the .t files:
+
+    ./test/util/reindex test/*.t
+
+Always run this tool before committing your newly editted tests.
diff --git a/tools/ktap/test/arithmetic.t b/tools/ktap/test/arithmetic.t
new file mode 100644
index 0000000..e56daf0
--- /dev/null
+++ b/tools/ktap/test/arithmetic.t
@@ -0,0 +1,109 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: arithmetic
+--- src
+if (1 > 2) {
+       print("failed")
+}
+
+if (200 < 100) {
+       print("failed")
+}
+
+if (1 == nil) {
+       print("failed")
+}
+
+if (1 != nil) {
+       print("1 != nil")
+}
+
+if (nil == 1) {
+       print("failed")
+}
+
+if (nil != 1) {
+       print("nil != 1")
+}
+
+if (1 == "test") {
+       print("failed")
+}
+
+if (1 != "test") {
+       print("1 != 'test'")
+}
+
+if ("test" == 1) {
+       print("failed")
+}
+
+if ("test" != 1) {
+       print("'test' != 1")
+}
+
+if ("1234" == "1") {
+       print("failed")
+}
+
+if ("1234" != "1") {
+       print("'1234' != '1'")
+}
+
+
+
+var a = 4
+var b = 5
+
+if ((a + b) != 9) {
+       print("failed")
+}
+
+if ((a - b) != -1) {
+       print("failed")
+}
+
+if ((a * b) != 20) {
+       print("failed")
+}
+
+if ((a % b) != 4) {
+       print("failed")
+}
+
+if ((a / b) != 0) {
+       print("failed")
+}
+
+
+
+#below checking only valid for 64-bit system
+
+var c = 0x1234567812345678
+var d = 0x2
+
+if (c + d != 0x123456781234567a) {
+       print("failed")
+}
+
+if (-1 != 0xffffffffffffffff) {
+       print("failed")
+}
+
+--- out
+1 != nil
+nil != 1
+1 != 'test'
+'test' != 1
+'1234' != '1'
+
+--- err
+
+
diff --git a/tools/ktap/test/benchmark/cmp_neq.sh 
b/tools/ktap/test/benchmark/cmp_neq.sh
new file mode 100644
index 0000000..da69131
--- /dev/null
+++ b/tools/ktap/test/benchmark/cmp_neq.sh
@@ -0,0 +1,158 @@
+#!/bin/sh
+
+# This script compare number equality performance between ktap and stap.
+# It also compare different ktap tracing interfaces.
+#
+# 1. ktap -e 'trace syscalls:sys_enter_futex {}'
+# 2. ktap -e 'kdebug.tracepoint("sys_enter_futex", function () {})'
+# 3. ktap -e 'trace probe:SyS_futex uaddr=%di {}'
+# 4. ktap -e 'kdebug.kprobe("SyS_futex", function () {})'
+# 5. stap -e 'probe syscall.futex {}'
+# 6. ktap -d -e 'trace syscalls:sys_enter_futex {}'
+# 7. ktap -d -e 'kdebug.tracepoint("sys_enter_futex", function () {})'
+# 8. ktap -e 'trace syscalls:sys_enter_futex /kernel_buildin_filter/ {}'
+
+#Result:
+#ktap number computation and comparsion overhead is bigger than stap,
+#nearly 10+% (4 vs. 5 in above)), ktap is not very slow.
+#
+#Perf backend tracing overhead is big, because it need copy temp buffer, and
+#code path is very long than direct callback(1 vs. 4 in above).
+
+gcc -o sembench sembench.c -O2 -lpthread
+
+COMMAND="./sembench -t 200 -w 20 -r 30 -o 2"
+
+#------------------------------------------------------------#
+
+echo -e "without tracing:"
+#$COMMAND; $COMMAND; $COMMAND
+
+#------------------------------------------------------------#
+
+../../ktap -q -e 'trace syscalls:sys_enter_futex {
+       var uaddr = arg2
+        if (uaddr == 0x100 || uaddr == 0x200 || uaddr == 0x300 ||
+           uaddr == 0x400 || uaddr == 0x500 || uaddr == 0x600 ||
+           uaddr == 0x700 || uaddr == 0x800 || uaddr == 0x900 ||
+           uaddr == 0x1000) {
+                printf("%x %x\n", arg1, arg2)
+        }}' &
+
+echo -e "\nktap tracing: trace syscalls:sys_enter_futex { if (arg2 == 0x100 || 
arg2 == 0x200 ... }"
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+#------------------------------------------------------------#
+
+../../ktap -q -e 'kdebug.tracepoint("sys_enter_futex", function () {
+       var arg = arg2
+        if (arg == 0x100 || arg == 0x200 || arg == 0x300 || arg == 0x400 ||
+            arg == 0x500 || arg == 0x600 || arg == 0x700 || arg == 0x800 ||
+            arg == 0x900 || arg == 0x1000) {
+                printf("%x %x\n", arg1, arg2)
+        }})' &
+
+echo -e '\nktap tracing: kdebug.tracepoint("sys_enter_futex", function (xxx) 
{})'
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+#------------------------------------------------------------#
+
+../../ktap -q -e 'trace probe:SyS_futex uaddr=%di {
+       var arg = arg1
+        if (arg == 0x100 || arg == 0x200 || arg == 0x300 || arg == 0x400 ||
+            arg == 0x500 || arg == 0x600 || arg == 0x700 || arg == 0x800 ||
+            arg == 0x900 || arg == 0x1000) {
+                printf("%x\n", arg1)
+        }}' &
+echo -e '\nktap tracing: trace probe:SyS_futex uaddr=%di {...}'
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+
+#------------------------------------------------------------#
+../../ktap -q -e 'kdebug.kprobe("SyS_futex", function () {
+       var uaddr = 1
+        if (uaddr == 0x100 || uaddr == 0x200 || uaddr == 0x300 ||
+           uaddr == 0x400 || uaddr == 0x500 || uaddr == 0x600 ||
+           uaddr == 0x700 || uaddr == 0x800 || uaddr == 0x900 ||
+           uaddr == 0x1000) {
+                printf("%x\n", uaddr)
+       }})' &
+echo -e '\nktap tracing: kdebug.kprobe("SyS_futex", function () {})'
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+#------------------------------------------------------------#
+
+stap -e 'probe syscall.futex {
+       uaddr = $uaddr
+        if (uaddr == 0x100 || uaddr == 0x200 || uaddr == 0x300 ||
+           uaddr == 0x400 || uaddr == 0x500 || uaddr == 0x600 ||
+           uaddr == 0x700 || uaddr == 0x800 || uaddr == 0x900 ||
+           uaddr == 0x1000) {
+                printf("%x\n", uaddr)
+        }}' &
+
+echo -e "\nstap tracing: probe syscall.futex { if (uaddr == 0x100 || addr == 
0x200 ... }"
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof stap`
+disown $pid; kill -9 $pid; sleep 1
+
+#------------------------------------------------------------#
+
+
+../../ktap -d -q -e 'trace syscalls:sys_enter_futex {
+       var uaddr = arg2
+        if (uaddr == 0x100 || uaddr == 0x200 || uaddr == 0x300 ||
+           uaddr == 0x400 || uaddr == 0x500 || uaddr == 0x600 ||
+           uaddr == 0x700 || uaddr == 0x800 || uaddr == 0x900 ||
+           uaddr == 0x1000) {
+                printf("%x %x\n", arg1, arg2)
+        }}' &
+
+echo -e "\nktap tracing dry-run: trace syscalls:sys_enter_futex { if (arg2 == 
0x100 || arg2 == 0x200 ... }"
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+
+#------------------------------------------------------------#
+
+../../ktap -d -q -e 'kdebug.tracepoint("sys_enter_futex", function () {
+       var arg = arg2
+        if (arg == 0x100 || arg == 0x200 || arg == 0x300 || arg == 0x400 ||
+            arg == 0x500 || arg == 0x600 || arg == 0x700 || arg == 0x800 ||
+            arg == 0x900 || arg == 0x1000) {
+                printf("%x %x\n", arg1, arg2)
+        }})' &
+
+echo -e '\nktap tracing dry-run: kdebug.tracepoint("sys_enter_futex", function 
(xxx) {})'
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+
+#------------------------------------------------------------#
+
+../../ktap -q -e 'trace syscalls:sys_enter_futex /
+       uaddr == 0x100 || uaddr == 0x200 || uaddr == 0x300 || uaddr == 0x400 ||
+       uaddr == 0x500 || uaddr == 0x600 || uaddr == 0x700 || uaddr == 0x800 ||
+       uaddr == 0x900 || uaddr == 0x1000/ {
+               printf("%x %x\n", arg1, arg2)
+       }' &
+
+echo -e "\nktap tracing: trace syscalls:sys_enter_futex /uaddr == 0x100 || 
uaddr == 0x200 .../ {}"
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+#------------------------------------------------------------#
+
+rm -rf ./sembench
+
diff --git a/tools/ktap/test/benchmark/cmp_profile.sh 
b/tools/ktap/test/benchmark/cmp_profile.sh
new file mode 100644
index 0000000..c400d8d
--- /dev/null
+++ b/tools/ktap/test/benchmark/cmp_profile.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# This script compare stack profiling performance between ktap and stap.
+#
+# 1. ktap -e 'profile-1000us { s[stack(-1, 12)] += 1 }'
+# 2. stap -e 'probe timer.profile { s[backtrace()] += 1 }'
+# 3. stap -e 'probe timer.profile { s[backtrace()] <<< 1 }'
+
+#Result:
+#Currently the stack profiling overhead is nearly same between ktap and stap.
+#
+#ktap reslove kernel stack to string in runtime, which is very time consuming,
+#optimize it in future.
+
+
+gcc -o sembench sembench.c -O2 -lpthread
+
+COMMAND="./sembench -t 200 -w 20 -r 30 -o 2"
+
+#------------------------------------------------------------#
+
+echo -e "without tracing:"
+$COMMAND; $COMMAND; $COMMAND
+
+#------------------------------------------------------------#
+
+../../ktap -q -e 'var s = table.new(0, 20000) profile-1000us { s[stack(-1, 
12)] += 1 }' &
+
+echo -e "\nktap tracing: profile-1000us { s[stack(-1, 12)] += 1 }"
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+#------------------------------------------------------------#
+
+stap -o /dev/null -e 'global s[20000]; probe timer.profile { s[backtrace()] += 
1 }' &
+
+echo -e "\nstap tracing: probe timer.profile { s[backtrace()] += 1 }"
+$COMMAND; $COMMAND; $COMMAND
+pkill stap
+
+#------------------------------------------------------------#
+
+stap -o /dev/null -e 'global s[20000]; probe timer.profile { s[backtrace()] 
<<< 1 }' &
+
+echo -e "\nstap tracing: probe timer.profile { s[backtrace()] <<< 1 }"
+$COMMAND; $COMMAND; $COMMAND
+pkill stap
+
+#------------------------------------------------------------#
+
+
+rm -rf ./sembench
+
diff --git a/tools/ktap/test/benchmark/cmp_table.sh 
b/tools/ktap/test/benchmark/cmp_table.sh
new file mode 100644
index 0000000..6e3f12f
--- /dev/null
+++ b/tools/ktap/test/benchmark/cmp_table.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+
+# This script compare table performance between ktap and stap.
+#
+# 1. ktap -e 'trace syscalls:sys_enter_futex { s[execname] += 1 }'
+# 2. ktap -e 'kdebug.tracepoint("sys_enter_futex", function () { s[execname] 
+= 1 })'
+# 3. ktap -e 'kdebug.kprobe("SyS_futex", function () { s[execname] += 1 })'
+# 4. stap -e 'probe syscall.futex { s[execname()] += 1 }'
+# 5. ktap -e 'kdebug.kprobe("SyS_futex", function () { s[probename] += 1 })'
+# 6. stap -e 'probe syscall.futex { s[name] += 1 }'
+# 7. ktap -e 'kdebug.kprobe("SyS_futex", function () { 
s["constant_string_key"] += 1 })'
+# 8. stap -e 'probe syscall.futex { s["constant_string_key"] += 1 }'
+
+#Result:
+#Currently ktap table operation overhead is smaller than stap.
+
+
+gcc -o sembench sembench.c -O2 -lpthread
+
+COMMAND="./sembench -t 200 -w 20 -r 30 -o 2"
+
+#------------------------------------------------------------#
+
+echo -e "without tracing:"
+$COMMAND; $COMMAND; $COMMAND
+
+#------------------------------------------------------------#
+
+../../ktap -q -e 'var s = {} trace syscalls:sys_enter_futex { s[execname] += 1 
}' &
+
+echo -e "\nktap tracing: trace syscalls:sys_enter_futex { s[execname] += 1 }"
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+#------------------------------------------------------------#
+
+../../ktap -q -e 'var s = {} kdebug.tracepoint("sys_enter_futex", function () {
+       s[execname] += 1 })' &
+
+echo -e '\nktap tracing: kdebug.tracepoint("sys_enter_futex", function () { 
s[execname] += 1})'
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+#------------------------------------------------------------#
+
+../../ktap -q -e 'var s = {} kdebug.kprobe("SyS_futex", function () {
+       s[execname] += 1 })' &
+
+echo -e '\nktap tracing: kdebug.kprobe("SyS_futex", function () { s[execname] 
+= 1 })'
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+#------------------------------------------------------------#
+
+stap -e 'global s; probe syscall.futex { s[execname()] += 1 }' &
+
+echo -e "\nstap tracing: probe syscall.futex { s[execname()] += 1 }"
+$COMMAND; $COMMAND; $COMMAND
+pkill stap
+
+#------------------------------------------------------------#
+
+../../ktap -q -e 'var s = {} kdebug.kprobe("SyS_futex", function () {
+       s[probename] += 1 })' &
+
+echo -e '\nktap tracing: kdebug.kprobe("SyS_futex", function () { s[probename] 
+= 1 })'
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+#------------------------------------------------------------#
+
+stap -e 'global s; probe syscall.futex { s[name] += 1 }' &
+
+echo -e "\nstap tracing: probe syscall.futex { s[name] += 1 }"
+$COMMAND; $COMMAND; $COMMAND
+pkill stap
+
+#------------------------------------------------------------#
+
+../../ktap -q -e 'var s = {} s["const_string_key"] = 0 
kdebug.kprobe("SyS_futex", function () {
+       s["const_string_key"] += 1 })' &
+
+echo -e '\nktap tracing: kdebug.kprobe("SyS_futex", function () { 
s["const_string_key"] += 1 })'
+$COMMAND; $COMMAND; $COMMAND
+pid=`pidof ktap`
+disown $pid; kill -9 $pid; sleep 1
+
+#------------------------------------------------------------#
+
+stap -e 'global s; probe syscall.futex { s["const_string_key"] += 1 }' &
+
+echo -e "\nstap tracing: probe syscall.futex { s["const_string_key"] += 1 }"
+$COMMAND; $COMMAND; $COMMAND
+pkill stap
+
+#------------------------------------------------------------#
+
+stap -o /dev/null -e 'global s; probe syscall.futex { s["const_string_key"] 
<<< 1 }' &
+
+echo -e "\nstap tracing: probe syscall.futex { s["const_string_key"] <<< 1 }"
+$COMMAND; $COMMAND; $COMMAND
+pkill stap
+
+#------------------------------------------------------------#
+
+
+rm -rf ./sembench
+
diff --git a/tools/ktap/test/benchmark/sembench.c 
b/tools/ktap/test/benchmark/sembench.c
new file mode 100644
index 0000000..5dfccd5
--- /dev/null
+++ b/tools/ktap/test/benchmark/sembench.c
@@ -0,0 +1,556 @@
+/*
+ * copyright Oracle 2007.  Licensed under GPLv2
+ * To compile: gcc -Wall -o sembench sembench.c -lpthread
+ *
+ * usage: sembench -t thread count -w wakenum -r runtime -o op
+ * op can be: 0 (ipc sem) 1 (nanosleep) 2 (futexes)
+ *
+ * example:
+ *     sembench -t 1024 -w 512 -r 60 -o 2
+ * runs 1024 threads, waking up 512 at a time, running for 60 seconds using
+ * futex locking.
+ *
+ */
+#define  _GNU_SOURCE
+#define _POSIX_C_SOURCE 199309
+#include <fcntl.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/sem.h>
+#include <sys/ipc.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/syscall.h>
+#include <errno.h>
+
+#define VERSION "0.2"
+
+/* futexes have been around since 2.5.something, but it still seems I
+ * need to make my own syscall.  Sigh.
+ */
+#define FUTEX_WAIT              0
+#define FUTEX_WAKE              1
+#define FUTEX_FD                2
+#define FUTEX_REQUEUE           3
+#define FUTEX_CMP_REQUEUE       4
+#define FUTEX_WAKE_OP           5
+static inline int futex (int *uaddr, int op, int val,
+                        const struct timespec *timeout,
+                        int *uaddr2, int val3)
+{
+       return syscall(__NR_futex, uaddr, op, val, timeout, uaddr2, val3);
+}
+
+static void smp_mb(void)
+{
+       __sync_synchronize();
+}
+
+static int all_done = 0;
+static int timeout_test = 0;
+
+#define SEMS_PERID 250
+
+struct sem_operations;
+
+struct lockinfo {
+       unsigned long id;
+       unsigned long index;
+       int data;
+       pthread_t tid;
+       struct lockinfo *next;
+       struct sem_operations *ops;
+       unsigned long ready;
+};
+
+struct sem_wakeup_info {
+       int wakeup_count;
+       struct sembuf sb[SEMS_PERID];
+};
+
+struct sem_operations {
+       void (*wait)(struct lockinfo *l);
+       int (*wake)(struct sem_wakeup_info *wi, int num_semids, int num);
+       void (*setup)(struct sem_wakeup_info **wi, int num_semids);
+       void (*cleanup)(int num_semids);
+       char *name;
+};
+
+int *semid_lookup = NULL;
+
+pthread_mutex_t worklist_mutex = PTHREAD_MUTEX_INITIALIZER;
+static unsigned long total_burns = 0;
+static unsigned long min_burns = ~0UL;
+static unsigned long max_burns = 0;
+
+/* currently running threads */
+static int thread_count = 0;
+
+struct lockinfo *worklist = NULL;
+static int workers_started = 0;
+
+/* total threads started */
+static int num_threads = 2048;
+
+static void worklist_add(struct lockinfo *l)
+{
+       smp_mb();
+       l->ready = 1;
+}
+
+static struct lockinfo *worklist_rm(void)
+{
+       static int last_index = 0;
+       int i;
+       struct lockinfo *l;
+
+       for (i = 0; i < num_threads; i++) {
+               int test = (last_index + i) % num_threads;
+
+               l = worklist + test;
+               smp_mb();
+               if (l->ready) {
+                       l->ready = 0;
+                       last_index = test;
+                       return l;
+               }
+       }
+       return NULL;
+}
+
+/* ipc semaphore post& wait */
+void wait_ipc_sem(struct lockinfo *l)
+{
+       struct sembuf sb;
+       int ret;
+       struct timespec *tvp = NULL;
+       struct timespec tv = { 0, 1 };
+
+       sb.sem_num = l->index;
+       sb.sem_flg = 0;
+
+       sb.sem_op = -1;
+       l->data = 1;
+
+       if (timeout_test && (l->id % 5) == 0)
+               tvp = &tv;
+
+       worklist_add(l);
+       ret = semtimedop(semid_lookup[l->id], &sb, 1, tvp);
+
+       while(l->data != 0 && tvp) {
+               struct timespec tv2 = { 0, 500 };
+               nanosleep(&tv2, NULL);
+       }
+
+       if (l->data != 0) {
+               if (tvp)
+                       return;
+               fprintf(stderr, "wakeup without data update\n");
+               exit(1);
+       }
+       if (ret) {
+               if (errno == EAGAIN && tvp)
+                       return;
+               perror("semtimed op");
+               exit(1);
+       }
+}
+
+int ipc_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
+{
+       int i;
+       int ret;
+       struct lockinfo *l;
+       int found = 0;
+
+       for (i = 0; i < num_semids; i++) {
+               wi[i].wakeup_count = 0;
+       }
+       while(num > 0) {
+               struct sembuf *sb;
+               l = worklist_rm();
+               if (!l)
+                       break;
+               if (l->data != 1)
+                       fprintf(stderr, "warning, lockinfo data was %d\n",
+                               l->data);
+               l->data = 0;
+               sb = wi[l->id].sb + wi[l->id].wakeup_count;
+               sb->sem_num = l->index;
+               sb->sem_op = 1;
+               sb->sem_flg = IPC_NOWAIT;
+               wi[l->id].wakeup_count++;
+               found++;
+               num--;
+       }
+       if (!found)
+               return 0;
+       for (i = 0; i < num_semids; i++) {
+               int wakeup_total;
+               int cur;
+               int offset = 0;
+               if (!wi[i].wakeup_count)
+                       continue;
+               wakeup_total = wi[i].wakeup_count;
+               while(wakeup_total > 0) {
+                       cur = wakeup_total > 64 ? 64 : wakeup_total;
+                       ret = semtimedop(semid_lookup[i], wi[i].sb + offset,
+                                        cur, NULL);
+                       if (ret) {
+                               perror("semtimedop");
+                               exit(1);
+                       }
+                       offset += cur;
+                       wakeup_total -= cur;
+               }
+       }
+       return found;
+}
+
+void setup_ipc_sems(struct sem_wakeup_info **wi, int num_semids)
+{
+       int i;
+       *wi = malloc(sizeof(**wi) * num_semids);
+       semid_lookup = malloc(num_semids * sizeof(int));
+       for(i = 0; i < num_semids; i++) {
+               semid_lookup[i] = semget(IPC_PRIVATE, SEMS_PERID,
+                                        IPC_CREAT | 0777);
+               if (semid_lookup[i] < 0) {
+                       perror("semget");
+                       exit(1);
+               }
+       }
+       sleep(10);
+}
+
+void cleanup_ipc_sems(int num)
+{
+       int i;
+       for (i = 0; i < num; i++) {
+               semctl(semid_lookup[i], 0, IPC_RMID);
+       }
+}
+
+struct sem_operations ipc_sem_ops = {
+       .wait = wait_ipc_sem,
+       .wake = ipc_wake_some,
+       .setup = setup_ipc_sems,
+       .cleanup = cleanup_ipc_sems,
+       .name = "ipc sem operations",
+};
+
+/* futex post & wait */
+void wait_futex_sem(struct lockinfo *l)
+{
+       int ret;
+       l->data = 1;
+       worklist_add(l);
+       while(l->data == 1) {
+               ret = futex(&l->data, FUTEX_WAIT, 1, NULL, NULL, 0);
+               /*
+               if (ret && ret != EWOULDBLOCK) {
+                       perror("futex wait");
+                       exit(1);
+               }*/
+       }
+}
+
+int futex_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
+{
+       int i;
+       int ret;
+       struct lockinfo *l;
+       int found = 0;
+
+       for (i = 0; i < num; i++) {
+               l = worklist_rm();
+               if (!l)
+                       break;
+               if (l->data != 1)
+                       fprintf(stderr, "warning, lockinfo data was %d\n",
+                               l->data);
+               l->data = 0;
+               ret = futex(&l->data, FUTEX_WAKE, 1, NULL, NULL, 0);
+               if (ret < 0) {
+                       perror("futex wake");
+                       exit(1);
+               }
+               found++;
+       }
+       return found;
+}
+
+void setup_futex_sems(struct sem_wakeup_info **wi, int num_semids)
+{
+       return;
+}
+
+void cleanup_futex_sems(int num)
+{
+       return;
+}
+
+struct sem_operations futex_sem_ops = {
+       .wait = wait_futex_sem,
+       .wake = futex_wake_some,
+       .setup = setup_futex_sems,
+       .cleanup = cleanup_futex_sems,
+       .name = "futex sem operations",
+};
+
+/* nanosleep sems here */
+void wait_nanosleep_sem(struct lockinfo *l)
+{
+       int ret;
+       struct timespec tv = { 0, 1000000 };
+       int count = 0;
+
+       l->data = 1;
+       worklist_add(l);
+       while(l->data) {
+               ret = nanosleep(&tv, NULL);
+               if (ret) {
+                       perror("nanosleep");
+                       exit(1);
+               }
+               count++;
+       }
+}
+
+int nanosleep_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
+{
+       int i;
+       struct lockinfo *l;
+
+       for (i = 0; i < num; i++) {
+               l = worklist_rm();
+               if (!l)
+                       break;
+               if (l->data != 1)
+                       fprintf(stderr, "warning, lockinfo data was %d\n",
+                               l->data);
+               l->data = 0;
+       }
+       return i;
+}
+
+void setup_nanosleep_sems(struct sem_wakeup_info **wi, int num_semids)
+{
+       return;
+}
+
+void cleanup_nanosleep_sems(int num)
+{
+       return;
+}
+
+struct sem_operations nanosleep_sem_ops = {
+       .wait = wait_nanosleep_sem,
+       .wake = nanosleep_wake_some,
+       .setup = setup_nanosleep_sems,
+       .cleanup = cleanup_nanosleep_sems,
+       .name = "nano sleep sem operations",
+};
+
+void *worker(void *arg)
+{
+       struct lockinfo *l = (struct lockinfo *)arg;
+       int burn_count = 0;
+       pthread_t tid = pthread_self();
+       size_t pagesize = getpagesize();
+       char *buf = malloc(pagesize);
+
+       if (!buf) {
+               perror("malloc");
+               exit(1);
+       }
+
+       l->tid = tid;
+       workers_started = 1;
+       smp_mb();
+
+       while(!all_done) {
+               l->ops->wait(l);
+               if (all_done)
+                       break;
+               burn_count++;
+       }
+       pthread_mutex_lock(&worklist_mutex);
+       total_burns += burn_count;
+       if (burn_count < min_burns)
+               min_burns = burn_count;
+       if (burn_count > max_burns)
+               max_burns = burn_count;
+       thread_count--;
+       pthread_mutex_unlock(&worklist_mutex);
+       return (void *)0;
+}
+
+void print_usage(void)
+{
+       printf("usage: sembench [-t threads] [-w wake incr] [-r runtime]");
+       printf("                [-o num] (0=ipc, 1=nanosleep, 2=futex)\n");
+       exit(1);
+}
+
+#define NUM_OPERATIONS 3
+struct sem_operations *allops[NUM_OPERATIONS] = { &ipc_sem_ops,
+                                               &nanosleep_sem_ops,
+                                               &futex_sem_ops};
+
+int main(int ac, char **av) {
+       int ret;
+       int i;
+       int semid = 0;
+       int sem_num = 0;
+       int burn_count = 0;
+       struct sem_wakeup_info *wi = NULL;
+       struct timeval start;
+       struct timeval now;
+       int num_semids = 0;
+       int wake_num = 256;
+       int run_secs = 30;
+       int pagesize = getpagesize();
+       char *buf = malloc(pagesize);
+       struct sem_operations *ops = allops[0];
+       cpu_set_t cpu_mask;
+       cpu_set_t target_mask;
+       int target_cpu = 0;
+       int max_cpu = -1;
+
+       if (!buf) {
+               perror("malloc");
+               exit(1);
+       }
+       for (i = 1; i < ac; i++) {
+               if (strcmp(av[i], "-t") == 0) {
+                       if (i == ac -1)
+                               print_usage();
+                       num_threads = atoi(av[i+1]);
+                       i++;
+               } else if (strcmp(av[i], "-w") == 0) {
+                       if (i == ac -1)
+                               print_usage();
+                       wake_num = atoi(av[i+1]);
+                       i++;
+               } else if (strcmp(av[i], "-r") == 0) {
+                       if (i == ac -1)
+                               print_usage();
+                       run_secs = atoi(av[i+1]);
+                       i++;
+               } else if (strcmp(av[i], "-o") == 0) {
+                       int index;
+                       if (i == ac -1)
+                               print_usage();
+                       index = atoi(av[i+1]);
+                       if (index >= NUM_OPERATIONS) {
+                               fprintf(stderr, "invalid operations %d\n",
+                                       index);
+                               exit(1);
+                       }
+                       ops = allops[index];
+                       i++;
+               } else if (strcmp(av[i], "-T") == 0) {
+                       timeout_test = 1;
+               } else if (strcmp(av[i], "-h") == 0) {
+                       print_usage();
+               }
+       }
+       num_semids = (num_threads + SEMS_PERID - 1) / SEMS_PERID;
+       ops->setup(&wi, num_semids);
+
+       ret = sched_getaffinity(0, sizeof(cpu_set_t), &cpu_mask);
+       if (ret) {
+               perror("sched_getaffinity");
+               exit(1);
+       }
+       for (i = 0; i < CPU_SETSIZE; i++)
+               if (CPU_ISSET(i, &cpu_mask))
+                       max_cpu = i;
+       if (max_cpu == -1) {
+               fprintf(stderr, "sched_getaffinity returned empty mask\n");
+               exit(1);
+       }
+
+       CPU_ZERO(&target_mask);
+
+       worklist = malloc(sizeof(*worklist) * num_threads);
+       memset(worklist, 0, sizeof(*worklist) * num_threads);
+
+       for (i = 0; i < num_threads; i++) {
+               struct lockinfo *l;
+               pthread_t tid;
+               thread_count++;
+               l = worklist + i;
+               if (!l) {
+                       perror("malloc");
+                       exit(1);
+               }
+               l->id = semid;
+               l->index = sem_num++;
+               l->ops = ops;
+               if (sem_num >= SEMS_PERID) {
+                       semid++;
+                       sem_num = 0;
+               }
+               ret = pthread_create(&tid, NULL, worker, (void *)l);
+               if (ret) {
+                       perror("pthread_create");
+                       exit(1);
+               }
+
+               while (!CPU_ISSET(target_cpu, &cpu_mask)) {
+                       target_cpu++;
+                       if (target_cpu > max_cpu)
+                               target_cpu = 0;
+               }
+               CPU_SET(target_cpu, &target_mask);
+               ret = pthread_setaffinity_np(tid, sizeof(cpu_set_t),
+                                            &target_mask);
+               CPU_CLR(target_cpu, &target_mask);
+               target_cpu++;
+
+               ret = pthread_detach(tid);
+               if (ret) {
+                       perror("pthread_detach");
+                       exit(1);
+               }
+       }
+       while(!workers_started) {
+               smp_mb();
+               usleep(200);
+       }
+       gettimeofday(&start, NULL);
+       //fprintf(stderr, "main loop going\n");
+       while(1) {
+               ops->wake(wi, num_semids, wake_num);
+               burn_count++;
+               gettimeofday(&now, NULL);
+               if (now.tv_sec - start.tv_sec >= run_secs)
+                       break;
+       }
+       //fprintf(stderr, "all done\n");
+       all_done = 1;
+       while(thread_count > 0) {
+               ops->wake(wi, num_semids, wake_num);
+               usleep(200);
+       }
+       //printf("%d threads, waking %d at a time\n", num_threads, wake_num);
+       //printf("using %s\n", ops->name);
+       //printf("main thread burns: %d\n", burn_count);
+       //printf("worker burn count total %lu min %lu max %lu avg %lu\n",
+       //       total_burns, min_burns, max_burns, total_burns / num_threads);
+       printf("%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/tools/ktap/test/cli-arg.t b/tools/ktap/test/cli-arg.t
new file mode 100644
index 0000000..4bf3f6c
--- /dev/null
+++ b/tools/ktap/test/cli-arg.t
@@ -0,0 +1,25 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: sanity
+--- args: 1 testing "2 3 4"
+--- src
+printf("arg 0: %s\n", arg[0])
+printf("arg 1: %d\n", arg[1])
+printf("arg 2: %s\n", arg[2])
+printf("arg 3: %s\n", arg[2])
+
+--- out_like chop
+^arg 0: /tmp/\S+\.kp
+arg 1: 1
+arg 2: testing
+arg 3: testing$
+
+--- err
+
diff --git a/tools/ktap/test/concat.t b/tools/ktap/test/concat.t
new file mode 100644
index 0000000..12e33ee
--- /dev/null
+++ b/tools/ktap/test/concat.t
@@ -0,0 +1,21 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: string concat
+--- src
+var a = "123"
+var b = "456"
+
+print(a..b)
+
+--- out
+123456
+--- err
+
+
diff --git a/tools/ktap/test/count.t b/tools/ktap/test/count.t
new file mode 100644
index 0000000..972bf86
--- /dev/null
+++ b/tools/ktap/test/count.t
@@ -0,0 +1,25 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: count
+--- src
+var t = {}
+
+t["key"] += 1
+print(t["key"])
+
+t["key"] += 1
+print(t["key"])
+
+--- out
+1
+2
+--- err
+
+
diff --git a/tools/ktap/test/deadloop.t b/tools/ktap/test/deadloop.t
new file mode 100644
index 0000000..3fc4f97
--- /dev/null
+++ b/tools/ktap/test/deadloop.t
@@ -0,0 +1,37 @@
+# vi: ft= et ts=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: exit dead loop
+--- src
+tick-1s {
+       exit()
+}
+
+tick-3s {
+       print("dead loop not exited")
+}
+
+while (1) {}
+
+--- out_like
+error: loop execute count exceed max limit(.*)
+--- err
+
+
+
+=== TEST 2: dead loop killed by signal
+--- src
+
+while (1) {}
+
+--- out_like
+error: loop execute count exceed max limit(.*)
+
+--- err
+
diff --git a/tools/ktap/test/fibonacci.t b/tools/ktap/test/fibonacci.t
new file mode 100644
index 0000000..f92d244
--- /dev/null
+++ b/tools/ktap/test/fibonacci.t
@@ -0,0 +1,42 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: regular recursive fibonacci
+--- src
+function fib(n) {
+       if (n < 2) {
+               return n
+       }
+       return fib(n-1) + fib(n-2)
+}
+
+print(fib(20))
+--- out
+6765
+--- err
+
+
+
+=== TEST 2: tail recursive fibonacci
+--- src
+function fib(n) {
+       function f(iter, res, next) {
+               if (iter == 0) {
+                       return res;
+               }
+               return f(iter-1, next, res+next)
+       }
+       return f(n, 0, 1)
+}
+
+print(fib(20))
+--- out
+6765
+--- err
+
diff --git a/tools/ktap/test/function.t b/tools/ktap/test/function.t
new file mode 100644
index 0000000..cd44ccb
--- /dev/null
+++ b/tools/ktap/test/function.t
@@ -0,0 +1,78 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: function
+--- src
+### basic function call ###
+function f1(a, b) {
+       return a + b
+}
+
+print(f1(2, 3))
+
+### return string ###
+function f2() {
+       return "function return"
+}
+
+print(f2())
+
+### closure testing ### 
+function f4() {
+       var f5 = function(a, b) {
+               return a * b
+       }
+       return f5
+}
+
+var f = f4()
+print(f(9, 9))
+
+### closure with lexcial variable ### 
+var i = 1
+function f6() {
+       i = 5
+       var f7 = function(a, b) {
+               return a * b + i
+       }
+       return f7
+}
+
+f = f6()
+print(f(9, 9))
+
+i = 6
+print(f(9, 9))
+
+### tail call
+### stack should not overflow in tail call mechanism
+var a = 0
+function f8(i) {
+       if (i == 1000000) {
+               a = 1000000
+               return
+       }
+       # must add return here, otherwise stack overflow
+       return f8(i+1)
+}
+
+f8(0)
+print(a)
+
+--- out
+5
+function return
+81
+86
+87
+1000000
+
+--- err
+
+
diff --git a/tools/ktap/test/if.t b/tools/ktap/test/if.t
new file mode 100644
index 0000000..05989f2
--- /dev/null
+++ b/tools/ktap/test/if.t
@@ -0,0 +1,32 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: test if
+--- src
+
+if (false) {
+       print("failed")
+}
+
+if (nil) {
+       print("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) {
+       print("number 0 is true")
+}
+
+--- out
+number 0 is true
+--- err
+
+
diff --git a/tools/ktap/test/kprobe.t b/tools/ktap/test/kprobe.t
new file mode 100644
index 0000000..4ea342e
--- /dev/null
+++ b/tools/ktap/test/kprobe.t
@@ -0,0 +1,82 @@
+# vi: ft= et ts=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: kprobe
+--- opts: -q
+--- src
+
+var n = 0
+trace probe:schedule {
+       n = n + 1
+}
+
+# share same event id with previous one
+trace probe:schedule {
+}
+
+# test event filter
+trace probe:do_sys_open dfd=%di filename=%si flags=%dx mode=%cx /dfd==1/ { }
+
+tick-1s {
+       print(n==0)
+       exit()
+}
+--- out
+false
+--- err
+
+
+
+=== TEST 2: kretprobe
+--- opts: -q
+--- src
+var n = 0
+trace probe:__schedule%return {
+       n = n + 1
+}
+
+tick-1s {
+       print(n==0)
+       exit()
+}
+
+--- out
+false
+--- err
+
+
+=== TEST 3: only can be called in mainthread
+--- opts: -q
+--- src
+
+trace probe:schedule {
+       trace *:* {
+       }
+}
+
+--- out
+error: only mainthread can create function
+--- err
+
+
+=== TEST 4: can not be called in trace_end context
+--- opts: -q
+--- src
+
+trace_end {
+       trace *:* {
+       }
+}
+
+--- out
+error: kdebug.trace_by_id only can be called in RUNNING state
+--- err
+
+
+
diff --git a/tools/ktap/test/kretprobe.t b/tools/ktap/test/kretprobe.t
new file mode 100644
index 0000000..2ee76ec
--- /dev/null
+++ b/tools/ktap/test/kretprobe.t
@@ -0,0 +1,35 @@
+# vi: ft= et ts=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: kprobe
+--- opts: -q
+--- src
+
+var n = 0
+trace probe:schedule {
+       n = n + 1
+}
+
+# share same event id with previous one
+trace probe:schedule {
+}
+
+# test event filter
+trace probe:do_sys_open dfd=%di filename=%si flags=%dx mode=%cx /dfd==1/ { }
+
+tick-1s {
+       print(n==0)
+       exit()
+}
+
+--- out
+false
+--- err
+
+
diff --git a/tools/ktap/test/len.t b/tools/ktap/test/len.t
new file mode 100644
index 0000000..9de5253
--- /dev/null
+++ b/tools/ktap/test/len.t
@@ -0,0 +1,27 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: len
+--- src
+var a = "123456789"
+
+print(len(a))
+
+var b = {}
+b[0] = 0
+b[1] = 1
+b["keys"] = "values"
+
+print(len(b))
+
+--- out
+9
+3
+--- err
+
diff --git a/tools/ktap/test/lib/Test/ktap.pm b/tools/ktap/test/lib/Test/ktap.pm
new file mode 100644
index 0000000..94c551f
--- /dev/null
+++ b/tools/ktap/test/lib/Test/ktap.pm
@@ -0,0 +1,128 @@
+# Copyright (C) Yichun Zhang (agentzh)
+
+package Test::ktap;
+
+use Test::Base -Base;
+use POSIX ();
+use IPC::Run ();
+
+our @EXPORT = qw( run_tests );
+
+sub run_tests () {
+    for my $block (Test::Base::blocks()) {
+        run_test($block);
+    }
+}
+
+sub bail_out (@) {
+    Test::More::BAIL_OUT(@_);
+}
+
+sub parse_cmd ($) {
+    my $cmd = shift;
+    my @cmd;
+    while (1) {
+        if ($cmd =~ /\G\s*"(.*?)"/gmsc) {
+            push @cmd, $1;
+
+        } elsif ($cmd =~ /\G\s*'(.*?)'/gmsc) {
+            push @cmd, $1;
+
+        } elsif ($cmd =~ /\G\s*(\S+)/gmsc) {
+            push @cmd, $1;
+
+        } else {
+            last;
+        }
+    }
+    return @cmd;
+}
+
+sub run_test ($) {
+    my $block = shift;
+    my $name = $block->name;
+
+    my $timeout = $block->timeout() || 10;
+    my $opts = $block->opts;
+    my $args = $block->args;
+
+    my $cmd = "./ktap";
+
+    if (defined $opts) {
+        $cmd .= " $opts";
+    }
+
+    my $kpfile;
+    if (defined $block->src) {
+        $kpfile = POSIX::tmpnam() . ".kp";
+        open my $out, ">$kpfile" or
+            bail_out("cannot open $kpfile for writing: $!");
+        print $out ($block->src);
+        close $out;
+        $cmd .= " $kpfile"
+    }
+
+    if (defined $args) {
+        $cmd .= " $args";
+    }
+
+    #warn "CMD: $cmd\n";
+
+    my @cmd = parse_cmd($cmd);
+
+    my ($out, $err);
+
+    eval {
+        IPC::Run::run(\@cmd, \undef, \$out, \$err,
+                      IPC::Run::timeout($timeout));
+    };
+    if ($@) {
+        # timed out
+        if ($@ =~ /timeout/) {
+            if (!defined $block->expect_timeout) {
+                fail("$name: ktap process timed out");
+            }
+       } else {
+            fail("$name: failed to run command [$cmd]: $@");
+        }
+    }
+
+    my $ret = ($? >> 8);
+
+    if (defined $kpfile) {
+        unlink $kpfile;
+    }
+
+    if (defined $block->out) {
+        is $out, $block->out, "$name - stdout eq okay";
+    }
+
+    my $regex = $block->out_like;
+    if (defined $regex) {
+        if (!ref $regex) {
+            $regex = qr/$regex/ms;
+        }
+        like $out, $regex, "$name - stdout like okay";
+    }
+
+    if (defined $block->err) {
+        is $err, $block->err, "$name - stderr eq okay";
+    }
+
+    $regex = $block->err_like;
+    if (defined $regex) {
+        if (!ref $regex) {
+            $regex = qr/$regex/ms;
+        }
+        like $err, $regex, "$name - stderr like okay";
+    }
+
+    my $exp_ret = $block->ret;
+    if (!defined $exp_ret) {
+        $exp_ret = 0;
+    }
+    is $ret, $exp_ret, "$name - exit code okay";
+}
+
+1;
+# vi: et
diff --git a/tools/ktap/test/looping.t b/tools/ktap/test/looping.t
new file mode 100644
index 0000000..3f61118
--- /dev/null
+++ b/tools/ktap/test/looping.t
@@ -0,0 +1,46 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: looping
+--- src
+
+### basic while-loop testing
+var a = 1
+while (a < 1000) {
+       a = a + 1
+}
+
+print(a)
+
+### break testing
+### Note that ktap don't have continue keyword
+var a = 1
+while (a < 1000) {
+       if (a == 10) {
+               break
+       }
+       a = a + 1
+}
+
+print(a)
+
+### for-loop testing
+var b = 0
+for (c = 0, 1000, 1) {
+       b = b + 1
+}
+
+print(b)
+
+--- out
+1000
+10
+1001
+--- err
+
diff --git a/tools/ktap/test/one-liner.t b/tools/ktap/test/one-liner.t
new file mode 100644
index 0000000..9998b1a
--- /dev/null
+++ b/tools/ktap/test/one-liner.t
@@ -0,0 +1,48 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: print
+--- args: -e 'print("one-liner testing")'
+--- out
+one-liner testing
+--- err
+
+
+
+=== TEST 2: exit
+--- args: -e 'exit() print("failed")'
+--- out
+--- err
+
+
+
+=== TEST 3: syscalls in "ls"
+--- args: -e 'trace syscalls:* { print(argstr) }' -- ls
+--- out_like
+sys_mprotect -> 0x0
+.*?
+sys_close\(fd: \d+\)
+--- err
+
+
+
+=== TEST 4: trace ktap syscalls
+--- args: -e 'trace syscalls:* { print(argstr) }' -- ./ktap -e 'print("trace 
ktap by self")'
+--- out_like
+sys_mprotect -> 0x0
+.*?
+sys_close\(fd: \d+\)
+--- err
+
+=== TEST 5: trace ktap function calls
+--- args: -q -e 'trace probe:kp_* {print(argstr)}' -- ./ktap 
samples/helloworld.kp
+--- out_like
+kp_vm_new_state: (.*)
+.*?
+
diff --git a/tools/ktap/test/pairs.t b/tools/ktap/test/pairs.t
new file mode 100644
index 0000000..bcf57cf
--- /dev/null
+++ b/tools/ktap/test/pairs.t
@@ -0,0 +1,52 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: looping
+--- src
+
+var 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"
+
+var n = 0
+
+for (k, v in pairs(t)) {
+       n = n + 1
+
+       if (k == 1 && v != 101) {
+               print("failed")
+       }
+       if (k == 2 && v != 102) {
+               print("failed")
+       }
+       if (k == 3 && v != 103) {
+               print("failed")
+       }
+       if (k == "key_1" && v != "value_1") {
+               print("failed")
+       }
+       if (k == "key_2" && v != "value_2") {
+               print("failed")
+       }
+       if (k == "key_3" && v != "value_3") {
+               print("failed")
+       }
+}
+
+if (n != len(t)) {
+       print("failed")
+}
+
+--- out
+--- err
+
diff --git a/tools/ktap/test/stack_overflow.t b/tools/ktap/test/stack_overflow.t
new file mode 100644
index 0000000..702d389
--- /dev/null
+++ b/tools/ktap/test/stack_overflow.t
@@ -0,0 +1,22 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: stack overflow
+--- src
+function f(a) {
+               return 1 + f(a+1)
+}
+
+print(f(0))
+
+--- out_like
+(.*)stack overflow(.*)
+--- err
+
+
diff --git a/tools/ktap/test/syntax-err.t b/tools/ktap/test/syntax-err.t
new file mode 100644
index 0000000..b400c2f
--- /dev/null
+++ b/tools/ktap/test/syntax-err.t
@@ -0,0 +1,19 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: bad assignment (unexpected eof)
+--- src
+a =
+
+--- out
+--- err_like
+unexpected symbol near '<eof>'
+
+--- ret: 255
+
diff --git a/tools/ktap/test/table.t b/tools/ktap/test/table.t
new file mode 100644
index 0000000..f7c52d8
--- /dev/null
+++ b/tools/ktap/test/table.t
@@ -0,0 +1,81 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: table
+--- src
+
+### table testing ###
+var x = {}
+x[1] = "1"
+if (x[1] != "1") {
+       print("failed")
+}
+
+x[1] = 22222222222222222222222222222222222222222
+if (x[1] != 22222222222222222222222222222222222222222) {
+       print("failed")
+}
+
+x[1] = "jovi"
+if (x[1] != "jovi") {
+       print("failed")
+}
+
+x[11111111111111111111111111111111] = "jovi"
+if (x[11111111111111111111111111111111] != "jovi") {
+       print("failed")
+}
+
+x["jovi"] = 1
+if (x["jovi"] != 1) {
+       print("failed")
+}
+
+x["long string....................................."] = 1
+if (x["long string....................................."] != 1) {
+       print("failed")
+}
+
+# issue: subx must declare firstly, otherwise kernel will oops
+var subx = {}
+subx["test"] = "this is test"
+x["test"] = subx
+if (x["test"]["test"] != "this is test") {
+       print("failed")
+}
+
+var tbl = table.new(9999, 0)
+var i = 1
+while (i < 10000) {
+       tbl[i] = i      
+       i = i + 1
+}
+
+var i = 1
+while (i < 10000) {
+       if (tbl[i] != i) {
+               print("failed")
+       }
+       i = i + 1
+}
+
+#### table initization
+var days = {"Sunday", "Monday", "Tuesday", "Wednesday",
+               "Thursday", "Friday", "Saturday"}
+
+if (days[2] != "Monday") {
+       print("failed")
+}
+
+
+--- out
+--- err
+
+
+
diff --git a/tools/ktap/test/time.t b/tools/ktap/test/time.t
new file mode 100644
index 0000000..eb1d5fe
--- /dev/null
+++ b/tools/ktap/test/time.t
@@ -0,0 +1,59 @@
+# vi: ft= ts=4 sw=4 et
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+our $SecPattern = time();
+$SecPattern =~ s{(\d)\d$}{ my $a = $1; my $b = $a + 1; "[$a$b]\\d" }e;
+
+#warn $SecPattern;
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: gettimeofday_s
+--- src
+var begin = gettimeofday_s()
+printf("sec: %d\n", begin)
+printf("elapsed: %d\n", begin - gettimeofday_s())
+
+--- out_like eval
+qr/^sec: $::SecPattern
+elapsed: 0$/
+
+--- err
+
+
+
+=== TEST 2: gettimeofday_ms
+--- src
+printf("%d\n", gettimeofday_ms())
+
+--- out_like eval
+qr/^$::SecPattern\d{3}$/
+
+--- err
+
+
+
+=== TEST 3: gettimeofday_us
+--- src
+printf("%d", gettimeofday_us())
+
+--- out_like eval
+qr/^$::SecPattern\d{6}$/
+
+--- err
+
+
+
+=== TEST 4: gettimeofday_ns
+--- src
+printf("%d", gettimeofday_ns())
+
+--- out_like eval
+qr/^$::SecPattern\d{9}$/
+
+--- err
+
diff --git a/tools/ktap/test/timer.t b/tools/ktap/test/timer.t
new file mode 100644
index 0000000..2be2be2
--- /dev/null
+++ b/tools/ktap/test/timer.t
@@ -0,0 +1,65 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: timer
+--- opts: -q
+--- src
+
+var n1 = 0
+var n2 = 0
+
+tick-1s {
+       n1 = n1 + 1
+}
+
+tick-1s {
+       n2 = n2 + 1
+}
+
+tick-4s {
+       if (n1 == 0 || n2 == 0) {
+               print("failed")
+       }
+       exit()
+}
+
+--- out
+--- err
+
+
+=== TEST 2: cannot call timer.tick in trace_end context
+--- opts: -q
+--- src
+
+trace_end {
+       tick-1s {
+               print("error")
+       }
+}
+
+--- out
+error: timer.tick only can be called in RUNNING state
+--- err
+
+
+=== TEST 3: cannot call timer.profile in trace_end context
+--- opts: -q
+--- src
+
+trace_end {
+       profile-1s {
+               print("error")
+       }
+}
+
+--- out
+error: timer.profile only can be called in RUNNING state
+
+--- err
+
diff --git a/tools/ktap/test/tracepoint.t b/tools/ktap/test/tracepoint.t
new file mode 100644
index 0000000..f504da1
--- /dev/null
+++ b/tools/ktap/test/tracepoint.t
@@ -0,0 +1,53 @@
+# vi: ft= et ts=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: tracepoint
+--- opts: -q
+--- src
+
+var n = 0
+
+trace sched:* {
+       n = n + 1
+}
+
+tick-1s {
+       if (n == 0) {
+               print("failed")
+       }
+       exit()
+}
+
+--- out
+--- err
+
+
+=== TEST 2: enable all tracepoints in dry-run mode
+--- opts: -q -d
+--- src
+
+trace *:* {}
+
+--- out
+--- err
+--- expect_timeout
+--- timeout: 10
+
+
+=== TEST 3: test kdebug.tracepoint
+--- opts: -q
+--- src
+
+kdebug.tracepoint("sys_enter_open", function () {})
+tick-1s {
+       exit()
+}
+
+--- out
+--- err
diff --git a/tools/ktap/test/util/reindex b/tools/ktap/test/util/reindex
new file mode 100755
index 0000000..e4e1b4e
--- /dev/null
+++ b/tools/ktap/test/util/reindex
@@ -0,0 +1,61 @@
+#!/usr/bin/env perl
+
+# reindex
+# reindex .t files for Test::Base based test files
+# Copyright (C) Yichun Zhang (agentzh)
+
+use strict;
+use warnings;
+
+use Getopt::Std;
+
+my %opts;
+getopts('hb:', \%opts);
+if ($opts{h} or ! @ARGV) {
+    die "Usage: reindex [-b 0] t/*.t\n";
+}
+
+my $init = $opts{b};
+$init = 1 if not defined $init;
+
+my @files = map glob, @ARGV;
+for my $file (@files) {
+    next if -d $file or $file !~ /\.t_?$/;
+    reindex($file);
+}
+
+sub reindex {
+    my $file = $_[0];
+    open my $in, $file or
+        die "Can't open $file for reading: $!";
+    my @lines;
+    my $counter = $init;
+    my $changed;
+    while (<$in>) {
+        s/\r$//;
+        my $num;
+        s/ ^ === \s+ TEST \s+ (\d+)/$num=$1; "=== TEST " . $counter++/xie;
+        next if !defined $num;
+        if ($num != $counter-1) {
+            $changed++;
+        }
+    } continue {
+        push @lines, $_;
+    }
+    close $in;
+    my $text = join '', @lines;
+    $text =~ s/(?x) \n+ === \s+ TEST/\n\n\n\n=== TEST/ixsg;
+    $text =~ s/__(DATA|END)__\n+=== TEST/__${1}__\n\n=== TEST/;
+    #$text =~ s/\n+$/\n\n/s;
+    if (! $changed and $text eq join '', @lines) {
+        warn "reindex: $file:\tskipped.\n";
+        return;
+    }
+    open my $out, "> $file" or
+        die "Can't open $file for writing: $!";
+    binmode $out;
+    print $out $text;
+    close $out;
+
+    warn "reindex: $file:\tdone.\n";
+}
diff --git a/tools/ktap/test/zerodivide.t b/tools/ktap/test/zerodivide.t
new file mode 100644
index 0000000..daf1ff6
--- /dev/null
+++ b/tools/ktap/test/zerodivide.t
@@ -0,0 +1,21 @@
+# vi: ft= et tw=4 sw=4
+
+use lib 'test/lib';
+use Test::ktap 'no_plan';
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: zero divide
+--- src
+
+var a = 1/0
+#should not go here
+print("failed")
+
+--- out_like
+(.*)divide 0(.*)
+--- err
+
+
-- 
1.8.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to