[PATCH v2 27/29] ktap: add testsuite and benchmark(tools/ktap/test/*)

From: Jovi Zhangwei
Date: Sat Mar 29 2014 - 11:28:32 EST


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.zhangwei@xxxxxxxxx>
---
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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/