From patchwork Tue Oct 21 15:21:43 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lei Maohui X-Patchwork-Id: 5129171 Return-Path: X-Original-To: patchwork-ltsi-dev@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 9DA5DC11AC for ; Wed, 22 Oct 2014 01:43:26 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 34EDF201F4 for ; Wed, 22 Oct 2014 01:43:26 +0000 (UTC) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 10D78201CE for ; Wed, 22 Oct 2014 01:43:26 +0000 (UTC) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 7D154A3F; Wed, 22 Oct 2014 01:43:25 +0000 (UTC) X-Original-To: ltsi-dev@lists.linuxfoundation.org Delivered-To: ltsi-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 28ADA8E2 for ; Tue, 21 Oct 2014 10:23:43 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from heian.cn.fujitsu.com (unknown [59.151.112.132]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id 66AC41F8C2 for ; Tue, 21 Oct 2014 10:23:32 +0000 (UTC) X-IronPort-AV: E=Sophos;i="5.04,761,1406563200"; d="scan'208";a="37654183" Received: from unknown (HELO edo.cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 21 Oct 2014 18:20:24 +0800 Received: from G08CNEXCHPEKD01.g08.fujitsu.local (localhost.localdomain [127.0.0.1]) by edo.cn.fujitsu.com (8.14.3/8.13.1) with ESMTP id s9LANPLI009082 for ; Tue, 21 Oct 2014 18:23:25 +0800 Received: from localhost.localdomain (10.167.226.37) by G08CNEXCHPEKD01.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.181.6; Tue, 21 Oct 2014 18:23:36 +0800 From: Lei Maohui To: Date: Tue, 21 Oct 2014 18:21:43 +0300 Message-ID: <1413904903-2014-2-git-send-email-leimaohui@cn.fujitsu.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1413904903-2014-1-git-send-email-leimaohui@cn.fujitsu.com> References: <1413904903-2014-1-git-send-email-leimaohui@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.37] X-Mailman-Approved-At: Wed, 22 Oct 2014 01:43:23 +0000 Subject: [LTSI-dev] [PATCH 1/1] ktap patch added X-BeenThere: ltsi-dev@lists.linuxfoundation.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: "A list to discuss patches, development, and other things related to the LTSI project" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ltsi-dev-bounces@lists.linuxfoundation.org Errors-To: ltsi-dev-bounces@lists.linuxfoundation.org X-Virus-Scanned: ClamAV using ClamSMTP From: Li xin Signed-off-by: Li xin --- patches.ktap/ktap-0.4.patch |23633 +++++++++++++++++++++++++++++++++++++++++++ series | 8 + 2 files changed, 23641 insertions(+) create mode 100644 patches.ktap/ktap-0.4.patch diff --git a/patches.ktap/ktap-0.4.patch b/patches.ktap/ktap-0.4.patch new file mode 100644 index 0000000..a9f07e4 --- /dev/null +++ b/patches.ktap/ktap-0.4.patch @@ -0,0 +1,23633 @@ +From 982d59f0bfdb7a85fd64fcebf4d18b49dfdf5120 Mon Sep 17 00:00:00 2001 +From: Li xin +Date: Fri, 17 Oct 2014 15:06:28 +0300 +Subject: [PATCH] ktap-0.4 + +--- + drivers/staging/Kconfig | 2 + + drivers/staging/Makefile | 1 + + drivers/staging/ktap/Kconfig | 32 + + drivers/staging/ktap/Makefile | 203 ++ + drivers/staging/ktap/README.md | 167 ++ + drivers/staging/ktap/RELEASES.txt | 155 ++ + drivers/staging/ktap/doc/tutorial.md | 691 +++++++ + drivers/staging/ktap/include/ktap_ffi.h | 180 ++ + drivers/staging/ktap/include/ktap_opcodes.h | 239 +++ + drivers/staging/ktap/include/ktap_types.h | 609 ++++++ + drivers/staging/ktap/runtime/ffi/call_x86_64.S | 143 ++ + drivers/staging/ktap/runtime/ffi/cdata.c | 67 + + drivers/staging/ktap/runtime/ffi/ffi_call.c | 427 +++++ + drivers/staging/ktap/runtime/ffi/ffi_symbol.c | 174 ++ + drivers/staging/ktap/runtime/ffi/ffi_type.c | 51 + + drivers/staging/ktap/runtime/ffi/ffi_util.c | 92 + + drivers/staging/ktap/runtime/kp_amalg.c | 43 + + drivers/staging/ktap/runtime/kp_load.c | 401 ++++ + drivers/staging/ktap/runtime/kp_load.h | 6 + + drivers/staging/ktap/runtime/kp_obj.c | 478 +++++ + drivers/staging/ktap/runtime/kp_obj.h | 29 + + drivers/staging/ktap/runtime/kp_opcode.c | 134 ++ + drivers/staging/ktap/runtime/kp_str.c | 460 +++++ + drivers/staging/ktap/runtime/kp_str.h | 20 + + drivers/staging/ktap/runtime/kp_tab.c | 1396 ++++++++++++++ + drivers/staging/ktap/runtime/kp_tab.h | 32 + + drivers/staging/ktap/runtime/kp_transport.c | 641 +++++++ + drivers/staging/ktap/runtime/kp_transport.h | 13 + + drivers/staging/ktap/runtime/kp_vm.c | 1496 +++++++++++++++ + drivers/staging/ktap/runtime/kp_vm.h | 16 + + drivers/staging/ktap/runtime/ktap.c | 217 +++ + drivers/staging/ktap/runtime/ktap.h | 130 ++ + drivers/staging/ktap/runtime/lib_ansi.c | 155 ++ + drivers/staging/ktap/runtime/lib_base.c | 607 ++++++ + drivers/staging/ktap/runtime/lib_ffi.c | 50 + + drivers/staging/ktap/runtime/lib_kdebug.c | 426 +++++ + drivers/staging/ktap/runtime/lib_timer.c | 193 ++ + .../staging/ktap/samples/ansi/ansi_color_demo.kp | 22 + + drivers/staging/ktap/samples/basic/backtrace.kp | 6 + + .../staging/ktap/samples/basic/event_trigger.kp | 24 + + .../ktap/samples/basic/event_trigger_ftrace.kp | 28 + + drivers/staging/ktap/samples/basic/ftrace.kp | 6 + + .../staging/ktap/samples/basic/function_time.kp | 57 + + drivers/staging/ktap/samples/basic/kretprobe.kp | 6 + + drivers/staging/ktap/samples/ffi/ffi_kmalloc.kp | 19 + + drivers/staging/ktap/samples/ffi/printk.kp | 10 + + drivers/staging/ktap/samples/ffi/sched_clock.kp | 6 + + drivers/staging/ktap/samples/game/tetris.kp | 293 +++ + drivers/staging/ktap/samples/helloworld.kp | 3 + + .../staging/ktap/samples/interrupt/hardirq_time.kp | 24 + + .../staging/ktap/samples/interrupt/softirq_time.kp | 24 + + .../staging/ktap/samples/io/kprobes-do-sys-open.kp | 20 + + drivers/staging/ktap/samples/io/traceio.kp | 54 + + drivers/staging/ktap/samples/mem/kmalloc-top.kp | 17 + + drivers/staging/ktap/samples/mem/kmem.kp | 30 + + .../ktap/samples/profiling/function_profiler.kp | 41 + + .../ktap/samples/profiling/stack_profile.kp | 30 + + .../ktap/samples/schedule/sched_transition.kp | 5 + + .../staging/ktap/samples/schedule/schedtimes.kp | 125 ++ + drivers/staging/ktap/samples/syscalls/errinfo.kp | 145 ++ + drivers/staging/ktap/samples/syscalls/execve.kp | 8 + + drivers/staging/ktap/samples/syscalls/opensnoop.kp | 31 + + drivers/staging/ktap/samples/syscalls/sctop.kp | 13 + + drivers/staging/ktap/samples/syscalls/syscalls.kp | 6 + + .../ktap/samples/syscalls/syscalls_count.kp | 54 + + .../samples/syscalls/syscalls_count_by_proc.kp | 22 + + drivers/staging/ktap/samples/syscalls/syslatl.kp | 30 + + drivers/staging/ktap/samples/syscalls/syslist.kp | 31 + + .../staging/ktap/samples/tracepoints/eventcount.kp | 210 +++ + .../ktap/samples/tracepoints/eventcount_by_proc.kp | 57 + + .../ktap/samples/tracepoints/tracepoints.kp | 6 + + .../staging/ktap/samples/userspace/gcc_unwind.kp | 9 + + .../ktap/samples/userspace/glibc_func_hist.kp | 44 + + .../staging/ktap/samples/userspace/glibc_sdt.kp | 11 + + .../staging/ktap/samples/userspace/glibc_trace.kp | 11 + + .../staging/ktap/samples/userspace/malloc_free.kp | 20 + + .../ktap/samples/userspace/malloc_size_hist.kp | 22 + + drivers/staging/ktap/test/arg.kp | 24 + + drivers/staging/ktap/test/arithmetic.kp | 50 + + drivers/staging/ktap/test/benchmark/sembench.c | 556 ++++++ + drivers/staging/ktap/test/benchmark/test.sh | 26 + + drivers/staging/ktap/test/concat.kp | 15 + + drivers/staging/ktap/test/count.kp | 20 + + drivers/staging/ktap/test/ffi/.gitignore | 2 + + drivers/staging/ktap/test/ffi/Makefile | 46 + + drivers/staging/ktap/test/ffi/cparser_test.c | 322 ++++ + drivers/staging/ktap/test/ffi/ffi_test.kp | 47 + + drivers/staging/ktap/test/ffi/ktap_ffi_test.c | 64 + + drivers/staging/ktap/test/fibonacci.kp | 36 + + drivers/staging/ktap/test/function.kp | 88 + + drivers/staging/ktap/test/if.kp | 24 + + drivers/staging/ktap/test/kprobe.kp | 19 + + drivers/staging/ktap/test/kretprobe.kp | 14 + + drivers/staging/ktap/test/ksym.kp | 17 + + drivers/staging/ktap/test/len.kp | 25 + + drivers/staging/ktap/test/looping.kp | 40 + + drivers/staging/ktap/test/pairs.kp | 84 + + drivers/staging/ktap/test/ptable.kp | 46 + + drivers/staging/ktap/test/run_test.sh | 62 + + drivers/staging/ktap/test/stack_overflow.kp | 9 + + drivers/staging/ktap/test/table.kp | 71 + + drivers/staging/ktap/test/timer.kp | 28 + + drivers/staging/ktap/test/tracepoint.kp | 22 + + drivers/staging/ktap/test/zerodivide.kp | 5 + + drivers/staging/ktap/userspace/code.c | 998 ++++++++++ + drivers/staging/ktap/userspace/cparser.h | 202 ++ + drivers/staging/ktap/userspace/dump.c | 251 +++ + drivers/staging/ktap/userspace/eventdef.c | 857 +++++++++ + drivers/staging/ktap/userspace/ffi/cparser.c | 1755 +++++++++++++++++ + drivers/staging/ktap/userspace/ffi/ctype.c | 551 ++++++ + drivers/staging/ktap/userspace/ktapc.h | 393 ++++ + drivers/staging/ktap/userspace/ktapio.c | 106 ++ + drivers/staging/ktap/userspace/lex.c | 632 +++++++ + drivers/staging/ktap/userspace/main.c | 727 ++++++++ + drivers/staging/ktap/userspace/parser.c | 1963 ++++++++++++++++++++ + drivers/staging/ktap/userspace/symbol.c | 291 +++ + drivers/staging/ktap/userspace/symbol.h | 50 + + drivers/staging/ktap/userspace/util.c | 381 ++++ + 118 files changed, 22675 insertions(+) + create mode 100644 drivers/staging/ktap/Kconfig + create mode 100644 drivers/staging/ktap/Makefile + create mode 100644 drivers/staging/ktap/README.md + create mode 100644 drivers/staging/ktap/RELEASES.txt + create mode 100644 drivers/staging/ktap/doc/tutorial.md + create mode 100644 drivers/staging/ktap/include/ktap_ffi.h + create mode 100644 drivers/staging/ktap/include/ktap_opcodes.h + create mode 100644 drivers/staging/ktap/include/ktap_types.h + create mode 100644 drivers/staging/ktap/runtime/ffi/call_x86_64.S + create mode 100644 drivers/staging/ktap/runtime/ffi/cdata.c + create mode 100644 drivers/staging/ktap/runtime/ffi/ffi_call.c + create mode 100644 drivers/staging/ktap/runtime/ffi/ffi_symbol.c + create mode 100644 drivers/staging/ktap/runtime/ffi/ffi_type.c + create mode 100644 drivers/staging/ktap/runtime/ffi/ffi_util.c + create mode 100644 drivers/staging/ktap/runtime/kp_amalg.c + create mode 100644 drivers/staging/ktap/runtime/kp_load.c + create mode 100644 drivers/staging/ktap/runtime/kp_load.h + create mode 100644 drivers/staging/ktap/runtime/kp_obj.c + create mode 100644 drivers/staging/ktap/runtime/kp_obj.h + create mode 100644 drivers/staging/ktap/runtime/kp_opcode.c + create mode 100644 drivers/staging/ktap/runtime/kp_str.c + create mode 100644 drivers/staging/ktap/runtime/kp_str.h + create mode 100644 drivers/staging/ktap/runtime/kp_tab.c + create mode 100644 drivers/staging/ktap/runtime/kp_tab.h + create mode 100644 drivers/staging/ktap/runtime/kp_transport.c + create mode 100644 drivers/staging/ktap/runtime/kp_transport.h + create mode 100644 drivers/staging/ktap/runtime/kp_vm.c + create mode 100644 drivers/staging/ktap/runtime/kp_vm.h + create mode 100644 drivers/staging/ktap/runtime/ktap.c + create mode 100644 drivers/staging/ktap/runtime/ktap.h + create mode 100644 drivers/staging/ktap/runtime/lib_ansi.c + create mode 100644 drivers/staging/ktap/runtime/lib_base.c + create mode 100644 drivers/staging/ktap/runtime/lib_ffi.c + create mode 100644 drivers/staging/ktap/runtime/lib_kdebug.c + create mode 100644 drivers/staging/ktap/runtime/lib_timer.c + create mode 100644 drivers/staging/ktap/samples/ansi/ansi_color_demo.kp + create mode 100644 drivers/staging/ktap/samples/basic/backtrace.kp + create mode 100644 drivers/staging/ktap/samples/basic/event_trigger.kp + create mode 100644 drivers/staging/ktap/samples/basic/event_trigger_ftrace.kp + create mode 100644 drivers/staging/ktap/samples/basic/ftrace.kp + create mode 100644 drivers/staging/ktap/samples/basic/function_time.kp + create mode 100644 drivers/staging/ktap/samples/basic/kretprobe.kp + create mode 100644 drivers/staging/ktap/samples/ffi/ffi_kmalloc.kp + create mode 100644 drivers/staging/ktap/samples/ffi/printk.kp + create mode 100644 drivers/staging/ktap/samples/ffi/sched_clock.kp + create mode 100644 drivers/staging/ktap/samples/game/tetris.kp + create mode 100644 drivers/staging/ktap/samples/helloworld.kp + create mode 100644 drivers/staging/ktap/samples/interrupt/hardirq_time.kp + create mode 100644 drivers/staging/ktap/samples/interrupt/softirq_time.kp + create mode 100644 drivers/staging/ktap/samples/io/kprobes-do-sys-open.kp + create mode 100644 drivers/staging/ktap/samples/io/traceio.kp + create mode 100644 drivers/staging/ktap/samples/mem/kmalloc-top.kp + create mode 100644 drivers/staging/ktap/samples/mem/kmem.kp + create mode 100644 drivers/staging/ktap/samples/profiling/function_profiler.kp + create mode 100644 drivers/staging/ktap/samples/profiling/stack_profile.kp + create mode 100644 drivers/staging/ktap/samples/schedule/sched_transition.kp + create mode 100644 drivers/staging/ktap/samples/schedule/schedtimes.kp + create mode 100644 drivers/staging/ktap/samples/syscalls/errinfo.kp + create mode 100644 drivers/staging/ktap/samples/syscalls/execve.kp + create mode 100644 drivers/staging/ktap/samples/syscalls/opensnoop.kp + create mode 100644 drivers/staging/ktap/samples/syscalls/sctop.kp + create mode 100644 drivers/staging/ktap/samples/syscalls/syscalls.kp + create mode 100644 drivers/staging/ktap/samples/syscalls/syscalls_count.kp + create mode 100644 drivers/staging/ktap/samples/syscalls/syscalls_count_by_proc.kp + create mode 100644 drivers/staging/ktap/samples/syscalls/syslatl.kp + create mode 100644 drivers/staging/ktap/samples/syscalls/syslist.kp + create mode 100644 drivers/staging/ktap/samples/tracepoints/eventcount.kp + create mode 100644 drivers/staging/ktap/samples/tracepoints/eventcount_by_proc.kp + create mode 100644 drivers/staging/ktap/samples/tracepoints/tracepoints.kp + create mode 100644 drivers/staging/ktap/samples/userspace/gcc_unwind.kp + create mode 100644 drivers/staging/ktap/samples/userspace/glibc_func_hist.kp + create mode 100644 drivers/staging/ktap/samples/userspace/glibc_sdt.kp + create mode 100644 drivers/staging/ktap/samples/userspace/glibc_trace.kp + create mode 100644 drivers/staging/ktap/samples/userspace/malloc_free.kp + create mode 100644 drivers/staging/ktap/samples/userspace/malloc_size_hist.kp + create mode 100644 drivers/staging/ktap/test/arg.kp + create mode 100644 drivers/staging/ktap/test/arithmetic.kp + create mode 100644 drivers/staging/ktap/test/benchmark/sembench.c + create mode 100644 drivers/staging/ktap/test/benchmark/test.sh + create mode 100644 drivers/staging/ktap/test/concat.kp + create mode 100644 drivers/staging/ktap/test/count.kp + create mode 100644 drivers/staging/ktap/test/ffi/.gitignore + create mode 100644 drivers/staging/ktap/test/ffi/Makefile + create mode 100644 drivers/staging/ktap/test/ffi/cparser_test.c + create mode 100644 drivers/staging/ktap/test/ffi/ffi_test.kp + create mode 100644 drivers/staging/ktap/test/ffi/ktap_ffi_test.c + create mode 100644 drivers/staging/ktap/test/fibonacci.kp + create mode 100644 drivers/staging/ktap/test/function.kp + create mode 100644 drivers/staging/ktap/test/if.kp + create mode 100644 drivers/staging/ktap/test/kprobe.kp + create mode 100644 drivers/staging/ktap/test/kretprobe.kp + create mode 100644 drivers/staging/ktap/test/ksym.kp + create mode 100644 drivers/staging/ktap/test/len.kp + create mode 100644 drivers/staging/ktap/test/looping.kp + create mode 100644 drivers/staging/ktap/test/pairs.kp + create mode 100644 drivers/staging/ktap/test/ptable.kp + create mode 100644 drivers/staging/ktap/test/run_test.sh + create mode 100644 drivers/staging/ktap/test/stack_overflow.kp + create mode 100644 drivers/staging/ktap/test/table.kp + create mode 100644 drivers/staging/ktap/test/timer.kp + create mode 100644 drivers/staging/ktap/test/tracepoint.kp + create mode 100644 drivers/staging/ktap/test/zerodivide.kp + create mode 100644 drivers/staging/ktap/userspace/code.c + create mode 100644 drivers/staging/ktap/userspace/cparser.h + create mode 100644 drivers/staging/ktap/userspace/dump.c + create mode 100644 drivers/staging/ktap/userspace/eventdef.c + create mode 100644 drivers/staging/ktap/userspace/ffi/cparser.c + create mode 100644 drivers/staging/ktap/userspace/ffi/ctype.c + create mode 100644 drivers/staging/ktap/userspace/ktapc.h + create mode 100644 drivers/staging/ktap/userspace/ktapio.c + create mode 100644 drivers/staging/ktap/userspace/lex.c + create mode 100644 drivers/staging/ktap/userspace/main.c + create mode 100644 drivers/staging/ktap/userspace/parser.c + create mode 100644 drivers/staging/ktap/userspace/symbol.c + create mode 100644 drivers/staging/ktap/userspace/symbol.h + create mode 100644 drivers/staging/ktap/userspace/util.c + +diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig +index 6c7a9b1..7ab94ac 100644 +--- a/drivers/staging/Kconfig ++++ b/drivers/staging/Kconfig +@@ -148,4 +148,6 @@ source "drivers/staging/dgap/Kconfig" + + source "drivers/staging/lttng/Kconfig" + ++source "drivers/staging/ktap/Kconfig" ++ + endif # STAGING +diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile +index 4ec98c9..a2b53b9 100644 +--- a/drivers/staging/Makefile ++++ b/drivers/staging/Makefile +@@ -66,3 +66,4 @@ obj-$(CONFIG_DGNC) += dgnc/ + obj-$(CONFIG_DGAP) += dgap/ + obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/ + obj-$(CONFIG_LTTNG) += lttng/ ++obj-$(CONFIG_KTAP) += ktap/ +diff --git a/drivers/staging/ktap/Kconfig b/drivers/staging/ktap/Kconfig +new file mode 100644 +index 0000000..6c6cf63 +--- /dev/null ++++ b/drivers/staging/ktap/Kconfig +@@ -0,0 +1,32 @@ ++config KTAP ++ tristate "a programable dynamic tracing tool for Linux" ++ depends on PERF_EVENTS && EVENT_TRACING ++ default n ++ help ++ ktap is a new script-based dynamic tracing tool for Linux, ++ it uses a scripting language and lets users trace the ++ Linux kernel dynamically. ktap is designed to give ++ operational insights with interoperability that allow ++ users to tune, troubleshoot and extend kernel and application. ++ It's similar with Linux Systemtap and Solaris Dtrace. ++ ++ ktap have different design principles from Linux mainstream ++ dynamic tracing language in that it's based on bytecode, ++ so it doesn't depend upon GCC, doesn't require compiling ++ kernel module for each script, safe to use in production ++ environment, fulfilling the embedded ecosystem's tracing needs. ++ ++ See ktap tutorial for more information: ++ http://www.ktap.org/doc/tutorial.html ++ ++config KTAP_FFI ++ tristate "FFI support for ktap" ++ depends on KTAP ++ depends on X86_64 ++ default n ++ help ++ This option brings FFI support to ktap. With FFI enabled ktap, ++ users can call into native kernel C function directly in ktap ++ script. Except for a new cdef keyword, this option also adds ++ a ffi module which exports helper functions like ffi.new and ++ ffi.sizeof. +diff --git a/drivers/staging/ktap/Makefile b/drivers/staging/ktap/Makefile +new file mode 100644 +index 0000000..f565438 +--- /dev/null ++++ b/drivers/staging/ktap/Makefile +@@ -0,0 +1,203 @@ ++ ++# ++# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds) ++# (this will also disable resolve resolving symbols in DSO functionality) ++# ++# Define FFI if you want to compile ktap with FFI support. By default This ++# toggle is off. ++# ++# Define amalg to enable amalgamation build, This compiles the ktapvm as ++# one huge C file and allows GCC to generate faster and shorter code. Alas, ++# this requires lots of memory during the build. ++# Recommend to use amalgmation build as default. ++amalg = 1 ++NO_LIBELF = 1 ++# Do not instrument the tracer itself: ++ifdef CONFIG_FUNCTION_TRACER ++ORIG_CFLAGS := $(KBUILD_CFLAGS) ++KBUILD_CFLAGS = $(subst -pg,,$(ORIG_CFLAGS)) ++endif ++ ++all: mod ktap ++ ++INC = include ++RUNTIME = runtime ++ ++FFIDIR = $(RUNTIME)/ffi ++KTAP_LIBS = -lpthread ++ ++LIB_OBJS += $(RUNTIME)/lib_base.o $(RUNTIME)/lib_kdebug.o $(RUNTIME)/lib_timer.o \ ++ $(RUNTIME)/lib_ansi.o ++ ++ifndef amalg ++ifdef FFI ++FFI_OBJS += $(FFIDIR)/ffi_call.o $(FFIDIR)/ffi_type.o $(FFIDIR)/ffi_symbol.o \ ++ $(FFIDIR)/cdata.o $(FFIDIR)/ffi_util.o ++RUNTIME_OBJS += $(FFI_OBJS) ++LIB_OBJS += $(RUNTIME)/lib_ffi.o ++endif ++RUNTIME_OBJS += $(RUNTIME)/ktap.o $(RUNTIME)/kp_load.o $(RUNTIME)/kp_obj.o \ ++ $(RUNTIME)/kp_str.o $(RUNTIME)/kp_tab.o $(RUNTIME)/kp_vm.o \ ++ $(RUNTIME)/kp_opcode.o $(RUNTIME)/kp_transport.o \ ++ $(LIB_OBJS) ++else ++RUNTIME_OBJS += $(RUNTIME)/kp_amalg.o ++endif ++ ++ifdef FFI ++ifeq ($(KBUILD_MODULES), 1) ++ifdef CONFIG_X86_64 ++# call_x86_64.o is compiled from call_x86_64.S ++RUNTIME_OBJS += $(FFIDIR)/call_x86_64.o ++else ++$(error ktap FFI only supports x86_64 for now!) ++endif ++endif ++ ++ ++ccflags-y += -DCONFIG_KTAP_FFI ++endif ++ ++obj-m += ktapvm.o ++ktapvm-y := $(RUNTIME_OBJS) ++ ++KVERSION ?= $(shell uname -r) ++KERNEL_SRC ?= /lib/modules/$(KVERSION)/build ++PWD := $(shell pwd) ++mod: ++ $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules ++ ++modules_install: ++ $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules_install ++ ++KTAPC_CFLAGS = -Wall -O2 ++ ++ ++# try-cc ++# Usage: option = $(call try-cc, source-to-build, cc-options, msg) ++ifneq ($(V),1) ++TRY_CC_OUTPUT= > /dev/null 2>&1 ++endif ++TRY_CC_MSG=echo " CHK $(3)" 1>&2; ++ ++try-cc = $(shell sh -c \ ++ 'TMP="/tmp/.$$$$"; \ ++ $(TRY_CC_MSG) \ ++ echo "$(1)" | \ ++ $(CC) -x c - $(2) -o "$$TMP" $(TRY_CC_OUTPUT) && echo y; \ ++ rm -f "$$TMP"') ++ ++ ++define SOURCE_LIBELF ++#include ++ ++int main(void) ++{ ++ Elf *elf = elf_begin(0, ELF_C_READ, 0); ++ return (long)elf; ++} ++endef ++ ++FLAGS_LIBELF = -lelf ++ ++ifdef NO_LIBELF ++ KTAPC_CFLAGS += -DNO_LIBELF ++else ++ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y) ++ $(warning No libelf found, disables symbol resolving, please install elfutils-libelf-devel/libelf-dev); ++ NO_LIBELF := 1 ++ KTAPC_CFLAGS += -DNO_LIBELF ++else ++ KTAP_LIBS += -lelf ++endif ++endif ++ ++UDIR = userspace ++ ++$(UDIR)/lex.o: $(UDIR)/lex.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/parser.o: $(UDIR)/parser.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/code.o: $(UDIR)/code.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/dump.o: $(UDIR)/dump.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/main.o: $(UDIR)/main.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/util.o: $(UDIR)/util.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/ktapio.o: $(UDIR)/ktapio.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/eventdef.o: $(UDIR)/eventdef.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/kp_opcode.o: $(RUNTIME)/kp_opcode.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/kp_tab.o: $(RUNTIME)/kp_tab.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/kp_str.o: $(RUNTIME)/kp_str.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/kp_obj.o: $(RUNTIME)/kp_obj.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++ifndef NO_LIBELF ++$(UDIR)/symbol.o: $(UDIR)/symbol.c ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++endif ++ifdef FFI ++KTAPC_CFLAGS += -DCONFIG_KTAP_FFI ++$(UDIR)/ffi_type.o: $(RUNTIME)/ffi/ffi_type.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/ffi/cparser.o: $(UDIR)/ffi/cparser.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++$(UDIR)/ffi/ctype.o: $(UDIR)/ffi/ctype.c $(INC)/* ++ $(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $< ++endif ++ ++ ++KTAPOBJS = ++KTAPOBJS += $(UDIR)/lex.o ++KTAPOBJS += $(UDIR)/parser.o ++KTAPOBJS += $(UDIR)/code.o ++KTAPOBJS += $(UDIR)/dump.o ++KTAPOBJS += $(UDIR)/main.o ++KTAPOBJS += $(UDIR)/util.o ++KTAPOBJS += $(UDIR)/ktapio.o ++KTAPOBJS += $(UDIR)/eventdef.o ++KTAPOBJS += $(UDIR)/kp_opcode.o ++KTAPOBJS += $(UDIR)/kp_tab.o ++KTAPOBJS += $(UDIR)/kp_str.o ++KTAPOBJS += $(UDIR)/kp_obj.o ++ifndef NO_LIBELF ++KTAPOBJS += $(UDIR)/symbol.o ++endif ++ifdef FFI ++KTAPOBJS += $(UDIR)/ffi_type.o ++KTAPOBJS += $(UDIR)/ffi/cparser.o ++KTAPOBJS += $(UDIR)/ffi/ctype.o ++endif ++ ++ktap: $(KTAPOBJS) ++ $(QUIET_LINK)$(CC) $(KTAPC_CFLAGS) -o $@ $(KTAPOBJS) $(KTAP_LIBS) ++ ++KMISC := /lib/modules/$(KVERSION)/ktapvm/ ++ ++install: mod ktap ++ install -d $(KMISC) ++ install -m 644 -c *.ko /lib/modules/$(KVERSION)/ktapvm/ ++ /sbin/depmod -a ++ ++load: ++ insmod ktapvm.ko ++ ++unload: ++ rmmod ktapvm ++ ++test: FORCE ++ cd test; sh ./run_test.sh; cd - ++ ++clean: ++ $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean ++ $(RM) ktap ++ ++PHONY += FORCE ++FORCE: ++ +diff --git a/drivers/staging/ktap/README.md b/drivers/staging/ktap/README.md +new file mode 100644 +index 0000000..8664a28 +--- /dev/null ++++ b/drivers/staging/ktap/README.md +@@ -0,0 +1,167 @@ ++# ktap ++ ++A New Scripting Dynamic Tracing Tool For Linux ++[www.ktap.org][homepage] ++ ++ktap is a new scripting dynamic tracing tool for Linux, ++it uses a scripting language and lets users trace the Linux kernel dynamically. ++ktap is designed to give operational insights with interoperability ++that allows users to tune, troubleshoot and extend kernel and application. ++It's similar with Linux Systemtap and Solaris Dtrace. ++ ++ktap have different design principles from Linux mainstream dynamic tracing ++language in that it's based on bytecode, so it doesn't depend upon GCC, ++doesn't require compiling kernel module for each script, safe to use in ++production environment, fulfilling the embedded ecosystem's tracing needs. ++ ++More information can be found at [ktap homepage][homepage]. ++ ++[homepage]: http://www.ktap.org ++ ++## Highlights ++ ++ * simple but powerful scripting language ++ * register based interpreter (heavily optimized) in Linux kernel ++ * small and lightweight (6KLOC of interpreter) ++ * not depend on gcc for each script running ++ * easy to use in embedded environment without debugging info ++ * support for tracepoint, kprobe, uprobe, function trace, timer, and more ++ * supported in x86, arm, ppc, mips ++ * safety in sandbox ++ ++## Building & Running ++ ++1. Clone ktap from github ++ ++ $ git clone http://github.com/ktap/ktap.git ++ ++2. Compiling ktap ++ ++ $ cd ktap ++ $ make #generate ktapvm kernel module and ktap binary ++ ++3. Load ktapvm kernel module(make sure debugfs mounted) ++ ++ $ make load #need to be root or have sudo access ++ ++4. Running ktap ++ ++ $ ./ktap samples/helloworld.kp ++ ++ ++## Examples ++ ++1. simplest one-liner command to enable all tracepoints ++ ++ ktap -e "trace *:* { print(argevent) }" ++ ++2. syscall tracing on target process ++ ++ ktap -e "trace syscalls:* { print(argevent) }" -- ls ++ ++3. ftrace(kernel newer than 3.3, and must compiled with CONFIG_FUNCTION_TRACER) ++ ++ ktap -e "trace ftrace:function { print(argevent) }" ++ ++ ktap -e "trace ftrace:function /ip==mutex*/ { print(argevent) }" ++ ++4. simple syscall tracing ++ ++ trace syscalls:* { ++ print(cpu(), pid(), execname(), argevent) ++ } ++ ++5. syscall tracing in histogram style ++ ++ s = {} ++ ++ trace syscalls:sys_enter_* { ++ s[argname] += 1 ++ } ++ ++ trace_end { ++ histogram(s) ++ } ++ ++6. kprobe tracing ++ ++ trace probe:do_sys_open dfd=%di fname=%dx flags=%cx mode=+4($stack) { ++ print("entry:", execname(), argevent) ++ } ++ ++ trace probe:do_sys_open%return fd=$retval { ++ print("exit:", execname(), argevent) ++ } ++ ++7. uprobe tracing ++ ++ trace probe:/lib/libc.so.6:malloc { ++ print("entry:", execname(), argevent) ++ } ++ ++ trace probe:/lib/libc.so.6:malloc%return { ++ print("exit:", execname(), argevent) ++ } ++ ++8. stapsdt tracing (userspace static marker) ++ ++ trace sdt:/lib64/libc.so.6:lll_futex_wake { ++ print("lll_futex_wake", execname(), argevent) ++ } ++ ++ or: ++ ++ #trace all static mark in libc ++ trace sdt:/lib64/libc.so.6:* { ++ print(execname(), argevent) ++ } ++ ++9. timer ++ ++ tick-1ms { ++ printf("time fired on one cpu\n"); ++ } ++ ++ profile-2s { ++ printf("time fired on every cpu\n"); ++ } ++ ++10. FFI (Call kernel function from ktap script, need compile with FFI=1) ++ ++ cdef[[ ++ int printk(char *fmt, ...); ++ ]] ++ ++ C.printk("This message is called from ktap ffi\n") ++ ++More examples can be found at [samples][samples_dir] directory. ++ ++[samples_dir]: https://github.com/ktap/ktap/tree/master/samples ++ ++## Mailing list ++ ++ktap@freelists.org ++You can subscribe to ktap mailing list at link (subscribe before posting): ++http://www.freelists.org/list/ktap ++ ++ ++## Copyright and License ++ ++ktap is licensed under GPL v2 ++ ++Copyright (C) 2012-2013, Jovi Zhangwei . ++All rights reserved. ++ ++ ++## Contribution ++ ++ktap is still under active development, so contributions are welcome. ++You are encouraged to report bugs, provide feedback, send feature request, ++or hack on it. ++ ++ ++## See More ++ ++More info can be found at [documentation][tutorial] ++[tutorial]: http://www.ktap.org/doc/tutorial.html ++ +diff --git a/drivers/staging/ktap/RELEASES.txt b/drivers/staging/ktap/RELEASES.txt +new file mode 100644 +index 0000000..7df25a9 +--- /dev/null ++++ b/drivers/staging/ktap/RELEASES.txt +@@ -0,0 +1,155 @@ ++Version 0.4 (Dec 9 2013) ++------------------------- ++= Highlight changes from v0.3 ++ ++ * kernel symbol read (syntax: `symbol_name`) ++ ++ * parse symbol on uprobe (need libelf link) ++ trace probe:/lib64/libc.so.6:malloc {} ++ trace probe:/lib64/libc.so.6:malloc%return {} ++ trace probe:/lib64/libc.so.6:* {} # trace all function in glibc ++ ++ * support static marker(SDT) ++ trace sdt:/lib64/libc.so.6:setjmp {} ++ trace sdt:/lib64/libc.so.6:* {} # trace all sdt in glibc ++ ++ * support kprobe wildcard ++ trace probe:vfs* {} ++ ++ * support run multiple ktap instances concurrently ++ ++ * add command option for list available events and symbols ++ -le [glob] : list pre-defined events in system ++ -lf DSO : list available functions from DSO ++ -lm DSO : list available sdt notes from DSO ++ ++ * better annotation for output of argname ++ ++ * basic FFI support (depend on CONFIG_KTAP_FFI) ++ FFI will allow call kernel function from ktap script ++ ++ cdef [[ int printk(char *fmt, ...); ]] ++ C.printk("this is ffi printk from ktap\n") ++ ++ (currently only support basic C types, structure support is ongoing) ++ ++ * New sample scripts ++ userspace/malloc_size_hist.kp ++ userspace/malloc_free.kp ++ userspace/gcc_unwind.kp ++ userspace/glibc_sdt.kp #trace all static marker in glibc ++ userspace/glibc_trace.kp #trace all functions in glibc ++ userspace/glibc_func_hist.kp #show glibc functions in histogram ++ syscalls/syslatl.kp #syscall latency linear aggregation ++ syscalls/syslist.kp #syscall latency as a list with counts ++ syscalls/opensnoop.kp #trace open() syscalls and print basic details ++ ffi/ffi_kmalloc.kp ++ ffi/printk.kp ++ ffi/sched_clock.kp ++ ++ * use amalgamation build as default ++ x86_64 build: ktap binary size is 98K, ktapvm.ko size is 983K ++ ++ * Big cleanups and lots of bugfix ++ ++ ++Version 0.3 (Oct 29 2013) ++------------------------- ++= Highlight changes from v0.2 ++ ++ * Homepage released: www.ktap.org ++ ++ * Tutorial: http://www.ktap.org/doc/tutorial.html ++ ++ * Wiki: https://github.com/ktap/ktap/wiki ++ ++ * simple new tracing block syntax ++ trace EVENTDEF { action } ++ trace_end { action } ++ ++ * New event tracing keywords: argevent, argname, arg1..arg9 ++ trace "syscalls:*" function () { ++ print(argevent) ++ } ++ ++ * New timer block syntax ++ tick-N { action } ++ profile-N { action } ++ ++ * Basic aggregation support ++ It's similar with systemtap, use "<<<" operator ++ support aggregate function: count, sum, avg, max, min ++ ++ * Introduce new "+=" operator ++ ++ * Introduce sort_paris for table sort iteration ++ ++ * New sample scripts ++ helloworld.kp ++ syscalls/sctop.kp ++ profiling/stack_profile.kp ++ io/traceio.kp ++ mem/kmalloc-top.kp ++ syscalls/errinfo.kp ++ schedule/schedtimes.kp ++ game/tetris.kp ++ ++ * ansi library for sending ANSI escape sequences ++ ++ ++ * statistics of ktapvm ++ ++ * Big cleanups and lots of bugfix ++ ++Version 0.2 (Jul 31 2013) ++------------------------- ++ ++= Script highlight changes from v0.1 ++ ++ * new tracing block syntax ++ trace EVENTDEF function (e) { BODY } ++ trace_end function (e) { BODY } ++ ++ * support trace filter ++ trace 'sched:sched_switch /prev_comm == foo || next_comm == foo/ ++ ++ * support kprobe/kretprobe ++ trace "probe:do_sys_open dfd=%di filename=%dx flags=%cx mode=+4($stack)" ++ trace "probe:do_sys_open%return fd=$retval" ++ ++ * support uprobe/uretprobe ++ trace "probe:/lib/libc.so.6:0x000773c0" ++ trace "probe:/lib/libc.so.6:0x000773c0%return" ++ ++ * support function tracing ++ trace "ftrace:function /ip == mutex*/" ++ ++ * support oneline scripting ++ ktap -e 'trace "syscalls:*" function (e) { print(e) }' ++ ++ * specific pid or cpu to tracing ++ ktap -C cpu *.kp ++ ktap -p pid *.kp ++ ++ * more sample scripts ++ ++ * support calling print_backtrace() in any context ++ ++ * support calling exit() in any context ++ ++= Backend highlight changes from v0.1 ++ ++ * unified perf callback mechanism ++ * use ring buffer transport instead of relayfs ++ * reentrant in ktap tracing ++ * performance boost(use percpu data in many case) ++ * safe table/string manipulation ++ * safe ktap exit ++ * big code cleanups ++ * fixed a lot of bugs, more stable than v0.1 ++ ++Version 0.1 (May 21 2013) ++------------------------- ++ ++ https://lwn.net/Articles/551253/ ++ +diff --git a/drivers/staging/ktap/doc/tutorial.md b/drivers/staging/ktap/doc/tutorial.md +new file mode 100644 +index 0000000..73a177d +--- /dev/null ++++ b/drivers/staging/ktap/doc/tutorial.md +@@ -0,0 +1,691 @@ ++% The ktap Tutorial ++ ++# Introduction ++ ++ktap is a new script-based dynamic tracing tool for linux ++http://www.ktap.org ++ ++ktap is a new script-based dynamic tracing tool for Linux, ++it uses a scripting language and lets users trace the Linux kernel dynamically. ++ktap is designed to give operational insights with interoperability ++that allows users to tune, troubleshoot and extend kernel and application. ++It's similar with Linux Systemtap and Solaris Dtrace. ++ ++ktap have different design principles from Linux mainstream dynamic tracing ++language in that it's based on bytecode, so it doesn't depend upon GCC, ++doesn't require compiling kernel module for each script, safe to use in ++production environment, fulfilling the embedded ecosystem's tracing needs. ++ ++Highlights features: ++ ++* simple but powerful scripting language ++* register based interpreter (heavily optimized) in Linux kernel ++* small and lightweight ++* not depend on gcc for each script running ++* easy to use in embedded environment without debugging info ++* support for tracepoint, kprobe, uprobe, function trace, timer, and more ++* supported in x86, arm, ppc, mips ++* safety in sandbox ++ ++ ++# Getting started ++ ++Requirements ++ ++* Linux 3.1 or later(Need some kernel patches for kernel earlier than 3.1) ++* CONFIG_EVENT_TRACING enabled ++* CONFIG_PERF_EVENTS enabled ++* CONFIG_DEBUG_FS enabled ++ make sure debugfs mounted before insmod ktapvm ++ mount debugfs: mount -t debugfs none /sys/kernel/debug/ ++* libelf (optional) ++ Install elfutils-libelf-devel on RHEL-based distros, or libelf-dev on ++ Debian-based distros. ++ Use `make NO_LIBELF=1` to build without libelf support. ++ libelf is required for resolving symbols to addresses in DSO, and for sdt. ++ ++Note that those configuration is always enabled in Linux distribution, ++like REHL, Fedora, Ubuntu, etc. ++ ++1. Clone ktap from github ++ ++ $ git clone http://github.com/ktap/ktap.git ++ ++2. Compiling ktap ++ ++ $ cd ktap ++ $ make #generate ktapvm kernel module and ktap binary ++ ++3. Load ktapvm kernel module(make sure debugfs mounted) ++ ++ $ make load #need to be root or have sudo access ++ ++4. Running ktap ++ ++ $ ./ktap scripts/helloworld.kp ++ ++ ++# Language basics ++ ++## Syntax basics ++ ++ktap's syntax is design on the mind of C language syntax friendly, ++to make it easy scripting by kernel developer. ++ ++1. Variable declaration ++The biggest syntax differences with C is that ktap is a dynamic typed ++language, so you won't need add any variable type declaration, just ++use the variable. ++ ++2. function ++All functions in ktap should use keyword "function" declaration ++ ++3. comments ++The comments of ktap is starting from '#', long comments doesn't support now. ++ ++4. others ++Don't need place any ';' at the ending of statement in ktap. ++ktap use free syntax style, so you can choose to use the ';' or not. ++ ++ktap use nil as NULL, the result of any number operate on nil is nil. ++ ++ktap don't have array structure, also don't have any pointer operation. ++ ++## Control structures ++ ++ktap if/else is same as C language. ++ ++There have two method of for-loop in ktap: ++ ++ for (i = init, limit, step) { body } ++ ++this is same as below in C: ++ ++ for (i = init; i < limit; i += step) { body } ++ ++The next for-loop method is: ++ ++ for (k, v in pairs(t)) { body } # looping all elements of table ++ ++Note that ktap don't have "continue" keyword, but C does. ++ ++## Date structures ++ ++Associative array is heavily used in ktap, it's also called by table. ++ ++table declaration: ++ ++ t = {} ++ ++how to use table: ++ ++ t[1] = 1 ++ t[1] = "xxx" ++ t["key"] = 10 ++ t["key"] = "value" ++ ++ for (k, v in pairs(t)) { body } # looping all elements of table ++ ++ ++# Built in functions and librarys ++ ++## Built in functions ++ ++**print (...)** ++Receives any number of arguments, and prints their values, ++print is not intended for formatted output, but only as a ++quick way to show a value, typically for debugging. ++For formatted output, use printf. ++ ++**printf (fmt, ...)** ++Similar with C printf, use for format string output. ++ ++**pairs (t)** ++Returns three values: the next function, the table t, and nil, ++so that the construction ++for (k,v in pairs(t)) { body } ++will iterate over all key-value pairs of table t. ++ ++**len (t) /len (s)** ++If the argument is string, return length of string, ++if the argument is table, return counts of table pairs. ++ ++**in_interrupt ()** ++checking is context is interrupt context ++ ++**exit ()** ++quit ktap executing, similar with exit syscall ++ ++**pid ()** ++return current process pid ++ ++**tid ()** ++return current thread id ++ ++**uid ()** ++return current process uid ++ ++**execname ()** ++return current process exec name string ++ ++**cpu ()** ++return current cpu id ++ ++**arch ()** ++return machine architecture, like x86, arm, etc. ++ ++**kernel_v ()** ++return Linux kernel version string, like 3.9, etc. ++ ++**user_string (addr)** ++Receive userspace address, read string from userspace, return string. ++ ++**histogram (t)** ++Receive table, output table histogram to user. ++ ++**curr_task_info (offset, fetch_bytes)** ++fetch value in field offset of task_struct structure, argument fetch_bytes ++could be 4 or 8, if fetch_bytes is not given, default is 4. ++ ++user may need to get field offset by gdb, for example: ++gdb vmlinux ++(gdb)p &(((struct task_struct *)0).prio) ++ ++**print_backtrace ()** ++print current task stack info ++ ++ ++## Librarys ++ ++### Kdebug Library ++ ++**kdebug.probe_by_id (eventdef_info, eventfun)** ++ ++This function is underly representation of high level tracing primitive. ++Note that eventdef_info is just a userspace memory pointer refer to real ++eventdef_info structure, the structure defintion is: ++ ++ struct ktap_eventdef_info { ++ int nr; /* the number to id */ ++ int *id_arr; /* id array */ ++ char *filter; ++ }; ++ ++Those id is read from /sys/kernel/debug/tracing/events/$SYS/$EVENT/id ++ ++The second argument in above examples is a function: ++function eventfun () { action } ++ ++ ++**kdebug.probe_end (endfunc)** ++ ++This function is used for invoking a function when tracing end, it will wait ++until user press CTRL+C to stop tracing, then ktap will call endfunc function, ++user could show tracing results in that function, or do other things. ++ ++User don't have to use kdebug library directly, use trace/trace_end keyword. ++ ++### Timer Library ++ ++ ++ ++# Linux tracing basics ++ ++tracepoints, probe, timer ++filters ++above explaintion ++Ring buffer ++ ++# Tracing semantics in ktap ++ ++## Tracing block ++ ++**trace EVENTDEF /FILTER/ { ACTION }** ++ ++This is the basic tracing block for ktap, you need to use a specific EVENTDEF ++string, and own event function. ++ ++There have four type of EVENTDEF, tracepoint, kprobe, uprobe, sdt. ++ ++- tracepoint: ++ ++ EventDef Description ++ -------------------- ------------------------------- ++ syscalls:* trace all syscalls events ++ syscalls:sys_enter_* trace all syscalls entry events ++ kmem:* trace all kmem related events ++ sched:* trace all sched related events ++ sched:sched_switch trace sched_switch tracepoint ++ \*:\* trace all tracepoints in system ++ ++ All tracepoint events are based on: ++ /sys/kernel/debug/tracing/events/$SYS/$EVENT ++ ++- ftrace(kernel newer than 3.3, and must compiled with CONFIG_FUNCTION_TRACER) ++ ++ EventDef Description ++ -------------------- ------------------------------- ++ ftrace:function trace kernel functions based on ftrace ++ ++ User need to use filter (/ip==*/) to trace specfic functions. ++ Function must be listed in /sys/kernel/debug/tracing/available_filter_functions ++ ++> ***Note*** of function event ++> ++> perf support ftrace:function tracepoint since Linux 3.3(see below commit), ++> ktap is based on perf callback, so it means kernel must be newer than 3.3 ++> then can use this feature. ++> ++> commit ced39002f5ea736b716ae233fb68b26d59783912 ++> Author: Jiri Olsa ++> Date: Wed Feb 15 15:51:52 2012 +0100 ++> ++> ftrace, perf: Add support to use function tracepoint in perf ++> ++ ++- kprobe: ++ ++ EventDef Description ++ -------------------- ----------------------------------- ++ probe:schedule trace schedule function ++ probe:schedule%return trace schedule function return ++ probe:SyS_write trace SyS_write function ++ probe:vfs* trace wildcards vfs related function ++ ++ kprobe functions must be listed in /proc/kallsyms ++- uprobe: ++ ++ EventDef Description ++ ------------------------------------ --------------------------- ++ probe:/lib64/libc.so.6:malloc trace malloc function ++ probe:/lib64/libc.so.6:malloc%return trace malloc function return ++ probe:/lib64/libc.so.6:free trace free function ++ probe:/lib64/libc.so.6:0x82000 trace function with file offset 0x82000 ++ probe:/lib64/libc.so.6:* trace all libc function ++ ++ symbol resolving need libelf support ++ ++- sdt: ++ ++ EventDef Description ++ ------------------------------------ -------------------------- ++ sdt:/libc64/libc.so.6:lll_futex_wake trace stapsdt lll_futex_wake ++ sdt:/libc64/libc.so.6:* trace all static markers in libc ++ ++ sdt resolving need libelf support ++ ++ ++**trace_end { ACTION }** ++ ++## Tracing built-in variables ++ ++**argevent** ++event object, you can print it by: print(argevent), it will print events ++into human readable string, the result is mostly same as each entry of ++/sys/kernel/debug/tracing/trace ++ ++**argname** ++event name, each event have a name associated with it. ++ ++**arg1..9** ++get argument 1..9 of event object. ++ ++> ***Note*** of arg offset ++> ++> The arg offset(1..9) is determined by event format shown in debugfs. ++> ++> #cat /sys/kernel/debug/tracing/events/sched/sched_switch/format ++> name: sched_switch ++> ID: 268 ++> format: ++> field:char prev_comm[32]; <- arg1 ++> field:pid_t prev_pid; <- arg2 ++> field:int prev_prio; <- arg3 ++> field:long prev_state; <- arg4 ++> field:char next_comm[32]; <- arg5 ++> field:pid_t next_pid; <- arg6 ++> field:int next_prio; <- arg7 ++> ++> As shown, tracepoint event sched:sched_switch have 7 arguments, from arg1 to ++> arg7. ++> ++> Need to note that arg1 of syscall event is syscall number, not first argument ++> of syscall function. Use arg2 as first argument of syscall function. ++> For example: ++> ++> SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) ++> ++> ++> This is similar with kprobe and uprobe, the arg1 of kprobe/uprobe event ++> always is _probe_ip, not the first argument given by user, for example: ++> ++> # ktap -e 'trace probe:/lib64/libc.so.6:malloc size=%di' ++> ++> # cat /sys/kernel/debug/tracing/events/ktap_uprobes_3796/malloc/format ++> field:unsigned long __probe_ip; <- arg1 ++> field:u64 size; <- arg2 ++ ++ ++## Timer syntax ++ ++**tick-Ns { ACTION }** ++**tick-Nsec { ACTION }** ++**tick-Nms { ACTION }** ++**tick-Nmsec { ACTION }** ++**tick-Nus { ACTION }** ++**tick-Nusec { ACTION }** ++ ++**profile-Ns { ACTION }** ++**profile-Nsec { ACTION }** ++**profile-Nms { ACTION }** ++**profile-Nmsec { ACTION }** ++**profile-Nus { ACTION }** ++**profile-Nusec { ACTION }** ++ ++architecture overview picture reference(pnp format) ++one-liners ++simple event tracing ++ ++# Advanced tracing pattern ++ ++Aggregation/Histogram ++thread local ++flame graph ++ ++# Overhead/Performance ++ ++ktap have more fast boot time thant Systemtap(try the helloword script) ++ktap have little memory usage than Systemtap ++and some scripts show that ktap have a little overhead than Systemtap ++(we choosed two scripts to compare, function profile, stack profile. ++this is not means all scripts in Systemtap have big overhead than ktap) ++ ++ ++# FAQ ++ ++**Q: Why use bytecode design?** ++A: Using bytecode would be a clean and lightweight solution, ++ you don't need gcc toolchain to compile every scripts, all you ++ need is a ktapvm kernel modules and userspace tool called ktap. ++ Since its language virtual machine design, it have great portability, ++ suppose you are working at a multi-arch cluster, if you want to run ++ a tracing script on each board, you won't need cross-compile tracing ++ script onto all board, what you really need to do is use ktap tool ++ to run script just in time. ++ ++ Bytecode based design also will make executing more safer, than native code ++ generation. ++ ++ Reality already showing that SystemTap is not widely used in embedded Linux, ++ caused by problem of SystemTap's architecture design choice, it's a natural ++ design for Redhat and IBM, because Redhat/IBM is focusing on server area, ++ not embedded area. ++ ++**Q: What's the differences with SystemTap and Dtrace?** ++A: For SystemTap, the answer is already mentioned at above question, ++ SystemTap use translator design, for trade-off on performance with usability, ++ based on GCC, that's what ktap want to solve. ++ ++ For Dtrace, one common design with Dtrace is also use bytecode, so basically ++ Dtrace and ktap is on the same road. There have some projects aim to porting ++ Dtrace from Solaris to Linux, but the process is still on the road, Dtrace ++ is rooted in Solaris, and there have many huge differences between Solaris ++ tracing infrastructure with Linux's. ++ ++ Dtrace is based on D language, a language subset of C, it's a restricted ++ language, like without for-looping, for safty use in production system. ++ It seems that Dtrace for Linux only support x86 architecture, not work on ++ powerpc and arm/mips, obviously it's not suit for embedded Linux currently. ++ ++ Dtrace use ctf as input for debuginfo handing, compare with vmlinux for ++ SystemTap. ++ ++ On the license part, Dtrace is released as CDDL, which is incompatible with ++ GPL(this is why it's impossible to upstream Dtrace into mainline). ++ ++**Q: Why use dynamically typed language? but not statically typed language?** ++A: It's hard to say which one is more better than other, dynamically typed ++ language bring efficiency and fast prototype production, but loosing type ++ check at compiling phase, and easy to make mistake in runtime, also it's ++ need many runtime checking, In contrast, statically typed language win on ++ programing safety, and performance. Statically language would suit for ++ interoperate with kernel, as kernel is wrote mainly in C, Need to note that ++ SystemTap and Dtrace both is statically language. ++ ++ ktap choose dynamically typed language as initial implementation. ++ ++**Q: Why we need ktap for event tracing? There already have a built-in ftrace** ++A: This also is a common question for all dynamic tracing tool, not only ktap. ++ ktap provide more flexibility than built-in tracing infrastructure. Suppose ++ you need print a global variable when tracepoint hit, or you want print ++ backtrace, even more, you want to store some info into associative array, and ++ display it in histogram style when tracing end, in these case, some of them ++ ftrace can take it, some of them ftrace can not. ++ Overall, ktap provide you with great flexibility to scripting your own trace ++ need. ++ ++**Q: How about the performance? Is ktap slow?** ++A: ktap is not slow, the bytecode is very high-level, based on lua, the language ++ virtual machine is register-based(compare with stack-based), with little ++ instruction, the table data structure is heavily optimized in ktapvm. ++ ktap use per-cpu allocation in many place, without global locking scheme, ++ it's very fast when executing tracepoint callback. ++ Performance benchmark showing that the overhead of ktap running is nearly ++ 10%(store event name into associative array), compare with full speed ++ running without any tracepoint enabled. ++ ++ ktap will optimize overhead all the time, hopefully the overhead will ++ decrease to little than 5%, even more. ++ ++**Q: Why not porting a high level language implementation into kernel directly? ++ Like python/JVM?** ++A: I take serious on the size of vm and memory footprint. Python vm is large, ++ it's not suit to embed into kernel, and python have some functionality ++ which we don't need. ++ ++ The bytecode of other high level language is also big, ktap only have 32 ++ bytecodes, python/java/erlang have nearly two hundred bytecodes. ++ There also have some problems when porting those language into kernel, ++ userspace programming have many differences with kernel programming, ++ like float numbers, handle sleeping code carefully in kernel, deadloop is ++ not allowed in kernel, multi-thread management, etc.., so it's impossible ++ to porting language implementation into kernel with little adaption work. ++ ++**Q: What's the status of ktap now?** ++A: Basically it works on x86-32, x86-64, powerpc, arm, it also could work for ++ other hardware architecture, but not proven yet(I don't have enough hardware ++ to test) ++ If you found some bug, fix it on you own programming skill, or report to me. ++ ++**Q: How to hack ktap? I want to write some extensions onto ktap.** ++A: welcome hacking. ++ You can write your own library to fulfill your specific need, ++ you can write any script as you want. ++ ++**Q: What's the plan of ktap? any roadmap?** ++A: the current plan is deliver stable ktapvm kernel modules, more ktap script, ++ and bugfix. ++ ++ ++# References ++ ++* [Linux Performance Analysis and Tools][LPAT] ++* [Dtrace Blog][dtraceblog] ++* [Dtrace User Guide][dug] ++* [LWN: ktap -- yet another kernel tracer][lwn1] ++* [LWN: Ktap almost gets into 3.13][lwn2] ++* [staging: ktap: add to the kernel tree][ktap_commit] ++* [ktap introduction in LinuxCon Japan 2013][lcj](content is out of date) ++* [ktap Examples by Brendan Gregg][KEBG] ++ ++[LPAT]: http://www.brendangregg.com/Slides/SCaLE_Linux_Performance2013.pdf ++[dtraceblog]: http://dtrace.org/blogs/ ++[dug]: http://docs.huihoo.com/opensolaris/dtrace-user-guide/html/index.html ++[lwn1]: http://lwn.net/Articles/551314/ ++[lwn2]: http://lwn.net/Articles/572788/ ++[ktap_commit]: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c63a164271f81220ff4966d41218a9101f3d0ec4 ++[lcj]: http://events.linuxfoundation.org/sites/events/files/lcjpcojp13_zhangwei.pdf ++[KEBG]: http://www.brendangregg.com/ktap.html ++ ++# History ++ ++* ktap was invented at 2002 ++* First RFC sent to LKML at 2012.12.31 ++* The code was released in github at 2013.01.18 ++* ktap released v0.1 at 2013.05.21 ++* ktap released v0.2 at 2013.07.31 ++* ktap released v0.3 at 2013.10.29 ++ ++For more release info, please look at RELEASES.txt in project root directory. ++ ++# Examples ++ ++1. simplest one-liner command to enable all tracepoints ++ ++ ktap -e "trace *:* { print(argevent) }" ++ ++2. syscall tracing on target process ++ ++ ktap -e "trace syscalls:* { print(argevent) }" -- ls ++ ++3. ftrace(kernel newer than 3.3, and must compiled with CONFIG_FUNCTION_TRACER) ++ ++ ktap -e "trace ftrace:function { print(argevent) }" ++ ++ ktap -e "trace ftrace:function /ip==mutex*/ { print(argevent) }" ++ ++4. simple syscall tracing ++ ++ trace syscalls:* { ++ print(cpu(), pid(), execname(), argevent) ++ } ++ ++5. syscall tracing in histogram style ++ ++ s = {} ++ ++ trace syscalls:sys_enter_* { ++ s[argname] += 1 ++ } ++ ++ trace_end { ++ histogram(s) ++ } ++ ++6. kprobe tracing ++ ++ trace probe:do_sys_open dfd=%di fname=%dx flags=%cx mode=+4($stack) { ++ print("entry:", execname(), argevent) ++ } ++ ++ trace probe:do_sys_open%return fd=$retval { ++ print("exit:", execname(), argevent) ++ } ++ ++7. uprobe tracing ++ ++ trace probe:/lib/libc.so.6:malloc { ++ print("entry:", execname(), argevent) ++ } ++ ++ trace probe:/lib/libc.so.6:malloc%return { ++ print("exit:", execname(), argevent) ++ } ++ ++8. stapsdt tracing (userspace static marker) ++ ++ trace sdt:/lib64/libc.so.6:lll_futex_wake { ++ print("lll_futex_wake", execname(), argevent) ++ } ++ ++ or: ++ ++ #trace all static mark in libc ++ trace sdt:/lib64/libc.so.6:* { ++ print(execname(), argevent) ++ } ++ ++9. timer ++ ++ tick-1ms { ++ printf("time fired on one cpu\n"); ++ } ++ ++ profile-2s { ++ printf("time fired on every cpu\n"); ++ } ++ ++10. FFI (Call kernel function from ktap script, need compile with FFI=1) ++ ++ cdef[[ ++ int printk(char *fmt, ...); ++ ]] ++ ++ C.printk("This message is called from ktap ffi\n") ++ ++More examples can be found at [samples][samples_dir] directory. ++ ++[samples_dir]: https://github.com/ktap/ktap/tree/master/samples ++ ++# Appendix ++ ++Here is the complete syntax of ktap in extended BNF. ++(based on lua syntax: http://www.lua.org/manual/5.1/manual.html#5.1) ++ ++ chunk ::= {stat [';']} [laststat [';'] ++ ++ block ::= chunk ++ ++ stat ::= varlist '=' explist | ++ functioncall | ++ { block } | ++ while exp { block } | ++ repeat block until exp | ++ if exp { block {elseif exp { block }} [else block] } | ++ for Name '=' exp ',' exp [',' exp] { block } | ++ for namelist in explist { block } | ++ function funcname funcbody | ++ local function Name funcbody | ++ local namelist ['=' explist] ++ ++ laststat ::= return [explist] | break ++ ++ funcname ::= Name {'.' Name} [':' Name] ++ ++ varlist ::= var {',' var} ++ ++ var ::= Name | prefixexp '[' exp ']'| prefixexp '.' Name ++ ++ namelist ::= Name {',' Name} ++ ++ explist ::= {exp ',' exp ++ ++ exp ::= nil | false | true | Number | String | '...' | function | ++ prefixexp | tableconstructor | exp binop exp | unop exp ++ ++ prefixexp ::= var | functioncall | '(' exp ')' ++ ++ functioncall ::= prefixexp args | prefixexp ':' Name args ++ ++ args ::= '(' [explist] ')' | tableconstructor | String ++ ++ function ::= function funcbody ++ ++ funcbody ::= '(' [parlist] ')' { block } ++ ++ parlist ::= namelist [',' '...'] | '...' ++ ++ tableconstructor ::= '{' [fieldlist] '}' ++ ++ fieldlist ::= field {fieldsep field} [fieldsep] ++ ++ field ::= '[' exp ']' '=' exp | Name '=' exp | exp ++ ++ fieldsep ::= ',' | ';' ++ ++ binop ::= '+' | '-' | '*' | '/' | '^' | '%' | '..' | ++ '<' | '<=' | '>' | '>=' | '==' | '!=' | ++ and | or ++ ++ unop ::= '-' ++ +diff --git a/drivers/staging/ktap/include/ktap_ffi.h b/drivers/staging/ktap/include/ktap_ffi.h +new file mode 100644 +index 0000000..c8167f0 +--- /dev/null ++++ b/drivers/staging/ktap/include/ktap_ffi.h +@@ -0,0 +1,180 @@ ++#ifndef __KTAP_FFI_H__ ++#define __KTAP_FFI_H__ ++ ++#ifdef CONFIG_KTAP_FFI ++ ++#include "../include/ktap_types.h" ++ ++/* ++ * Types design in FFI module ++ * ++ * ktap_cdata is an instance of csymbol, so it's a combination of csymbol ++ * and it's actual data in memory. ++ * ++ * csymbol structs are globally unique and readonly type that represent C ++ * types. For non scalar C types like struct and function, helper structs are ++ * used to store detailed information. See csymbol_func and csymbol_struct for ++ * more information. ++ */ ++ ++typedef enum { ++ /* 0 - 4 */ ++ FFI_VOID, ++ FFI_UINT8, ++ FFI_INT8, ++ FFI_UINT16, ++ FFI_INT16, ++ /* 5 - 9 */ ++ FFI_UINT32, ++ FFI_INT32, ++ FFI_UINT64, ++ FFI_INT64, ++ FFI_PTR, ++ /* 10 - 12 */ ++ FFI_FUNC, ++ FFI_STRUCT, ++ FFI_UNKNOWN, ++} ffi_type; ++#define NUM_FFI_TYPE ((int)FFI_UNKNOWN) ++ ++ ++/* following struct and macros are added for C typedef ++ * size and alignment calculation */ ++typedef struct { ++ size_t size; ++ size_t align; ++ const char *name; ++} ffi_mode; ++extern const ffi_mode const ffi_type_modes[]; ++ ++#define ffi_type_size(t) (ffi_type_modes[t].size) ++#define ffi_type_align(t) (ffi_type_modes[t].align) ++#define ffi_type_name(t) (ffi_type_modes[t].name) ++ ++ ++/* start of csymbol definition */ ++#define CSYM_NAME_MAX_LEN 64 ++ ++typedef struct csymbol_func { ++ void *addr; /* function address */ ++ csymbol_id ret_id; /* function return type */ ++ int arg_nr; /* number of arguments */ ++ csymbol_id *arg_ids; /* function argument types */ ++ unsigned has_var_arg; /* is this a var arg function? */ ++} csymbol_func; ++ ++typedef struct struct_member { ++ char name[CSYM_NAME_MAX_LEN]; /* name for this struct member */ ++ csymbol_id id; /* type for this struct member */ ++} struct_member; ++ ++typedef struct csymbol_struct { ++ int memb_nr; /* number of members */ ++ struct_member *members; /* array for each member definition */ ++ size_t size; /* bytes used to store struct */ ++ /* alignment of the struct, 0 indicates uninitialization */ ++ size_t align; ++} csymbol_struct; ++ ++ ++/* wrapper struct for all C symbols */ ++typedef struct csymbol { ++ char name[CSYM_NAME_MAX_LEN]; /* name for this symbol */ ++ ffi_type type; /* type for this symbol */ ++ /* following members are used only for non scalar C types */ ++ union { ++ csymbol_id p; /* pointer type */ ++ csymbol_func f; /* C function type */ ++ csymbol_struct st; /* struct type */ ++ csymbol_id td; /* typedef type */ ++ } u; ++} csymbol; ++ ++/* lookup csymbol address by it's id */ ++inline csymbol *ffi_get_csym_by_id(ktap_state *ks, csymbol_id id); ++#define id_to_csym(ks, id) (ffi_get_csym_by_id(ks, id)) ++ ++/* helper macros for struct csymbol */ ++#define csym_type(cs) ((cs)->type) ++#define csym_name(cs) ((cs)->name) ++ ++/* ++ * helper macros for pointer symbol ++ */ ++#define csym_ptr_deref_id(cs) ((cs)->u.p) ++#define csym_set_ptr_deref_id(cs, id) ((cs)->u.p = (id)) ++/* following macro gets csymbol address */ ++#define csym_ptr_deref(ks, cs) (id_to_csym(ks, csym_ptr_deref_id(cs))) ++ ++/* ++ * helper macros for function symbol ++ * csym_* accepts csymbol type ++ * csymf_* accepts csymbol_func type ++ */ ++#define csymf_addr(csf) ((csf)->addr) ++#define csymf_ret_id(csf) ((csf)->ret_id) ++#define csymf_arg_nr(csf) ((csf)->arg_nr) ++#define csymf_arg_ids(csf) ((csf)->arg_ids) ++/* get csymbol id for the nth argument */ ++#define csymf_arg_id(csf, n) ((csf)->arg_ids[n]) ++#define csym_func(cs) (&((cs)->u.f)) ++#define csym_func_addr(cs) (csymf_addr(csym_func(cs))) ++#define csym_func_arg_ids(cs) (csymf_arg_ids(csym_func(cs))) ++/* following macros get csymbol address */ ++#define csymf_ret(ks, csf) (id_to_csym(ks, csymf_ret_id(csf))) ++/* get csymbol address for the nth argument */ ++#define csymf_arg(ks, csf, n) (id_to_csym(ks, csymf_arg_id(csf, n))) ++#define csym_func_arg(ks, cs, n) (csymf_arg(ks, csym_func(cs), n)) ++ ++/* ++ * helper macors for struct symbol ++ * csym_* accepts csymbol type ++ * csymst_* accepts csymbol_struct type ++ */ ++#define csymst_mb_nr(csst) ((csst)->memb_nr) ++#define csym_struct(cs) (&(cs)->u.st) ++#define csym_struct_mb(cs) (csymst_mb(ks, csym_struct(cs), n)) ++/* following macro gets csymbol address for the nth struct member */ ++#define csymst_mb(ks, csst, n) (id_to_csym(ks, (csst)->members[n].id)) ++ ++ ++/* ++ * helper macros for ktap_cdata type ++ */ ++#define cd_csym_id(cd) ((cd)->id) ++#define cd_set_csym_id(cd, id) (cd_csym_id(cd) = (id)) ++#define cd_csym(ks, cd) (id_to_csym(ks, cd_csym_id(cd))) ++#define cd_type(ks, cd) (cd_csym(ks, cd)->type) ++ ++#define cd_int(cd) ((cd)->u.i) ++#define cd_ptr(cd) ((cd)->u.p) ++#define cd_struct(cd) ((cd)->u.st) ++ ++ ++#ifdef __KERNEL__ ++size_t csym_size(ktap_state *ks, csymbol *sym); ++size_t csym_align(ktap_state *ks, csymbol *sym); ++size_t csym_struct_offset(ktap_state *ks, csymbol_struct *csst, int idx); ++void init_csym_struct(ktap_state *ks, csymbol_struct *csst); ++ ++void kp_ffi_free_symbol(ktap_state *ks); ++csymbol_id ffi_get_csym_id(ktap_state *ks, char *name); ++ ++ktap_cdata *kp_cdata_new(ktap_state *ks); ++void kp_cdata_dump(ktap_state *ks, ktap_cdata *cd); ++ktap_cdata *kp_cdata_new_ptr(ktap_state *ks, void *addr, csymbol_id id); ++ktap_cdata *kp_cdata_new_struct(ktap_state *ks, void *val, csymbol_id id); ++ ++int kp_ffi_call(ktap_state *ks, csymbol_func *cf); ++#endif /* for __KERNEL__ */ ++ ++#else ++ ++static void __maybe_unused kp_ffi_free_symbol(ktap_state *ks) ++{ ++ return; ++} ++ ++#endif /* CONFIG_KTAP_FFI */ ++ ++#endif /* __KTAP_FFI_H__ */ +diff --git a/drivers/staging/ktap/include/ktap_opcodes.h b/drivers/staging/ktap/include/ktap_opcodes.h +new file mode 100644 +index 0000000..f9ac4e5 +--- /dev/null ++++ b/drivers/staging/ktap/include/ktap_opcodes.h +@@ -0,0 +1,239 @@ ++#ifndef __KTAP_BYTECODE_H__ ++#define __KTAP_BYTECODE_H__ ++ ++ ++/* opcode is copied from lua initially */ ++ ++typedef enum { ++/*---------------------------------------------------------------------- ++ * name args description ++ * ------------------------------------------------------------------------*/ ++OP_MOVE,/* A B R(A) := R(B) */ ++OP_LOADK,/* A Bx R(A) := Kst(Bx) */ ++OP_LOADKX,/* A R(A) := Kst(extra arg) */ ++OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */ ++OP_LOADNIL,/* A B R(A), R(A+1), ..., R(A+B) := nil */ ++OP_GETUPVAL,/* A B R(A) := UpValue[B] */ ++ ++OP_GETTABUP,/* A B C R(A) := UpValue[B][RK(C)] */ ++OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */ ++ ++OP_SETTABUP,/* A B C UpValue[A][RK(B)] := RK(C) */ ++OP_SETTABUP_INCR,/* A B C UpValue[A][RK(B)] += RK(C) */ ++OP_SETTABUP_AGGR,/* A B C UpValue[A][RK(B)] <<< RK(C) */ ++OP_SETUPVAL,/* A B UpValue[B] := R(A) */ ++OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */ ++OP_SETTABLE_INCR,/* A B C R(A)[RK(B)] += RK(C) */ ++OP_SETTABLE_AGGR,/* A B C R(A)[RK(B)] <<< RK(C) */ ++ ++OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */ ++ ++OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */ ++ ++OP_ADD,/* A B C R(A) := RK(B) + RK(C) */ ++OP_SUB,/* A B C R(A) := RK(B) - RK(C) */ ++OP_MUL,/* A B C R(A) := RK(B) * RK(C) */ ++OP_DIV,/* A B C R(A) := RK(B) / RK(C) */ ++OP_MOD,/* A B C R(A) := RK(B) % RK(C) */ ++OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */ ++OP_UNM,/* A B R(A) := -R(B) */ ++OP_NOT,/* A B R(A) := not R(B) */ ++OP_LEN,/* A B R(A) := length of R(B) */ ++ ++OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */ ++ ++OP_JMP,/* A sBx pc+=sBx; if (A) close all upvalues >= R(A) + 1 */ ++OP_EQ,/* A B C if ((RK(B) == RK(C)) != A) then pc++ */ ++OP_LT,/* A B C if ((RK(B) < RK(C)) != A) then pc++ */ ++OP_LE,/* A B C if ((RK(B) <= RK(C)) != A) then pc++ */ ++ ++OP_TEST,/* A C if not (R(A) <=> C) then pc++ */ ++OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ ++ ++OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ ++OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ ++OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */ ++ ++OP_FORLOOP,/* A sBx R(A)+=R(A+2); ++ if R(A) >1) /* `sBx' is signed */ ++ ++#define MAXARG_Ax ((1<>POS_OP) & MASK1(SIZE_OP,0)) ++#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ ++ ((((ktap_instruction)o)<>pos) & MASK1(size,0)) ++#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \ ++ ((((ktap_instruction)v)<> 4) & 3) ++#define getCMode(m) ((enum OpArgMask)(ktap_opmodes[m] >> 2) & 3) ++#define testAMode(m) (ktap_opmodes[m] & (1 << 6)) ++#define testTMode(m) (ktap_opmodes[m] & (1 << 7)) ++ ++ ++/* number of list items to accumulate before a SETLIST instruction */ ++#define LFIELDS_PER_FLUSH 50 ++ ++extern const char *const ktap_opnames[NUM_OPCODES + 1]; ++ ++#endif /* __KTAP_BYTECODE_H__ */ +diff --git a/drivers/staging/ktap/include/ktap_types.h b/drivers/staging/ktap/include/ktap_types.h +new file mode 100644 +index 0000000..c1c94bc +--- /dev/null ++++ b/drivers/staging/ktap/include/ktap_types.h +@@ -0,0 +1,609 @@ ++#ifndef __KTAP_TYPES_H__ ++#define __KTAP_TYPES_H__ ++ ++#ifdef __KERNEL__ ++#include ++#else ++typedef char u8; ++#include ++#include ++#include ++#include ++#endif ++ ++/* ++ * The first argument type of kdebug.probe_by_id() ++ * The value is a userspace memory pointer. ++ */ ++typedef struct ktap_eventdef_info { ++ int nr; ++ int *id_arr; ++ char *filter; ++} ktap_eventdef_info; ++ ++typedef struct ktap_parm { ++ char *trunk; /* __user */ ++ int trunk_len; ++ int argc; ++ char **argv; /* __user */ ++ int verbose; ++ int trace_pid; ++ int workload; ++ int trace_cpu; ++ int print_timestamp; ++ int quiet; ++} ktap_parm; ++ ++/* ++ * Ioctls that can be done on a ktap fd: ++ * todo: use _IO macro in include/uapi/asm-generic/ioctl.h ++ */ ++#define KTAP_CMD_IOC_VERSION ('$' + 0) ++#define KTAP_CMD_IOC_RUN ('$' + 1) ++#define KTAP_CMD_IOC_EXIT ('$' + 3) ++ ++#define KTAP_ENV "_ENV" ++ ++#define KTAP_VERSION_MAJOR "0" ++#define KTAP_VERSION_MINOR "4" ++ ++#define KTAP_VERSION "ktap " KTAP_VERSION_MAJOR "." KTAP_VERSION_MINOR ++#define KTAP_AUTHOR "Jovi Zhangwei " ++#define KTAP_COPYRIGHT KTAP_VERSION " Copyright (C) 2012-2013, " KTAP_AUTHOR ++ ++#define MYINT(s) (s[0] - '0') ++#define VERSION (MYINT(KTAP_VERSION_MAJOR) * 16 + MYINT(KTAP_VERSION_MINOR)) ++#define FORMAT 0 /* this is the official format */ ++ ++#define KTAP_SIGNATURE "\033ktap" ++ ++/* data to catch conversion errors */ ++#define KTAPC_TAIL "\x19\x93\r\n\x1a\n" ++ ++/* size in bytes of header of binary files */ ++#define KTAPC_HEADERSIZE (sizeof(KTAP_SIGNATURE) - sizeof(char) + 2 + \ ++ 6 + sizeof(KTAPC_TAIL) - sizeof(char)) ++ ++typedef long ktap_number; ++#define kp_number2int(i, n) ((i) = (int)(n)) ++ ++typedef int ktap_instruction; ++ ++typedef union ktap_gcobject ktap_gcobject; ++ ++#define CommonHeader ktap_gcobject *next; u8 tt; ++ ++typedef union ktap_string { ++ int dummy; /* ensures maximum alignment for strings */ ++ struct { ++ CommonHeader; ++ u8 extra; /* reserved words for short strings; "has hash" for longs */ ++ unsigned int hash; ++ size_t len; /* number of characters in string */ ++ } tsv; ++ /* short string is stored here, just after tsv */ ++} ktap_string; ++ ++ ++struct ktap_state; ++typedef int (*ktap_cfunction) (struct ktap_state *ks); ++ ++typedef struct ktap_value { ++ union { ++ ktap_gcobject *gc; /* collectable objects */ ++ void *p; /* light userdata */ ++ int b; /* booleans */ ++ ktap_cfunction f; /* light C functions */ ++ ktap_number n; /* numbers */ ++ } val; ++ int type; ++} ktap_value; ++ ++typedef ktap_value * StkId; ++ ++ ++/* ++ * Description of an upvalue for function prototypes ++ */ ++typedef struct ktap_upvaldesc { ++ ktap_string *name; /* upvalue name (for debug information) */ ++ u8 instack; /* whether it is in stack */ ++ u8 idx; /* index of upvalue (in stack or in outer function's list) */ ++} ktap_upvaldesc; ++ ++/* ++ * Description of a local variable for function prototypes ++ * (used for debug information) ++ */ ++typedef struct ktap_locvar { ++ ktap_string *varname; ++ int startpc; /* first point where variable is active */ ++ int endpc; /* first point where variable is dead */ ++} ktap_locvar; ++ ++ ++typedef struct ktap_upval { ++ CommonHeader; ++ ktap_value *v; /* points to stack or to its own value */ ++ union { ++ ktap_value value; /* the value (when closed) */ ++ struct { /* double linked list (when open) */ ++ struct ktap_upval *prev; ++ struct ktap_upval *next; ++ } l; ++ } u; ++} ktap_upval; ++ ++ ++#define KTAP_MAX_STACK_ENTRIES 100 ++ ++typedef struct ktap_btrace { ++ CommonHeader; ++ unsigned int nr_entries; ++ /* entries stored in here, after nr_entries */ ++} ktap_btrace; ++ ++typedef struct ktap_closure { ++ CommonHeader; ++ u8 nupvalues; ++ struct ktap_proto *p; ++ struct ktap_upval *upvals[1]; /* list of upvalues */ ++ ktap_gcobject *gclist; ++} ktap_closure; ++ ++typedef struct ktap_proto { ++ CommonHeader; ++ ktap_value *k; /* constants used by the function */ ++ ktap_instruction *code; ++ struct ktap_proto **p; /* functions defined inside the function */ ++ int *lineinfo; /* map from opcodes to source lines (debug information) */ ++ struct ktap_locvar *locvars; /* information about local variables (debug information) */ ++ struct ktap_upvaldesc *upvalues; /* upvalue information */ ++ ktap_closure *cache; /* last created closure with this prototype */ ++ ktap_string *source; /* used for debug information */ ++ int sizeupvalues; /* size of 'upvalues' */ ++ int sizek; /* size of `k' */ ++ int sizecode; ++ int sizelineinfo; ++ int sizep; /* size of `p' */ ++ int sizelocvars; ++ int linedefined; ++ int lastlinedefined; ++ u8 numparams; /* number of fixed parameters */ ++ u8 is_vararg; ++ u8 maxstacksize; /* maximum stack used by this function */ ++} ktap_proto; ++ ++ ++/* ++ * information about a call ++ */ ++typedef struct ktap_callinfo { ++ StkId func; /* function index in the stack */ ++ StkId top; /* top for this function */ ++ struct ktap_callinfo *prev, *next; /* dynamic call link */ ++ short nresults; /* expected number of results from this function */ ++ u8 callstatus; ++ int extra; ++ union { ++ struct { /* only for ktap functions */ ++ StkId base; /* base for this function */ ++ const unsigned int *savedpc; ++ } l; ++ struct { /* only for C functions */ ++ int ctx; /* context info. in case of yields */ ++ u8 status; ++ } c; ++ } u; ++} ktap_callinfo; ++ ++ ++/* ++ * ktap_tab ++ */ ++typedef struct ktap_tkey { ++ struct ktap_tnode *next; /* for chaining */ ++ ktap_value tvk; ++} ktap_tkey; ++ ++ ++typedef struct ktap_tnode { ++ ktap_value i_val; ++ ktap_tkey i_key; ++} ktap_tnode; ++ ++ ++typedef struct ktap_stat_data { ++ int count; ++ int sum; ++ int min, max; ++} ktap_stat_data; ++ ++ ++typedef struct ktap_tab { ++ CommonHeader; ++#ifdef __KERNEL__ ++ arch_spinlock_t lock; ++#endif ++ u8 flags; /* 1<

stats) ++ ++enum { ++ KTAP_PERCPU_DATA_STATE, ++ KTAP_PERCPU_DATA_STACK, ++ KTAP_PERCPU_DATA_BUFFER, ++ KTAP_PERCPU_DATA_BUFFER2, ++ KTAP_PERCPU_DATA_BTRACE, ++ ++ KTAP_PERCPU_DATA_MAX ++}; ++ ++typedef struct ktap_global_state { ++ ktap_stringtable strt; /* hash table for strings */ ++ ktap_value registry; ++ unsigned int seed; /* randonized seed for hashes */ ++ ++ ktap_gcobject *allgc; /* list of all collectable objects */ ++ ++ ktap_upval uvhead; /* head of double-linked list of all open upvalues */ ++ ++ struct ktap_state *mainthread; ++#ifdef __KERNEL__ ++ /* global percpu data(like stack) */ ++ void __percpu *pcpu_data[KTAP_PERCPU_DATA_MAX][PERF_NR_CONTEXTS]; ++ ++ int __percpu *recursion_context[PERF_NR_CONTEXTS]; ++ ++ arch_spinlock_t str_lock; /* string opertion lock */ ++ ++ ktap_parm *parm; ++ pid_t trace_pid; ++ struct task_struct *trace_task; ++ cpumask_var_t cpumask; ++ struct ring_buffer *buffer; ++ struct dentry *trace_pipe_dentry; ++ int nr_builtin_cfunction; ++ ktap_value *cfunction_tbl; ++ struct task_struct *task; ++ int trace_enabled; ++ struct list_head timers; ++ struct list_head probe_events_head; ++ int exit; ++ int wait_user; ++ ktap_closure *trace_end_closure; ++ struct ktap_stats __percpu *stats; ++ struct kmem_cache *pevent_cache; ++#ifdef CONFIG_KTAP_FFI ++ ffi_state ffis; ++#endif ++#endif ++ int error; ++} ktap_global_state; ++ ++typedef struct ktap_state { ++ CommonHeader; ++ ktap_global_state *g; ++ int stop; ++ StkId top; ++ ktap_callinfo *ci; ++ const unsigned long *oldpc; ++ StkId stack_last; ++ StkId stack; ++ ktap_gcobject *openupval; ++ ktap_callinfo baseci; ++ ++ /* list of temp collectable objects, free when thread exit */ ++ ktap_gcobject *gclist; ++ ++#ifdef __KERNEL__ ++ struct ktap_event *current_event; ++#endif ++} ktap_state; ++ ++ ++typedef struct gcheader { ++ CommonHeader; ++} gcheader; ++ ++/* ++ * Union of all collectable objects ++ */ ++union ktap_gcobject { ++ gcheader gch; /* common header */ ++ union ktap_string ts; ++ struct ktap_closure cl; ++ struct ktap_tab h; ++ struct ktap_ptab ph; ++ struct ktap_proto p; ++ struct ktap_upval uv; ++ struct ktap_state th; /* thread */ ++ struct ktap_btrace bt; /* backtrace object */ ++#ifdef CONFIG_KTAP_FFI ++ struct ktap_cdata cd; ++#endif ++}; ++ ++#define gch(o) (&(o)->gch) ++ ++/* macros to convert a GCObject into a specific value */ ++#define rawgco2ts(o) (&((o)->ts)) ++ ++#define gco2ts(o) (&rawgco2ts(o)->tsv) ++#define gco2uv(o) (&((o)->uv)) ++#define obj2gco(v) ((ktap_gcobject *)(v)) ++#define check_exp(c, e) (e) ++ ++ ++/* predefined values in the registry */ ++#define KTAP_RIDX_MAINTHREAD 1 ++#define KTAP_RIDX_GLOBALS 2 ++#define KTAP_RIDX_LAST KTAP_RIDX_GLOBALS ++ ++#define KTAP_TNONE (-1) ++ ++#define KTAP_TNIL 0 ++#define KTAP_TBOOLEAN 1 ++#define KTAP_TLIGHTUSERDATA 2 ++#define KTAP_TNUMBER 3 ++#define KTAP_TSTRING 4 ++#define KTAP_TSHRSTR (KTAP_TSTRING | (0 << 4)) /* short strings */ ++#define KTAP_TLNGSTR (KTAP_TSTRING | (1 << 4)) /* long strings */ ++#define KTAP_TTABLE 5 ++#define KTAP_TFUNCTION 6 ++#define KTAP_TCLOSURE (KTAP_TFUNCTION | (0 << 4)) /* closure */ ++#define KTAP_TCFUNCTION (KTAP_TFUNCTION | (1 << 4)) /* light C function */ ++#define KTAP_TTHREAD 7 ++#define KTAP_TPROTO 8 ++#define KTAP_TUPVAL 9 ++#define KTAP_TEVENT 10 ++#define KTAP_TBTRACE 11 ++#define KTAP_TPTABLE 12 ++#define KTAP_TSTATDATA 13 ++#define KTAP_TCDATA 14 ++/* ++ * type number is ok so far, but it may collide later between ++ * 16+ and | (1 << 4), so be careful on this. ++ */ ++ ++#define ttype(o) ((o->type) & 0x3F) ++#define settype(obj, t) ((obj)->type = (t)) ++ ++/* raw type tag of a TValue */ ++#define rttype(o) ((o)->type) ++ ++/* tag with no variants (bits 0-3) */ ++#define novariant(x) ((x) & 0x0F) ++ ++/* type tag of a TValue with no variants (bits 0-3) */ ++#define ttypenv(o) (novariant(rttype(o))) ++ ++#define val_(o) ((o)->val) ++#define gcvalue(o) (val_(o).gc) ++ ++#define bvalue(o) (val_(o).b) ++#define nvalue(o) (val_(o).n) ++#define hvalue(o) (&val_(o).gc->h) ++#define phvalue(o) (&val_(o).gc->ph) ++#define clvalue(o) (&val_(o).gc->cl) ++ ++#define getstr(ts) (const char *)((ts) + 1) ++#define eqshrstr(a, b) ((a) == (b)) ++#define rawtsvalue(o) (&val_(o).gc->ts) ++#define svalue(o) getstr(rawtsvalue(o)) ++ ++#define pvalue(o) (&val_(o).p) ++#define sdvalue(o) ((ktap_stat_data *)val_(o).p) ++#define fvalue(o) (val_(o).f) ++#define evalue(o) (val_(o).p) ++#define btvalue(o) (&val_(o).gc->bt) ++#define cdvalue(o) (&val_(o).gc->cd) ++ ++#define is_nil(o) ((o)->type == KTAP_TNIL) ++#define is_boolean(o) ((o)->type == KTAP_TBOOLEAN) ++#define is_false(o) (is_nil(o) || (is_boolean(o) && bvalue(o) == 0)) ++#define is_shrstring(o) ((o)->type == KTAP_TSHRSTR) ++#define is_string(o) (((o)->type & 0x0F) == KTAP_TSTRING) ++#define is_number(o) ((o)->type == KTAP_TNUMBER) ++#define is_table(o) ((o)->type == KTAP_TTABLE) ++#define is_ptable(o) ((o)->type == KTAP_TPTABLE) ++#define is_statdata(o) ((o)->type == KTAP_TSTATDATA) ++#define is_event(o) ((o)->type == KTAP_TEVENT) ++#define is_btrace(o) ((o)->type == KTAP_TBTRACE) ++#define is_needclone(o) is_btrace(o) ++#ifdef CONFIG_KTAP_FFI ++#define is_cdata(o) ((o)->type == KTAP_TCDATA) ++#endif ++ ++ ++#define set_nil(obj) \ ++ { ktap_value *io = (obj); io->val.n = 0; settype(io, KTAP_TNIL); } ++ ++#define set_boolean(obj, x) \ ++ { ktap_value *io = (obj); io->val.b = (x); settype(io, KTAP_TBOOLEAN); } ++ ++#define set_number(obj, x) \ ++ { ktap_value *io = (obj); io->val.n = (x); settype(io, KTAP_TNUMBER); } ++ ++#define set_statdata(obj, x) \ ++ { ktap_value *io = (obj); \ ++ io->val.p = (x); settype(io, KTAP_TSTATDATA); } ++ ++#define set_string(obj, x) \ ++ { ktap_value *io = (obj); \ ++ ktap_string *x_ = (x); \ ++ io->val.gc = (ktap_gcobject *)x_; settype(io, x_->tsv.tt); } ++ ++#define set_closure(obj, x) \ ++ { ktap_value *io = (obj); \ ++ io->val.gc = (ktap_gcobject *)x; settype(io, KTAP_TCLOSURE); } ++ ++#define set_cfunction(obj, x) \ ++ { ktap_value *io = (obj); val_(io).f = (x); settype(io, KTAP_TCFUNCTION); } ++ ++#define set_table(obj, x) \ ++ { ktap_value *io = (obj); \ ++ val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TTABLE); } ++ ++#define set_ptable(obj, x) \ ++ { ktap_value *io = (obj); \ ++ val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TPTABLE); } ++ ++#define set_thread(obj, x) \ ++ { ktap_value *io = (obj); \ ++ val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TTHREAD); } ++ ++#define set_event(obj, x) \ ++ { ktap_value *io = (obj); val_(io).p = (x); settype(io, KTAP_TEVENT); } ++ ++#define set_btrace(obj, x) \ ++ { ktap_value *io = (obj); \ ++ val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TBTRACE); } ++ ++#ifdef CONFIG_KTAP_FFI ++#define set_cdata(obj, x) \ ++ { ktap_value *io=(obj); \ ++ val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TCDATA); } ++#endif ++ ++#define set_obj(obj1, obj2) \ ++ { const ktap_value *io2 = (obj2); ktap_value *io1 = (obj1); \ ++ io1->val = io2->val; io1->type = io2->type; } ++ ++#define rawequalobj(t1, t2) \ ++ (((t1)->type == (t2)->type) && kp_equalobjv(NULL, t1, t2)) ++ ++#define incr_top(ks) {ks->top++;} ++ ++#define NUMADD(a, b) ((a) + (b)) ++#define NUMSUB(a, b) ((a) - (b)) ++#define NUMMUL(a, b) ((a) * (b)) ++#define NUMDIV(a, b) ((a) / (b)) ++#define NUMUNM(a) (-(a)) ++#define NUMEQ(a, b) ((a) == (b)) ++#define NUMLT(a, b) ((a) < (b)) ++#define NUMLE(a, b) ((a) <= (b)) ++#define NUMISNAN(a) (!NUMEQ((a), (a))) ++ ++/* todo: floor and pow in kernel */ ++#define NUMMOD(a, b) ((a) % (b)) ++#define NUMPOW(a, b) (pow(a, b)) ++ ++#define ktap_assert(s) ++ ++#define kp_realloc(ks, v, osize, nsize, t) \ ++ ((v) = (t *)kp_reallocv(ks, v, osize * sizeof(t), nsize * sizeof(t))) ++ ++#define kp_error(ks, args...) \ ++ do { \ ++ kp_printf(ks, "error: "args); \ ++ G(ks)->error = 1; \ ++ kp_exit(ks); \ ++ } while(0) ++ ++#ifdef __KERNEL__ ++#define G(ks) (ks->g) ++ ++void kp_printf(ktap_state *ks, const char *fmt, ...); ++extern void __kp_puts(ktap_state *ks, const char *str); ++extern void __kp_bputs(ktap_state *ks, const char *str); ++ ++#define kp_puts(ks, str) ({ \ ++ static const char *trace_printk_fmt \ ++ __attribute__((section("__trace_printk_fmt"))) = \ ++ __builtin_constant_p(str) ? str : NULL; \ ++ \ ++ if (__builtin_constant_p(str)) \ ++ __kp_bputs(ks, trace_printk_fmt); \ ++ else \ ++ __kp_puts(ks, str); \ ++}) ++ ++#else ++/* ++ * this is used for ktapc tstring operation, tstring need G(ks)->strt ++ * and G(ks)->seed, so ktapc need to init those field ++ */ ++#define G(ks) (&dummy_global_state) ++extern ktap_global_state dummy_global_state; ++ ++#define kp_printf(ks, args...) printf(args) ++#define kp_puts(ks, str) printf("%s", str) ++#define kp_exit(ks) exit(EXIT_FAILURE) ++ ++#endif ++ ++#define __maybe_unused __attribute__((unused)) ++ ++/* ++ * KTAP_QL describes how error messages quote program elements. ++ * CHANGE it if you want a different appearance. ++ */ ++#define KTAP_QL(x) "'" x "'" ++#define KTAP_QS KTAP_QL("%s") ++ ++#define STRINGIFY(type) #type ++ ++#endif /* __KTAP_TYPES_H__ */ ++ +diff --git a/drivers/staging/ktap/runtime/ffi/call_x86_64.S b/drivers/staging/ktap/runtime/ffi/call_x86_64.S +new file mode 100644 +index 0000000..29b2915 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/ffi/call_x86_64.S +@@ -0,0 +1,143 @@ ++/* ++ * call_x86_64.S - assembly code to call C function and handle return value ++ * ++ * This file is part of ktap by Jovi Zhangwei ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#ifdef __x86_64 ++ ++ .file "call_x86_64.S" ++ .text ++ ++/* ffi_call_assem_x86_64(void *stack, void *temp_stack, ++ * void *rvalue, void *func_addr, ffi_type rftype) ++ * @stack: base address of register values and new stack ++ * @temp_stack: stack to store temporary values ++ * @func_addr: Function address ++ * @rvalue: where to put return value ++ * @rftype: FFI type of return value ++ */ ++ .align 2 ++ .globl ffi_call_assem_x86_64 ++ .type ffi_call_assem_x86_64,@function ++ ++ffi_call_assem_x86_64: ++ movq (%rsp), %rax /* save return address */ ++ /* move stuffs to temp memory region(void *temp_stack) */ ++ movq %rcx, (%rsi) /* save pointer to return value */ ++ movq %r8, 8(%rsi) /* save return_ffi_type */ ++ movq %rbp, 16(%rsi) /* save %rbp */ ++ movq %rax, 24(%rsi) /* save return address */ ++ movq %rsp, 32(%rsi) /* save %rsp */ ++ movq %rsi, %rbp /* point %rbp to temp memory region */ ++ ++ movq %rdx, %r11 /* move function address to %r11 */ ++ ++ movq %rdi, %r10 /* set %r10 point to register region */ ++ movq (%r10), %rdi /* load registers */ ++ movq 8(%r10), %rsi ++ movq 16(%r10), %rdx ++ movq 24(%r10), %rcx ++ movq 32(%r10), %r8 ++ movq 40(%r10), %r9 ++ xorq %rax, %rax ++ ++ leaq 48(%r10), %rsp ++ ++ callq *%r11 ++ ++ movq 32(%rbp), %rsp /* restore %rsp */ ++ movq 24(%rbp), %rcx /* restore return address */ ++ movq %rcx, (%rsp) ++ ++ movq (%rbp), %rcx /* get pointer to return value */ ++ movq 8(%rbp), %r8 /* get return_ffi_type */ ++ movq 16(%rbp), %rbp /* restore rbp */ ++ ++ leaq .Lreturn_table(%rip), %r11 /* start address of return_table */ ++ movslq (%r11, %r8, 8), %r11 /* fetch target address from table */ ++ jmpq *%r11 /* jump according to value in table */ ++ ++ .align 8 ++.Lreturn_table: ++ .quad .Lreturn_void /* FFI_VOID */ ++ .quad .Lreturn_uint8 /* FFI_UINT8 */ ++ .quad .Lreturn_int8 /* FFI_INT8 */ ++ .quad .Lreturn_uint16 /* FFI_UINT16 */ ++ .quad .Lreturn_int16 /* FFI_INT16 */ ++ .quad .Lreturn_uint32 /* FFI_UINT32 */ ++ .quad .Lreturn_int32 /* FFI_INT32 */ ++ .quad .Lreturn_uint64 /* FFI_UINT64 */ ++ .quad .Lreturn_int64 /* FFI_INT64 */ ++ .quad .Lreturn_ptr /* FFI_PTR */ ++ .quad .Lreturn_func /* FFI_FUNC */ ++ .quad .Lreturn_struct /* FFI_STRUCT */ ++ .quad .Lreturn_unknown /* FFI_UNKNOWN */ ++ ++ .align 8 ++.Lreturn_void: ++.Lreturn_func: ++.Lreturn_unknown: ++ retq ++ .align 8 ++.Lreturn_uint8: ++ movzbq %al, %rax ++ movq %rax, (%rcx) ++ retq ++ .align 8 ++.Lreturn_int8: ++ movsbq %al, %rax ++ movq %rax, (%rcx) ++ retq ++ .align 8 ++.Lreturn_uint16: ++ movzwq %ax, %rax ++ movq %rax, (%rcx) ++ retq ++ .align 8 ++.Lreturn_int16: ++ movswq %ax, %rax ++ movq %rax, (%rcx) ++ retq ++ .align 8 ++.Lreturn_uint32: ++ movl %eax, %eax ++ movq %rax, (%rcx) ++ retq ++ .align 8 ++.Lreturn_int32: ++ movslq %eax, %rax ++ movq %rax, (%rcx) ++ retq ++ .align 8 ++.Lreturn_uint64: ++.Lreturn_int64: ++.Lreturn_ptr: ++ movq %rax, (%rcx) ++ retq ++/* Struct type indicates that struct is put into at most two registers, ++ * and 16 bytes space is always available ++ */ ++ .align 8 ++.Lreturn_struct: ++ movq %rax, (%rcx) ++ movq %rdx, 8(%rcx) ++ retq ++ ++#endif /* end for __x86_64 */ +diff --git a/drivers/staging/ktap/runtime/ffi/cdata.c b/drivers/staging/ktap/runtime/ffi/cdata.c +new file mode 100644 +index 0000000..aaea559 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/ffi/cdata.c +@@ -0,0 +1,67 @@ ++/* ++ * cdata.c - support functions for ktap_cdata ++ * ++ * This file is part of ktap by Jovi Zhangwei ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#include "../../include/ktap_types.h" ++#include "../../include/ktap_ffi.h" ++#include "../kp_obj.h" ++ ++ktap_cdata *kp_cdata_new(ktap_state *ks) ++{ ++ ktap_cdata *cd; ++ ++ cd = &kp_newobject(ks, KTAP_TCDATA, sizeof(ktap_cdata), NULL)->cd; ++ ++ return cd; ++} ++ ++ktap_cdata *kp_cdata_new_ptr(ktap_state *ks, void *addr, csymbol_id id) ++{ ++ ktap_cdata *cd; ++ ++ cd = kp_cdata_new(ks); ++ cd_set_csym_id(cd, id); ++ cd_ptr(cd) = addr; ++ ++ return cd; ++} ++ ++ktap_cdata *kp_cdata_new_struct(ktap_state *ks, void *val, csymbol_id id) ++{ ++ ktap_cdata *cd; ++ ++ cd = kp_cdata_new(ks); ++ cd_set_csym_id(cd, id); ++ cd_struct(cd) = val; ++ ++ return cd; ++} ++ ++void kp_cdata_dump(ktap_state *ks, ktap_cdata *cd) ++{ ++ switch (cd_type(ks, cd)) { ++ case FFI_PTR: ++ kp_printf(ks, "pointer(%p)", cd_ptr(cd)); ++ break; ++ default: ++ kp_printf(ks, "unsupported cdata type %d!\n", cd_type(ks, cd)); ++ } ++} +diff --git a/drivers/staging/ktap/runtime/ffi/ffi_call.c b/drivers/staging/ktap/runtime/ffi/ffi_call.c +new file mode 100644 +index 0000000..7bd845a +--- /dev/null ++++ b/drivers/staging/ktap/runtime/ffi/ffi_call.c +@@ -0,0 +1,427 @@ ++/* ++ * ffi_call.c - foreign function calling library support for ktap ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include "../../include/ktap_types.h" ++#include "../../include/ktap_ffi.h" ++#include "../ktap.h" ++#include "../kp_vm.h" ++#include "../kp_obj.h" ++ ++static int ffi_type_check(ktap_state *ks, csymbol_func *csf, int idx) ++{ ++ StkId arg; ++ csymbol *cs; ++ ffi_type type; ++ ++ if (idx >= csymf_arg_nr(csf)) ++ return 0; ++ arg = kp_arg(ks, idx + 1); ++ cs = csymf_arg(ks, csf, idx); ++ type = csym_type(cs); ++ ++ if (type == FFI_FUNC) ++ goto error; ++ ++ switch (ttypenv(arg)) { ++ case KTAP_TLIGHTUSERDATA: ++ if (type != FFI_PTR) goto error; ++ break; ++ case KTAP_TBOOLEAN: ++ case KTAP_TNUMBER: ++ if (type != FFI_UINT8 && type != FFI_INT8 ++ && type != FFI_UINT16 && type != FFI_INT16 ++ && type != FFI_UINT32 && type != FFI_INT32 ++ && type != FFI_UINT64 && type != FFI_INT64) ++ goto error; ++ break; ++ case KTAP_TSTRING: ++ if (type != FFI_PTR && type != FFI_UINT8 && type != FFI_INT8) ++ goto error; ++ break; ++ case KTAP_TCDATA: ++ if (cs != cd_csym(ks, cdvalue(arg))) ++ goto error; ++ break; ++ default: ++ goto error; ++ } ++ return 0; ++ ++ error: ++ kp_error(ks, "Error: Cannot convert to csymbol %s for arg %d\n", ++ csym_name(cs), idx); ++ return -1; ++} ++ ++static csymbol *ffi_get_arg_csym(ktap_state *ks, csymbol_func *csf, int idx) ++{ ++ StkId arg; ++ csymbol *cs; ++ ++ if (idx < csymf_arg_nr(csf)) ++ return csymf_arg(ks, csf, idx); ++ ++ arg = kp_arg(ks, idx + 1); ++ cs = id_to_csym(ks, ffi_get_csym_id(ks, "void *")); ++ switch (ttypenv(arg)) { ++ case KTAP_TLIGHTUSERDATA: ++ case KTAP_TBOOLEAN: ++ case KTAP_TNUMBER: ++ case KTAP_TSTRING: ++ return cs; ++ case KTAP_TCDATA: ++ return cd_csym(ks, cdvalue(arg)); ++ default: ++ kp_error(ks, "Error: Cannot get type for arg %d\n", idx); ++ return cs; ++ } ++} ++ ++static void ffi_unpack(ktap_state *ks, csymbol_func *csf, int idx, ++ char *dst, int align) ++{ ++ StkId arg = kp_arg(ks, idx + 1); ++ csymbol *cs = ffi_get_arg_csym(ks, csf, idx); ++ ffi_type type = csym_type(cs); ++ size_t size = csym_size(ks, cs); ++ void *p; ++ struct ktap_cdata *cd; ++ ++ /* initialize the destination section */ ++ memset(dst, 0, ALIGN(size, align)); ++ ++ switch (ttypenv(arg)) { ++ case KTAP_TBOOLEAN: ++ memcpy(dst, &bvalue(arg), sizeof(bool)); ++ return; ++ case KTAP_TLIGHTUSERDATA: ++ memcpy(dst, pvalue(arg), size); ++ return; ++ case KTAP_TNUMBER: ++ memcpy(dst, &nvalue(arg), size < sizeof(ktap_number) ? ++ size : sizeof(ktap_number)); ++ return; ++ case KTAP_TSTRING: ++ p = &rawtsvalue(arg)->tsv + 1; ++ memcpy(dst, &p, size); ++ return; ++ } ++ ++ cd = cdvalue(arg); ++ switch (type) { ++ case FFI_VOID: ++ kp_error(ks, "Error: Cannot copy data from void type\n"); ++ return; ++ case FFI_UINT8: ++ case FFI_INT8: ++ case FFI_UINT16: ++ case FFI_INT16: ++ case FFI_UINT32: ++ case FFI_INT32: ++ case FFI_UINT64: ++ case FFI_INT64: ++ memcpy(dst, &cd_int(cd), size); ++ return; ++ case FFI_PTR: ++ memcpy(dst, &cd_ptr(cd), size); ++ return; ++ case FFI_STRUCT: ++ memcpy(dst, cd_struct(cd), size); ++ return; ++ case FFI_FUNC: ++ case FFI_UNKNOWN: ++ kp_error(ks, "Error: internal error for csymbol %s\n", ++ csym_name(cs)); ++ return; ++ } ++} ++ ++#ifdef __x86_64 ++ ++enum arg_status { ++ IN_REGISTER, ++ IN_MEMORY, ++ IN_STACK, ++}; ++ ++#define ALIGN_STACK(v, a) ((void *)(ALIGN(((uint64_t)v), a))) ++#define STACK_ALIGNMENT 8 ++#define REDZONE_SIZE 128 ++#define GPR_SIZE (sizeof(void *)) ++#define MAX_GPR 6 ++#define MAX_GPR_SIZE (MAX_GPR * GPR_SIZE) ++#define NEWSTACK_SIZE 512 ++ ++#define ffi_call(ks, cf, rvalue) ffi_call_x86_64(ks, cf, rvalue) ++ ++extern void ffi_call_assem_x86_64(void *stack, void *temp_stack, ++ void *func_addr, void *rvalue, ffi_type rtype); ++ ++static void ffi_call_x86_64(ktap_state *ks, csymbol_func *csf, void *rvalue) ++{ ++ int i; ++ int gpr_nr; ++ int arg_bytes; /* total bytes needed for exceeded args in stack */ ++ int mem_bytes; /* total bytes needed for memory storage */ ++ char *stack, *stack_p, *gpr_p, *arg_p, *mem_p, *tmp_p; ++ int arg_nr; ++ csymbol *rsym; ++ ffi_type rtype; ++ size_t rsize; ++ bool ret_in_memory; ++ /* New stack to call C function */ ++ char space[NEWSTACK_SIZE]; ++ ++ arg_nr = kp_arg_nr(ks); ++ rsym = csymf_ret(ks, csf); ++ rtype = csym_type(rsym); ++ rsize = csym_size(ks, rsym); ++ ret_in_memory = false; ++ if (rtype == FFI_STRUCT) { ++ if (rsize > 16) { ++ rvalue = kp_malloc(ks, rsize); ++ rtype = FFI_VOID; ++ ret_in_memory = true; ++ } else { ++ /* much easier to always copy 16 bytes from registers */ ++ rvalue = kp_malloc(ks, 16); ++ } ++ } ++ ++ gpr_nr = 0; ++ arg_bytes = mem_bytes = 0; ++ if (ret_in_memory) ++ gpr_nr++; ++ /* calculate bytes needed for stack */ ++ for (i = 0; i < arg_nr; i++) { ++ csymbol *cs = ffi_get_arg_csym(ks, csf, i); ++ size_t size = csym_size(ks, cs); ++ size_t align = csym_align(ks, cs); ++ enum arg_status st = IN_REGISTER; ++ int n_gpr_nr = 0; ++ if (size > 32) { ++ st = IN_MEMORY; ++ n_gpr_nr = 1; ++ } else if (size > 16) ++ st = IN_STACK; ++ else ++ n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE; ++ ++ if (gpr_nr + n_gpr_nr > MAX_GPR) { ++ if (st == IN_MEMORY) ++ arg_bytes += GPR_SIZE; ++ else ++ st = IN_STACK; ++ } else ++ gpr_nr += n_gpr_nr; ++ if (st == IN_STACK) { ++ arg_bytes = ALIGN(arg_bytes, align); ++ arg_bytes += size; ++ arg_bytes = ALIGN(arg_bytes, STACK_ALIGNMENT); ++ } ++ if (st == IN_MEMORY) { ++ mem_bytes = ALIGN(mem_bytes, align); ++ mem_bytes += size; ++ mem_bytes = ALIGN(mem_bytes, STACK_ALIGNMENT); ++ } ++ } ++ ++ /* apply space to fake stack for C function call */ ++ if (16 + REDZONE_SIZE + MAX_GPR_SIZE + arg_bytes + ++ mem_bytes + 6 * 8 >= NEWSTACK_SIZE) { ++ kp_error(ks, "Unable to handle that many arguments by now\n"); ++ return; ++ } ++ stack = space; ++ /* 128 bytes below %rsp is red zone */ ++ /* stack should be 16-bytes aligned */ ++ stack_p = ALIGN_STACK(stack + REDZONE_SIZE, 16); ++ /* save general purpose registers here */ ++ gpr_p = stack_p; ++ memset(gpr_p, 0, MAX_GPR_SIZE); ++ /* save arguments in stack here */ ++ arg_p = gpr_p + MAX_GPR_SIZE; ++ /* save arguments in memory here */ ++ mem_p = arg_p + arg_bytes; ++ /* set additional space as temporary space */ ++ tmp_p = mem_p + mem_bytes; ++ ++ /* copy arguments here */ ++ gpr_nr = 0; ++ if (ret_in_memory) { ++ memcpy(gpr_p, &rvalue, GPR_SIZE); ++ gpr_p += GPR_SIZE; ++ gpr_nr++; ++ } ++ for (i = 0; i < arg_nr; i++) { ++ csymbol *cs = ffi_get_arg_csym(ks, csf, i); ++ size_t size = csym_size(ks, cs); ++ size_t align = csym_align(ks, cs); ++ enum arg_status st = IN_REGISTER; ++ int n_gpr_nr = 0; ++ if (size > 32) { ++ st = IN_MEMORY; ++ n_gpr_nr = 1; ++ } else if (size > 16) ++ st = IN_STACK; ++ else ++ n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE; ++ ++ if (st == IN_MEMORY) ++ mem_p = ALIGN_STACK(mem_p, align); ++ /* Tricky way about storing it above mem_p. It won't overflow ++ * because temp region can be temporarily used if necesseary. */ ++ ffi_unpack(ks, csf, i, mem_p, GPR_SIZE); ++ if (gpr_nr + n_gpr_nr > MAX_GPR) { ++ if (st == IN_MEMORY) { ++ memcpy(arg_p, &mem_p, GPR_SIZE); ++ arg_p += GPR_SIZE; ++ } else ++ st = IN_STACK; ++ } else { ++ memcpy(gpr_p, mem_p, n_gpr_nr * GPR_SIZE); ++ gpr_p += n_gpr_nr * GPR_SIZE; ++ gpr_nr += n_gpr_nr; ++ } ++ if (st == IN_STACK) { ++ arg_p = ALIGN_STACK(arg_p, align); ++ memcpy(arg_p, mem_p, size); ++ arg_p += size; ++ arg_p = ALIGN_STACK(arg_p, STACK_ALIGNMENT); ++ } ++ if (st == IN_MEMORY) { ++ mem_p += size; ++ mem_p = ALIGN_STACK(mem_p, STACK_ALIGNMENT); ++ } ++ } ++ ++ kp_verbose_printf(ks, "Stack location: %p -redzone- %p -general purpose " ++ "register used- %p -zero- %p -stack for argument- %p" ++ " -memory for argument- %p -temp stack-\n", ++ stack, stack_p, gpr_p, stack_p + MAX_GPR_SIZE, ++ arg_p, mem_p); ++ kp_verbose_printf(ks, "GPR number: %d; arg in stack: %d; " ++ "arg in mem: %d\n", ++ gpr_nr, arg_bytes, mem_bytes); ++ kp_verbose_printf(ks, "Return: address %p type %d\n", rvalue, rtype); ++ kp_verbose_printf(ks, "Number of register used: %d\n", gpr_nr); ++ kp_verbose_printf(ks, "Start FFI call on %p\n", csf->addr); ++ ffi_call_assem_x86_64(stack_p, tmp_p, csf->addr, rvalue, rtype); ++} ++ ++#else /* non-supported platform */ ++ ++#define ffi_call(ks, cf, rvalue) ffi_call_unsupported(ks, cf, rvalue) ++ ++static void ffi_call_unsupported(ktap_state *ks, ++ csymbol_func *csf, void *rvalue) ++{ ++ kp_error(ks, "unsupported architecture.\n"); ++} ++ ++#endif /* end for platform-specific setting */ ++ ++ ++static int ffi_set_return(ktap_state *ks, void *rvalue, csymbol_id ret_id) ++{ ++ ktap_cdata *cd; ++ ffi_type type = csym_type(id_to_csym(ks, ret_id)); ++ ++ /* push return value to ktap stack */ ++ switch (type) { ++ case FFI_VOID: ++ return 0; ++ case FFI_UINT8: ++ case FFI_INT8: ++ case FFI_UINT16: ++ case FFI_INT16: ++ case FFI_UINT32: ++ case FFI_INT32: ++ case FFI_UINT64: ++ case FFI_INT64: ++ set_number(ks->top, (ktap_number)rvalue); ++ break; ++ case FFI_PTR: ++ cd = kp_cdata_new_ptr(ks, rvalue, ret_id); ++ set_cdata(ks->top, cd); ++ break; ++ case FFI_STRUCT: ++ cd = kp_cdata_new_struct(ks, rvalue, ret_id); ++ set_cdata(ks->top, cd); ++ break; ++ case FFI_FUNC: ++ case FFI_UNKNOWN: ++ kp_error(ks, "Error: Have not support ffi_type %s\n", ++ ffi_type_name(type)); ++ return 0; ++ } ++ incr_top(ks); ++ return 1; ++} ++ ++/* ++ * Call C into function ++ * First argument should be function symbol address, argument types ++ * and return type. ++ * Left arguments should be arguments for calling the C function. ++ * Types between Ktap and C are converted automatically. ++ * Only support x86_64 function call by now ++ */ ++int kp_ffi_call(ktap_state *ks, csymbol_func *csf) ++{ ++ int i; ++ int expected_arg_nr, arg_nr; ++ ktap_closure *cl; ++ void *rvalue; ++ ++ expected_arg_nr = csymf_arg_nr(csf); ++ arg_nr = kp_arg_nr(ks); ++ ++ /* check stack status for C call */ ++ if (!csf->has_var_arg && expected_arg_nr != arg_nr) { ++ kp_error(ks, "wrong argument number %d, which should be %d\n", ++ arg_nr, expected_arg_nr); ++ goto out; ++ } ++ if (csf->has_var_arg && expected_arg_nr > arg_nr) { ++ kp_error(ks, "argument number %d, which should be bigger than %d\n", ++ arg_nr, expected_arg_nr); ++ goto out; ++ } ++ ++ /* maybe useful later, leave it here first */ ++ cl = clvalue(kp_arg(ks, arg_nr + 1)); ++ ++ /* check the argument types */ ++ for (i = 0; i < arg_nr; i++) { ++ if (ffi_type_check(ks, csf, i) < 0) ++ goto out; ++ } ++ ++ /* platform-specific calling workflow */ ++ ffi_call(ks, csf, &rvalue); ++ kp_verbose_printf(ks, "Finish FFI call\n"); ++ ++out: ++ return ffi_set_return(ks, rvalue, csymf_ret_id(csf)); ++} +diff --git a/drivers/staging/ktap/runtime/ffi/ffi_symbol.c b/drivers/staging/ktap/runtime/ffi/ffi_symbol.c +new file mode 100644 +index 0000000..0534c37 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/ffi/ffi_symbol.c +@@ -0,0 +1,174 @@ ++/* ++ * ffi_symbol.c - ktapvm kernel module ffi symbol submodule ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#include "../../include/ktap_types.h" ++#include "../../include/ktap_ffi.h" ++#include "../ktap.h" ++#include "../kp_vm.h" ++#include "../kp_obj.h" ++#include "../kp_str.h" ++#include "../kp_tab.h" ++ ++void setup_kp_ffi_symbol_table(ktap_state *ks); ++ ++ ++static inline csymbol *get_csym_arr(ktap_state *ks) ++{ ++ return G(ks)->ffis.csym_arr; ++} ++ ++static inline int get_csym_nr(ktap_state *ks) ++{ ++ return G(ks)->ffis.csym_nr; ++} ++ ++static inline void set_csym_arr(ktap_state *ks, csymbol *csym) ++{ ++ G(ks)->ffis.csym_arr = csym; ++} ++ ++static inline void set_csym_nr(ktap_state *ks, int nr) ++{ ++ G(ks)->ffis.csym_nr = nr; ++} ++ ++ ++static inline ktap_tab *get_ffi_ctable(ktap_state *ks) ++{ ++ return G(ks)->ffis.ctable; ++} ++ ++static void setup_ffi_ctable(ktap_state *ks) ++{ ++ ktap_value ffi_lib_name, ffi_mt; ++ ktap_tab *registry; ++ const ktap_value *gt; ++ ++ gt = kp_tab_getint(hvalue(&G(ks)->registry), KTAP_RIDX_GLOBALS); ++ ++ G(ks)->ffis.ctable = kp_tab_new(ks); ++ ++ /* insert ffi C table to global table */ ++ set_table(&ffi_mt, get_ffi_ctable(ks)); ++ set_string(&ffi_lib_name, kp_tstring_new(ks, "C")); ++ registry = hvalue(gt); ++ kp_tab_setvalue(ks, registry, &ffi_lib_name, &ffi_mt); ++} ++ ++void ffi_set_csym_arr(ktap_state *ks, int cs_nr, csymbol *new_arr) ++{ ++ set_csym_nr(ks, cs_nr); ++ set_csym_arr(ks, new_arr); ++ ++ if (!new_arr) ++ return; ++ ++ setup_kp_ffi_symbol_table(ks); ++} ++ ++inline csymbol *ffi_get_csym_by_id(ktap_state *ks, int id) ++{ ++ return &(get_csym_arr(ks)[id]); ++} ++ ++csymbol_id ffi_get_csym_id(ktap_state *ks, char *name) ++{ ++ int i; ++ ++ for (i = 0; i < get_csym_nr(ks); i++) { ++ if (!strcmp(name, csym_name(ffi_get_csym_by_id(ks, i)))) { ++ return i; ++ } ++ } ++ ++ kp_error(ks, "Cannot find csymbol with name %s\n", name); ++ return 0; ++} ++ ++static void add_ffi_func_to_ctable(ktap_state *ks, csymbol_id id) ++{ ++ ktap_value func_name, fv; ++ ktap_cdata *cd; ++ csymbol *cs; ++ ++ /* push cdata to ctable */ ++ set_cdata(&fv, kp_newobject(ks, KTAP_TCDATA, sizeof(ktap_cdata), NULL)); ++ cd = cdvalue(&fv); ++ cd_set_csym_id(cd, id); ++ ++ cs = id_to_csym(ks, id); ++ set_string(&func_name, kp_tstring_new(ks, csym_name(cs))); ++ kp_tab_setvalue(ks, get_ffi_ctable(ks), &func_name, &fv); ++} ++ ++void setup_kp_ffi_symbol_table(ktap_state *ks) ++{ ++ int i; ++ csymbol *cs; ++ ++ setup_ffi_ctable(ks); ++ ++ /* push all functions to ctable */ ++ for (i = 0; i < get_csym_nr(ks); i++) { ++ cs = &get_csym_arr(ks)[i]; ++ switch (cs->type) { ++ case FFI_FUNC: ++ kp_verbose_printf(ks, "[%d] loading C function %s\n", ++ i, csym_name(cs)); ++ add_ffi_func_to_ctable(ks, i); ++ kp_verbose_printf(ks, "%s loaded\n", csym_name(cs)); ++ break; ++ case FFI_STRUCT: ++ break; ++ default: ++ break; ++ } ++ } ++} ++ ++void kp_ffi_free_symbol(ktap_state *ks) ++{ ++ int i; ++ csymbol_id *arg_ids; ++ csymbol *cs; ++ ++ if (!get_csym_arr(ks)) ++ return; ++ ++ for (i = 0; i < get_csym_nr(ks); i++) { ++ cs = &get_csym_arr(ks)[i]; ++ switch (csym_type(cs)) { ++ case FFI_FUNC: ++ arg_ids = csym_func_arg_ids(cs); ++ if (arg_ids) ++ kp_free(ks, arg_ids); ++ break; ++ case FFI_STRUCT: ++ /*@TODO finish this 20.11 2013 (houqp)*/ ++ break; ++ default: ++ break; ++ } ++ } ++ ++ kp_free(ks, get_csym_arr(ks)); ++} +diff --git a/drivers/staging/ktap/runtime/ffi/ffi_type.c b/drivers/staging/ktap/runtime/ffi/ffi_type.c +new file mode 100644 +index 0000000..6bb8316 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/ffi/ffi_type.c +@@ -0,0 +1,51 @@ ++#include "../../include/ktap_ffi.h" ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#include ++#endif ++ ++#define CTYPE_MODE_HELPER(name, type) \ ++struct _##name##_align { \ ++ type t1; \ ++ char c; \ ++ type t2; \ ++}; ++ ++#define CTYPE_MODE(name) \ ++{ \ ++ offsetof(struct _##name##_align, c), \ ++ offsetof(struct _##name##_align, t2) - \ ++ offsetof(struct _##name##_align, c), \ ++ #name \ ++} ++ ++#define CTYPE_MODE_NAME(name) _##name##_mode ++ ++/* ffi_ctype_mode should be corresponded to ffi_ctype */ ++CTYPE_MODE_HELPER(uint8, uint8_t); ++CTYPE_MODE_HELPER(int8, int8_t); ++CTYPE_MODE_HELPER(uint16, uint16_t); ++CTYPE_MODE_HELPER(int16, int16_t); ++CTYPE_MODE_HELPER(uint32, uint32_t); ++CTYPE_MODE_HELPER(int32, int32_t); ++CTYPE_MODE_HELPER(uint64, uint64_t); ++CTYPE_MODE_HELPER(int64, int64_t); ++CTYPE_MODE_HELPER(pointer, void*); ++ ++const ffi_mode ffi_type_modes[NUM_FFI_TYPE+1] = { ++ {0, 1, "void"}, ++ CTYPE_MODE(uint8), ++ CTYPE_MODE(int8), ++ CTYPE_MODE(uint16), ++ CTYPE_MODE(int16), ++ CTYPE_MODE(uint32), ++ CTYPE_MODE(int32), ++ CTYPE_MODE(uint64), ++ CTYPE_MODE(int64), ++ CTYPE_MODE(pointer), ++ {0, 1, "function"}, ++ {0, 1, "struct"}, ++ {0, 1, "unknown"}, ++}; +diff --git a/drivers/staging/ktap/runtime/ffi/ffi_util.c b/drivers/staging/ktap/runtime/ffi/ffi_util.c +new file mode 100644 +index 0000000..6b0376d +--- /dev/null ++++ b/drivers/staging/ktap/runtime/ffi/ffi_util.c +@@ -0,0 +1,92 @@ ++/* ++ * ffi_util.c - utility function for ffi module ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#include "../../include/ktap_types.h" ++#include "../../include/ktap_ffi.h" ++#include "../ktap.h" ++ ++size_t csym_size(ktap_state *ks, csymbol *cs) ++{ ++ ffi_type type = csym_type(cs); ++ switch(type) { ++ case FFI_STRUCT: ++ if (csym_struct(cs)->align == 0) ++ init_csym_struct(ks, csym_struct(cs)); ++ return csym_struct(cs)->size; ++ default: ++ return ffi_type_size(type); ++ } ++} ++ ++size_t csym_align(ktap_state *ks, csymbol *cs) ++{ ++ ffi_type type = csym_type(cs); ++ switch(type) { ++ case FFI_STRUCT: ++ if (csym_struct(cs)->align == 0) ++ init_csym_struct(ks, csym_struct(cs)); ++ return csym_struct(cs)->align; ++ default: ++ return ffi_type_align(type); ++ } ++} ++ ++size_t csym_struct_offset(ktap_state *ks, csymbol_struct *csst, int idx) ++{ ++ int nr = csymst_mb_nr(csst); ++ size_t off = 0; ++ size_t align = 1; ++ int i; ++ ++ if (idx < 0 || idx > nr) ++ return -1; ++ for (i = 0; i < idx; i++) { ++ csymbol *var_cs = csymst_mb(ks, csst, i); ++ size_t var_size = csym_size(ks, var_cs); ++ size_t var_align = csym_align(ks, var_cs); ++ off = ALIGN(off, var_align); ++ off += var_size; ++ align = align > var_align ? align : var_align; ++ } ++ off = ALIGN(off, align); ++ return off; ++} ++ ++void init_csym_struct(ktap_state *ks, csymbol_struct *csst) ++{ ++ int nr = csymst_mb_nr(csst); ++ size_t size = 0; ++ size_t align = 1; ++ int i; ++ ++ for (i = 0; i < nr; i++) { ++ csymbol *var_cs = csymst_mb(ks, csst, i); ++ size_t var_size = csym_size(ks, var_cs); ++ size_t var_align = csym_align(ks, var_cs); ++ size = ALIGN(size, var_align); ++ size += var_size; ++ align = align > var_align ? align : var_align; ++ } ++ size = ALIGN(size, align); ++ csst->size = size; ++ csst->align = align; ++} +diff --git a/drivers/staging/ktap/runtime/kp_amalg.c b/drivers/staging/ktap/runtime/kp_amalg.c +new file mode 100644 +index 0000000..c244822 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_amalg.c +@@ -0,0 +1,43 @@ ++/* ++ * kp_amalg.c - ktapvm kernel module amalgamation. ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#include "ktap.c" ++#include "kp_opcode.c" ++#include "kp_obj.c" ++#include "kp_load.c" ++#include "kp_str.c" ++#include "kp_tab.c" ++#include "kp_transport.c" ++#include "kp_vm.c" ++#include "lib_base.c" ++#include "lib_ansi.c" ++#include "lib_kdebug.c" ++#include "lib_timer.c" ++ ++#ifdef CONFIG_KTAP_FFI ++#include "ffi/ffi_call.c" ++#include "ffi/ffi_type.c" ++#include "ffi/ffi_symbol.c" ++#include "ffi/cdata.c" ++#include "ffi/ffi_util.c" ++#include "lib_ffi.c" ++#endif +diff --git a/drivers/staging/ktap/runtime/kp_load.c b/drivers/staging/ktap/runtime/kp_load.c +new file mode 100644 +index 0000000..74f1da4 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_load.c +@@ -0,0 +1,401 @@ ++/* ++ * kp_load.c - loader for ktap bytecode chunk file ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * Copyright (C) 1994-2013 Lua.org, PUC-Rio. ++ * - The part of code in this file is copied from lua initially. ++ * - lua's MIT license is compatible with GPL. ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include "../include/ktap_types.h" ++#include "../include/ktap_ffi.h" ++#include "ktap.h" ++#include "kp_load.h" ++#include "kp_obj.h" ++#include "kp_str.h" ++#include "kp_tab.h" ++#include "kp_vm.h" ++ ++#define KTAPC_TAIL "\x19\x93\r\n\x1a\n" ++ ++struct load_state { ++ unsigned char *buff; ++ int pos; ++ ktap_state *ks; ++}; ++ ++#define READ_CHAR(S) (S->buff[S->pos++]) ++#define READ_BYTE(S) READ_CHAR(S) ++#define READ_INT(S) load_int(S) ++#define READ_NUMBER(S) load_number(S) ++#define READ_STRING(S) load_string(S) ++#define READ_VECTOR(S, dst, size) \ ++ do { \ ++ memcpy(dst, &S->buff[S->pos], size); \ ++ S->pos += size; \ ++ } while(0) ++ ++#define NEW_VECTOR(S, size) kp_malloc(S->ks, size) ++#define FREE_VECTOR(S, v) kp_free(S->ks, v) ++#define GET_CURRENT(S) &S->buff[S->pos] ++#define ADD_POS(S, size) S->pos += size ++ ++ ++static int load_function(struct load_state *S, ktap_proto *f); ++ ++ ++static int load_int(struct load_state *S) ++{ ++ int x; ++ ++ READ_VECTOR(S, &x, sizeof(int)); ++ return x; ++} ++ ++static long load_number(struct load_state *S) ++{ ++ long x; ++ ++ READ_VECTOR(S, &x, sizeof(ktap_number)); ++ return x; ++} ++ ++static ktap_string *load_string(struct load_state *S) ++{ ++ ktap_string *ts; ++ size_t size; ++ ++ size = READ_INT(S); ++ ++ if (!size) ++ return NULL; ++ else { ++ char *s = GET_CURRENT(S); ++ ADD_POS(S, size); ++ /* remove trailing '\0' */ ++ ts = kp_tstring_newlstr(S->ks, s, size - 1); ++ return ts; ++ } ++} ++ ++ ++static int load_code(struct load_state *S, ktap_proto *f) ++{ ++ int n = READ_INT(S); ++ ++ f->sizecode = n; ++ f->code = NEW_VECTOR(S, n * sizeof(ktap_instruction)); ++ READ_VECTOR(S, f->code, n * sizeof(ktap_instruction)); ++ ++ return 0; ++} ++ ++static int load_constants(struct load_state *S, ktap_proto *f) ++{ ++ int i,n; ++ ++ n = READ_INT(S); ++ ++ f->sizek = n; ++ f->k = NEW_VECTOR(S, n * sizeof(ktap_value)); ++ for (i = 0; i < n; i++) ++ set_nil(&f->k[i]); ++ ++ for (i=0; i < n; i++) { ++ ktap_value *o = &f->k[i]; ++ ++ int t = READ_CHAR(S); ++ switch (t) { ++ case KTAP_TNIL: ++ set_nil(o); ++ break; ++ case KTAP_TBOOLEAN: ++ set_boolean(o, READ_CHAR(S)); ++ break; ++ case KTAP_TNUMBER: ++ /* ++ * todo: kernel not support fp, check double when ++ * loading ++ */ ++ set_number(o, READ_NUMBER(S)); ++ break; ++ case KTAP_TSTRING: ++ set_string(o, READ_STRING(S)); ++ break; ++ default: ++ kp_error(S->ks, "ktap: load_constants: " ++ "unknow ktap_value\n"); ++ return -1; ++ ++ } ++ } ++ ++ n = READ_INT(S); ++ f->p = NEW_VECTOR(S, n * sizeof(ktap_proto)); ++ f->sizep = n; ++ for (i = 0; i < n; i++) ++ f->p[i] = NULL; ++ for (i = 0; i < n; i++) { ++ f->p[i] = kp_newproto(S->ks); ++ if (load_function(S, f->p[i])) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++static int load_upvalues(struct load_state *S, ktap_proto *f) ++{ ++ int i,n; ++ ++ n = READ_INT(S); ++ f->upvalues = NEW_VECTOR(S, n * sizeof(ktap_upvaldesc)); ++ f->sizeupvalues = n; ++ ++ for (i = 0; i < n; i++) ++ f->upvalues[i].name = NULL; ++ ++ for (i = 0; i < n; i++) { ++ f->upvalues[i].instack = READ_BYTE(S); ++ f->upvalues[i].idx = READ_BYTE(S); ++ } ++ ++ return 0; ++} ++ ++static int load_debuginfo(struct load_state *S, ktap_proto *f) ++{ ++ int i,n; ++ ++ f->source = READ_STRING(S); ++ n = READ_INT(S); ++ f->sizelineinfo = n; ++ f->lineinfo = NEW_VECTOR(S, n * sizeof(int)); ++ READ_VECTOR(S, f->lineinfo, n * sizeof(int)); ++ n = READ_INT(S); ++ f->locvars = NEW_VECTOR(S, n * sizeof(struct ktap_locvar)); ++ f->sizelocvars = n; ++ for (i = 0; i < n; i++) ++ f->locvars[i].varname = NULL; ++ for (i = 0; i < n; i++) { ++ f->locvars[i].varname = READ_STRING(S); ++ f->locvars[i].startpc = READ_INT(S); ++ f->locvars[i].endpc = READ_INT(S); ++ } ++ n = READ_INT(S); ++ for (i = 0; i < n; i++) ++ f->upvalues[i].name = READ_STRING(S); ++ ++ return 0; ++} ++ ++static int load_function(struct load_state *S, ktap_proto *f) ++{ ++ f->linedefined = READ_INT(S); ++ f->lastlinedefined = READ_INT(S); ++ f->numparams = READ_BYTE(S); ++ f->is_vararg = READ_BYTE(S); ++ f->maxstacksize = READ_BYTE(S); ++ if (load_code(S, f)) ++ return -1; ++ if (load_constants(S, f)) ++ return -1; ++ if (load_upvalues(S, f)) ++ return -1; ++ if (load_debuginfo(S, f)) ++ return -1; ++ ++ return 0; ++} ++ ++ ++#define error(S, why) \ ++ kp_error(S->ks, "load failed: %s precompiled chunk\n", why) ++ ++#define N0 KTAPC_HEADERSIZE ++#define N1 (sizeof(KTAP_SIGNATURE) - sizeof(char)) ++#define N2 N1 + 2 ++#define N3 N2 + 6 ++ ++static int load_header(struct load_state *S) ++{ ++ u8 h[KTAPC_HEADERSIZE]; ++ u8 s[KTAPC_HEADERSIZE]; ++ ++ kp_header(h); ++ READ_VECTOR(S, s, KTAPC_HEADERSIZE); ++ ++ if (memcmp(h, s, N0) == 0) ++ return 0; ++ if (memcmp(h, s, N1) != 0) ++ error(S, "not a"); ++ else if (memcmp(h, s, N2) != 0) ++ error(S, "version mismatch in"); ++ else if (memcmp(h, s, N3) != 0) ++ error(S, "incompatible"); ++ else ++ error(S,"corrupted"); ++ ++ return -1; ++} ++ ++#ifdef CONFIG_KTAP_FFI ++void ffi_set_csym_arr(ktap_state *ks, int cs_nr, csymbol *new_arr); ++ ++static void load_csymbol_func(struct load_state *S, csymbol *cs) ++{ ++ csymbol_func *csf = csym_func(cs); ++ int arg_nr = csymf_arg_nr(csf); ++ ++ if (arg_nr > 0) { ++ csf->arg_ids = NEW_VECTOR(S, arg_nr*sizeof(int)); ++ READ_VECTOR(S, csf->arg_ids, arg_nr*sizeof(int)); ++ } else { ++ csf->arg_ids = NULL; ++ } ++} ++ ++static void load_csymbol_struct(struct load_state *S, csymbol *cs) ++{ ++ csymbol_struct *csst = csym_struct(cs); ++ int mb_nr = csymst_mb_nr(csst); ++ ++ csst->members = NEW_VECTOR(S, mb_nr*sizeof(struct_member)); ++ READ_VECTOR(S, csst->members, mb_nr*sizeof(struct_member)); ++} ++ ++static int load_csymbols(struct load_state *S) ++{ ++ csymbol *cs_arr, *cs; ++ int i, csym_nr; ++ ++ /* read number of csymbols */ ++ csym_nr = READ_INT(S); ++ if (csym_nr <= 0) { ++ ffi_set_csym_arr(S->ks, 0, NULL); ++ return 0; ++ } ++ ++ /* csymbol size safty check */ ++ if (sizeof(csymbol) != READ_INT(S)) { ++ kp_error(S->ks, "invalid csymbol size in chunk\n"); ++ return -1; ++ } ++ ++ cs_arr = NEW_VECTOR(S, sizeof(csymbol)*csym_nr); ++ for (i = 0; i < csym_nr; i++) { ++ cs = &cs_arr[i]; ++ READ_VECTOR(S, cs, sizeof(csymbol)); ++ switch (cs->type) { ++ case FFI_FUNC: ++ load_csymbol_func(S, cs); ++ break; ++ case FFI_STRUCT: ++ load_csymbol_struct(S, cs); ++ break; ++ default: ++ break; ++ } ++ } ++ ++ ffi_set_csym_arr(S->ks, csym_nr, cs_arr); ++ ++ return 0; ++} ++#else ++static int load_csymbols(struct load_state *S) ++{ ++ int csym_nr = READ_INT(S); ++ ++ /* if FFI is disabled in ktapc, csym_nr should be 0 */ ++ if (csym_nr != 0) { ++ /* skip corrupted csymbol chunk */ ++ int cs_size = READ_INT(S); ++ ADD_POS(S, cs_size*csym_nr); ++ kp_error(S->ks, "VM compiled without FFI support!\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++#endif ++ ++static int verify_code(struct load_state *S, ktap_proto *f) ++{ ++ /* not support now */ ++ return 0; ++} ++ ++ ++ktap_closure *kp_load(ktap_state *ks, unsigned char *buff) ++{ ++ struct load_state S; ++ ktap_closure *cl; ++ int ret, i; ++ ++ S.ks = ks; ++ S.buff = buff; ++ S.pos = 0; ++ ++ ret = load_header(&S); ++ if (ret) ++ return NULL; ++ ++ ret = load_csymbols(&S); ++ if (ret) ++ return NULL; ++ ++ cl = kp_newclosure(ks, 1); ++ if (!cl) ++ return cl; ++ ++ /* put closure on the top, prepare to run with this closure */ ++ set_closure(ks->top, cl); ++ incr_top(ks); ++ ++ cl->p = kp_newproto(ks); ++ if (load_function(&S, cl->p)) ++ return NULL; ++ ++ if (cl->p->sizeupvalues != 1) { ++ ktap_proto *p = cl->p; ++ cl = kp_newclosure(ks, cl->p->sizeupvalues); ++ cl->p = p; ++ set_closure(ks->top - 1, cl); ++ } ++ ++ for (i = 0; i < cl->nupvalues; i++) { /* initialize upvalues */ ++ ktap_upval *up = kp_newupval(ks); ++ cl->upvals[i] = up; ++ } ++ ++ /* set global table as 1st upvalue of 'f' */ ++ if (cl->nupvalues == 1) { ++ ktap_tab *reg = hvalue(&G(ks)->registry); ++ const ktap_value *gt = kp_tab_getint(reg, KTAP_RIDX_GLOBALS); ++ set_obj(cl->upvals[0]->v, gt); ++ } ++ ++ verify_code(&S, cl->p); ++ ++ return cl; ++} ++ +diff --git a/drivers/staging/ktap/runtime/kp_load.h b/drivers/staging/ktap/runtime/kp_load.h +new file mode 100644 +index 0000000..fc41fa9 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_load.h +@@ -0,0 +1,6 @@ ++#ifndef __KTAP_LOAD_H__ ++#define __KTAP_LOAD_H__ ++ ++ktap_closure *kp_load(ktap_state *ks, unsigned char *buff); ++ ++#endif /* __KTAP_LOAD_H__ */ +diff --git a/drivers/staging/ktap/runtime/kp_obj.c b/drivers/staging/ktap/runtime/kp_obj.c +new file mode 100644 +index 0000000..5149637 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_obj.c +@@ -0,0 +1,478 @@ ++/* ++ * kp_obj.c - ktap object generic operation ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * Copyright (C) 1994-2013 Lua.org, PUC-Rio. ++ * - The part of code in this file is copied from lua initially. ++ * - lua's MIT license is compatible with GPL. ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "../include/ktap_types.h" ++#include "../include/ktap_ffi.h" ++#include "kp_obj.h" ++#include "kp_str.h" ++#include "kp_tab.h" ++ ++#ifdef __KERNEL__ ++#include ++#include "ktap.h" ++#include "kp_vm.h" ++#include "kp_transport.h" ++ ++#define KTAP_ALLOC_FLAGS ((GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN) \ ++ & ~__GFP_WAIT) ++ ++void *kp_malloc(ktap_state *ks, int size) ++{ ++ void *addr; ++ ++ /* ++ * Normally we don't want to trace under memory pressure, ++ * so we use a simple rule to handle memory allocation failure: ++ * ++ * retry until allocation success, this will make caller don't need ++ * to handle the unlikely failure case, then ktap exit. ++ * ++ * In this approach, if user find there have memory allocation failure, ++ * user should re-run the ktap script, or fix the memory pressure ++ * issue, or figure out why the script need so many memory. ++ * ++ * Perhaps return pre-allocated stub memory trunk when allocate failed ++ * is a better approch? ++ */ ++ addr = kmalloc(size, KTAP_ALLOC_FLAGS); ++ if (unlikely(!addr)) { ++ kp_error(ks, "kmalloc size %d failed, retry again\n", size); ++ printk("ktap kmalloc size %d failed, retry again\n", size); ++ dump_stack(); ++ while (1) { ++ addr = kmalloc(size, KTAP_ALLOC_FLAGS); ++ if (addr) ++ break; ++ } ++ kp_printf(ks, "kmalloc retry success after failed, exit\n"); ++ } ++ ++ preempt_disable(); ++ KTAP_STATS(ks)->nr_mem_allocate += 1; ++ KTAP_STATS(ks)->mem_allocated += size; ++ preempt_enable(); ++ ++ return addr; ++} ++ ++void kp_free(ktap_state *ks, void *addr) ++{ ++ preempt_disable(); ++ KTAP_STATS(ks)->nr_mem_free += 1; ++ preempt_enable(); ++ ++ kfree(addr); ++} ++ ++void *kp_reallocv(ktap_state *ks, void *addr, int oldsize, int newsize) ++{ ++ void *new_addr; ++ ++ new_addr = krealloc(addr, newsize, KTAP_ALLOC_FLAGS); ++ if (unlikely(!new_addr)) { ++ kp_error(ks, "krealloc size %d failed, retry again\n", newsize); ++ printk("ktap krealloc size %d failed, retry again\n", newsize); ++ dump_stack(); ++ while (1) { ++ new_addr = krealloc(addr, newsize, KTAP_ALLOC_FLAGS); ++ if (new_addr) ++ break; ++ } ++ kp_printf(ks, "krealloc retry success after failed, exit\n"); ++ } ++ ++ preempt_disable(); ++ if (oldsize == 0) { ++ KTAP_STATS(ks)->nr_mem_allocate += 1; ++ } ++ KTAP_STATS(ks)->mem_allocated += newsize - oldsize; ++ preempt_enable(); ++ ++ return new_addr; ++} ++ ++void *kp_zalloc(ktap_state *ks, int size) ++{ ++ void *addr; ++ ++ addr = kzalloc(size, KTAP_ALLOC_FLAGS); ++ if (unlikely(!addr)) { ++ kp_error(ks, "kzalloc size %d failed, retry again\n", size); ++ printk("ktap kzalloc size %d failed, retry again\n", size); ++ dump_stack(); ++ while (1) { ++ addr = kzalloc(size, KTAP_ALLOC_FLAGS); ++ if (addr) ++ break; ++ } ++ kp_printf(ks, "kzalloc retry success after failed, exit\n"); ++ } ++ ++ preempt_disable(); ++ KTAP_STATS(ks)->nr_mem_allocate += 1; ++ KTAP_STATS(ks)->mem_allocated += size; ++ preempt_enable(); ++ ++ return addr; ++} ++#endif ++ ++void kp_obj_dump(ktap_state *ks, const ktap_value *v) ++{ ++ switch (ttype(v)) { ++ case KTAP_TNIL: ++ kp_puts(ks, "NIL"); ++ break; ++ case KTAP_TNUMBER: ++ kp_printf(ks, "NUMBER %ld", nvalue(v)); ++ break; ++ case KTAP_TBOOLEAN: ++ kp_printf(ks, "BOOLEAN %d", bvalue(v)); ++ break; ++ case KTAP_TLIGHTUSERDATA: ++ kp_printf(ks, "LIGHTUSERDATA 0x%lx", (unsigned long)pvalue(v)); ++ break; ++ case KTAP_TCFUNCTION: ++ kp_printf(ks, "LIGHTCFCUNTION 0x%lx", (unsigned long)fvalue(v)); ++ break; ++ case KTAP_TSHRSTR: ++ case KTAP_TLNGSTR: ++ kp_printf(ks, "SHRSTR #%s", svalue(v)); ++ break; ++ case KTAP_TTABLE: ++ kp_printf(ks, "TABLE 0x%lx", (unsigned long)hvalue(v)); ++ break; ++ default: ++ kp_printf(ks, "GCVALUE 0x%lx", (unsigned long)gcvalue(v)); ++ break; ++ } ++} ++ ++#ifdef __KERNEL__ ++#include ++#include ++#include ++ ++static void kp_btrace_dump(ktap_state *ks, ktap_btrace *bt) ++{ ++ char str[KSYM_SYMBOL_LEN]; ++ unsigned long *entries = (unsigned long *)(bt + 1); ++ int i; ++ ++ for (i = 0; i < bt->nr_entries; i++) { ++ unsigned long p = entries[i]; ++ ++ if (p == ULONG_MAX) ++ break; ++ ++ SPRINT_SYMBOL(str, p); ++ kp_printf(ks, "%s\n", str); ++ } ++} ++ ++static int kp_btrace_equal(ktap_btrace *bt1, ktap_btrace *bt2) ++{ ++ unsigned long *entries1 = (unsigned long *)(bt1 + 1); ++ unsigned long *entries2 = (unsigned long *)(bt2 + 1); ++ int i; ++ ++ if (bt1->nr_entries != bt2->nr_entries) ++ return 0; ++ ++ for (i = 0; i < bt1->nr_entries; i++) { ++ if (entries1[i] != entries2[i]) ++ return 0; ++ } ++ ++ return 1; ++} ++#endif ++ ++void kp_showobj(ktap_state *ks, const ktap_value *v) ++{ ++ switch (ttype(v)) { ++ case KTAP_TNIL: ++ kp_puts(ks, "nil"); ++ break; ++ case KTAP_TNUMBER: ++ kp_printf(ks, "%ld", nvalue(v)); ++ break; ++ case KTAP_TBOOLEAN: ++ kp_puts(ks, (bvalue(v) == 1) ? "true" : "false"); ++ break; ++ case KTAP_TLIGHTUSERDATA: ++ kp_printf(ks, "0x%lx", (unsigned long)pvalue(v)); ++ break; ++ case KTAP_TCFUNCTION: ++ kp_printf(ks, "0x%lx", (unsigned long)fvalue(v)); ++ break; ++ case KTAP_TSHRSTR: ++ case KTAP_TLNGSTR: ++ kp_puts(ks, svalue(v)); ++ break; ++ case KTAP_TTABLE: ++ kp_tab_dump(ks, hvalue(v)); ++ break; ++#ifdef __KERNEL__ ++#ifdef CONFIG_KTAP_FFI ++ case KTAP_TCDATA: ++ kp_cdata_dump(ks, cdvalue(v)); ++ break; ++#endif ++ case KTAP_TEVENT: ++ kp_transport_event_write(ks, evalue(v)); ++ break; ++ case KTAP_TBTRACE: ++ kp_btrace_dump(ks, btvalue(v)); ++ break; ++ case KTAP_TPTABLE: ++ kp_ptab_dump(ks, phvalue(v)); ++ break; ++ case KTAP_TSTATDATA: ++ kp_statdata_dump(ks, sdvalue(v)); ++ break; ++#endif ++ default: ++ kp_error(ks, "print unknown value type: %d\n", ttype(v)); ++ break; ++ } ++} ++ ++ ++/* ++ * equality of ktap values. ks == NULL means raw equality ++ */ ++int kp_equalobjv(ktap_state *ks, const ktap_value *t1, const ktap_value *t2) ++{ ++ switch (ttype(t1)) { ++ case KTAP_TNIL: ++ return 1; ++ case KTAP_TNUMBER: ++ return nvalue(t1) == nvalue(t2); ++ case KTAP_TBOOLEAN: ++ return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ ++ case KTAP_TLIGHTUSERDATA: ++ return pvalue(t1) == pvalue(t2); ++ case KTAP_TCFUNCTION: ++ return fvalue(t1) == fvalue(t2); ++ case KTAP_TSHRSTR: ++ return eqshrstr(rawtsvalue(t1), rawtsvalue(t2)); ++ case KTAP_TLNGSTR: ++ return kp_tstring_eqlngstr(rawtsvalue(t1), rawtsvalue(t2)); ++ case KTAP_TTABLE: ++ if (hvalue(t1) == hvalue(t2)) ++ return 1; ++ else if (ks == NULL) ++ return 0; ++#ifdef __KERNEL__ ++ case KTAP_TBTRACE: ++ return kp_btrace_equal(btvalue(t1), btvalue(t2)); ++#endif ++ default: ++ return gcvalue(t1) == gcvalue(t2); ++ } ++ ++ return 0; ++} ++ ++/* ++ * ktap will not use lua's length operator on table meaning, ++ * also # is not for length operator any more in ktap. ++ */ ++int kp_objlen(ktap_state *ks, const ktap_value *v) ++{ ++ switch(v->type) { ++ case KTAP_TTABLE: ++ return kp_tab_length(ks, hvalue(v)); ++ case KTAP_TSTRING: ++ return rawtsvalue(v)->tsv.len; ++ default: ++ kp_printf(ks, "cannot get length of type %d\n", v->type); ++ return -1; ++ } ++ return 0; ++} ++ ++/* need to protect allgc field? */ ++ktap_gcobject *kp_newobject(ktap_state *ks, int type, size_t size, ++ ktap_gcobject **list) ++{ ++ ktap_gcobject *o; ++ ++ o = kp_malloc(ks, size); ++ if (list == NULL) ++ list = &G(ks)->allgc; ++ ++ gch(o)->tt = type; ++ gch(o)->next = *list; ++ *list = o; ++ ++ return o; ++} ++ ++ktap_upval *kp_newupval(ktap_state *ks) ++{ ++ ktap_upval *uv; ++ ++ uv = &kp_newobject(ks, KTAP_TUPVAL, sizeof(ktap_upval), NULL)->uv; ++ uv->v = &uv->u.value; ++ set_nil(uv->v); ++ return uv; ++} ++ ++static ktap_btrace *kp_newbacktrace(ktap_state *ks, int nr_entries, ++ ktap_gcobject **list) ++{ ++ ktap_btrace *bt; ++ int size = sizeof(ktap_btrace) + nr_entries * sizeof(unsigned long); ++ ++ bt = &kp_newobject(ks, KTAP_TBTRACE, size, list)->bt; ++ bt->nr_entries = nr_entries; ++ return bt; ++} ++ ++void kp_objclone(ktap_state *ks, const ktap_value *o, ktap_value *newo, ++ ktap_gcobject **list) ++{ ++ if (is_btrace(o)) { ++ int nr_entries = btvalue(o)->nr_entries; ++ ktap_btrace *bt; ++ ++ bt = kp_newbacktrace(ks, nr_entries, list); ++ memcpy((unsigned long *)(bt + 1), btvalue(o) + 1, ++ nr_entries * sizeof(unsigned long)); ++ set_btrace(newo, bt); ++ } else { ++ kp_error(ks, "cannot clone ktap value type %d\n", ttype(o)); ++ set_nil(newo); ++ } ++} ++ ++ktap_closure *kp_newclosure(ktap_state *ks, int n) ++{ ++ ktap_closure *cl; ++ ++ cl = (ktap_closure *)kp_newobject(ks, KTAP_TCLOSURE, sizeof(*cl), NULL); ++ cl->p = NULL; ++ cl->nupvalues = n; ++ while (n--) ++ cl->upvals[n] = NULL; ++ ++ return cl; ++} ++ ++static void free_proto(ktap_state *ks, ktap_proto *f) ++{ ++ kp_free(ks, f->code); ++ kp_free(ks, f->p); ++ kp_free(ks, f->k); ++ kp_free(ks, f->lineinfo); ++ kp_free(ks, f->locvars); ++ kp_free(ks, f->upvalues); ++ kp_free(ks, f); ++} ++ ++ktap_proto *kp_newproto(ktap_state *ks) ++{ ++ ktap_proto *f; ++ f = (ktap_proto *)kp_newobject(ks, KTAP_TPROTO, sizeof(*f), NULL); ++ f->k = NULL; ++ f->sizek = 0; ++ f->p = NULL; ++ f->sizep = 0; ++ f->code = NULL; ++ f->cache = NULL; ++ f->sizecode = 0; ++ f->lineinfo = NULL; ++ f->sizelineinfo = 0; ++ f->upvalues = NULL; ++ f->sizeupvalues = 0; ++ f->numparams = 0; ++ f->is_vararg = 0; ++ f->maxstacksize = 0; ++ f->locvars = NULL; ++ f->sizelocvars = 0; ++ f->linedefined = 0; ++ f->lastlinedefined = 0; ++ f->source = NULL; ++ return f; ++} ++ ++void kp_free_gclist(ktap_state *ks, ktap_gcobject *o) ++{ ++ while (o) { ++ ktap_gcobject *next; ++ ++ next = gch(o)->next; ++ switch (gch(o)->tt) { ++ case KTAP_TTABLE: ++ kp_tab_free(ks, (ktap_tab *)o); ++ break; ++ case KTAP_TPROTO: ++ free_proto(ks, (ktap_proto *)o); ++ break; ++#ifdef __KERNEL__ ++ case KTAP_TPTABLE: ++ kp_ptab_free(ks, (ktap_ptab *)o); ++ break; ++#endif ++ default: ++ kp_free(ks, o); ++ } ++ o = next; ++ } ++} ++ ++void kp_free_all_gcobject(ktap_state *ks) ++{ ++ kp_free_gclist(ks, G(ks)->allgc); ++ G(ks)->allgc = NULL; ++} ++ ++/******************************************************************************/ ++ ++/* ++ * make header for precompiled chunks ++ * if you change the code below be sure to update load_header and FORMAT above ++ * and KTAPC_HEADERSIZE in ktap_types.h ++ */ ++void kp_header(u8 *h) ++{ ++ int x = 1; ++ ++ memcpy(h, KTAP_SIGNATURE, sizeof(KTAP_SIGNATURE) - sizeof(char)); ++ h += sizeof(KTAP_SIGNATURE) - sizeof(char); ++ *h++ = (u8)VERSION; ++ *h++ = (u8)FORMAT; ++ *h++ = (u8)(*(char*)&x); /* endianness */ ++ *h++ = (u8)(sizeof(int)); ++ *h++ = (u8)(sizeof(size_t)); ++ *h++ = (u8)(sizeof(ktap_instruction)); ++ *h++ = (u8)(sizeof(ktap_number)); ++ *h++ = (u8)(((ktap_number)0.5) == 0); /* is ktap_number integral? */ ++ memcpy(h, KTAPC_TAIL, sizeof(KTAPC_TAIL) - sizeof(char)); ++} ++ ++ +diff --git a/drivers/staging/ktap/runtime/kp_obj.h b/drivers/staging/ktap/runtime/kp_obj.h +new file mode 100644 +index 0000000..02aea07 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_obj.h +@@ -0,0 +1,29 @@ ++#ifndef __KTAP_OBJ_H__ ++#define __KTAP_OBJ_H__ ++ ++#ifdef __KERNEL__ ++void *kp_malloc(ktap_state *ks, int size); ++void kp_free(ktap_state *ks, void *addr); ++void *kp_reallocv(ktap_state *ks, void *addr, int oldsize, int newsize); ++void *kp_zalloc(ktap_state *ks, int size); ++#else ++#define kp_malloc(ks, size) malloc(size) ++#define kp_free(ks, block) free(block) ++#define kp_reallocv(ks, block, osize, nsize) realloc(block, nsize) ++#endif ++ ++void kp_obj_dump(ktap_state *ks, const ktap_value *v); ++void kp_showobj(ktap_state *ks, const ktap_value *v); ++int kp_objlen(ktap_state *ks, const ktap_value *rb); ++void kp_objclone(ktap_state *ks, const ktap_value *o, ktap_value *newo, ++ ktap_gcobject **list); ++ktap_gcobject *kp_newobject(ktap_state *ks, int type, size_t size, ktap_gcobject **list); ++int kp_equalobjv(ktap_state *ks, const ktap_value *t1, const ktap_value *t2); ++ktap_closure *kp_newclosure(ktap_state *ks, int n); ++ktap_proto *kp_newproto(ktap_state *ks); ++ktap_upval *kp_newupval(ktap_state *ks); ++void kp_free_gclist(ktap_state *ks, ktap_gcobject *o); ++void kp_free_all_gcobject(ktap_state *ks); ++void kp_header(u8 *h); ++ ++#endif /* __KTAP_OBJ_H__ */ +diff --git a/drivers/staging/ktap/runtime/kp_opcode.c b/drivers/staging/ktap/runtime/kp_opcode.c +new file mode 100644 +index 0000000..bdafbb6 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_opcode.c +@@ -0,0 +1,134 @@ ++/* ++ * kp_opcode.c ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * Copyright (C) 1994-2013 Lua.org, PUC-Rio. ++ * - The part of code in this file is copied from lua initially. ++ * - lua's MIT license is compatible with GPL. ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "../include/ktap_types.h" ++#include "../include/ktap_opcodes.h" ++ ++const char *const ktap_opnames[NUM_OPCODES + 1] = { ++ "MOVE", ++ "LOADK", ++ "LOADKX", ++ "LOADBOOL", ++ "LOADNIL", ++ "GETUPVAL", ++ "GETTABUP", ++ "GETTABLE", ++ "SETTABUP", ++ "SETTABUP_INCR", ++ "SETTABUP_AGGR", ++ "SETUPVAL", ++ "SETTABLE", ++ "SETTABLE_INCR", ++ "SETTABLE_AGGR", ++ "NEWTABLE", ++ "SELF", ++ "ADD", ++ "SUB", ++ "MUL", ++ "DIV", ++ "MOD", ++ "POW", ++ "UNM", ++ "NOT", ++ "LEN", ++ "CONCAT", ++ "JMP", ++ "EQ", ++ "LT", ++ "LE", ++ "TEST", ++ "TESTSET", ++ "CALL", ++ "TAILCALL", ++ "RETURN", ++ "FORLOOP", ++ "FORPREP", ++ "TFORCALL", ++ "TFORLOOP", ++ "SETLIST", ++ "CLOSURE", ++ "VARARG", ++ "EXTRAARG", ++ ++ "EVENT", ++ "EVENT_NAME", ++ "EVENT_ARG", /* arg1, arg2 .. arg9 */ ++ NULL ++}; ++ ++ ++#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) ++ ++const u8 ktap_opmodes[NUM_OPCODES] = { ++/* T A B C mode opcode */ ++ opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ ++ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ ++ ,opmode(0, 1, OpArgN, OpArgN, iABx) /* OP_LOADKX */ ++ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ ++ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_LOADNIL */ ++ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ ++ ,opmode(0, 1, OpArgU, OpArgK, iABC) /* OP_GETTABUP */ ++ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ ++ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABUP */ ++ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABUP_INCR */ ++ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABUP_AGGR */ ++ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */ ++ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */ ++ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABUP_INCR */ ++ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABUP_AGGR */ ++ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */ ++ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */ ++ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ ++ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ ++ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ ++ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ ++ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ ++ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ ++ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ ++ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ ++ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ ++ ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ ++ ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ ++ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ ++ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ ++ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ ++ ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TEST */ ++ ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */ ++ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */ ++ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */ ++ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */ ++ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */ ++ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */ ++ ,opmode(0, 0, OpArgN, OpArgU, iABC) /* OP_TFORCALL */ ++ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_TFORLOOP */ ++ ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */ ++ ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */ ++ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */ ++ ,opmode(0, 0, OpArgU, OpArgU, iAx) /* OP_EXTRAARG */ ++ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_EVENT */ ++ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_EVENTNAME */ ++ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_EVENTARG */ ++}; ++ +diff --git a/drivers/staging/ktap/runtime/kp_str.c b/drivers/staging/ktap/runtime/kp_str.c +new file mode 100644 +index 0000000..1af42fe +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_str.c +@@ -0,0 +1,460 @@ ++/* ++ * kp_str.c - ktap string data struction manipulation ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * Copyright (C) 1994-2013 Lua.org, PUC-Rio. ++ * - The part of code in this file is copied from lua initially. ++ * - lua's MIT license is compatible with GPL. ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "../include/ktap_types.h" ++#include "kp_obj.h" ++#include "kp_str.h" ++ ++#ifdef __KERNEL__ ++#include ++#include ++#include ++#include "ktap.h" ++#include "kp_transport.h" ++#include "kp_vm.h" ++#endif ++ ++#define STRING_MAXSHORTLEN 40 ++ ++int kp_tstring_cmp(const ktap_string *ls, const ktap_string *rs) ++{ ++ const char *l = getstr(ls); ++ size_t ll = ls->tsv.len; ++ const char *r = getstr(rs); ++ size_t lr = rs->tsv.len; ++ ++ for (;;) { ++ int temp = strcmp(l, r); ++ if (temp != 0) ++ return temp; ++ else { ++ /* strings are equal up to a `\0' */ ++ ++ /* index of first `\0' in both strings */ ++ size_t len = strlen(l); ++ ++ /* r is finished? */ ++ if (len == lr) ++ return (len == ll) ? 0 : 1; ++ else if (len == ll) /* l is finished? */ ++ return -1; ++ ++ /* ++ * both strings longer than `len'; ++ * go on comparing (after the `\0') ++ */ ++ len++; ++ l += len; ll -= len; r += len; lr -= len; ++ } ++ } ++} ++ ++/* ++ * equality for long strings ++ */ ++int kp_tstring_eqlngstr(ktap_string *a, ktap_string *b) ++{ ++ size_t len = a->tsv.len; ++ ++ return (a == b) || ((len == b->tsv.len) && ++ (memcmp(getstr(a), getstr(b), len) == 0)); ++} ++ ++/* ++ * equality for strings ++ */ ++int kp_tstring_eqstr(ktap_string *a, ktap_string *b) ++{ ++ return (a->tsv.tt == b->tsv.tt) && ++ (a->tsv.tt == KTAP_TSHRSTR ? eqshrstr(a, b) : ++ kp_tstring_eqlngstr(a, b)); ++} ++ ++#define STRING_HASHLIMIT 5 ++unsigned int kp_string_hash(const char *str, size_t l, unsigned int seed) ++{ ++ unsigned int h = seed ^ l; ++ size_t l1; ++ size_t step = (l >> STRING_HASHLIMIT) + 1; ++ ++ for (l1 = l; l1 >= step; l1 -= step) ++ h = h ^ ((h<<5) + (h>>2) + (u8)(str[l1 - 1])); ++ ++ return h; ++} ++ ++ ++/* ++ * resizes the string table ++ */ ++void kp_tstring_resize(ktap_state *ks, int newsize) ++{ ++ int i; ++ ktap_stringtable *tb = &G(ks)->strt; ++ ++ if (newsize > tb->size) { ++ kp_realloc(ks, tb->hash, tb->size, newsize, ktap_gcobject *); ++ ++ for (i = tb->size; i < newsize; i++) ++ tb->hash[i] = NULL; ++ } ++ ++ /* rehash */ ++ for (i = 0; i < tb->size; i++) { ++ ktap_gcobject *p = tb->hash[i]; ++ tb->hash[i] = NULL; ++ ++ while (p) { ++ ktap_gcobject *next = gch(p)->next; ++ unsigned int h = lmod(gco2ts(p)->hash, newsize); ++ ++ gch(p)->next = tb->hash[h]; ++ tb->hash[h] = p; ++ p = next; ++ } ++ } ++ ++ if (newsize < tb->size) { ++ /* shrinking slice must be empty */ ++ kp_realloc(ks, tb->hash, tb->size, newsize, ktap_gcobject *); ++ } ++ ++ tb->size = newsize; ++} ++ ++/* ++ * creates a new string object ++ */ ++static ktap_string *createstrobj(ktap_state *ks, const char *str, size_t l, ++ int tag, unsigned int h, ktap_gcobject **list) ++{ ++ ktap_string *ts; ++ size_t totalsize; /* total size of TString object */ ++ ++ totalsize = sizeof(ktap_string) + ((l + 1) * sizeof(char)); ++ ts = &kp_newobject(ks, tag, totalsize, list)->ts; ++ ts->tsv.len = l; ++ ts->tsv.hash = h; ++ ts->tsv.extra = 0; ++ memcpy(ts + 1, str, l * sizeof(char)); ++ ((char *)(ts + 1))[l] = '\0'; /* ending 0 */ ++ return ts; ++} ++ ++/* ++ * creates a new short string, inserting it into string table ++ */ ++static ktap_string *newshrstr(ktap_state *ks, const char *str, size_t l, ++ unsigned int h) ++{ ++ ktap_gcobject **list; ++ ktap_stringtable *tb = &G(ks)->strt; ++ ktap_string *s; ++ ++ if (tb->nuse >= (int)tb->size) ++ kp_tstring_resize(ks, tb->size * 2); /* too crowded */ ++ ++ list = &tb->hash[lmod(h, tb->size)]; ++ s = createstrobj(ks, str, l, KTAP_TSHRSTR, h, list); ++ tb->nuse++; ++ return s; ++} ++ ++/* ++ * checks whether short string exists and reuses it or creates a new one ++ */ ++static ktap_string *internshrstr(ktap_state *ks, const char *str, size_t l) ++{ ++ ktap_gcobject *o; ++ ktap_global_state *g = G(ks); ++ ktap_string *ts; ++ unsigned int h = kp_string_hash(str, l, g->seed); ++ unsigned long __maybe_unused flags; ++ ++#ifdef __KERNEL__ ++ local_irq_save(flags); ++ arch_spin_lock(&G(ks)->str_lock); ++#endif ++ ++ for (o = g->strt.hash[lmod(h, g->strt.size)]; o != NULL; ++ o = gch(o)->next) { ++ ts = rawgco2ts(o); ++ ++ if (h == ts->tsv.hash && ts->tsv.len == l && ++ (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) ++ goto out; ++ } ++ ++ ts = newshrstr(ks, str, l, h); /* not found; create a new string */ ++ ++ out: ++#ifdef __KERNEL__ ++ arch_spin_unlock(&G(ks)->str_lock); ++ local_irq_restore(flags); ++#endif ++ return ts; ++} ++ ++ ++/* ++ * new string (with explicit length) ++ */ ++ktap_string *kp_tstring_newlstr(ktap_state *ks, const char *str, size_t l) ++{ ++ /* short string? */ ++ if (l <= STRING_MAXSHORTLEN) ++ return internshrstr(ks, str, l); ++ else ++ return createstrobj(ks, str, l, KTAP_TLNGSTR, G(ks)->seed, ++ NULL); ++} ++ ++ktap_string *kp_tstring_newlstr_local(ktap_state *ks, const char *str, size_t l) ++{ ++ return createstrobj(ks, str, l, KTAP_TLNGSTR, G(ks)->seed, ++ &ks->gclist); ++} ++ ++/* ++ * new zero-terminated string ++ */ ++ktap_string *kp_tstring_new(ktap_state *ks, const char *str) ++{ ++ return kp_tstring_newlstr(ks, str, strlen(str)); ++} ++ ++ktap_string *kp_tstring_new_local(ktap_state *ks, const char *str) ++{ ++ return createstrobj(ks, str, strlen(str), KTAP_TLNGSTR, G(ks)->seed, ++ &ks->gclist); ++} ++ ++void kp_tstring_freeall(ktap_state *ks) ++{ ++ ktap_global_state *g = G(ks); ++ int h; ++ ++ for (h = 0; h < g->strt.size; h++) { ++ ktap_gcobject *o, *next; ++ o = g->strt.hash[h]; ++ while (o) { ++ next = gch(o)->next; ++ kp_free(ks, o); ++ o = next; ++ } ++ g->strt.hash[h] = NULL; ++ } ++ ++ kp_free(ks, g->strt.hash); ++} ++ ++/* todo: dump long string, strt table only contain short string */ ++void kp_tstring_dump(ktap_state *ks) ++{ ++ ktap_gcobject *o; ++ ktap_global_state *g = G(ks); ++ int h; ++ ++ kp_printf(ks, "tstring dump: strt size: %d, nuse: %d\n", g->strt.size, ++ g->strt.nuse); ++ for (h = 0; h < g->strt.size; h++) { ++ for (o = g->strt.hash[h]; o != NULL; o = gch(o)->next) { ++ ktap_string *ts = rawgco2ts(o); ++ kp_printf(ks, "%s [%d]\n", getstr(ts), (int)ts->tsv.len); ++ } ++ } ++} ++ ++#ifdef __KERNEL__ ++/* kp_str_fmt - printf implementation */ ++ ++/* macro to `unsign' a character */ ++#define uchar(c) ((unsigned char)(c)) ++ ++#define L_ESC '%' ++ ++/* valid flags in a format specification */ ++#define FLAGS "-+ #0" ++ ++#define INTFRMLEN "ll" ++#define INTFRM_T long long ++ ++/* ++ * maximum size of each format specification (such as '%-099.99d') ++ * (+10 accounts for %99.99x plus margin of error) ++ */ ++#define MAX_FORMAT (sizeof(FLAGS) + sizeof(INTFRMLEN) + 10) ++ ++static const char *scanformat(ktap_state *ks, const char *strfrmt, char *form) ++{ ++ const char *p = strfrmt; ++ while (*p != '\0' && strchr(FLAGS, *p) != NULL) ++ p++; /* skip flags */ ++ ++ if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char)) { ++ kp_error(ks, "invalid format (repeated flags)\n"); ++ return NULL; ++ } ++ ++ if (isdigit(uchar(*p))) ++ p++; /* skip width */ ++ ++ if (isdigit(uchar(*p))) ++ p++; /* (2 digits at most) */ ++ ++ if (*p == '.') { ++ p++; ++ if (isdigit(uchar(*p))) ++ p++; /* skip precision */ ++ if (isdigit(uchar(*p))) ++ p++; /* (2 digits at most) */ ++ } ++ ++ if (isdigit(uchar(*p))) { ++ kp_error(ks, "invalid format (width or precision too long)\n"); ++ return NULL; ++ } ++ ++ *(form++) = '%'; ++ memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char)); ++ form += p - strfrmt + 1; ++ *form = '\0'; ++ return p; ++} ++ ++ ++/* ++ * add length modifier into formats ++ */ ++static void addlenmod(char *form, const char *lenmod) ++{ ++ size_t l = strlen(form); ++ size_t lm = strlen(lenmod); ++ char spec = form[l - 1]; ++ ++ strcpy(form + l - 1, lenmod); ++ form[l + lm - 1] = spec; ++ form[l + lm] = '\0'; ++} ++ ++ ++static void ktap_argerror(ktap_state *ks, int narg, const char *extramsg) ++{ ++ kp_error(ks, "bad argument #%d: (%s)\n", narg, extramsg); ++} ++ ++int kp_str_fmt(ktap_state *ks, struct trace_seq *seq) ++{ ++ int arg = 1; ++ size_t sfl; ++ ktap_value *arg_fmt = kp_arg(ks, 1); ++ int argnum = kp_arg_nr(ks); ++ const char *strfrmt, *strfrmt_end; ++ ++ strfrmt = svalue(arg_fmt); ++ sfl = rawtsvalue(arg_fmt)->tsv.len; ++ strfrmt_end = strfrmt + sfl; ++ ++ while (strfrmt < strfrmt_end) { ++ if (*strfrmt != L_ESC) ++ trace_seq_putc(seq, *strfrmt++); ++ else if (*++strfrmt == L_ESC) ++ trace_seq_putc(seq, *strfrmt++); ++ else { /* format item */ ++ char form[MAX_FORMAT]; ++ ++ if (++arg > argnum) { ++ ktap_argerror(ks, arg, "no value"); ++ return -1; ++ } ++ ++ strfrmt = scanformat(ks, strfrmt, form); ++ switch (*strfrmt++) { ++ case 'c': ++ trace_seq_printf(seq, form, ++ nvalue(kp_arg(ks, arg))); ++ break; ++ case 'd': case 'i': { ++ ktap_number n = nvalue(kp_arg(ks, arg)); ++ INTFRM_T ni = (INTFRM_T)n; ++ addlenmod(form, INTFRMLEN); ++ trace_seq_printf(seq, form, ni); ++ break; ++ } ++ case 'p': { ++ char str[KSYM_SYMBOL_LEN]; ++ SPRINT_SYMBOL(str, nvalue(kp_arg(ks, arg))); ++ _trace_seq_puts(seq, str); ++ break; ++ } ++ case 'o': case 'u': case 'x': case 'X': { ++ ktap_number n = nvalue(kp_arg(ks, arg)); ++ unsigned INTFRM_T ni = (unsigned INTFRM_T)n; ++ addlenmod(form, INTFRMLEN); ++ trace_seq_printf(seq, form, ni); ++ break; ++ } ++ case 's': { ++ ktap_value *v = kp_arg(ks, arg); ++ const char *s; ++ size_t l; ++ ++ if (is_nil(v)) { ++ _trace_seq_puts(seq, "nil"); ++ return 0; ++ } ++ ++ if (is_event(v)) { ++ kp_event_tostring(ks, seq); ++ return 0; ++ } ++ ++ s = svalue(v); ++ l = rawtsvalue(v)->tsv.len; ++ if (!strchr(form, '.') && l >= 100) { ++ /* ++ * no precision and string is too long ++ * to be formatted; ++ * keep original string ++ */ ++ _trace_seq_puts(seq, s); ++ break; ++ } else { ++ trace_seq_printf(seq, form, s); ++ break; ++ } ++ } ++ default: /* also treat cases `pnLlh' */ ++ kp_error(ks, "invalid option " KTAP_QL("%%%c") ++ " to " KTAP_QL("format"), ++ *(strfrmt - 1)); ++ } ++ } ++ } ++ ++ return 0; ++} ++#endif ++ +diff --git a/drivers/staging/ktap/runtime/kp_str.h b/drivers/staging/ktap/runtime/kp_str.h +new file mode 100644 +index 0000000..9fc8da1 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_str.h +@@ -0,0 +1,20 @@ ++#ifndef __KTAP_STR_H__ ++#define __KTAP_STR_H__ ++ ++ktap_string *kp_tstring_newlstr(ktap_state *ks, const char *str, size_t l); ++ktap_string *kp_tstring_newlstr_local(ktap_state *ks, const char *str, size_t l); ++ktap_string *kp_tstring_new(ktap_state *ks, const char *str); ++ktap_string *kp_tstring_new_local(ktap_state *ks, const char *str); ++int kp_tstring_eqstr(ktap_string *a, ktap_string *b); ++unsigned int kp_string_hash(const char *str, size_t l, unsigned int seed); ++int kp_tstring_eqlngstr(ktap_string *a, ktap_string *b); ++int kp_tstring_cmp(const ktap_string *ls, const ktap_string *rs); ++void kp_tstring_resize(ktap_state *ks, int newsize); ++void kp_tstring_freeall(ktap_state *ks); ++ ++#ifdef __KERNEL__ ++#include ++int kp_str_fmt(ktap_state *ks, struct trace_seq *seq); ++#endif ++ ++#endif /* __KTAP_STR_H__ */ +diff --git a/drivers/staging/ktap/runtime/kp_tab.c b/drivers/staging/ktap/runtime/kp_tab.c +new file mode 100644 +index 0000000..0d5cf63 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_tab.c +@@ -0,0 +1,1396 @@ ++/* ++ * kp_tab.c - ktap table data structure manipulation ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * Copyright (C) 1994-2013 Lua.org, PUC-Rio. ++ * - The part of code in this file is copied from lua initially. ++ * - lua's MIT license is compatible with GPL. ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "../include/ktap_types.h" ++ ++#ifdef __KERNEL__ ++#include ++#include ++#include ++#include ++#include "ktap.h" ++#include "kp_vm.h" ++#else ++static inline void sort(void *base, size_t num, size_t size, ++ int (*cmp_func)(const void *, const void *), ++ void (*swap_func)(void *, void *, int size)) ++{} ++#endif ++ ++#include "kp_obj.h" ++#include "kp_str.h" ++ ++#ifdef __KERNEL__ ++#define kp_tab_lock_init(t) \ ++ do { \ ++ (t)->lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; \ ++ } while (0) ++#define kp_tab_lock(t) \ ++ do { \ ++ local_irq_save(flags); \ ++ arch_spin_lock(&(t)->lock); \ ++ } while (0) ++#define kp_tab_unlock(t) \ ++ do { \ ++ arch_spin_unlock(&(t)->lock); \ ++ local_irq_restore(flags); \ ++ } while (0) ++ ++#else ++#define kp_tab_lock_init(t) ++#define kp_tab_lock(t) ++#define kp_tab_unlock(t) ++#endif ++ ++#define MAXBITS 30 ++#define MAXASIZE (1 << MAXBITS) ++ ++ ++#define NILCONSTANT {NULL}, KTAP_TNIL ++const struct ktap_value ktap_nilobjectv = {NILCONSTANT}; ++#define ktap_nilobject (&ktap_nilobjectv) ++ ++static const ktap_tnode dummynode_ = { ++ {NILCONSTANT}, /* value */ ++ {NULL, {NILCONSTANT}}, /* key */ ++}; ++ ++#define gnode(t,i) (&(t)->node[i]) ++#define gkey(n) (&(n)->i_key.tvk) ++#define gval(n) (&(n)->i_val) ++#define gnext(n) ((n)->i_key.next) ++ ++#define twoto(x) (1<<(x)) ++#define sizenode(t) (twoto((t)->lsizenode)) ++ ++#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) ++ ++#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) ++ ++#define hashstr(t,str) hashpow2(t, (str)->tsv.hash) ++#define hashboolean(t,p) hashpow2(t, p) ++#define hashnum(t, n) hashmod(t, (unsigned int)n) ++#define hashpointer(t,p) hashmod(t, (unsigned long)(p)) ++ ++#define dummynode (&dummynode_) ++#define isdummy(n) ((n) == dummynode) ++ ++static void table_setint(ktap_state *ks, ktap_tab *t, int key, ktap_value *v); ++static ktap_value *table_set(ktap_state *ks, ktap_tab *t, ++ const ktap_value *key); ++static void setnodevector(ktap_state *ks, ktap_tab *t, int size); ++ ++static int ceillog2(unsigned int x) ++{ ++ static const u8 log_2[256] = { ++ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, ++ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, ++ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, ++ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, ++ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, ++ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, ++ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, ++ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 ++ }; ++ ++ int l = 0; ++ ++ x--; ++ while (x >= 256) { l += 8; x >>= 8; } ++ return l + log_2[x]; ++} ++ ++#ifdef __KERNEL__ ++static inline ktap_stat_data *_read_sd(const ktap_value *v, ++ ktap_tnode *hnode, ktap_stat_data *hsd) ++{ ++ ktap_tnode *node = container_of(v, ktap_tnode, i_val); ++ return hsd + (node - hnode); ++} ++ ++static inline ktap_stat_data *read_sd(ktap_tab *t, const ktap_value *v) ++{ ++ if (v >= &t->array[0] && v < &t->array[t->sizearray]) ++ return &t->sd_arr[v - &t->array[0]]; ++ else ++ return _read_sd(v, t->node, t->sd_rec); ++} ++ ++#else ++static inline ktap_stat_data *_read_sd(const ktap_value *v, ++ ktap_tnode *hnode, ktap_stat_data *hsd) ++{ ++ return NULL; ++} ++ ++static inline ktap_stat_data *read_sd(ktap_tab *t, const ktap_value *v) ++{ ++ return NULL; ++} ++#endif ++ ++ ++ktap_tab *kp_tab_new(ktap_state *ks) ++{ ++ ktap_tab *t = &kp_newobject(ks, KTAP_TTABLE, sizeof(ktap_tab), ++ NULL)->h; ++ t->flags = (u8)(~0); ++ t->array = NULL; ++ t->sizearray = 0; ++ t->node = (ktap_tnode *)dummynode; ++ t->gclist = NULL; ++ t->with_stats = 0; ++ t->sd_arr = NULL; ++ t->sd_rec = NULL; ++ setnodevector(ks, t, 0); ++ ++ t->sorted = NULL; ++ t->sort_head = NULL; ++ ++ kp_tab_lock_init(t); ++ return t; ++} ++ ++static const ktap_value *table_getint(ktap_tab *t, int key) ++{ ++ ktap_tnode *n; ++ ++ if ((unsigned int)(key - 1) < (unsigned int)t->sizearray) ++ return &t->array[key - 1]; ++ ++ n = hashnum(t, key); ++ do { ++ if (is_number(gkey(n)) && nvalue(gkey(n)) == key) ++ return gval(n); ++ else ++ n = gnext(n); ++ } while (n); ++ ++ return ktap_nilobject; ++} ++ ++const ktap_value *kp_tab_getint(ktap_tab *t, int key) ++{ ++ const ktap_value *val; ++ unsigned long __maybe_unused flags; ++ ++ kp_tab_lock(t); ++ val = table_getint(t, key); ++ kp_tab_unlock(t); ++ ++ return val; ++} ++ ++static ktap_tnode *mainposition (const ktap_tab *t, const ktap_value *key) ++{ ++ switch (ttype(key)) { ++ case KTAP_TNUMBER: ++ return hashnum(t, nvalue(key)); ++ case KTAP_TLNGSTR: { ++ ktap_string *s = rawtsvalue(key); ++ if (s->tsv.extra == 0) { /* no hash? */ ++ s->tsv.hash = kp_string_hash(getstr(s), s->tsv.len, ++ s->tsv.hash); ++ s->tsv.extra = 1; /* now it has its hash */ ++ } ++ return hashstr(t, rawtsvalue(key)); ++ } ++ case KTAP_TSHRSTR: ++ return hashstr(t, rawtsvalue(key)); ++ case KTAP_TBOOLEAN: ++ return hashboolean(t, bvalue(key)); ++ case KTAP_TLIGHTUSERDATA: ++ return hashpointer(t, pvalue(key)); ++ case KTAP_TCFUNCTION: ++ return hashpointer(t, fvalue(key)); ++ case KTAP_TBTRACE: { ++ /* use first entry as hash key, cannot use gcvalue as key */ ++ unsigned long *entries = (unsigned long *)(btvalue(key) + 1); ++ return hashpointer(t, entries[0]); ++ } ++ default: ++ return hashpointer(t, gcvalue(key)); ++ } ++} ++ ++static int arrayindex(const ktap_value *key) ++{ ++ if (is_number(key)) { ++ ktap_number n = nvalue(key); ++ int k = (int)n; ++ if ((ktap_number)k == n) ++ return k; ++ } ++ ++ /* `key' did not match some condition */ ++ return -1; ++} ++ ++/* ++ * returns the index of a `key' for table traversals. First goes all ++ * elements in the array part, then elements in the hash part. The ++ * beginning of a traversal is signaled by -1. ++ */ ++static int findindex(ktap_state *ks, ktap_tab *t, StkId key) ++{ ++ int i; ++ ++ if (is_nil(key)) ++ return -1; /* first iteration */ ++ ++ i = arrayindex(key); ++ if (i > 0 && i <= t->sizearray) /* is `key' inside array part? */ ++ return i - 1; /* yes; that's the index (corrected to C) */ ++ else { ++ ktap_tnode *n = mainposition(t, key); ++ for (;;) { /* check whether `key' is somewhere in the chain */ ++ /* key may be dead already, but it is ok to use it in `next' */ ++ if (kp_equalobjv(ks, gkey(n), key)) { ++ i = n - gnode(t, 0); /* key index in hash table */ ++ /* hash elements are numbered after array ones */ ++ return i + t->sizearray; ++ } else ++ n = gnext(n); ++ ++ if (n == NULL) ++ /* key not found */ ++ kp_error(ks, "invalid table key to next"); ++ } ++ } ++} ++ ++int kp_tab_next(ktap_state *ks, ktap_tab *t, StkId key) ++{ ++ unsigned long __maybe_unused flags; ++ int i; ++ ++ kp_tab_lock(t); ++ ++ i = findindex(ks, t, key); /* find original element */ ++ ++ for (i++; i < t->sizearray; i++) { /* try first array part */ ++ if (!is_nil(&t->array[i])) { /* a non-nil value? */ ++ set_number(key, i+1); ++ set_obj(key+1, &t->array[i]); ++ kp_tab_unlock(t); ++ return 1; ++ } ++ } ++ ++ for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ ++ if (!is_nil(gval(gnode(t, i)))) { /* a non-nil value? */ ++ set_obj(key, gkey(gnode(t, i))); ++ set_obj(key+1, gval(gnode(t, i))); ++ kp_tab_unlock(t); ++ return 1; ++ } ++ } ++ ++ kp_tab_unlock(t); ++ return 0; /* no more elements */ ++} ++ ++#ifdef __KERNEL__ ++int kp_tab_sort_next(ktap_state *ks, ktap_tab *t, StkId key) ++{ ++ unsigned long __maybe_unused flags; ++ ktap_tnode *node = t->sort_head; ++ ++ kp_tab_lock(t); ++ ++ if (is_nil(key)) { ++ /* first iteration */ ++ set_obj(key, gkey(node)); ++ set_obj(key + 1, gval(node)); ++ kp_tab_unlock(t); ++ return 1; ++ } ++ ++ while (node && !is_nil(gval(node))) { ++ if (kp_equalobjv(ks, gkey(node), key)) { ++ node = gnext(node); ++ if (!node) ++ goto out; ++ ++ set_obj(key, gkey(node)); ++ set_obj(key + 1, gval(node)); ++ kp_tab_unlock(t); ++ return 1; ++ } ++ node = gnext(node); ++ } ++ ++ out: ++ kp_tab_unlock(t); ++ return 0; /* no more elements */ ++} ++ ++ ++static int default_compare(ktap_state *ks, ktap_closure *cmp_func, ++ ktap_value *v1, ktap_value *v2) ++{ ++ return nvalue(v1) < nvalue(v2); ++} ++ ++static int closure_compare(ktap_state *ks, ktap_closure *cmp_func, ++ ktap_value *v1, ktap_value *v2) ++{ ++ ktap_value *func; ++ int res; ++ ++ func = ks->top; ++ set_closure(ks->top++, cmp_func); ++ set_obj(ks->top++, v1); ++ set_obj(ks->top++, v2); ++ ++ kp_call(ks, func, 1); ++ ++ res = !is_false(ks->top - 1); ++ ++ ks->top = func; /* restore ks->top */ ++ ++ return res; ++} ++ ++static void insert_sorted_list(ktap_state *ks, ktap_tab *t, ++ ktap_closure *cmp_func, ++ ktap_value *key, ktap_value *val) ++{ ++ ktap_tnode *node = t->sort_head; ++ ktap_tnode *newnode, *prevnode = NULL; ++ int (*compare)(ktap_state *ks, ktap_closure *cmp_func, ++ ktap_value *v1, ktap_value *v2); ++ int i = 0; ++ ++ if (is_nil(gval(node))) { ++ *gkey(node) = *key; ++ *gval(node) = *val; ++ return; ++ } ++ ++ if (!cmp_func) ++ compare = default_compare; ++ else ++ compare = closure_compare; ++ ++ while (node) { ++ //if (nvalue(gval(node)) < nvalue(val)) { ++ if (compare(ks, cmp_func, gval(node), val)) { ++ prevnode = node; ++ node = gnext(node); ++ continue; ++ } else ++ break; ++ } ++ ++ /* find free position */ ++ while (!is_nil(gval(&t->sorted[i]))) { ++ i++; ++ } ++ ++ newnode = &t->sorted[i]; ++ *gkey(newnode) = *key; ++ *gval(newnode) = *val; ++ gnext(newnode) = node; ++ if (prevnode) ++ gnext(prevnode) = newnode; ++ else ++ t->sort_head = newnode; ++} ++ ++void kp_tab_sort(ktap_state *ks, ktap_tab *t, ktap_closure *cmp_func) ++{ ++ unsigned long __maybe_unused flags; ++ int size = t->sizearray + sizenode(t); ++ int i; ++ ++ kp_tab_lock(t); ++ ++ kp_realloc(ks, t->sorted, 0, size, ktap_tnode); ++ memset(t->sorted, 0, size * sizeof(ktap_tnode)); ++ t->sort_head = t->sorted; ++ ++ for (i = 0; i < t->sizearray; i++) { ++ ktap_value *v = &t->array[i]; ++ ktap_value key; ++ ++ if (!is_nil(v)) { ++ set_number(&key, i + 1); ++ insert_sorted_list(ks, t, cmp_func, &key, v); ++ } ++ } ++ ++ for (i = 0; i < sizenode(t); i++) { ++ ktap_tnode *node = &t->node[i]; ++ ++ if (is_nil(gkey(node))) ++ continue; ++ ++ insert_sorted_list(ks, t, cmp_func, gkey(node), gval(node)); ++ } ++ ++ kp_tab_unlock(t); ++} ++#endif ++ ++static int computesizes (int nums[], int *narray) ++{ ++ int i; ++ int twotoi; /* 2^i */ ++ int a = 0; /* number of elements smaller than 2^i */ ++ int na = 0; /* number of elements to go to array part */ ++ int n = 0; /* optimal size for array part */ ++ ++ for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { ++ if (nums[i] > 0) { ++ a += nums[i]; ++ /* more than half elements present? */ ++ if (a > twotoi/2) { ++ /* optimal size (till now) */ ++ n = twotoi; ++ /* ++ * all elements smaller than n will go to ++ * array part ++ */ ++ na = a; ++ } ++ } ++ if (a == *narray) ++ break; /* all elements already counted */ ++ } ++ *narray = n; ++ return na; ++} ++ ++ ++static int countint(const ktap_value *key, int *nums) ++{ ++ int k = arrayindex(key); ++ ++ /* is `key' an appropriate array index? */ ++ if (0 < k && k <= MAXASIZE) { ++ nums[ceillog2(k)]++; /* count as such */ ++ return 1; ++ } else ++ return 0; ++} ++ ++ ++static int numusearray(const ktap_tab *t, int *nums) ++{ ++ int lg; ++ int ttlg; /* 2^lg */ ++ int ause = 0; /* summation of `nums' */ ++ int i = 1; /* count to traverse all array keys */ ++ ++ /* for each slice */ ++ for (lg=0, ttlg=1; lg <= MAXBITS; lg++, ttlg *= 2) { ++ int lc = 0; /* counter */ ++ int lim = ttlg; ++ ++ if (lim > t->sizearray) { ++ lim = t->sizearray; /* adjust upper limit */ ++ if (i > lim) ++ break; /* no more elements to count */ ++ } ++ ++ /* count elements in range (2^(lg-1), 2^lg] */ ++ for (; i <= lim; i++) { ++ if (!is_nil(&t->array[i-1])) ++ lc++; ++ } ++ nums[lg] += lc; ++ ause += lc; ++ } ++ return ause; ++} ++ ++static int numusehash(const ktap_tab *t, int *nums, int *pnasize) ++{ ++ int totaluse = 0; /* total number of elements */ ++ int ause = 0; /* summation of `nums' */ ++ int i = sizenode(t); ++ ++ while (i--) { ++ ktap_tnode *n = &t->node[i]; ++ if (!is_nil(gval(n))) { ++ ause += countint(gkey(n), nums); ++ totaluse++; ++ } ++ } ++ ++ *pnasize += ause; ++ return totaluse; ++} ++ ++static void update_array_sd(ktap_tab *t) ++{ ++ int i; ++ ++ for (i = 0; i < t->sizearray; i++) { ++ ktap_value *v = &t->array[i]; ++ ++ if (!is_statdata(v)) ++ continue; ++ ++ set_statdata(v, &t->sd_arr[i]); ++ } ++} ++ ++static void setarrayvector(ktap_state *ks, ktap_tab *t, int size) ++{ ++ int i; ++ ++ kp_realloc(ks, t->array, t->sizearray, size, ktap_value); ++ if (t->with_stats) { ++ kp_realloc(ks, t->sd_arr, t->sizearray, size, ++ ktap_stat_data); ++ update_array_sd(t); ++ } ++ ++ for (i = t->sizearray; i < size; i++) ++ set_nil(&t->array[i]); ++ ++ t->sizearray = size; ++} ++ ++static void setnodevector(ktap_state *ks, ktap_tab *t, int size) ++{ ++ int lsize; ++ ++ if (size == 0) { /* no elements to hash part? */ ++ t->node = (ktap_tnode *)dummynode; /* use common `dummynode' */ ++ lsize = 0; ++ } else { ++ int i; ++ lsize = ceillog2(size); ++ if (lsize > MAXBITS) { ++ kp_error(ks, "table overflow\n"); ++ return; ++ } ++ ++ size = twoto(lsize); ++ t->node = kp_malloc(ks, size * sizeof(ktap_tnode)); ++ if (t->with_stats) ++ t->sd_rec = kp_malloc(ks, size * ++ sizeof(ktap_stat_data)); ++ for (i = 0; i < size; i++) { ++ ktap_tnode *n = gnode(t, i); ++ gnext(n) = NULL; ++ set_nil(gkey(n)); ++ set_nil(gval(n)); ++ } ++ } ++ ++ t->lsizenode = (u8)lsize; ++ t->lastfree = gnode(t, size); /* all positions are free */ ++} ++ ++static void table_resize(ktap_state *ks, ktap_tab *t, int nasize, int nhsize) ++{ ++ int oldasize = t->sizearray; ++ int oldhsize = t->lsizenode; ++ ktap_tnode *nold = t->node; /* save old hash */ ++ ktap_stat_data *sd_rec_old = t->sd_rec; /* save stat_data */ ++ int i; ++ ++#ifdef __KERNEL__ ++ kp_verbose_printf(ks, "table resize, nasize: %d, nhsize: %d\n", ++ nasize, nhsize); ++#endif ++ ++ if (nasize > oldasize) /* array part must grow? */ ++ setarrayvector(ks, t, nasize); ++ ++ /* create new hash part with appropriate size */ ++ setnodevector(ks, t, nhsize); ++ ++ if (nasize < oldasize) { /* array part must shrink? */ ++ t->sizearray = nasize; ++ /* re-insert elements from vanishing slice */ ++ for (i = nasize; i < oldasize; i++) { ++ if (!is_nil(&t->array[i])) { ++ ktap_value *v; ++ v = (ktap_value *)table_getint(t, i + 1); ++ set_obj(v, &t->array[i]); ++ ++ if (t->with_stats) { ++ *read_sd(t, v) = t->sd_arr[i]; ++ set_statdata(v, read_sd(t, v)); ++ } ++ } ++ } ++ ++ /* shrink array */ ++ kp_realloc(ks, t->array, oldasize, nasize, ktap_value); ++ if (t->with_stats) { ++ kp_realloc(ks, t->sd_arr, oldasize, nasize, ++ ktap_stat_data); ++ update_array_sd(t); ++ } ++ } ++ ++ /* re-insert elements from hash part */ ++ for (i = twoto(oldhsize) - 1; i >= 0; i--) { ++ ktap_tnode *old = nold + i; ++ if (!is_nil(gval(old))) { ++ ktap_value *v = table_set(ks, t, gkey(old)); ++ /* ++ * doesn't need barrier/invalidate cache, as entry was ++ * already present in the table ++ */ ++ set_obj(v, gval(old)); ++ ++ if (t->with_stats) { ++ ktap_stat_data *sd; ++ ++ sd = read_sd(t, v); ++ *sd = *_read_sd(gval(old), nold, sd_rec_old); ++ set_statdata(v, sd); ++ } ++ } ++ } ++ ++ if (!isdummy(nold)) { ++ kp_free(ks, nold); /* free old array */ ++ kp_free(ks, sd_rec_old); ++ } ++} ++ ++void kp_tab_resize(ktap_state *ks, ktap_tab *t, int nasize, int nhsize) ++{ ++ unsigned long __maybe_unused flags; ++ ++ kp_tab_lock(t); ++ table_resize(ks, t, nasize, nhsize); ++ kp_tab_unlock(t); ++} ++ ++void kp_tab_resizearray(ktap_state *ks, ktap_tab *t, int nasize) ++{ ++ unsigned long __maybe_unused flags; ++ int nsize; ++ ++ kp_tab_lock(t); ++ ++ nsize = isdummy(t->node) ? 0 : sizenode(t); ++ table_resize(ks, t, nasize, nsize); ++ ++ kp_tab_unlock(t); ++} ++ ++static void rehash(ktap_state *ks, ktap_tab *t, const ktap_value *ek) ++{ ++ int nasize, na; ++ /* nums[i] = number of keys with 2^(i-1) < k <= 2^i */ ++ int nums[MAXBITS+1]; ++ int i; ++ int totaluse; ++ ++ for (i = 0; i <= MAXBITS; i++) ++ nums[i] = 0; /* reset counts */ ++ ++ nasize = numusearray(t, nums); /* count keys in array part */ ++ totaluse = nasize; /* all those keys are integer keys */ ++ totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ ++ /* count extra key */ ++ nasize += countint(ek, nums); ++ totaluse++; ++ /* compute new size for array part */ ++ na = computesizes(nums, &nasize); ++ /* resize the table to new computed sizes */ ++ table_resize(ks, t, nasize, totaluse - na); ++} ++ ++ ++static ktap_tnode *getfreepos(ktap_tab *t) ++{ ++ while (t->lastfree > t->node) { ++ t->lastfree--; ++ if (is_nil(gkey(t->lastfree))) ++ return t->lastfree; ++ } ++ return NULL; /* could not find a free place */ ++} ++ ++ ++static ktap_value *table_newkey(ktap_state *ks, ktap_tab *t, ++ const ktap_value *key) ++{ ++ ktap_tnode *mp; ++ ktap_value newkey; ++ ++ mp = mainposition(t, key); ++ if (!is_nil(gval(mp)) || isdummy(mp)) { /* main position is taken? */ ++ ktap_tnode *othern; ++ ktap_tnode *n = getfreepos(t); /* get a free place */ ++ if (n == NULL) { /* cannot find a free place? */ ++ rehash(ks, t, key); /* grow table */ ++ /* insert key into grown table */ ++ return table_set(ks, t, key); ++ } ++ ++ othern = mainposition(t, gkey(mp)); ++ if (othern != mp) { ++ /* is colliding node out of its main position? */ ++ ++ /* move colliding node into free position */ ++ while (gnext(othern) != mp) ++ othern = gnext(othern); /* find previous */ ++ ++ /* redo the chain with `n' in place of `mp' */ ++ gnext(othern) = n; ++ ++ /* copy colliding node into free pos */ ++ *n = *mp; ++ ++ if (t->with_stats) { ++ ktap_stat_data *sd = read_sd(t, gval(n)); ++ *sd = *read_sd(t, gval(mp)); ++ set_statdata(gval(n), sd); ++ } ++ ++ gnext(mp) = NULL; /* now `mp' is free */ ++ set_nil(gval(mp)); ++ } else { ++ /* colliding node is in its own main position */ ++ ++ /* new node will go into free position */ ++ gnext(n) = gnext(mp); /* chain new position */ ++ gnext(mp) = n; ++ mp = n; ++ } ++ } ++ ++ /* special handling for cloneable object, maily for btrace object */ ++ if (is_needclone(key)) ++ kp_objclone(ks, key, &newkey, &t->gclist); ++ else ++ newkey = *key; ++ ++ set_obj(gkey(mp), &newkey); ++ return gval(mp); ++} ++ ++ ++/* ++ * search function for short strings ++ */ ++static const ktap_value *table_getstr(ktap_tab *t, ktap_string *key) ++{ ++ ktap_tnode *n = hashstr(t, key); ++ ++ do { /* check whether `key' is somewhere in the chain */ ++ if (is_shrstring(gkey(n)) && eqshrstr(rawtsvalue(gkey(n)), ++ key)) ++ return gval(n); /* that's it */ ++ else ++ n = gnext(n); ++ } while (n); ++ ++ return ktap_nilobject; ++} ++ ++ ++/* ++ * main search function ++ */ ++static const ktap_value *table_get(ktap_tab *t, const ktap_value *key) ++{ ++ switch (ttype(key)) { ++ case KTAP_TNIL: ++ return ktap_nilobject; ++ case KTAP_TSHRSTR: ++ return table_getstr(t, rawtsvalue(key)); ++ case KTAP_TNUMBER: { ++ ktap_number n = nvalue(key); ++ int k = (int)n; ++ if ((ktap_number)k == nvalue(key)) /* index is int? */ ++ return table_getint(t, k); /* use specialized version */ ++ /* else go through */ ++ } ++ default: { ++ ktap_tnode *n = mainposition(t, key); ++ do { /* check whether `key' is somewhere in the chain */ ++ if (rawequalobj(gkey(n), key)) ++ return gval(n); /* that's it */ ++ else ++ n = gnext(n); ++ } while (n); ++ ++ return ktap_nilobject; ++ } ++ } ++} ++ ++const ktap_value *kp_tab_get(ktap_tab *t, const ktap_value *key) ++{ ++ const ktap_value *val; ++ unsigned long __maybe_unused flags; ++ ++ kp_tab_lock(t); ++ val = table_get(t, key); ++ kp_tab_unlock(t); ++ ++ return val; ++} ++ ++static ktap_value *table_set(ktap_state *ks, ktap_tab *t, ++ const ktap_value *key) ++{ ++ const ktap_value *p = table_get(t, key); ++ ++ if (p != ktap_nilobject) ++ return (ktap_value *)p; ++ else ++ return table_newkey(ks, t, key); ++} ++ ++void kp_tab_setvalue(ktap_state *ks, ktap_tab *t, ++ const ktap_value *key, ktap_value *val) ++{ ++ unsigned long __maybe_unused flags; ++ ++ if (is_nil(key)) { ++ kp_printf(ks, "table index is nil\n"); ++ kp_exit(ks); ++ return; ++ } ++ ++ kp_tab_lock(t); ++ set_obj(table_set(ks, t, key), val); ++ kp_tab_unlock(t); ++} ++ ++static void table_setint(ktap_state *ks, ktap_tab *t, int key, ktap_value *v) ++{ ++ const ktap_value *p; ++ ktap_value *cell; ++ ++ p = table_getint(t, key); ++ ++ if (p != ktap_nilobject) ++ cell = (ktap_value *)p; ++ else { ++ ktap_value k; ++ set_number(&k, key); ++ cell = table_newkey(ks, t, &k); ++ } ++ ++ set_obj(cell, v); ++} ++ ++void kp_tab_setint(ktap_state *ks, ktap_tab *t, int key, ktap_value *val) ++{ ++ unsigned long __maybe_unused flags; ++ ++ kp_tab_lock(t); ++ table_setint(ks, t, key, val); ++ kp_tab_unlock(t); ++} ++ ++void kp_tab_atomic_inc(ktap_state *ks, ktap_tab *t, ktap_value *key, int n) ++{ ++ unsigned long __maybe_unused flags; ++ ktap_value *v; ++ ++ if (is_nil(key)) { ++ kp_printf(ks, "table index is nil\n"); ++ kp_exit(ks); ++ return; ++ } ++ ++ kp_tab_lock(t); ++ ++ v = table_set(ks, t, key); ++ if (is_nil(v)) { ++ set_number(v, n); ++ } else ++ set_number(v, nvalue(v) + n); ++ ++ kp_tab_unlock(t); ++} ++ ++int kp_tab_length(ktap_state *ks, ktap_tab *t) ++{ ++ unsigned long __maybe_unused flags; ++ int i, len = 0; ++ ++ kp_tab_lock(t); ++ ++ for (i = 0; i < t->sizearray; i++) { ++ ktap_value *v = &t->array[i]; ++ ++ if (is_nil(v)) ++ continue; ++ len++; ++ } ++ ++ for (i = 0; i < sizenode(t); i++) { ++ ktap_tnode *n = &t->node[i]; ++ ++ if (is_nil(gkey(n))) ++ continue; ++ ++ len++; ++ } ++ ++ kp_tab_unlock(t); ++ return len; ++} ++ ++void kp_tab_free(ktap_state *ks, ktap_tab *t) ++{ ++ if (t->sizearray > 0) { ++ kp_free(ks, t->array); ++ kp_free(ks, t->sd_arr); ++ } ++ ++ if (!isdummy(t->node)) { ++ kp_free(ks, t->node); ++ kp_free(ks, t->sd_rec); ++ } ++ ++ kp_free(ks, t->sorted); ++ kp_free_gclist(ks, t->gclist); ++ kp_free(ks, t); ++} ++ ++void kp_tab_dump(ktap_state *ks, ktap_tab *t) ++{ ++ int i; ++ ++ for (i = 0; i < t->sizearray; i++) { ++ ktap_value *v = &t->array[i]; ++ ++ if (is_nil(v)) ++ continue; ++ ++ kp_printf(ks, "%d:\t", i + 1); ++ kp_showobj(ks, v); ++ kp_puts(ks, "\n"); ++ } ++ ++ for (i = 0; i < sizenode(t); i++) { ++ ktap_tnode *n = &t->node[i]; ++ ++ if (is_nil(gkey(n))) ++ continue; ++ ++ kp_showobj(ks, gkey(n)); ++ kp_puts(ks, ":\t"); ++ kp_showobj(ks, gval(n)); ++ kp_puts(ks, "\n"); ++ } ++} ++ ++/* ++ * table-clear only set nil of all elements, not free t->array and nodes. ++ * we assume user will reuse table soon after clear table, so reserve array ++ * and nodes will avoid memory allocation when insert key-value again. ++ */ ++void kp_tab_clear(ktap_state *ks, ktap_tab *t) ++{ ++ unsigned long __maybe_unused flags; ++ ++ kp_tab_lock(t); ++ ++ memset(t->array, 0, t->sizearray * sizeof(ktap_value)); ++ memset(t->node, 0, sizenode(t) * sizeof(ktap_tnode)); ++ ++ kp_tab_unlock(t); ++} ++ ++#ifdef __KERNEL__ ++static void string_convert(char *output, const char *input) ++{ ++ if (strlen(input) > 32) { ++ strncpy(output, input, 32-4); ++ memset(output + 32-4, '.', 3); ++ } else ++ memcpy(output, input, strlen(input)); ++} ++ ++struct table_hist_record { ++ ktap_value key; ++ ktap_value val; ++}; ++ ++static int hist_record_cmp(const void *r1, const void *r2) ++{ ++ const struct table_hist_record *i = r1; ++ const struct table_hist_record *j = r2; ++ ++ if ((nvalue(&i->val) == nvalue(&j->val))) { ++ return 0; ++ } else if ((nvalue(&i->val) < nvalue(&j->val))) { ++ return 1; ++ } else ++ return -1; ++} ++ ++/* todo: make histdump to be faster */ ++ ++/* histogram: key should be number or string, value must be number */ ++static void table_histdump(ktap_state *ks, ktap_tab *t, int shownums) ++{ ++ struct table_hist_record *thr; ++ unsigned long __maybe_unused flags; ++ char dist_str[40]; ++ int i, ratio, total = 0, count = 0, top_num, is_kernel_address = 0; ++ int size, num; ++ ++ size = sizeof(*thr) * (t->sizearray + sizenode(t)); ++ thr = kp_malloc(ks, size); ++ if (!thr) { ++ kp_error(ks, "Cannot allocate %d of histogram memory", size); ++ return; ++ } ++ ++ kp_tab_lock(t); ++ ++ for (i = 0; i < t->sizearray; i++) { ++ ktap_value *v = &t->array[i]; ++ ++ if (is_nil(v)) ++ continue; ++ ++ if (is_number(v)) ++ num = nvalue(v); ++ else if (is_statdata(v)) ++ num = sdvalue(v)->count; ++ else { ++ kp_tab_unlock(t); ++ goto error; ++ } ++ ++ set_number(&thr[count].key, i + 1); ++ set_number(&thr[count].val, num); ++ count++; ++ total += num; ++ } ++ ++ for (i = 0; i < sizenode(t); i++) { ++ ktap_tnode *n = &t->node[i]; ++ ktap_value *v = gval(n); ++ ++ if (is_nil(gkey(n))) ++ continue; ++ ++ if (is_number(v)) ++ num = nvalue(v); ++ else if (is_statdata(v)) ++ num = sdvalue(v)->count; ++ else { ++ kp_tab_unlock(t); ++ goto error; ++ } ++ ++ set_obj(&thr[count].key, gkey(n)); ++ set_number(&thr[count].val, num); ++ count++; ++ total += num; ++ } ++ ++ kp_tab_unlock(t); ++ ++ sort(thr, count, sizeof(struct table_hist_record), ++ hist_record_cmp, NULL); ++ ++ dist_str[sizeof(dist_str) - 1] = '\0'; ++ ++ /* check the first key is a kernel text symbol or not */ ++ if (is_number(&thr[0].key)) { ++ char str[KSYM_SYMBOL_LEN]; ++ ++ SPRINT_SYMBOL(str, nvalue(&thr[0].key)); ++ if (str[0] != '0' || str[1] != 'x') ++ is_kernel_address = 1; ++ } ++ ++ top_num = min(shownums, count); ++ for (i = 0; i < top_num; i++) { ++ ktap_value *key = &thr[i].key; ++ ktap_value *val = &thr[i].val; ++ ++ memset(dist_str, ' ', sizeof(dist_str) - 1); ++ ratio = (nvalue(val) * (sizeof(dist_str) - 1)) / total; ++ memset(dist_str, '@', ratio); ++ ++ if (is_string(key)) { ++ char buf[32 + 1] = {0}; ++ ++ string_convert(buf, svalue(key)); ++ kp_printf(ks, "%32s |%s%-7d\n", buf, dist_str, ++ nvalue(val)); ++ } else if (is_number(key)) { ++ char str[KSYM_SYMBOL_LEN]; ++ char buf[32 + 1] = {0}; ++ ++ if (is_kernel_address) { ++ /* suppose it's a symbol, fix it in future */ ++ SPRINT_SYMBOL(str, nvalue(key)); ++ string_convert(buf, str); ++ kp_printf(ks, "%32s |%s%-7d\n", buf, dist_str, ++ nvalue(val)); ++ } else { ++ kp_printf(ks, "%32d |%s%-7d\n", nvalue(key), ++ dist_str, nvalue(val)); ++ } ++ } ++ } ++ ++ if (count > shownums) ++ kp_printf(ks, "%32s |\n", "..."); ++ ++ goto out; ++ ++ error: ++ kp_puts(ks, "error: table histogram only handle " ++ " (key: string/number val: number)\n"); ++ out: ++ kp_free(ks, thr); ++} ++ ++#define HISTOGRAM_DEFAULT_TOP_NUM 20 ++ ++#define DISTRIBUTION_STR "------------- Distribution -------------" ++void kp_tab_histogram(ktap_state *ks, ktap_tab *t) ++{ ++ kp_printf(ks, "%32s%s%s\n", "value ", DISTRIBUTION_STR, " count"); ++ table_histdump(ks, t, HISTOGRAM_DEFAULT_TOP_NUM); ++} ++ ++/* ++ * Parallel Table ++ */ ++ ++void kp_statdata_dump(ktap_state *ks, ktap_stat_data *sd) ++{ ++ kp_printf(ks, "[count: %6d sum: %6d max: %6d min: %6d avg: %6d]", ++ sd->count, sd->sum, sd->max, sd->min, sd->sum/sd->count); ++} ++ ++static void statdata_add(ktap_stat_data *sd1, ktap_stat_data *sd2) ++{ ++ sd2->count += sd1->count; ++ sd2->sum += sd1->sum; ++ if (sd1->max > sd2->max) ++ sd2->max = sd1->max; ++ if (sd1->min < sd2->min) ++ sd2->min = sd1->min; ++} ++ ++static void merge_table(ktap_state *ks, ktap_tab *t1, ktap_tab *t2) ++{ ++ unsigned long __maybe_unused flags; ++ ktap_value *newv; ++ ktap_value n; ++ int i; ++ ++ kp_tab_lock(t1); ++ kp_tab_lock(t2); ++ ++ for (i = 0; i < t1->sizearray; i++) { ++ ktap_value *v = &t1->array[i]; ++ ktap_stat_data *sd; ++ ++ if (is_nil(v)) ++ continue; ++ ++ set_number(&n, i); ++ ++ newv = table_set(ks, t2, &n); ++ sd = read_sd(t2, newv); ++ if (is_nil(newv)) { ++ *sd = *read_sd(t1, v); ++ set_statdata(newv, sd); ++ } else ++ statdata_add(read_sd(t1, v), sd); ++ } ++ ++ for (i = 0; i < sizenode(t1); i++) { ++ ktap_tnode *node = &t1->node[i]; ++ ++ if (is_nil(gkey(node))) ++ continue; ++ ++ newv = table_set(ks, t2, gkey(node)); ++ if (is_nil(newv)) { ++ *read_sd(t2, newv) = *read_sd(t1, gval(node)); ++ set_statdata(newv, read_sd(t2, newv)); ++ } else ++ statdata_add(read_sd(t1, gval(node)), ++ read_sd(t2, newv)); ++ } ++ ++ kp_tab_unlock(t2); ++ kp_tab_unlock(t1); ++} ++ ++ktap_tab *kp_ptab_synthesis(ktap_state *ks, ktap_ptab *ph) ++{ ++ ktap_tab *agg; ++ int cpu; ++ ++ agg = ph->agg; ++ ++ /* clear the table content before store new elements */ ++ kp_tab_clear(ks, agg); ++ ++ for_each_possible_cpu(cpu) { ++ ktap_tab **t = per_cpu_ptr(ph->tbl, cpu); ++ merge_table(ks, *t, agg); ++ } ++ ++ return agg; ++} ++ ++void kp_ptab_dump(ktap_state *ks, ktap_ptab *ph) ++{ ++ kp_tab_dump(ks, kp_ptab_synthesis(ks, ph)); ++} ++ ++ktap_ptab *kp_ptab_new(ktap_state *ks) ++{ ++ ktap_ptab *ph; ++ int cpu; ++ ++ ph = &kp_newobject(ks, KTAP_TPTABLE, sizeof(ktap_ptab), ++ NULL)->ph; ++ ph->tbl = alloc_percpu(ktap_tab *); ++ ++ for_each_possible_cpu(cpu) { ++ ktap_tab **t = per_cpu_ptr(ph->tbl, cpu); ++ *t = kp_tab_new(ks); ++ ++ (*t)->with_stats = 1; ++ ++ /* todo: make this value to be configuable, MAXENTRIES? */ ++ table_resize(ks, *t, 0, 2000); ++ } ++ ++ ph->agg = kp_tab_new(ks); ++ ph->agg->with_stats = 1; ++ table_resize(ks, ph->agg, 0, 2000); ++ ++ return ph; ++} ++ ++void kp_ptab_free(ktap_state *ks, ktap_ptab *ph) ++{ ++ free_percpu(ph->tbl); ++ kp_free(ks, ph); ++} ++ ++void kp_ptab_set(ktap_state *ks, ktap_ptab *ph, ++ ktap_value *key, ktap_value *val) ++{ ++ ktap_tab *t = *__this_cpu_ptr(ph->tbl); ++ unsigned long __maybe_unused flags; ++ ktap_value *v; ++ ktap_stat_data *sd; ++ int aggval;; ++ ++ if (unlikely(!is_number(val))) { ++ kp_error(ks, "add non number value to aggregation table\n"); ++ return; ++ } ++ ++ aggval = nvalue(val); ++ ++ kp_tab_lock(t); ++ ++ v = table_set(ks, t, key); ++ sd = read_sd(t, v); ++ ++ if (is_nil(v)) { ++ sd->count = 1; ++ sd->sum = sd->min = sd->max = aggval; ++ set_statdata(v, sd); ++ kp_tab_unlock(t); ++ return; ++ } ++ ++ sd->count++; ++ sd->sum += aggval; ++ if (aggval > sd->max) ++ sd->max = aggval; ++ if (aggval < sd->min) ++ sd->min = aggval; ++ ++ kp_tab_unlock(t); ++} ++ ++void kp_ptab_get(ktap_state *ks, ktap_ptab *ph, ++ ktap_value *key, ktap_value *val) ++{ ++ unsigned long __maybe_unused flags; ++ ktap_stat_data sd, *aggsd; ++ const ktap_value *v; ++ ktap_value *aggval; ++ int cpu; ++ ++ sd.count = sd.sum = sd.max = sd.min = -1; ++ ++ for_each_possible_cpu(cpu) { ++ ktap_tab **t = per_cpu_ptr(ph->tbl, cpu); ++ ++ kp_tab_lock(*t); ++ v = table_get(*t, key); ++ if (is_nil(v)) { ++ kp_tab_unlock(*t); ++ continue; ++ } ++ ++ if (sd.count == -1) { ++ sd = *read_sd(*t, v); ++ kp_tab_unlock(*t); ++ continue; ++ } ++ ++ statdata_add(read_sd(*t, v), &sd); ++ kp_tab_unlock(*t); ++ } ++ ++ if (sd.count == -1) { ++ set_nil(val); ++ return; ++ } ++ ++ kp_tab_lock(ph->agg); ++ aggval = table_set(ks, ph->agg, key); ++ aggsd = read_sd(ph->agg, aggval); ++ *aggsd = sd; ++ set_statdata(aggval, aggsd); ++ set_statdata(val, aggsd); ++ kp_tab_unlock(ph->agg); ++} ++ ++void kp_ptab_histogram(ktap_state *ks, ktap_ptab *ph) ++{ ++ kp_tab_histogram(ks, kp_ptab_synthesis(ks, ph)); ++} ++#endif +diff --git a/drivers/staging/ktap/runtime/kp_tab.h b/drivers/staging/ktap/runtime/kp_tab.h +new file mode 100644 +index 0000000..51be4af +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_tab.h +@@ -0,0 +1,32 @@ ++#ifndef __KTAP_TAB_H__ ++#define __KTAP_TAB_H__ ++ ++ktap_value *kp_tab_set(ktap_state *ks, ktap_tab *t, const ktap_value *key); ++ktap_tab *kp_tab_new(ktap_state *ks); ++const ktap_value *kp_tab_getint(ktap_tab *t, int key); ++void kp_tab_setint(ktap_state *ks, ktap_tab *t, int key, ktap_value *v); ++const ktap_value *kp_tab_get(ktap_tab *t, const ktap_value *key); ++void kp_tab_setvalue(ktap_state *ks, ktap_tab *t, const ktap_value *key, ktap_value *val); ++void kp_tab_resize(ktap_state *ks, ktap_tab *t, int nasize, int nhsize); ++void kp_tab_resizearray(ktap_state *ks, ktap_tab *t, int nasize); ++void kp_tab_free(ktap_state *ks, ktap_tab *t); ++int kp_tab_length(ktap_state *ks, ktap_tab *t); ++void kp_tab_dump(ktap_state *ks, ktap_tab *t); ++void kp_tab_clear(ktap_state *ks, ktap_tab *t); ++void kp_tab_histogram(ktap_state *ks, ktap_tab *t); ++int kp_tab_next(ktap_state *ks, ktap_tab *t, StkId key); ++int kp_tab_sort_next(ktap_state *ks, ktap_tab *t, StkId key); ++void kp_tab_sort(ktap_state *ks, ktap_tab *t, ktap_closure *cmp_func); ++void kp_tab_atomic_inc(ktap_state *ks, ktap_tab *t, ktap_value *key, int n); ++void kp_statdata_dump(ktap_state *ks, ktap_stat_data *sd); ++ktap_ptab *kp_ptab_new(ktap_state *ks); ++ktap_tab *kp_ptab_synthesis(ktap_state *ks, ktap_ptab *ph); ++void kp_ptab_dump(ktap_state *ks, ktap_ptab *ph); ++void kp_ptab_free(ktap_state *ks, ktap_ptab *ph); ++void kp_ptab_set(ktap_state *ks, ktap_ptab *ph, ++ ktap_value *key, ktap_value *val); ++void kp_ptab_get(ktap_state *ks, ktap_ptab *ph, ++ ktap_value *key, ktap_value *val); ++void kp_ptab_histogram(ktap_state *ks, ktap_ptab *ph); ++ ++#endif /* __KTAP_TAB_H__ */ +diff --git a/drivers/staging/ktap/runtime/kp_transport.c b/drivers/staging/ktap/runtime/kp_transport.c +new file mode 100644 +index 0000000..5a1b1e1 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_transport.c +@@ -0,0 +1,641 @@ ++/* ++ * kp_transport.c - ktap transport functionality ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../include/ktap_types.h" ++#include "ktap.h" ++#include "kp_transport.h" ++ ++struct ktap_trace_iterator { ++ struct ring_buffer *buffer; ++ int print_timestamp; ++ void *private; ++ ++ struct trace_iterator iter; ++}; ++ ++enum ktap_trace_type { ++ __TRACE_FIRST_TYPE = 0, ++ ++ TRACE_FN = 1, /* must be same as ftrace definition in kernel */ ++ TRACE_PRINT, ++ TRACE_BPUTS, ++ TRACE_STACK, ++ TRACE_USER_STACK, ++ ++ __TRACE_LAST_TYPE, ++}; ++ ++#define KTAP_TRACE_ITER(iter) \ ++ container_of(iter, struct ktap_trace_iterator, iter) ++ ++static ++ssize_t _trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt) ++{ ++ int len; ++ int ret; ++ ++ if (!cnt) ++ return 0; ++ ++ if (s->len <= s->readpos) ++ return -EBUSY; ++ ++ len = s->len - s->readpos; ++ if (cnt > len) ++ cnt = len; ++ ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt); ++ if (ret == cnt) ++ return -EFAULT; ++ ++ cnt -= ret; ++ ++ s->readpos += cnt; ++ return cnt; ++} ++ ++int _trace_seq_puts(struct trace_seq *s, const char *str) ++{ ++ int len = strlen(str); ++ ++ if (s->full) ++ return 0; ++ ++ if (len > ((PAGE_SIZE - 1) - s->len)) { ++ s->full = 1; ++ return 0; ++ } ++ ++ memcpy(s->buffer + s->len, str, len); ++ s->len += len; ++ ++ return len; ++} ++ ++static int trace_empty(struct trace_iterator *iter) ++{ ++ struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter); ++ int cpu; ++ ++ for_each_online_cpu(cpu) { ++ if (!ring_buffer_empty_cpu(ktap_iter->buffer, cpu)) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static void trace_consume(struct trace_iterator *iter) ++{ ++ struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter); ++ ++ ring_buffer_consume(ktap_iter->buffer, iter->cpu, &iter->ts, ++ &iter->lost_events); ++} ++ ++unsigned long long ns2usecs(cycle_t nsec) ++{ ++ nsec += 500; ++ do_div(nsec, 1000); ++ return nsec; ++} ++ ++static int trace_print_timestamp(struct trace_iterator *iter) ++{ ++ struct trace_seq *s = &iter->seq; ++ unsigned long long t; ++ unsigned long secs, usec_rem; ++ ++ t = ns2usecs(iter->ts); ++ usec_rem = do_div(t, USEC_PER_SEC); ++ secs = (unsigned long)t; ++ ++ return trace_seq_printf(s, "%5lu.%06lu: ", secs, usec_rem); ++} ++ ++/* todo: export kernel function ftrace_find_event in future, and make faster */ ++static struct trace_event *(*ftrace_find_event)(int type); ++ ++static enum print_line_t print_trace_fmt(struct trace_iterator *iter) ++{ ++ struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter); ++ struct trace_entry *entry = iter->ent; ++ struct trace_event *ev; ++ ++ ev = ftrace_find_event(entry->type); ++ ++ if (ktap_iter->print_timestamp && !trace_print_timestamp(iter)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ if (ev) { ++ int ret = ev->funcs->trace(iter, 0, ev); ++ ++ /* overwrite '\n' at the ending */ ++ iter->seq.buffer[iter->seq.len - 1] = '\0'; ++ iter->seq.len--; ++ return ret; ++ } ++ ++ return TRACE_TYPE_PARTIAL_LINE; ++} ++ ++static enum print_line_t print_trace_stack(struct trace_iterator *iter) ++{ ++ struct trace_entry *entry = iter->ent; ++ struct stack_trace trace; ++ char str[KSYM_SYMBOL_LEN]; ++ int i; ++ ++ trace.entries = (unsigned long *)(entry + 1); ++ trace.nr_entries = (iter->ent_size - sizeof(*entry)) / ++ sizeof(unsigned long); ++ ++ if (!_trace_seq_puts(&iter->seq, "\n")) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ for (i = 0; i < trace.nr_entries; i++) { ++ unsigned long p = trace.entries[i]; ++ ++ if (p == ULONG_MAX) ++ break; ++ ++ sprint_symbol(str, p); ++ if (!trace_seq_printf(&iter->seq, " => %s\n", str)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ } ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++struct ktap_ftrace_entry { ++ struct trace_entry entry; ++ unsigned long ip; ++ unsigned long parent_ip; ++}; ++ ++static enum print_line_t print_trace_fn(struct trace_iterator *iter) ++{ ++ struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter); ++ struct ktap_ftrace_entry *field = (struct ktap_ftrace_entry *)iter->ent; ++ char str[KSYM_SYMBOL_LEN]; ++ ++ if (ktap_iter->print_timestamp && !trace_print_timestamp(iter)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ sprint_symbol(str, field->ip); ++ if (!_trace_seq_puts(&iter->seq, str)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ if (!_trace_seq_puts(&iter->seq, " <- ")) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ sprint_symbol(str, field->parent_ip); ++ if (!_trace_seq_puts(&iter->seq, str)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t print_trace_bputs(struct trace_iterator *iter) ++{ ++ if (!_trace_seq_puts(&iter->seq, ++ (const char *)(*(unsigned long *)(iter->ent + 1)))) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t print_trace_line(struct trace_iterator *iter) ++{ ++ struct trace_entry *entry = iter->ent; ++ char *str = (char *)(entry + 1); ++ ++ if (entry->type == TRACE_PRINT) { ++ if (!trace_seq_printf(&iter->seq, "%s", str)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ return TRACE_TYPE_HANDLED; ++ } ++ ++ if (entry->type == TRACE_BPUTS) ++ return print_trace_bputs(iter); ++ ++ if (entry->type == TRACE_STACK) ++ return print_trace_stack(iter); ++ ++ if (entry->type == TRACE_FN) ++ return print_trace_fn(iter); ++ ++ return print_trace_fmt(iter); ++} ++ ++static struct trace_entry * ++peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts, ++ unsigned long *lost_events) ++{ ++ struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter); ++ struct ring_buffer_event *event; ++ ++ event = ring_buffer_peek(ktap_iter->buffer, cpu, ts, lost_events); ++ if (event) { ++ iter->ent_size = ring_buffer_event_length(event); ++ return ring_buffer_event_data(event); ++ } ++ ++ return NULL; ++} ++ ++static struct trace_entry * ++__find_next_entry(struct trace_iterator *iter, int *ent_cpu, ++ unsigned long *missing_events, u64 *ent_ts) ++{ ++ struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter); ++ struct ring_buffer *buffer = ktap_iter->buffer; ++ struct trace_entry *ent, *next = NULL; ++ unsigned long lost_events = 0, next_lost = 0; ++ u64 next_ts = 0, ts; ++ int next_cpu = -1; ++ int next_size = 0; ++ int cpu; ++ ++ for_each_online_cpu(cpu) { ++ if (ring_buffer_empty_cpu(buffer, cpu)) ++ continue; ++ ++ ent = peek_next_entry(iter, cpu, &ts, &lost_events); ++ /* ++ * Pick the entry with the smallest timestamp: ++ */ ++ if (ent && (!next || ts < next_ts)) { ++ next = ent; ++ next_cpu = cpu; ++ next_ts = ts; ++ next_lost = lost_events; ++ next_size = iter->ent_size; ++ } ++ } ++ ++ iter->ent_size = next_size; ++ ++ if (ent_cpu) ++ *ent_cpu = next_cpu; ++ ++ if (ent_ts) ++ *ent_ts = next_ts; ++ ++ if (missing_events) ++ *missing_events = next_lost; ++ ++ return next; ++} ++ ++/* Find the next real entry, and increment the iterator to the next entry */ ++static void *trace_find_next_entry_inc(struct trace_iterator *iter) ++{ ++ iter->ent = __find_next_entry(iter, &iter->cpu, ++ &iter->lost_events, &iter->ts); ++ if (iter->ent) ++ iter->idx++; ++ ++ return iter->ent ? iter : NULL; ++} ++ ++static void poll_wait_pipe(void) ++{ ++ set_current_state(TASK_INTERRUPTIBLE); ++ /* sleep for 100 msecs, and try again. */ ++ schedule_timeout(HZ / 10); ++} ++ ++static int tracing_wait_pipe(struct file *filp) ++{ ++ struct trace_iterator *iter = filp->private_data; ++ struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter); ++ ktap_state *ks = ktap_iter->private; ++ ++ while (trace_empty(iter)) { ++ ++ if ((filp->f_flags & O_NONBLOCK)) { ++ return -EAGAIN; ++ } ++ ++ mutex_unlock(&iter->mutex); ++ ++ poll_wait_pipe(); ++ ++ mutex_lock(&iter->mutex); ++ ++ if (G(ks)->wait_user && trace_empty(iter)) ++ return -EINTR; ++ } ++ ++ return 1; ++} ++ ++static ssize_t ++tracing_read_pipe(struct file *filp, char __user *ubuf, size_t cnt, ++ loff_t *ppos) ++{ ++ struct trace_iterator *iter = filp->private_data; ++ ssize_t sret; ++ ++ /* return any leftover data */ ++ sret = _trace_seq_to_user(&iter->seq, ubuf, cnt); ++ if (sret != -EBUSY) ++ return sret; ++ /* ++ * Avoid more than one consumer on a single file descriptor ++ * This is just a matter of traces coherency, the ring buffer itself ++ * is protected. ++ */ ++ mutex_lock(&iter->mutex); ++ ++waitagain: ++ sret = tracing_wait_pipe(filp); ++ if (sret <= 0) ++ goto out; ++ ++ /* stop when tracing is finished */ ++ if (trace_empty(iter)) { ++ sret = 0; ++ goto out; ++ } ++ ++ if (cnt >= PAGE_SIZE) ++ cnt = PAGE_SIZE - 1; ++ ++ /* reset all but tr, trace, and overruns */ ++ memset(&iter->seq, 0, ++ sizeof(struct trace_iterator) - ++ offsetof(struct trace_iterator, seq)); ++ iter->pos = -1; ++ ++ while (trace_find_next_entry_inc(iter) != NULL) { ++ enum print_line_t ret; ++ int len = iter->seq.len; ++ ++ ret = print_trace_line(iter); ++ if (ret == TRACE_TYPE_PARTIAL_LINE) { ++ /* don't print partial lines */ ++ iter->seq.len = len; ++ break; ++ } ++ if (ret != TRACE_TYPE_NO_CONSUME) ++ trace_consume(iter); ++ ++ if (iter->seq.len >= cnt) ++ break; ++ ++ /* ++ * Setting the full flag means we reached the trace_seq buffer ++ * size and we should leave by partial output condition above. ++ * One of the trace_seq_* functions is not used properly. ++ */ ++ WARN_ONCE(iter->seq.full, "full flag set for trace type %d", ++ iter->ent->type); ++ } ++ ++ /* Now copy what we have to the user */ ++ sret = _trace_seq_to_user(&iter->seq, ubuf, cnt); ++ if (iter->seq.readpos >= iter->seq.len) ++ trace_seq_init(&iter->seq); ++ ++ /* ++ * If there was nothing to send to user, in spite of consuming trace ++ * entries, go back to wait for more entries. ++ */ ++ if (sret == -EBUSY) ++ goto waitagain; ++ ++out: ++ mutex_unlock(&iter->mutex); ++ ++ return sret; ++} ++ ++static int tracing_open_pipe(struct inode *inode, struct file *filp) ++{ ++ struct ktap_trace_iterator *ktap_iter; ++ ktap_state *ks = inode->i_private; ++ ++ /* create a buffer to store the information to pass to userspace */ ++ ktap_iter = kzalloc(sizeof(*ktap_iter), GFP_KERNEL); ++ if (!ktap_iter) ++ return -ENOMEM; ++ ++ ktap_iter->private = ks; ++ ktap_iter->buffer = G(ks)->buffer; ++ ktap_iter->print_timestamp = G(ks)->parm->print_timestamp; ++ mutex_init(&ktap_iter->iter.mutex); ++ filp->private_data = &ktap_iter->iter; ++ ++ nonseekable_open(inode, filp); ++ ++ return 0; ++} ++ ++static int tracing_release_pipe(struct inode *inode, struct file *file) ++{ ++ struct trace_iterator *iter = file->private_data; ++ struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter); ++ ++ mutex_destroy(&iter->mutex); ++ kfree(ktap_iter); ++ return 0; ++} ++ ++static const struct file_operations tracing_pipe_fops = { ++ .open = tracing_open_pipe, ++ .read = tracing_read_pipe, ++ .splice_read = NULL, ++ .release = tracing_release_pipe, ++ .llseek = no_llseek, ++}; ++ ++/* ++ * preempt disabled in ring_buffer_lock_reserve ++ * ++ * The implementation is similar with funtion __ftrace_trace_stack. ++ */ ++void kp_transport_print_backtrace(ktap_state *ks, int skip, int max_entries) ++{ ++ struct ring_buffer *buffer = G(ks)->buffer; ++ struct ring_buffer_event *event; ++ struct trace_entry *entry; ++ int size; ++ ++ size = max_entries * sizeof(unsigned long); ++ event = ring_buffer_lock_reserve(buffer, sizeof(*entry) + size); ++ if (!event) { ++ KTAP_STATS(ks)->events_missed += 1; ++ return; ++ } else { ++ struct stack_trace trace; ++ ++ entry = ring_buffer_event_data(event); ++ tracing_generic_entry_update(entry, 0, 0); ++ entry->type = TRACE_STACK; ++ ++ trace.nr_entries = 0; ++ trace.skip = skip; ++ trace.max_entries = max_entries; ++ trace.entries = (unsigned long *)(entry + 1); ++ save_stack_trace(&trace); ++ ++ ring_buffer_unlock_commit(buffer, event); ++ } ++} ++ ++void kp_transport_event_write(ktap_state *ks, struct ktap_event *e) ++{ ++ struct ring_buffer *buffer = G(ks)->buffer; ++ struct ring_buffer_event *event; ++ struct trace_entry *entry; ++ ++ event = ring_buffer_lock_reserve(buffer, e->entry_size + ++ sizeof(struct ftrace_event_call *)); ++ if (!event) { ++ KTAP_STATS(ks)->events_missed += 1; ++ return; ++ } else { ++ entry = ring_buffer_event_data(event); ++ ++ memcpy(entry, e->entry, e->entry_size); ++ ++ ring_buffer_unlock_commit(buffer, event); ++ } ++} ++ ++void kp_transport_write(ktap_state *ks, const void *data, size_t length) ++{ ++ struct ring_buffer *buffer = G(ks)->buffer; ++ struct ring_buffer_event *event; ++ struct trace_entry *entry; ++ int size; ++ ++ size = sizeof(struct trace_entry) + length; ++ ++ event = ring_buffer_lock_reserve(buffer, size); ++ if (!event) { ++ KTAP_STATS(ks)->events_missed += 1; ++ return; ++ } else { ++ entry = ring_buffer_event_data(event); ++ ++ tracing_generic_entry_update(entry, 0, 0); ++ entry->type = TRACE_PRINT; ++ memcpy(entry + 1, data, length); ++ ++ ring_buffer_unlock_commit(buffer, event); ++ } ++} ++ ++/* general print function */ ++void kp_printf(ktap_state *ks, const char *fmt, ...) ++{ ++ char buff[1024]; ++ va_list args; ++ int len; ++ ++ va_start(args, fmt); ++ len = vscnprintf(buff, 1024, fmt, args); ++ va_end(args); ++ ++ buff[len] = '\0'; ++ kp_transport_write(ks, buff, len + 1); ++} ++ ++void __kp_puts(ktap_state *ks, const char *str) ++{ ++ kp_transport_write(ks, str, strlen(str) + 1); ++} ++ ++void __kp_bputs(ktap_state *ks, const char *str) ++{ ++ struct ring_buffer *buffer = G(ks)->buffer; ++ struct ring_buffer_event *event; ++ struct trace_entry *entry; ++ int size; ++ ++ size = sizeof(struct trace_entry) + sizeof(unsigned long *); ++ ++ event = ring_buffer_lock_reserve(buffer, size); ++ if (!event) { ++ KTAP_STATS(ks)->events_missed += 1; ++ return; ++ } else { ++ entry = ring_buffer_event_data(event); ++ ++ tracing_generic_entry_update(entry, 0, 0); ++ entry->type = TRACE_BPUTS; ++ *(unsigned long *)(entry + 1) = (unsigned long)str; ++ ++ ring_buffer_unlock_commit(buffer, event); ++ } ++} ++ ++void kp_transport_exit(ktap_state *ks) ++{ ++ ring_buffer_free(G(ks)->buffer); ++ debugfs_remove(G(ks)->trace_pipe_dentry); ++} ++ ++#define TRACE_BUF_SIZE_DEFAULT 1441792UL /* 16384 * 88 (sizeof(entry)) */ ++ ++int kp_transport_init(ktap_state *ks, struct dentry *dir) ++{ ++ struct ring_buffer *buffer; ++ struct dentry *dentry; ++ char filename[32] = {0}; ++ ++ ftrace_find_event = (void *)kallsyms_lookup_name("ftrace_find_event"); ++ if (!ftrace_find_event) { ++ printk("ktap: cannot lookup ftrace_find_event in kallsyms\n"); ++ return -EINVAL; ++ } ++ ++ buffer = ring_buffer_alloc(TRACE_BUF_SIZE_DEFAULT, RB_FL_OVERWRITE); ++ if (!buffer) ++ return -ENOMEM; ++ ++ sprintf(filename, "trace_pipe_%d", (int)task_tgid_vnr(current)); ++ ++ dentry = debugfs_create_file(filename, 0444, dir, ++ ks, &tracing_pipe_fops); ++ if (!dentry) { ++ pr_err("ktapvm: cannot create trace_pipe file in debugfs\n"); ++ ring_buffer_free(buffer); ++ return -1; ++ } ++ ++ G(ks)->buffer = buffer; ++ G(ks)->trace_pipe_dentry = dentry; ++ ++ return 0; ++} ++ +diff --git a/drivers/staging/ktap/runtime/kp_transport.h b/drivers/staging/ktap/runtime/kp_transport.h +new file mode 100644 +index 0000000..71b9c53 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_transport.h +@@ -0,0 +1,13 @@ ++#ifndef __KTAP_TRANSPORT_H__ ++#define __KTAP_TRANSPORT_H__ ++ ++void kp_transport_write(ktap_state *ks, const void *data, size_t length); ++void kp_transport_event_write(ktap_state *ks, struct ktap_event *e); ++void kp_transport_print_backtrace(ktap_state *ks, int skip, int max_entries); ++void *kp_transport_reserve(ktap_state *ks, size_t length); ++void kp_transport_exit(ktap_state *ks); ++int kp_transport_init(ktap_state *ks, struct dentry *dir); ++ ++int _trace_seq_puts(struct trace_seq *s, const char *str); ++ ++#endif /* __KTAP_TRANSPORT_H__ */ +diff --git a/drivers/staging/ktap/runtime/kp_vm.c b/drivers/staging/ktap/runtime/kp_vm.c +new file mode 100644 +index 0000000..9c498c9 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_vm.c +@@ -0,0 +1,1496 @@ ++/* ++ * kp_vm.c - ktap script virtual machine in Linux kernel ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * Copyright (C) 1994-2013 Lua.org, PUC-Rio. ++ * - The part of code in this file is copied from lua initially. ++ * - lua's MIT license is compatible with GPL. ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "../include/ktap_types.h" ++#include "../include/ktap_opcodes.h" ++#include "../include/ktap_ffi.h" ++#include "ktap.h" ++#include "kp_obj.h" ++#include "kp_str.h" ++#include "kp_tab.h" ++#include "kp_transport.h" ++#include "kp_vm.h" ++ ++#define KTAP_MIN_RESERVED_STACK_SIZE 20 ++#define KTAP_STACK_SIZE 120 /* enlarge this value for big stack */ ++#define KTAP_STACK_SIZE_BYTES (KTAP_STACK_SIZE * sizeof(ktap_value)) ++ ++#define CIST_KTAP (1 << 0) /* call is running a ktap function */ ++#define CIST_REENTRY (1 << 2) ++ ++#define isktapfunc(ci) ((ci)->callstatus & CIST_KTAP) ++ ++ ++/* common helper function */ ++int gettimeofday_us(void) ++{ ++ struct timeval tv; ++ ++ do_gettimeofday(&tv); ++ return tv.tv_sec * USEC_PER_SEC + tv.tv_usec; ++} ++ ++ ++static void ktap_concat(ktap_state *ks, int start, int end) ++{ ++ int i, len = 0; ++ StkId top = ks->ci->u.l.base; ++ ktap_string *ts; ++ char *ptr, *buffer; ++ ++ for (i = start; i <= end; i++) { ++ if (!is_string(top + i)) { ++ kp_error(ks, "cannot concat non-string\n"); ++ set_nil(top + start); ++ return; ++ } ++ ++ len += rawtsvalue(top + i)->tsv.len; ++ } ++ ++ if (len >= KTAP_PERCPU_BUFFER_SIZE) { ++ kp_error(ks, "Error: too long string concatenation\n"); ++ return; ++ } ++ ++ preempt_disable_notrace(); ++ ++ buffer = kp_percpu_data(ks, KTAP_PERCPU_DATA_BUFFER); ++ ptr = buffer; ++ ++ for (i = start; i <= end; i++) { ++ int len = rawtsvalue(top + i)->tsv.len; ++ strncpy(ptr, svalue(top + i), len); ++ ptr += len; ++ } ++ ts = kp_tstring_newlstr(ks, buffer, len); ++ set_string(top + start, ts); ++ ++ preempt_enable_notrace(); ++} ++ ++/* todo: compare l == r if both is tstring type? */ ++static int lessthan(ktap_state *ks, const ktap_value *l, const ktap_value *r) ++{ ++ if (is_number(l) && is_number(r)) ++ return NUMLT(nvalue(l), nvalue(r)); ++ else if (is_string(l) && is_string(r)) ++ return kp_tstring_cmp(rawtsvalue(l), rawtsvalue(r)) < 0; ++ ++ return 0; ++} ++ ++static int lessequal(ktap_state *ks, const ktap_value *l, const ktap_value *r) ++{ ++ if (is_number(l) && is_number(r)) ++ return NUMLE(nvalue(l), nvalue(r)); ++ else if (is_string(l) && is_string(r)) ++ return kp_tstring_cmp(rawtsvalue(l), rawtsvalue(r)) <= 0; ++ ++ return 0; ++} ++ ++static int fb2int (int x) ++{ ++ int e = (x >> 3) & 0x1f; ++ if (e == 0) ++ return x; ++ else ++ return ((x & 7) + 8) << (e - 1); ++} ++ ++static const ktap_value *ktap_tonumber(const ktap_value *obj, ktap_value *n) ++{ ++ if (is_number(obj)) ++ return obj; ++ ++ return NULL; ++} ++ ++static ktap_upval *findupval(ktap_state *ks, StkId level) ++{ ++ ktap_global_state *g = G(ks); ++ ktap_gcobject **pp = &ks->openupval; ++ ktap_upval *p; ++ ktap_upval *uv; ++ ++ while (*pp != NULL && (p = gco2uv(*pp))->v >= level) { ++ if (p->v == level) { /* found a corresponding upvalue? */ ++ return p; ++ } ++ pp = &p->next; ++ } ++ ++ /* not found: create a new one */ ++ uv = &kp_newobject(ks, KTAP_TUPVAL, sizeof(ktap_upval), pp)->uv; ++ uv->v = level; /* current value lives in the stack */ ++ uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */ ++ uv->u.l.next = g->uvhead.u.l.next; ++ uv->u.l.next->u.l.prev = uv; ++ g->uvhead.u.l.next = uv; ++ return uv; ++} ++ ++/* todo: implement this*/ ++static void function_close (ktap_state *ks, StkId level) ++{ ++} ++ ++/* create a new closure */ ++static void pushclosure(ktap_state *ks, ktap_proto *p, ktap_upval **encup, ++ StkId base, StkId ra) ++{ ++ int nup = p->sizeupvalues; ++ ktap_upvaldesc *uv = p->upvalues; ++ int i; ++ ktap_closure *ncl = kp_newclosure(ks, nup); ++ ++ ncl->p = p; ++ set_closure(ra, ncl); /* anchor new closure in stack */ ++ ++ /* fill in its upvalues */ ++ for (i = 0; i < nup; i++) { ++ if (uv[i].instack) { ++ /* upvalue refers to local variable? */ ++ ncl->upvals[i] = findupval(ks, base + uv[i].idx); ++ } else { ++ /* get upvalue from enclosing function */ ++ ncl->upvals[i] = encup[uv[i].idx]; ++ } ++ } ++ //p->cache = ncl; /* save it on cache for reuse */ ++} ++ ++static void gettable(ktap_state *ks, const ktap_value *t, ktap_value *key, ++ StkId val) ++{ ++ if (is_table(t)) { ++ set_obj(val, kp_tab_get(hvalue(t), key)); ++ } else if (is_ptable(t)) { ++ kp_ptab_get(ks, phvalue(t), key, val); ++ } else { ++ kp_error(ks, "get key from non-table\n"); ++ } ++} ++ ++static void settable(ktap_state *ks, const ktap_value *t, ktap_value *key, ++ StkId val) ++{ ++ if (is_table(t)) { ++ kp_tab_setvalue(ks, hvalue(t), key, val); ++ } else if (is_ptable(t)) { ++ kp_ptab_set(ks, phvalue(t), key, val); ++ } else { ++ kp_error(ks, "set key to non-table\n"); ++ } ++} ++ ++static void settable_incr(ktap_state *ks, const ktap_value *t, ktap_value *key, ++ StkId val) ++{ ++ if (unlikely(!is_table(t))) { ++ kp_error(ks, "use += operator for non-table\n"); ++ return; ++ } ++ ++ if (unlikely(!is_number(val))) { ++ kp_error(ks, "use non-number to += operator\n"); ++ return; ++ } ++ ++ kp_tab_atomic_inc(ks, hvalue(t), key, nvalue(val)); ++} ++ ++static inline int checkstack(ktap_state *ks, int n) ++{ ++ if (unlikely(ks->stack_last - ks->top <= n)) { ++ kp_error(ks, "stack overflow, please enlarge stack size\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static StkId adjust_varargs(ktap_state *ks, ktap_proto *p, int actual) ++{ ++ int i; ++ int nfixargs = p->numparams; ++ StkId base, fixed; ++ ++ /* move fixed parameters to final position */ ++ fixed = ks->top - actual; /* first fixed argument */ ++ base = ks->top; /* final position of first argument */ ++ ++ for (i=0; i < nfixargs; i++) { ++ set_obj(ks->top++, fixed + i); ++ set_nil(fixed + i); ++ } ++ ++ return base; ++} ++ ++static int poscall(ktap_state *ks, StkId first_result) ++{ ++ ktap_callinfo *ci; ++ StkId res; ++ int wanted, i; ++ ++ ci = ks->ci; ++ ++ res = ci->func; ++ wanted = ci->nresults; ++ ++ ks->ci = ci = ci->prev; ++ ++ for (i = wanted; i != 0 && first_result < ks->top; i--) ++ set_obj(res++, first_result++); ++ ++ while(i-- > 0) ++ set_nil(res++); ++ ++ ks->top = res; ++ ++ return (wanted - (-1)); ++} ++ ++static ktap_callinfo *extend_ci(ktap_state *ks) ++{ ++ ktap_callinfo *ci; ++ ++ ci = kp_malloc(ks, sizeof(ktap_callinfo)); ++ ks->ci->next = ci; ++ ci->prev = ks->ci; ++ ci->next = NULL; ++ ++ return ci; ++} ++ ++static void free_ci(ktap_state *ks) ++{ ++ ktap_callinfo *ci = ks->ci; ++ ktap_callinfo *next; ++ ++ if (!ci) ++ return; ++ ++ next = ci->next; ++ ci->next = NULL; ++ while ((ci = next) != NULL) { ++ next = ci->next; ++ kp_free(ks, ci); ++ } ++} ++ ++#define next_ci(ks) (ks->ci = ks->ci->next ? ks->ci->next : extend_ci(ks)) ++#define savestack(ks, p) ((char *)(p) - (char *)ks->stack) ++#define restorestack(ks, n) ((ktap_value *)((char *)ks->stack + (n))) ++ ++static int precall(ktap_state *ks, StkId func, int nresults) ++{ ++ ktap_cfunction f; ++ ktap_callinfo *ci; ++ ktap_proto *p; ++#ifdef CONFIG_KTAP_FFI ++ ktap_cdata *cd; ++ csymbol *cs; ++#endif ++ StkId base; ++ ptrdiff_t funcr = savestack(ks, func); ++ int n; ++ ++ switch (ttype(func)) { ++ case KTAP_TCFUNCTION: /* light C function */ ++ f = fvalue(func); ++ ++ if (checkstack(ks, KTAP_MIN_RESERVED_STACK_SIZE)) ++ return 1; ++ ++ ci = next_ci(ks); ++ ci->nresults = nresults; ++ ci->func = restorestack(ks, funcr); ++ ci->top = ks->top + KTAP_MIN_RESERVED_STACK_SIZE; ++ ci->callstatus = 0; ++ n = (*f)(ks); ++ poscall(ks, ks->top - n); ++ return 1; ++ case KTAP_TCLOSURE: ++ p = clvalue(func)->p; ++ ++ if (checkstack(ks, p->maxstacksize)) ++ return 1; ++ ++ func = restorestack(ks, funcr); ++ n = (int)(ks->top - func) - 1; /* number of real arguments */ ++ ++ /* complete missing arguments */ ++ for (; n < p->numparams; n++) ++ set_nil(ks->top++); ++ ++ base = (!p->is_vararg) ? func + 1 : adjust_varargs(ks, p, n); ++ ci = next_ci(ks); ++ ci->nresults = nresults; ++ ci->func = func; ++ ci->u.l.base = base; ++ ci->top = base + p->maxstacksize; ++ ci->u.l.savedpc = p->code; /* starting point */ ++ ci->callstatus = CIST_KTAP; ++ ks->top = ci->top; ++ return 0; ++#ifdef CONFIG_KTAP_FFI ++ case KTAP_TCDATA: ++ cd = cdvalue(func); ++ ++ if (checkstack(ks, KTAP_MIN_RESERVED_STACK_SIZE)) ++ return 1; ++ ++ if (cd_type(ks, cd) != FFI_FUNC) ++ kp_error(ks, "Value in cdata is not a c funcion\n"); ++ cs = cd_csym(ks, cd); ++ kp_verbose_printf(ks, "calling ffi function [%s] with address %p\n", ++ csym_name(cs), csym_func_addr(cs)); ++ ++ ci = next_ci(ks); ++ ci->nresults = nresults; ++ ci->func = restorestack(ks, funcr); ++ ci->top = ks->top + KTAP_MIN_RESERVED_STACK_SIZE; ++ ci->callstatus = 0; ++ ++ n = kp_ffi_call(ks, csym_func(cs)); ++ kp_verbose_printf(ks, "returned from ffi call...\n"); ++ poscall(ks, ks->top - n); ++ return 1; ++#endif ++ default: ++ kp_error(ks, "attempt to call nil function\n"); ++ } ++ ++ return 0; ++} ++ ++#define RA(i) (base+GETARG_A(i)) ++#define RB(i) (base+GETARG_B(i)) ++#define ISK(x) ((x) & BITRK) ++#define RC(i) base+GETARG_C(i) ++#define RKB(i) \ ++ ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i) ++#define RKC(i) \ ++ ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i) ++ ++#define dojump(ci,i,e) { \ ++ ci->u.l.savedpc += GETARG_sBx(i) + e; } ++#define donextjump(ci) { instr = *ci->u.l.savedpc; dojump(ci, instr, 1); } ++ ++#define arith_op(ks, op) { \ ++ ktap_value *rb = RKB(instr); \ ++ ktap_value *rc = RKC(instr); \ ++ if (is_number(rb) && is_number(rc)) { \ ++ ktap_number nb = nvalue(rb), nc = nvalue(rc); \ ++ set_number(ra, op(nb, nc)); \ ++ } else { \ ++ kp_puts(ks, "Error: Cannot make arith operation\n"); \ ++ return; \ ++ } } ++ ++static ktap_value *cfunction_cache_get(ktap_state *ks, int index); ++ ++static void ktap_execute(ktap_state *ks) ++{ ++ int exec_count = 0; ++ ktap_callinfo *ci; ++ ktap_closure *cl; ++ ktap_value *k; ++ unsigned int instr, opcode; ++ StkId base; /* stack pointer */ ++ StkId ra; /* register pointer */ ++ int res, nresults; /* temp varible */ ++ ++ ci = ks->ci; ++ ++ newframe: ++ cl = clvalue(ci->func); ++ k = cl->p->k; ++ base = ci->u.l.base; ++ ++ mainloop: ++ /* main loop of interpreter */ ++ ++ /* dead loop detaction */ ++ if (exec_count++ == kp_max_exec_count) { ++ if (G(ks)->mainthread != ks) { ++ kp_error(ks, "non-mainthread executed instructions " ++ "exceed max limit(%d)\n", ++ kp_max_exec_count); ++ return; ++ } ++ ++ cond_resched(); ++ if (signal_pending(current)) { ++ flush_signals(current); ++ return; ++ } ++ exec_count = 0; ++ } ++ ++ instr = *(ci->u.l.savedpc++); ++ opcode = GET_OPCODE(instr); ++ ++ /* ra is target register */ ++ ra = RA(instr); ++ ++ switch (opcode) { ++ case OP_MOVE: ++ set_obj(ra, base + GETARG_B(instr)); ++ break; ++ case OP_LOADK: ++ set_obj(ra, k + GETARG_Bx(instr)); ++ break; ++ case OP_LOADKX: ++ set_obj(ra, k + GETARG_Ax(*ci->u.l.savedpc++)); ++ break; ++ case OP_LOADBOOL: ++ set_boolean(ra, GETARG_B(instr)); ++ if (GETARG_C(instr)) ++ ci->u.l.savedpc++; ++ break; ++ case OP_LOADNIL: { ++ int b = GETARG_B(instr); ++ do { ++ set_nil(ra++); ++ } while (b--); ++ break; ++ } ++ case OP_GETUPVAL: { ++ int b = GETARG_B(instr); ++ set_obj(ra, cl->upvals[b]->v); ++ break; ++ } ++ case OP_GETTABUP: { ++ int b = GETARG_B(instr); ++ gettable(ks, cl->upvals[b]->v, RKC(instr), ra); ++ base = ci->u.l.base; ++ break; ++ } ++ case OP_GETTABLE: ++ gettable(ks, RB(instr), RKC(instr), ra); ++ base = ci->u.l.base; ++ break; ++ case OP_SETTABUP: { ++ int a = GETARG_A(instr); ++ settable(ks, cl->upvals[a]->v, RKB(instr), RKC(instr)); ++ base = ci->u.l.base; ++ break; ++ } ++ case OP_SETTABUP_INCR: { ++ int a = GETARG_A(instr); ++ settable_incr(ks, cl->upvals[a]->v, RKB(instr), RKC(instr)); ++ base = ci->u.l.base; ++ break; ++ } ++ case OP_SETTABUP_AGGR: { ++ int a = GETARG_A(instr); ++ ktap_value *v = cl->upvals[a]->v; ++ if (!is_ptable(v)) { ++ kp_error(ks, "<<< must be operate on ptable\n"); ++ return; ++ } ++ ++ kp_ptab_set(ks, phvalue(v), RKB(instr), RKC(instr)); ++ base = ci->u.l.base; ++ break; ++ } ++ case OP_SETUPVAL: { ++ ktap_upval *uv = cl->upvals[GETARG_B(instr)]; ++ set_obj(uv->v, ra); ++ break; ++ } ++ case OP_SETTABLE: ++ settable(ks, ra, RKB(instr), RKC(instr)); ++ base = ci->u.l.base; ++ break; ++ case OP_SETTABLE_INCR: ++ settable_incr(ks, ra, RKB(instr), RKC(instr)); ++ base = ci->u.l.base; ++ break; ++ case OP_SETTABLE_AGGR: ++ if (!is_ptable(ra)) { ++ kp_error(ks, "<<< must be operate on ptable\n"); ++ return; ++ } ++ ++ kp_ptab_set(ks, phvalue(ra), RKB(instr), RKC(instr)); ++ base = ci->u.l.base; ++ break; ++ case OP_NEWTABLE: { ++ int b = GETARG_B(instr); ++ int c = GETARG_C(instr); ++ ktap_tab *t = kp_tab_new(ks); ++ set_table(ra, t); ++ if (b != 0 || c != 0) ++ kp_tab_resize(ks, t, fb2int(b), fb2int(c)); ++ break; ++ } ++ case OP_SELF: { ++ StkId rb = RB(instr); ++ set_obj(ra+1, rb); ++ gettable(ks, rb, RKC(instr), ra); ++ base = ci->u.l.base; ++ break; ++ } ++ case OP_ADD: ++ arith_op(ks, NUMADD); ++ break; ++ case OP_SUB: ++ arith_op(ks, NUMSUB); ++ break; ++ case OP_MUL: ++ arith_op(ks, NUMMUL); ++ break; ++ case OP_DIV: ++ /* divide 0 checking */ ++ if (!nvalue(RKC(instr))) { ++ kp_error(ks, "divide 0 arith operation\n"); ++ return; ++ } ++ arith_op(ks, NUMDIV); ++ break; ++ case OP_MOD: ++ /* divide 0 checking */ ++ if (!nvalue(RKC(instr))) { ++ kp_error(ks, "mod 0 arith operation\n"); ++ return; ++ } ++ arith_op(ks, NUMMOD); ++ break; ++ case OP_POW: ++ kp_error(ks, "ktap don't support pow arith in kernel\n"); ++ return; ++ case OP_UNM: { ++ ktap_value *rb = RB(instr); ++ if (is_number(rb)) { ++ ktap_number nb = nvalue(rb); ++ set_number(ra, NUMUNM(nb)); ++ } ++ break; ++ } ++ case OP_NOT: ++ res = is_false(RB(instr)); ++ set_boolean(ra, res); ++ break; ++ case OP_LEN: { ++ int len = kp_objlen(ks, RB(instr)); ++ if (len < 0) ++ return; ++ set_number(ra, len); ++ break; ++ } ++ case OP_CONCAT: { ++ int b = GETARG_B(instr); ++ int c = GETARG_C(instr); ++ ktap_concat(ks, b, c); ++ break; ++ } ++ case OP_JMP: ++ dojump(ci, instr, 0); ++ break; ++ case OP_EQ: { ++ ktap_value *rb = RKB(instr); ++ ktap_value *rc = RKC(instr); ++ if ((int)rawequalobj(rb, rc) != GETARG_A(instr)) ++ ci->u.l.savedpc++; ++ else ++ donextjump(ci); ++ ++ base = ci->u.l.base; ++ break; ++ } ++ case OP_LT: { ++ if (lessthan(ks, RKB(instr), RKC(instr)) != GETARG_A(instr)) { ++ ci->u.l.savedpc++; ++ } else ++ donextjump(ci); ++ base = ci->u.l.base; ++ break; ++ } ++ case OP_LE: ++ if (lessequal(ks, RKB(instr), RKC(instr)) != GETARG_A(instr)) ++ ci->u.l.savedpc++; ++ else ++ donextjump(ci); ++ base = ci->u.l.base; ++ break; ++ case OP_TEST: ++ if (GETARG_C(instr) ? is_false(ra) : !is_false(ra)) ++ ci->u.l.savedpc++; ++ else ++ donextjump(ci); ++ break; ++ case OP_TESTSET: { ++ ktap_value *rb = RB(instr); ++ if (GETARG_C(instr) ? is_false(rb) : !is_false(rb)) ++ ci->u.l.savedpc++; ++ else { ++ set_obj(ra, rb); ++ donextjump(ci); ++ } ++ break; ++ } ++ case OP_CALL: { ++ int b = GETARG_B(instr); ++ int ret; ++ ++ nresults = GETARG_C(instr) - 1; ++ ++ if (b != 0) ++ ks->top = ra + b; ++ ++ ret = precall(ks, ra, nresults); ++ if (ret) { /* C function */ ++ if (nresults >= 0) ++ ks->top = ci->top; ++ base = ci->u.l.base; ++ break; ++ } else { /* ktap function */ ++ ci = ks->ci; ++ /* this flag is used for return time, see OP_RETURN */ ++ ci->callstatus |= CIST_REENTRY; ++ goto newframe; ++ } ++ break; ++ } ++ case OP_TAILCALL: { ++ int b = GETARG_B(instr); ++ ++ if (b != 0) ++ ks->top = ra+b; ++ if (precall(ks, ra, -1)) /* C function? */ ++ base = ci->u.l.base; ++ else { ++ int aux; ++ ++ /* ++ * tail call: put called frame (n) in place of ++ * caller one (o) ++ */ ++ ktap_callinfo *nci = ks->ci; /* called frame */ ++ ktap_callinfo *oci = nci->prev; /* caller frame */ ++ StkId nfunc = nci->func; /* called function */ ++ StkId ofunc = oci->func; /* caller function */ ++ /* last stack slot filled by 'precall' */ ++ StkId lim = nci->u.l.base + ++ clvalue(nfunc)->p->numparams; ++ ++ /* close all upvalues from previous call */ ++ if (cl->p->sizep > 0) ++ function_close(ks, oci->u.l.base); ++ ++ /* move new frame into old one */ ++ for (aux = 0; nfunc + aux < lim; aux++) ++ set_obj(ofunc + aux, nfunc + aux); ++ /* correct base */ ++ oci->u.l.base = ofunc + (nci->u.l.base - nfunc); ++ /* correct top */ ++ oci->top = ks->top = ofunc + (ks->top - nfunc); ++ oci->u.l.savedpc = nci->u.l.savedpc; ++ /* remove new frame */ ++ ci = ks->ci = oci; ++ /* restart ktap_execute over new ktap function */ ++ goto newframe; ++ } ++ break; ++ } ++ case OP_RETURN: { ++ int b = GETARG_B(instr); ++ if (b != 0) ++ ks->top = ra+b-1; ++ if (cl->p->sizep > 0) ++ function_close(ks, base); ++ b = poscall(ks, ra); ++ ++ /* if it's called from external invocation, just return */ ++ if (!(ci->callstatus & CIST_REENTRY)) ++ return; ++ ++ ci = ks->ci; ++ if (b) ++ ks->top = ci->top; ++ goto newframe; ++ } ++ case OP_FORLOOP: { ++ ktap_number step = nvalue(ra+2); ++ /* increment index */ ++ ktap_number idx = NUMADD(nvalue(ra), step); ++ ktap_number limit = nvalue(ra+1); ++ if (NUMLT(0, step) ? NUMLE(idx, limit) : NUMLE(limit, idx)) { ++ ci->u.l.savedpc += GETARG_sBx(instr); /* jump back */ ++ set_number(ra, idx); /* update internal index... */ ++ set_number(ra+3, idx); /* ...and external index */ ++ } ++ break; ++ } ++ case OP_FORPREP: { ++ const ktap_value *init = ra; ++ const ktap_value *plimit = ra + 1; ++ const ktap_value *pstep = ra + 2; ++ ++ if (!ktap_tonumber(init, ra)) { ++ kp_error(ks, KTAP_QL("for") ++ " initial value must be a number\n"); ++ return; ++ } else if (!ktap_tonumber(plimit, ra + 1)) { ++ kp_error(ks, KTAP_QL("for") ++ " limit must be a number\n"); ++ return; ++ } else if (!ktap_tonumber(pstep, ra + 2)) { ++ kp_error(ks, KTAP_QL("for") " step must be a number\n"); ++ return; ++ } ++ ++ set_number(ra, NUMSUB(nvalue(ra), nvalue(pstep))); ++ ci->u.l.savedpc += GETARG_sBx(instr); ++ break; ++ } ++ case OP_TFORCALL: { ++ StkId cb = ra + 3; /* call base */ ++ set_obj(cb + 2, ra + 2); ++ set_obj(cb + 1, ra + 1); ++ set_obj(cb, ra); ++ ks->top = cb + 3; /* func. + 2 args (state and index) */ ++ kp_call(ks, cb, GETARG_C(instr)); ++ base = ci->u.l.base; ++ ks->top = ci->top; ++ instr = *(ci->u.l.savedpc++); /* go to next instruction */ ++ ra = RA(instr); ++ } ++ /*go through */ ++ case OP_TFORLOOP: ++ if (!is_nil(ra + 1)) { /* continue loop? */ ++ set_obj(ra, ra + 1); /* save control variable */ ++ ci->u.l.savedpc += GETARG_sBx(instr); /* jump back */ ++ } ++ break; ++ case OP_SETLIST: { ++ int n = GETARG_B(instr); ++ int c = GETARG_C(instr); ++ int last; ++ ktap_tab *h; ++ ++ if (n == 0) ++ n = (int)(ks->top - ra) - 1; ++ if (c == 0) ++ c = GETARG_Ax(*ci->u.l.savedpc++); ++ ++ h = hvalue(ra); ++ last = ((c - 1) * LFIELDS_PER_FLUSH) + n; ++ if (last > h->sizearray) /* needs more space? */ ++ kp_tab_resizearray(ks, h, last); ++ ++ for (; n > 0; n--) { ++ ktap_value *val = ra+n; ++ kp_tab_setint(ks, h, last--, val); ++ } ++ /* correct top (in case of previous open call) */ ++ ks->top = ci->top; ++ break; ++ } ++ case OP_CLOSURE: { ++ /* need to use closure cache? (multithread contention issue)*/ ++ ktap_proto *p = cl->p->p[GETARG_Bx(instr)]; ++ pushclosure(ks, p, cl->upvals, base, ra); ++ break; ++ } ++ case OP_VARARG: { ++ int b = GETARG_B(instr) - 1; ++ int j; ++ int n = (int)(base - ci->func) - cl->p->numparams - 1; ++ if (b < 0) { /* B == 0? */ ++ b = n; /* get all var. arguments */ ++ if(checkstack(ks, n)) ++ return; ++ /* previous call may change the stack */ ++ ra = RA(instr); ++ ks->top = ra + n; ++ } ++ for (j = 0; j < b; j++) { ++ if (j < n) { ++ set_obj(ra + j, base - n + j); ++ } else ++ set_nil(ra + j); ++ } ++ break; ++ } ++ case OP_EXTRAARG: ++ return; ++ ++ case OP_EVENT: { ++ struct ktap_event *e = ks->current_event; ++ ++ if (unlikely(!e)) { ++ kp_error(ks, "invalid event context\n"); ++ return; ++ } ++ set_event(ra, e); ++ break; ++ } ++ ++ case OP_EVENTNAME: { ++ struct ktap_event *e = ks->current_event; ++ ++ if (unlikely(!e)) { ++ kp_error(ks, "invalid event context\n"); ++ return; ++ } ++ set_string(ra, kp_tstring_new(ks, e->call->name)); ++ break; ++ } ++ case OP_EVENTARG: ++ if (unlikely(!ks->current_event)) { ++ kp_error(ks, "invalid event context\n"); ++ return; ++ } ++ ++ kp_event_getarg(ks, ra, GETARG_B(instr)); ++ break; ++ case OP_LOAD_GLOBAL: { ++ ktap_value *cfunc = cfunction_cache_get(ks, GETARG_C(instr)); ++ set_obj(ra, cfunc); ++ } ++ break; ++ ++ case OP_EXIT: ++ return; ++ } ++ ++ goto mainloop; ++} ++ ++void kp_call(ktap_state *ks, StkId func, int nresults) ++{ ++ if (!precall(ks, func, nresults)) ++ ktap_execute(ks); ++} ++ ++static int cfunction_cache_getindex(ktap_state *ks, ktap_value *fname); ++ ++/* ++ * This function must be called before all code loaded. ++ */ ++void kp_optimize_code(ktap_state *ks, int level, ktap_proto *f) ++{ ++ int i; ++ ++ for (i = 0; i < f->sizecode; i++) { ++ int instr = f->code[i]; ++ ktap_value *k = f->k; ++ ++ if (GET_OPCODE(instr) == OP_GETTABUP) { ++ if ((GETARG_B(instr) == 0) && ISK(GETARG_C(instr))) { ++ ktap_value *field = k + INDEXK(GETARG_C(instr)); ++ if (ttype(field) == KTAP_TSTRING) { ++ int index = cfunction_cache_getindex(ks, ++ field); ++ if (index == -1) ++ break; ++ ++ SET_OPCODE(instr, OP_LOAD_GLOBAL); ++ SETARG_C(instr, index); ++ f->code[i] = instr; ++ break; ++ } ++ } ++ } ++ } ++ ++ /* continue optimize sub functions */ ++ for (i = 0; i < f->sizep; i++) ++ kp_optimize_code(ks, level + 1, f->p[i]); ++} ++ ++static ktap_value *cfunction_cache_get(ktap_state *ks, int index) ++{ ++ return &G(ks)->cfunction_tbl[index]; ++} ++ ++static int cfunction_cache_getindex(ktap_state *ks, ktap_value *fname) ++{ ++ const ktap_value *gt = kp_tab_getint(hvalue(&G(ks)->registry), ++ KTAP_RIDX_GLOBALS); ++ const ktap_value *cfunc; ++ int nr, i; ++ ++ nr = G(ks)->nr_builtin_cfunction; ++ cfunc = kp_tab_get(hvalue(gt), fname); ++ ++ for (i = 0; i < nr; i++) { ++ if (rawequalobj(&G(ks)->cfunction_tbl[i], cfunc)) ++ return i; ++ } ++ ++ return -1; ++} ++ ++static void cfunction_cache_add(ktap_state *ks, ktap_value *func) ++{ ++ int nr = G(ks)->nr_builtin_cfunction; ++ set_obj(&G(ks)->cfunction_tbl[nr], func); ++ G(ks)->nr_builtin_cfunction++; ++} ++ ++static void cfunction_cache_exit(ktap_state *ks) ++{ ++ kp_free(ks, G(ks)->cfunction_tbl); ++} ++ ++static int cfunction_cache_init(ktap_state *ks) ++{ ++ G(ks)->cfunction_tbl = kp_zalloc(ks, sizeof(ktap_value) * 128); ++ if (!G(ks)->cfunction_tbl) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++/* function for register library */ ++void kp_register_lib(ktap_state *ks, const char *libname, const ktap_Reg *funcs) ++{ ++ int i; ++ ktap_tab *target_tbl; ++ const ktap_value *gt = kp_tab_getint(hvalue(&G(ks)->registry), ++ KTAP_RIDX_GLOBALS); ++ ++ /* lib is null when register baselib function */ ++ if (libname == NULL) ++ target_tbl = hvalue(gt); ++ else { ++ ktap_value key, val; ++ ++ target_tbl = kp_tab_new(ks); ++ kp_tab_resize(ks, target_tbl, 0, ++ sizeof(*funcs) / sizeof(ktap_Reg)); ++ ++ set_string(&key, kp_tstring_new(ks, libname)); ++ set_table(&val, target_tbl); ++ kp_tab_setvalue(ks, hvalue(gt), &key, &val); ++ } ++ ++ for (i = 0; funcs[i].name != NULL; i++) { ++ ktap_value func_name, cl; ++ ++ set_string(&func_name, kp_tstring_new(ks, funcs[i].name)); ++ set_cfunction(&cl, funcs[i].func); ++ kp_tab_setvalue(ks, target_tbl, &func_name, &cl); ++ ++ cfunction_cache_add(ks, &cl); ++ } ++} ++ ++static void kp_init_registry(ktap_state *ks) ++{ ++ ktap_value mt; ++ ktap_tab *registry = kp_tab_new(ks); ++ ++ set_table(&G(ks)->registry, registry); ++ kp_tab_resize(ks, registry, KTAP_RIDX_LAST, 0); ++ set_thread(&mt, ks); ++ kp_tab_setint(ks, registry, KTAP_RIDX_MAINTHREAD, &mt); ++ set_table(&mt, kp_tab_new(ks)); ++ kp_tab_setint(ks, registry, KTAP_RIDX_GLOBALS, &mt); ++} ++ ++static int kp_init_arguments(ktap_state *ks, int argc, char __user **user_argv) ++{ ++ const ktap_value *gt = kp_tab_getint(hvalue(&G(ks)->registry), ++ KTAP_RIDX_GLOBALS); ++ ktap_tab *global_tbl = hvalue(gt); ++ ktap_tab *arg_tbl = kp_tab_new(ks); ++ ktap_value arg_tblval; ++ ktap_value arg_tsval; ++ char **argv; ++ int i, ret; ++ ++ set_string(&arg_tsval, kp_tstring_new(ks, "arg")); ++ set_table(&arg_tblval, arg_tbl); ++ kp_tab_setvalue(ks, global_tbl, &arg_tsval, &arg_tblval); ++ ++ if (!argc) ++ return 0; ++ ++ if (argc > 1024) ++ return -EINVAL; ++ ++ argv = kzalloc(argc * sizeof(char *), GFP_KERNEL); ++ if (!argv) ++ return -ENOMEM; ++ ++ ret = copy_from_user(argv, user_argv, argc * sizeof(char *)); ++ if (ret < 0) { ++ kfree(argv); ++ return -EFAULT; ++ } ++ ++ kp_tab_resize(ks, arg_tbl, argc, 1); ++ ++ ret = 0; ++ for (i = 0; i < argc; i++) { ++ ktap_value val; ++ char __user *ustr = argv[i]; ++ char *kstr; ++ int len; ++ int res; ++ ++ len = strlen_user(ustr); ++ if (len > 0x1000) { ++ ret = -EINVAL; ++ break; ++ } ++ ++ kstr = kmalloc(len + 1, GFP_KERNEL); ++ if (!kstr) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ if (strncpy_from_user(kstr, ustr, len) < 0) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ kstr[len] = '\0'; ++ ++ if (!kstrtoint(kstr, 10, &res)) { ++ set_number(&val, res); ++ } else ++ set_string(&val, kp_tstring_new(ks, kstr)); ++ ++ kp_tab_setint(ks, arg_tbl, i, &val); ++ ++ kfree(kstr); ++ } ++ ++ kfree(argv); ++ return ret; ++} ++ ++static void free_kp_percpu_data(ktap_state *ks) ++{ ++ int i, j; ++ ++ for (i = 0; i < KTAP_PERCPU_DATA_MAX; i++) { ++ for (j = 0; j < PERF_NR_CONTEXTS; j++) ++ free_percpu(G(ks)->pcpu_data[i][j]); ++ } ++ ++ for (j = 0; j < PERF_NR_CONTEXTS; j++) ++ if (G(ks)->recursion_context[j]) ++ free_percpu(G(ks)->recursion_context[j]); ++} ++ ++static int alloc_kp_percpu_data(ktap_state *ks) ++{ ++ int data_size[KTAP_PERCPU_DATA_MAX] = { ++ sizeof(ktap_state), KTAP_STACK_SIZE_BYTES, ++ KTAP_PERCPU_BUFFER_SIZE, KTAP_PERCPU_BUFFER_SIZE, ++ sizeof(ktap_btrace) + (KTAP_MAX_STACK_ENTRIES * ++ sizeof(unsigned long))}; ++ int i, j; ++ ++ for (i = 0; i < KTAP_PERCPU_DATA_MAX; i++) { ++ for (j = 0; j < PERF_NR_CONTEXTS; j++) { ++ void __percpu *data = __alloc_percpu(data_size[i], ++ __alignof__(char)); ++ if (!data) ++ goto fail; ++ G(ks)->pcpu_data[i][j] = data; ++ } ++ } ++ ++ for (j = 0; j < PERF_NR_CONTEXTS; j++) { ++ G(ks)->recursion_context[j] = alloc_percpu(int); ++ if (!G(ks)->recursion_context[j]) ++ goto fail; ++ } ++ ++ return 0; ++ ++ fail: ++ free_kp_percpu_data(ks); ++ return -ENOMEM; ++} ++ ++static void kp_init_state(ktap_state *ks) ++{ ++ ktap_callinfo *ci; ++ ++ /* init all stack vaule to nil */ ++ memset(ks->stack, 0, KTAP_STACK_SIZE_BYTES); ++ ++ ks->top = ks->stack; ++ ks->stack_last = ks->stack + KTAP_STACK_SIZE; ++ ++ ci = &ks->baseci; ++ ci->callstatus = 0; ++ ci->func = ks->top; ++ ci->top = ks->top + KTAP_MIN_RESERVED_STACK_SIZE; ++ ks->ci = ci; ++} ++ ++static void free_all_ci(ktap_state *ks) ++{ ++ int cpu, j; ++ ++ for_each_possible_cpu(cpu) { ++ for (j = 0; j < PERF_NR_CONTEXTS; j++) { ++ void *pcd = G(ks)->pcpu_data[KTAP_PERCPU_DATA_STATE][j]; ++ ktap_state *ks; ++ ++ if (!pcd) ++ break; ++ ++ ks = per_cpu_ptr(pcd, cpu); ++ if (!ks) ++ break; ++ ++ free_ci(ks); ++ } ++ } ++ ++ free_ci(ks); ++} ++ ++void kp_exitthread(ktap_state *ks) ++{ ++ /* free local allocation objects, like annotate strings */ ++ kp_free_gclist(ks, ks->gclist); ++} ++ ++ktap_state *kp_newthread(ktap_state *mainthread) ++{ ++ ktap_state *ks; ++ ++ ks = kp_percpu_data(mainthread, KTAP_PERCPU_DATA_STATE); ++ ks->stack = kp_percpu_data(mainthread, KTAP_PERCPU_DATA_STACK); ++ G(ks) = G(mainthread); ++ ks->gclist = NULL; ++ kp_init_state(ks); ++ return ks; ++} ++ ++/* ++ * wait ktapio thread read all content in ring buffer. ++ * ++ * Here we use stupid approach to sync with ktapio thread, ++ * note that we cannot use semaphore/completion/other sync method, ++ * because ktapio thread could be killed by SIG_KILL in anytime, there ++ * have no safe way to up semaphore or wake waitqueue before thread exit. ++ * ++ * we also cannot use waitqueue of current->signal->wait_chldexit to sync ++ * exit, becasue mainthread and ktapio thread are in same thread group. ++ * ++ * Also ktap mainthread must wait ktapio thread exit, otherwise ktapio ++ * thread will oops when access ktap structure. ++ */ ++static void wait_user_completion(ktap_state *ks) ++{ ++ struct task_struct *tsk = G(ks)->task; ++ G(ks)->wait_user = 1; ++ ++ while (1) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ /* sleep for 100 msecs, and try again. */ ++ schedule_timeout(HZ / 10); ++ ++ if (get_nr_threads(tsk) == 1) ++ break; ++ } ++} ++ ++static void sleep_loop(ktap_state *ks, ++ int (*actor)(ktap_state *ks, void *arg), void *arg) ++{ ++ while (!ks->stop) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ /* sleep for 100 msecs, and try again. */ ++ schedule_timeout(HZ / 10); ++ ++ if (actor(ks, arg)) ++ return; ++ } ++} ++ ++static int sl_wait_task_pause_actor(ktap_state *ks, void *arg) ++{ ++ struct task_struct *task = (struct task_struct *)arg; ++ ++ if (task->state) ++ return 1; ++ else ++ return 0; ++} ++ ++static int sl_wait_task_exit_actor(ktap_state *ks, void *arg) ++{ ++ struct task_struct *task = (struct task_struct *)arg; ++ ++ if (signal_pending(current)) { ++ flush_signals(current); ++ ++ /* newline for handle CTRL+C display as ^C */ ++ kp_puts(ks, "\n"); ++ return 1; ++ } ++ ++ /* stop waiting if target pid is exited */ ++ if (task && task->state == TASK_DEAD) ++ return 1; ++ ++ return 0; ++} ++ ++/* kp_wait: used for mainthread waiting for exit */ ++static void kp_wait(ktap_state *ks) ++{ ++ struct task_struct *task = G(ks)->trace_task; ++ ++ if (G(ks)->exit) ++ return; ++ ++ ks->stop = 0; ++ ++ if (G(ks)->parm->workload) { ++ /* make sure workload is in pause state ++ * so it won't miss the signal */ ++ sleep_loop(ks, sl_wait_task_pause_actor, task); ++ /* tell workload process to start executing */ ++ send_sig(SIGINT, G(ks)->trace_task, 0); ++ } ++ ++ if (!G(ks)->parm->quiet) ++ kp_printf(ks, "Tracing... Hit Ctrl-C to end.\n"); ++ ++ sleep_loop(ks, sl_wait_task_exit_actor, task); ++} ++ ++static unsigned int kp_stub_exit_instr; ++ ++static inline void set_next_as_exit(ktap_state *ks) ++{ ++ ktap_callinfo *ci; ++ ++ ci = ks->ci; ++ if (!ci) ++ return; ++ ++ ci->u.l.savedpc = &kp_stub_exit_instr; ++ ++ /* See precall, ci changed to ci->prev after invoke C function */ ++ if (ci->prev) { ++ ci = ci->prev; ++ ci->u.l.savedpc = &kp_stub_exit_instr; ++ } ++} ++ ++void kp_exit(ktap_state *ks) ++{ ++ set_next_as_exit(ks); ++ ++ G(ks)->mainthread->stop = 1; ++ G(ks)->exit = 1; ++} ++ ++void kp_init_exit_instruction(void) ++{ ++ SET_OPCODE(kp_stub_exit_instr, OP_EXIT); ++} ++ ++/* ++ * Be careful in stats_cleanup, only can use kp_printf, since almost ++ * all ktap resources already freed now. ++ */ ++static void kp_stats_cleanup(ktap_state *ks) ++{ ++ ktap_stats __percpu *stats = G(ks)->stats; ++ int mem_allocated = 0, nr_mem_allocate = 0, nr_mem_free = 0; ++ int events_hits = 0, events_missed = 0; ++ int cpu; ++ ++ for_each_possible_cpu(cpu) { ++ ktap_stats *per_stats = per_cpu_ptr(stats, cpu); ++ mem_allocated += per_stats->mem_allocated; ++ nr_mem_allocate += per_stats->nr_mem_allocate; ++ nr_mem_free += per_stats->nr_mem_free; ++ events_hits += per_stats->events_hits; ++ events_missed += per_stats->events_missed; ++ } ++ ++ kp_verbose_printf(ks, "ktap stats:\n"); ++ kp_verbose_printf(ks, "memory allocated size: %d\n", mem_allocated); ++ kp_verbose_printf(ks, "memory allocate num: %d\n", nr_mem_allocate); ++ kp_verbose_printf(ks, "memory free num: %d\n", nr_mem_free); ++ kp_verbose_printf(ks, "events_hits: %d\n", events_hits); ++ kp_verbose_printf(ks, "events_missed: %d\n", events_missed); ++ ++ if (stats) ++ free_percpu(stats); ++} ++ ++static int kp_stats_init(ktap_state *ks) ++{ ++ ktap_stats __percpu *stats = alloc_percpu(ktap_stats); ++ if (!stats) ++ return -ENOMEM; ++ ++ G(ks)->stats = stats; ++ return 0; ++} ++ ++void kp_final_exit(ktap_state *ks) ++{ ++ if (!list_empty(&G(ks)->probe_events_head) || ++ !list_empty(&G(ks)->timers)) ++ kp_wait(ks); ++ ++ kp_exit_timers(ks); ++ kp_probe_exit(ks); ++ ++ /* free all resources got by ktap */ ++ kp_ffi_free_symbol(ks); ++ kp_tstring_freeall(ks); ++ kp_free_all_gcobject(ks); ++ cfunction_cache_exit(ks); ++ ++ kp_exitthread(ks); ++ kp_free(ks, ks->stack); ++ free_all_ci(ks); ++ ++ free_kp_percpu_data(ks); ++ free_cpumask_var(G(ks)->cpumask); ++ ++ kp_stats_cleanup(ks); ++ wait_user_completion(ks); ++ ++ /* should invoke after wait_user_completion */ ++ if (G(ks)->trace_task) ++ put_task_struct(G(ks)->trace_task); ++ ++ kp_transport_exit(ks); ++ kp_free(ks, ks); ++} ++ ++/* ktap mainthread initization, main entry for ktap */ ++ktap_state *kp_newstate(ktap_parm *parm, struct dentry *dir) ++{ ++ ktap_state *ks; ++ pid_t pid; ++ int cpu; ++ ++ ks = kzalloc(sizeof(ktap_state) + sizeof(ktap_global_state), ++ GFP_KERNEL); ++ if (!ks) ++ return NULL; ++ ++ G(ks) = (ktap_global_state *)(ks + 1); ++ G(ks)->mainthread = ks; ++ G(ks)->seed = 201236; /* todo: make more random in future */ ++ G(ks)->task = current; ++ G(ks)->parm = parm; ++ G(ks)->str_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; ++ INIT_LIST_HEAD(&(G(ks)->timers)); ++ INIT_LIST_HEAD(&(G(ks)->probe_events_head)); ++ G(ks)->exit = 0; ++ ++ if (kp_stats_init(ks)) ++ goto out; ++ ++ if (kp_transport_init(ks, dir)) ++ goto out; ++ ++ ks->stack = kp_malloc(ks, KTAP_STACK_SIZE_BYTES); ++ ++ pid = (pid_t)parm->trace_pid; ++ if (pid != -1) { ++ struct task_struct *task; ++ ++ rcu_read_lock(); ++ task = pid_task(find_vpid(pid), PIDTYPE_PID); ++ if (!task) { ++ kp_error(ks, "cannot find pid %d\n", pid); ++ rcu_read_unlock(); ++ goto out; ++ } ++ G(ks)->trace_task = task; ++ get_task_struct(task); ++ rcu_read_unlock(); ++ } ++ ++ if( !alloc_cpumask_var(&G(ks)->cpumask, GFP_KERNEL)) ++ goto out; ++ ++ cpumask_copy(G(ks)->cpumask, cpu_online_mask); ++ ++ cpu = parm->trace_cpu; ++ if (cpu != -1) { ++ if (!cpu_online(cpu)) { ++ kp_error(ks, "ktap: cpu %d is not online\n", cpu); ++ goto out; ++ } ++ ++ cpumask_clear(G(ks)->cpumask); ++ cpumask_set_cpu(cpu, G(ks)->cpumask); ++ } ++ ++ if (cfunction_cache_init(ks)) ++ goto out; ++ ++ kp_tstring_resize(ks, 512); /* set inital string hashtable size */ ++ ++ kp_init_state(ks); ++ kp_init_registry(ks); ++ kp_init_arguments(ks, parm->argc, parm->argv); ++ ++ /* init library */ ++ kp_init_baselib(ks); ++ kp_init_kdebuglib(ks); ++ kp_init_timerlib(ks); ++ kp_init_ansilib(ks); ++ kp_init_ffilib(ks); ++ ++ if (alloc_kp_percpu_data(ks)) ++ goto out; ++ ++ if (kp_probe_init(ks)) ++ goto out; ++ ++ return ks; ++ ++ out: ++ G(ks)->exit = 1; ++ kp_final_exit(ks); ++ return NULL; ++} ++ +diff --git a/drivers/staging/ktap/runtime/kp_vm.h b/drivers/staging/ktap/runtime/kp_vm.h +new file mode 100644 +index 0000000..3c7dfa5 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/kp_vm.h +@@ -0,0 +1,16 @@ ++#ifndef __KTAP_VM_H__ ++#define __KTAP_VM_H__ ++ ++int gettimeofday_us(void); /* common helper function */ ++ktap_state *kp_newstate(struct ktap_parm *parm, struct dentry *dir); ++void kp_exit(ktap_state *ks); ++void kp_init_exit_instruction(void); ++void kp_final_exit(ktap_state *ks); ++ktap_state *kp_newthread(ktap_state *mainthread); ++void kp_exitthread(ktap_state *ks); ++void kp_call(ktap_state *ks, StkId func, int nresults); ++void kp_optimize_code(ktap_state *ks, int level, ktap_proto *f); ++void kp_register_lib(ktap_state *ks, const char *libname, ++ const ktap_Reg *funcs); ++ ++#endif /* __KTAP_VM_H__ */ +diff --git a/drivers/staging/ktap/runtime/ktap.c b/drivers/staging/ktap/runtime/ktap.c +new file mode 100644 +index 0000000..3df1928 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/ktap.c +@@ -0,0 +1,217 @@ ++/* ++ * ktap.c - ktapvm kernel module main entry ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++/* ++ * this file is the first file to be compile, add CONFIG_ checking in here. ++ * See Requirements in doc/introduction.txt ++ */ ++ ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) ++#error "Currently ktap don't support kernel older than 3.1" ++#endif ++ ++#if !CONFIG_EVENT_TRACING ++#error "Please enable CONFIG_EVENT_TRACING before compile ktap" ++#endif ++ ++#if !CONFIG_PERF_EVENTS ++#error "Please enable CONFIG_PERF_EVENTS before compile ktap" ++#endif ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../include/ktap_types.h" ++#include "ktap.h" ++#include "kp_load.h" ++#include "kp_vm.h" ++ ++static int load_trunk(struct ktap_parm *parm, unsigned long **buff) ++{ ++ int ret; ++ unsigned long *vmstart; ++ ++ vmstart = vmalloc(parm->trunk_len); ++ if (!vmstart) ++ return -ENOMEM; ++ ++ ret = copy_from_user(vmstart, (void __user *)parm->trunk, ++ parm->trunk_len); ++ if (ret < 0) { ++ vfree(vmstart); ++ return -EFAULT; ++ } ++ ++ *buff = vmstart; ++ return 0; ++} ++ ++static struct dentry *kp_dir_dentry; ++ ++/* Ktap Main Entry */ ++static int ktap_main(struct file *file, ktap_parm *parm) ++{ ++ unsigned long *buff = NULL; ++ ktap_state *ks; ++ ktap_closure *cl; ++ int start_time, delta_time; ++ int ret; ++ ++ start_time = gettimeofday_us(); ++ ++ ks = kp_newstate(parm, kp_dir_dentry); ++ if (unlikely(!ks)) ++ return -ENOEXEC; ++ ++ file->private_data = ks; ++ ++ ret = load_trunk(parm, &buff); ++ if (ret) { ++ pr_err("cannot load file\n"); ++ return ret; ++ } ++ ++ cl = kp_load(ks, (unsigned char *)buff); ++ ++ vfree(buff); ++ ++ if (cl) { ++ /* optimize bytecode before excuting */ ++ kp_optimize_code(ks, 0, cl->p); ++ ++ delta_time = gettimeofday_us() - start_time; ++ kp_verbose_printf(ks, "booting time: %d (us)\n", delta_time); ++ kp_call(ks, ks->top - 1, 0); ++ } ++ ++ kp_final_exit(ks); ++ return ret; ++} ++ ++ ++static void print_version(void) ++{ ++} ++ ++static long ktap_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ ktap_parm parm; ++ int ret; ++ ++ switch (cmd) { ++ case KTAP_CMD_IOC_VERSION: ++ print_version(); ++ return 0; ++ case KTAP_CMD_IOC_RUN: ++ ret = copy_from_user(&parm, (void __user *)arg, ++ sizeof(ktap_parm)); ++ if (ret < 0) ++ return -EFAULT; ++ ++ return ktap_main(file, &parm); ++ default: ++ return -EINVAL; ++ }; ++ ++ return 0; ++} ++ ++static const struct file_operations ktap_fops = { ++ .llseek = no_llseek, ++ .unlocked_ioctl = ktap_ioctl, ++}; ++ ++static long ktapvm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int new_fd, err; ++ struct file *new_file; ++ ++ new_fd = get_unused_fd(); ++ if (new_fd < 0) ++ return new_fd; ++ ++ new_file = anon_inode_getfile("[ktap]", &ktap_fops, NULL, O_RDWR); ++ if (IS_ERR(new_file)) { ++ err = PTR_ERR(new_file); ++ put_unused_fd(new_fd); ++ return err; ++ } ++ ++ file->private_data = NULL; ++ fd_install(new_fd, new_file); ++ return new_fd; ++} ++ ++static const struct file_operations ktapvm_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = ktapvm_ioctl, ++}; ++ ++static int __init init_ktap(void) ++{ ++ struct dentry *ktapvm_dentry; ++ ++ kp_dir_dentry = debugfs_create_dir("ktap", NULL); ++ if (!kp_dir_dentry) { ++ pr_err("ktap: debugfs_create_dir failed\n"); ++ return -1; ++ } ++ ++ ktapvm_dentry = debugfs_create_file("ktapvm", 0444, kp_dir_dentry, NULL, ++ &ktapvm_fops); ++ ++ if (!ktapvm_dentry) { ++ pr_err("ktapvm: cannot create ktapvm file\n"); ++ debugfs_remove_recursive(kp_dir_dentry); ++ return -1; ++ } ++ ++ kp_init_exit_instruction(); ++ ++ return 0; ++} ++ ++static void __exit exit_ktap(void) ++{ ++ debugfs_remove_recursive(kp_dir_dentry); ++} ++ ++module_init(init_ktap); ++module_exit(exit_ktap); ++ ++MODULE_AUTHOR("Jovi Zhangwei "); ++MODULE_DESCRIPTION("ktap"); ++MODULE_LICENSE("GPL"); ++ ++int kp_max_exec_count = 10000; ++module_param_named(max_exec_count, kp_max_exec_count, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(max_exec_count, "non-mainthread max instruction execution count"); ++ +diff --git a/drivers/staging/ktap/runtime/ktap.h b/drivers/staging/ktap/runtime/ktap.h +new file mode 100644 +index 0000000..06ede8c +--- /dev/null ++++ b/drivers/staging/ktap/runtime/ktap.h +@@ -0,0 +1,130 @@ ++#ifndef __KTAP_H__ ++#define __KTAP_H__ ++ ++#ifdef __KERNEL__ ++#include ++#include ++#include ++#endif ++ ++typedef struct ktap_Reg { ++ const char *name; ++ ktap_cfunction func; ++} ktap_Reg; ++ ++struct ktap_probe_event { ++ struct list_head list; ++ struct perf_event *perf; ++ ktap_state *ks; ++ ktap_closure *cl; ++}; ++ ++/* this structure allocate on stack */ ++struct ktap_event { ++ struct ktap_probe_event *pevent; ++ struct ftrace_event_call *call; ++ struct trace_entry *entry; ++ int entry_size; ++ struct pt_regs *regs; ++}; ++ ++#define KTAP_PERCPU_BUFFER_SIZE (3 * PAGE_SIZE) ++ ++void kp_init_baselib(ktap_state *ks); ++void kp_init_oslib(ktap_state *ks); ++void kp_init_kdebuglib(ktap_state *ks); ++void kp_init_timerlib(ktap_state *ks); ++void kp_init_ansilib(ktap_state *ks); ++#ifdef CONFIG_KTAP_FFI ++void kp_init_ffilib(ktap_state *ks); ++#else ++static void __maybe_unused kp_init_ffilib(ktap_state *ks) ++{ ++ return; ++} ++#endif ++ ++ ++int kp_probe_init(ktap_state *ks); ++void kp_probe_exit(ktap_state *ks); ++ ++void kp_perf_event_register(ktap_state *ks, struct perf_event_attr *attr, ++ struct task_struct *task, char *filter, ++ ktap_closure *cl); ++ ++void kp_event_getarg(ktap_state *ks, ktap_value *ra, int n); ++void kp_event_tostring(ktap_state *ks, struct trace_seq *seq); ++void kp_exit_timers(ktap_state *ks); ++ ++extern int kp_max_exec_count; ++ ++/* get from kernel/trace/trace.h */ ++static __always_inline int trace_get_context_bit(void) ++{ ++ int bit; ++ ++ if (in_interrupt()) { ++ if (in_nmi()) ++ bit = 0; ++ else if (in_irq()) ++ bit = 1; ++ else ++ bit = 2; ++ } else ++ bit = 3; ++ ++ return bit; ++} ++ ++static __always_inline int get_recursion_context(ktap_state *ks) ++{ ++ int rctx = trace_get_context_bit(); ++ int *val = __this_cpu_ptr(G(ks)->recursion_context[rctx]); ++ ++ if (*val) ++ return -1; ++ ++ *val = true; ++ barrier(); ++ ++ return rctx; ++} ++ ++static inline void put_recursion_context(ktap_state *ks, int rctx) ++{ ++ int *val = __this_cpu_ptr(G(ks)->recursion_context[rctx]); ++ ++ barrier(); ++ *val = false; ++} ++ ++static inline void *kp_percpu_data(ktap_state *ks, int type) ++{ ++ return this_cpu_ptr(G(ks)->pcpu_data[type][trace_get_context_bit()]); ++} ++ ++ ++#define kp_verbose_printf(ks, ...) \ ++ if (G(ks)->parm->verbose) \ ++ kp_printf(ks, "[verbose] "__VA_ARGS__); ++ ++/* get argument operation macro */ ++#define kp_arg(ks, n) ((ks)->ci->func + (n)) ++#define kp_arg_nr(ks) ((int)(ks->top - (ks->ci->func + 1))) ++ ++#define kp_arg_check(ks, narg, type) \ ++ do { \ ++ if (unlikely(ttypenv(kp_arg(ks, narg)) != type)) { \ ++ kp_error(ks, "wrong type of argument %d\n", narg);\ ++ return -1; \ ++ } \ ++ } while (0) ++ ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 5, 0) ++#define SPRINT_SYMBOL sprint_symbol_no_offset ++#else ++#define SPRINT_SYMBOL sprint_symbol ++#endif ++ ++#endif /* __KTAP_H__ */ +diff --git a/drivers/staging/ktap/runtime/lib_ansi.c b/drivers/staging/ktap/runtime/lib_ansi.c +new file mode 100644 +index 0000000..d5066ea +--- /dev/null ++++ b/drivers/staging/ktap/runtime/lib_ansi.c +@@ -0,0 +1,155 @@ ++/* ++ * ansilib.c - ANSI escape sequences library ++ * ++ * http://en.wikipedia.org/wiki/ANSI_escape_code ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "../include/ktap_types.h" ++#include "ktap.h" ++#include "kp_vm.h" ++ ++/** ++ * function ansi.clear_screen - Move cursor to top left and clear screen. ++ * ++ * Description: Sends ansi code for moving cursor to top left and then the ++ * ansi code for clearing the screen from the cursor position to the end. ++ */ ++ ++static int ktap_lib_clear_screen(ktap_state *ks) ++{ ++ kp_printf(ks, "\033[1;1H\033[J"); ++ return 0; ++} ++ ++/** ++ * function ansi.set_color - Set the ansi Select Graphic Rendition mode. ++ * @fg: Foreground color to set. ++ * ++ * Description: Sends ansi code for Select Graphic Rendition mode for the ++ * given forground color. Black (30), Blue (34), Green (32), Cyan (36), ++ * Red (31), Purple (35), Brown (33), Light Gray (37). ++ */ ++ ++static int ktap_lib_set_color(ktap_state *ks) ++{ ++ int fg; ++ ++ kp_arg_check(ks, 1, KTAP_TNUMBER); ++ ++ fg = nvalue(kp_arg(ks, 1)); ++ kp_printf(ks, "\033[%dm", fg); ++ return 0; ++} ++ ++/** ++ * function ansi.set_color2 - Set the ansi Select Graphic Rendition mode. ++ * @fg: Foreground color to set. ++ * @bg: Background color to set. ++ * ++ * Description: Sends ansi code for Select Graphic Rendition mode for the ++ * given forground color, Black (30), Blue (34), Green (32), Cyan (36), ++ * Red (31), Purple (35), Brown (33), Light Gray (37) and the given ++ * background color, Black (40), Red (41), Green (42), Yellow (43), ++ * Blue (44), Magenta (45), Cyan (46), White (47). ++ */ ++static int ktap_lib_set_color2(ktap_state *ks) ++{ ++ int fg, bg; ++ ++ kp_arg_check(ks, 1, KTAP_TNUMBER); ++ kp_arg_check(ks, 2, KTAP_TNUMBER); ++ ++ fg = nvalue(kp_arg(ks, 1)); ++ bg = nvalue(kp_arg(ks, 2)); ++ kp_printf(ks, "\033[%d;%dm", fg, bg); ++ return 0; ++} ++ ++/** ++ * function ansi.set_color3 - Set the ansi Select Graphic Rendition mode. ++ * @fg: Foreground color to set. ++ * @bg: Background color to set. ++ * @attr: Color attribute to set. ++ * ++ * Description: Sends ansi code for Select Graphic Rendition mode for the ++ * given forground color, Black (30), Blue (34), Green (32), Cyan (36), ++ * Red (31), Purple (35), Brown (33), Light Gray (37), the given ++ * background color, Black (40), Red (41), Green (42), Yellow (43), ++ * Blue (44), Magenta (45), Cyan (46), White (47) and the color attribute ++ * All attributes off (0), Intensity Bold (1), Underline Single (4), ++ * Blink Slow (5), Blink Rapid (6), Image Negative (7). ++ */ ++static int ktap_lib_set_color3(ktap_state *ks) ++{ ++ int fg, bg, attr; ++ ++ kp_arg_check(ks, 1, KTAP_TNUMBER); ++ kp_arg_check(ks, 2, KTAP_TNUMBER); ++ kp_arg_check(ks, 3, KTAP_TNUMBER); ++ ++ fg = nvalue(kp_arg(ks, 1)); ++ bg = nvalue(kp_arg(ks, 2)); ++ attr = nvalue(kp_arg(ks, 3)); ++ ++ if (attr) ++ kp_printf(ks, "\033[%d;%d;%dm", fg, bg, attr); ++ else ++ kp_printf(ks, "\033[%d;%dm", fg, bg); ++ ++ return 0; ++} ++ ++/** ++ * function ansi.reset_color - Resets Select Graphic Rendition mode. ++ * ++ * Description: Sends ansi code to reset foreground, background and color ++ * attribute to default values. ++ */ ++static int ktap_lib_reset_color(ktap_state *ks) ++{ ++ kp_printf(ks, "\033[0;0m"); ++ return 0; ++} ++ ++/** ++ * function ansi.new_line - Move cursor to new line. ++ * ++ * Description: Sends ansi code new line. ++ */ ++static int ktap_lib_new_line (ktap_state *ks) ++{ ++ kp_printf(ks, "\12"); ++ return 0; ++} ++ ++static const ktap_Reg ansi_funcs[] = { ++ {"clear_screen", ktap_lib_clear_screen}, ++ {"set_color", ktap_lib_set_color}, ++ {"set_color2", ktap_lib_set_color2}, ++ {"set_color3", ktap_lib_set_color3}, ++ {"reset_color", ktap_lib_reset_color}, ++ {"new_line", ktap_lib_new_line}, ++ {NULL} ++}; ++ ++void kp_init_ansilib(ktap_state *ks) ++{ ++ kp_register_lib(ks, "ansi", ansi_funcs); ++} +diff --git a/drivers/staging/ktap/runtime/lib_base.c b/drivers/staging/ktap/runtime/lib_base.c +new file mode 100644 +index 0000000..45a70d3 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/lib_base.c +@@ -0,0 +1,607 @@ ++/* ++ * baselib.c - ktapvm kernel module base library ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) ++#include ++#endif ++#include "../include/ktap_types.h" ++#include "ktap.h" ++#include "kp_obj.h" ++#include "kp_str.h" ++#include "kp_tab.h" ++#include "kp_transport.h" ++#include "kp_vm.h" ++ ++static int ktap_lib_next(ktap_state *ks) ++{ ++ ktap_tab *t = hvalue(ks->top - 2); ++ ++ if (kp_tab_next(ks, t, ks->top-1)) { ++ ks->top += 1; ++ return 2; ++ } else { ++ ks->top -= 1; ++ set_nil(ks->top++); ++ return 1; ++ } ++} ++ ++static int ktap_lib_pairs(ktap_state *ks) ++{ ++ ktap_value *v = kp_arg(ks, 1); ++ ktap_tab *t; ++ ++ if (is_table(v)) { ++ t = hvalue(v); ++ } else if (is_ptable(v)) { ++ t = kp_ptab_synthesis(ks, phvalue(v)); ++ } else if (is_nil(v)) { ++ kp_error(ks, "table is nil in pairs\n"); ++ return 0; ++ } else { ++ kp_error(ks, "wrong argument for pairs\n"); ++ return 0; ++ } ++ ++ set_cfunction(ks->top++, ktap_lib_next); ++ set_table(ks->top++, t); ++ set_nil(ks->top++); ++ return 3; ++} ++ ++static int ktap_lib_sort_next(ktap_state *ks) ++{ ++ ktap_tab *t = hvalue(ks->top - 2); ++ ++ if (kp_tab_sort_next(ks, t, ks->top-1)) { ++ ks->top += 1; ++ return 2; ++ } else { ++ ks->top -= 1; ++ set_nil(ks->top++); ++ return 1; ++ } ++} ++ ++static int ktap_lib_sort_pairs(ktap_state *ks) ++{ ++ ktap_value *v = kp_arg(ks, 1); ++ ktap_closure *cmp_func = NULL; ++ ktap_tab *t; ++ ++ if (is_table(v)) { ++ t = hvalue(v); ++ } else if (is_ptable(v)) { ++ t = kp_ptab_synthesis(ks, phvalue(v)); ++ } else if (is_nil(v)) { ++ kp_error(ks, "table is nil in pairs\n"); ++ return 0; ++ } else { ++ kp_error(ks, "wrong argument for pairs\n"); ++ return 0; ++ } ++ ++ if (kp_arg_nr(ks) > 1) { ++ kp_arg_check(ks, 2, KTAP_TFUNCTION); ++ cmp_func = clvalue(kp_arg(ks, 2)); ++ } ++ ++ kp_tab_sort(ks, t, cmp_func); ++ set_cfunction(ks->top++, ktap_lib_sort_next); ++ set_table(ks->top++, t); ++ set_nil(ks->top++); ++ return 3; ++} ++ ++static int ktap_lib_len(ktap_state *ks) ++{ ++ int len = kp_objlen(ks, kp_arg(ks, 1)); ++ ++ if (len < 0) ++ return -1; ++ ++ set_number(ks->top, len); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_print(ktap_state *ks) ++{ ++ int i; ++ int n = kp_arg_nr(ks); ++ ++ for (i = 1; i <= n; i++) { ++ ktap_value *arg = kp_arg(ks, i); ++ if (i > 1) ++ kp_puts(ks, "\t"); ++ kp_showobj(ks, arg); ++ } ++ ++ kp_puts(ks, "\n"); ++ ++ return 0; ++} ++ ++/* don't engage with tstring when printf, use buffer directly */ ++static int ktap_lib_printf(ktap_state *ks) ++{ ++ struct trace_seq *seq; ++ ++ preempt_disable_notrace(); ++ ++ seq = kp_percpu_data(ks, KTAP_PERCPU_DATA_BUFFER); ++ trace_seq_init(seq); ++ ++ if (kp_str_fmt(ks, seq)) ++ goto out; ++ ++ seq->buffer[seq->len] = '\0'; ++ kp_transport_write(ks, seq->buffer, seq->len + 1); ++ ++ out: ++ preempt_enable_notrace(); ++ return 0; ++} ++ ++#ifdef CONFIG_STACKTRACE ++static int ktap_lib_print_backtrace(ktap_state *ks) ++{ ++ int skip = 10, max_entries = 10; ++ int n = kp_arg_nr(ks); ++ ++ if (n >= 1) { ++ kp_arg_check(ks, 1, KTAP_TNUMBER); ++ skip = nvalue(kp_arg(ks, 1)); ++ } ++ if (n >= 2) { ++ kp_arg_check(ks, 2, KTAP_TNUMBER); ++ max_entries = nvalue(kp_arg(ks, 2)); ++ max_entries = min(max_entries, KTAP_MAX_STACK_ENTRIES); ++ } ++ ++ kp_transport_print_backtrace(ks, skip, max_entries); ++ return 0; ++} ++#else ++static int ktap_lib_print_backtrace(ktap_state *ks) ++{ ++ kp_error(ks, "Please enable CONFIG_STACKTRACE before use " ++ "ktap print_backtrace\n"); ++ return 0; ++} ++#endif ++ ++static int ktap_lib_backtrace(ktap_state *ks) ++{ ++ struct stack_trace trace; ++ int skip = 10, max_entries = 10; ++ int n = kp_arg_nr(ks); ++ ktap_btrace *bt; ++ ++ if (n >= 1) { ++ kp_arg_check(ks, 1, KTAP_TNUMBER); ++ skip = nvalue(kp_arg(ks, 1)); ++ } ++ if (n >= 2) { ++ kp_arg_check(ks, 2, KTAP_TNUMBER); ++ max_entries = nvalue(kp_arg(ks, 2)); ++ max_entries = min(max_entries, KTAP_MAX_STACK_ENTRIES); ++ } ++ ++ bt = kp_percpu_data(ks, KTAP_PERCPU_DATA_BTRACE); ++ ++ trace.nr_entries = 0; ++ trace.skip = skip; ++ trace.max_entries = max_entries; ++ trace.entries = (unsigned long *)(bt + 1); ++ save_stack_trace(&trace); ++ ++ bt->nr_entries = trace.nr_entries; ++ set_btrace(ks->top, bt); ++ incr_top(ks); ++ return 1; ++} ++ ++extern unsigned long long ns2usecs(cycle_t nsec); ++static int ktap_lib_print_trace_clock(ktap_state *ks) ++{ ++ unsigned long long t; ++ unsigned long secs, usec_rem; ++ u64 timestamp; ++ ++ /* use ring buffer's timestamp */ ++ timestamp = ring_buffer_time_stamp(G(ks)->buffer, smp_processor_id()); ++ ++ t = ns2usecs(timestamp); ++ usec_rem = do_div(t, USEC_PER_SEC); ++ secs = (unsigned long)t; ++ ++ kp_printf(ks, "%5lu.%06lu\n", secs, usec_rem); ++ ++ return 0; ++} ++ ++static int ktap_lib_exit(ktap_state *ks) ++{ ++ kp_exit(ks); ++ ++ /* do not execute bytecode any more in this thread */ ++ return -1; ++} ++ ++static int ktap_lib_pid(ktap_state *ks) ++{ ++ set_number(ks->top, (int)current->pid); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_tid(ktap_state *ks) ++{ ++ pid_t pid = task_pid_vnr(current); ++ ++ set_number(ks->top, (int)pid); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_uid(ktap_state *ks) ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) ++ uid_t uid = from_kuid_munged(current_user_ns(), current_uid()); ++#else ++ uid_t uid = current_uid(); ++#endif ++ set_number(ks->top, (int)uid); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_execname(ktap_state *ks) ++{ ++ ktap_string *ts = kp_tstring_new(ks, current->comm); ++ set_string(ks->top, ts); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_cpu(ktap_state *ks) ++{ ++ set_number(ks->top, smp_processor_id()); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_num_cpus(ktap_state *ks) ++{ ++ set_number(ks->top, num_online_cpus()); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_in_interrupt(ktap_state *ks) ++{ ++ int ret = in_interrupt(); ++ ++ set_number(ks->top, ret); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_arch(ktap_state *ks) ++{ ++ set_string(ks->top, kp_tstring_new(ks, utsname()->machine)); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_kernel_v(ktap_state *ks) ++{ ++ set_string(ks->top, kp_tstring_new(ks, utsname()->release)); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_kernel_string(ktap_state *ks) ++{ ++ unsigned long addr; ++ char str[256] = {0}; ++ char *ret; ++ ++ kp_arg_check(ks, 1, KTAP_TNUMBER); ++ ++ addr = nvalue(kp_arg(ks, 1)); ++ ++ ret = strncpy((void *)str, (const void *)addr, 256); ++ (void) &ret; /* Silence compiler warning. */ ++ ++ str[255] = '\0'; ++ set_string(ks->top, kp_tstring_new_local(ks, str)); ++ ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_user_string(ktap_state *ks) ++{ ++ unsigned long addr; ++ char str[256] = {0}; ++ int ret; ++ ++ kp_arg_check(ks, 1, KTAP_TNUMBER); ++ ++ addr = nvalue(kp_arg(ks, 1)); ++ ++ pagefault_disable(); ++ ret = __copy_from_user_inatomic((void *)str, (const void *)addr, 256); ++ (void) &ret; /* Silence compiler warning. */ ++ pagefault_enable(); ++ str[255] = '\0'; ++ set_string(ks->top, kp_tstring_new(ks, str)); ++ ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_histogram(ktap_state *ks) ++{ ++ ktap_value *v = kp_arg(ks, 1); ++ ++ if (is_table(v)) ++ kp_tab_histogram(ks, hvalue(v)); ++ else if (is_ptable(v)) ++ kp_ptab_histogram(ks, phvalue(v)); ++ ++ return 0; ++} ++ ++static int ktap_lib_ptable(ktap_state *ks) ++{ ++ ktap_ptab *ph; ++ ++ ph = kp_ptab_new(ks); ++ set_ptable(ks->top, ph); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_count(ktap_state *ks) ++{ ++ ktap_value *v = kp_arg(ks, 1); ++ ktap_stat_data *sd; ++ ++ if (is_nil(v)) { ++ set_number(ks->top, 0); ++ incr_top(ks); ++ return 1; ++ } ++ ++ kp_arg_check(ks, 1, KTAP_TSTATDATA); ++ sd = sdvalue(v); ++ ++ set_number(ks->top, sd->count); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_max(ktap_state *ks) ++{ ++ ktap_value *v = kp_arg(ks, 1); ++ ktap_stat_data *sd; ++ ++ if (is_nil(v)) { ++ set_number(ks->top, 0); ++ incr_top(ks); ++ return 1; ++ } ++ ++ kp_arg_check(ks, 1, KTAP_TSTATDATA); ++ sd = sdvalue(v); ++ ++ set_number(ks->top, sd->max); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_min(ktap_state *ks) ++{ ++ ktap_value *v = kp_arg(ks, 1); ++ ktap_stat_data *sd; ++ ++ if (is_nil(v)) { ++ set_number(ks->top, 0); ++ incr_top(ks); ++ return 1; ++ } ++ ++ kp_arg_check(ks, 1, KTAP_TSTATDATA); ++ sd = sdvalue(v); ++ ++ set_number(ks->top, sd->min); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_sum(ktap_state *ks) ++{ ++ ktap_value *v = kp_arg(ks, 1); ++ ktap_stat_data *sd; ++ ++ if (is_nil(v)) { ++ set_number(ks->top, 0); ++ incr_top(ks); ++ return 1; ++ } ++ ++ kp_arg_check(ks, 1, KTAP_TSTATDATA); ++ sd = sdvalue(v); ++ ++ set_number(ks->top, sd->sum); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_avg(ktap_state *ks) ++{ ++ ktap_value *v = kp_arg(ks, 1); ++ ktap_stat_data *sd; ++ ++ if (is_nil(v)) { ++ set_number(ks->top, 0); ++ incr_top(ks); ++ return 1; ++ } ++ ++ kp_arg_check(ks, 1, KTAP_TSTATDATA); ++ sd = sdvalue(v); ++ ++ set_number(ks->top, sd->sum / sd->count); ++ incr_top(ks); ++ return 1; ++} ++ ++static int ktap_lib_delete(ktap_state *ks) ++{ ++ kp_arg_check(ks, 1, KTAP_TTABLE); ++ ++ kp_tab_clear(ks, hvalue(kp_arg(ks, 1))); ++ return 0; ++} ++ ++static int ktap_lib_gettimeofday_us(ktap_state *ks) ++{ ++ set_number(ks->top, gettimeofday_us()); ++ incr_top(ks); ++ ++ return 1; ++} ++ ++/* ++ * use gdb to get field offset of struct task_struct, for example: ++ * ++ * gdb vmlinux ++ * (gdb)p &(((struct task_struct *)0).prio) ++ */ ++static int ktap_lib_curr_task_info(ktap_state *ks) ++{ ++ int offset; ++ int fetch_bytes; ++ ++ kp_arg_check(ks, 1, KTAP_TNUMBER); ++ ++ offset = nvalue(kp_arg(ks, 1)); ++ ++ if (kp_arg_nr(ks) == 1) ++ fetch_bytes = 4; /* default fetch 4 bytes*/ ++ else { ++ kp_arg_check(ks, 2, KTAP_TNUMBER); ++ fetch_bytes = nvalue(kp_arg(ks, 2)); ++ } ++ ++ if (offset >= sizeof(struct task_struct)) { ++ set_nil(ks->top++); ++ kp_error(ks, "access out of bound value of task_struct\n"); ++ return 1; ++ } ++ ++#define RET_VALUE ((unsigned long)current + offset) ++ ++ switch (fetch_bytes) { ++ case 4: ++ set_number(ks->top, *(unsigned int *)RET_VALUE); ++ break; ++ case 8: ++ set_number(ks->top, *(unsigned long *)RET_VALUE); ++ break; ++ default: ++ kp_error(ks, "unsupported fetch bytes in curr_task_info\n"); ++ set_nil(ks->top); ++ break; ++ } ++ ++#undef RET_VALUE ++ ++ incr_top(ks); ++ return 1; ++} ++ ++/* ++ * This built-in function mainly purpose scripts/schedule/schedtimes.kp ++ */ ++static int ktap_lib_in_iowait(ktap_state *ks) ++{ ++ set_number(ks->top, current->in_iowait); ++ incr_top(ks); ++ ++ return 1; ++} ++ ++static const ktap_Reg base_funcs[] = { ++ {"pairs", ktap_lib_pairs}, ++ {"sort_pairs", ktap_lib_sort_pairs}, ++ {"len", ktap_lib_len}, ++ {"print", ktap_lib_print}, ++ {"printf", ktap_lib_printf}, ++ {"print_backtrace", ktap_lib_print_backtrace}, ++ {"backtrace", ktap_lib_backtrace}, ++ {"print_trace_clock", ktap_lib_print_trace_clock}, ++ {"in_interrupt", ktap_lib_in_interrupt}, ++ {"exit", ktap_lib_exit}, ++ {"pid", ktap_lib_pid}, ++ {"tid", ktap_lib_tid}, ++ {"uid", ktap_lib_uid}, ++ {"execname", ktap_lib_execname}, ++ {"cpu", ktap_lib_cpu}, ++ {"num_cpus", ktap_lib_num_cpus}, ++ {"arch", ktap_lib_arch}, ++ {"kernel_v", ktap_lib_kernel_v}, ++ {"kernel_string", ktap_lib_kernel_string}, ++ {"user_string", ktap_lib_user_string}, ++ {"histogram", ktap_lib_histogram}, ++ {"ptable", ktap_lib_ptable}, ++ {"count", ktap_lib_count}, ++ {"max", ktap_lib_max}, ++ {"min", ktap_lib_min}, ++ {"sum", ktap_lib_sum}, ++ {"avg", ktap_lib_avg}, ++ ++ {"delete", ktap_lib_delete}, ++ {"gettimeofday_us", ktap_lib_gettimeofday_us}, ++ {"curr_taskinfo", ktap_lib_curr_task_info}, ++ {"in_iowait", ktap_lib_in_iowait}, ++ {NULL} ++}; ++ ++void kp_init_baselib(ktap_state *ks) ++{ ++ kp_register_lib(ks, NULL, base_funcs); ++} +diff --git a/drivers/staging/ktap/runtime/lib_ffi.c b/drivers/staging/ktap/runtime/lib_ffi.c +new file mode 100644 +index 0000000..ac1e4ef +--- /dev/null ++++ b/drivers/staging/ktap/runtime/lib_ffi.c +@@ -0,0 +1,50 @@ ++/* ++ * ffi.c - ktapvm kernel module ffi library ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "../include/ktap_types.h" ++#include "../include/ktap_ffi.h" ++#include "ktap.h" ++#include "kp_vm.h" ++ ++/*@TODO Design how to implement ffi helper functions 22.11 2013 (unihorn)*/ ++ ++static int kp_ffi_new(ktap_state *ks) ++{ ++ /*@TODO finish this 08.11 2013 (houqp)*/ ++ return 0; ++} ++ ++static int kp_ffi_sizeof(ktap_state *ks) ++{ ++ /*@TODO finish this 08.11 2013 (houqp)*/ ++ return 0; ++} ++ ++static const ktap_Reg ffi_funcs[] = { ++ {"sizeof", kp_ffi_sizeof}, ++ {"new", kp_ffi_new}, ++ {NULL} ++}; ++ ++void kp_init_ffilib(ktap_state *ks) ++{ ++ kp_register_lib(ks, "ffi", ffi_funcs); ++} +diff --git a/drivers/staging/ktap/runtime/lib_kdebug.c b/drivers/staging/ktap/runtime/lib_kdebug.c +new file mode 100644 +index 0000000..2a070c4 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/lib_kdebug.c +@@ -0,0 +1,426 @@ ++/* ++ * kdebug.c - ktap probing core implementation ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "../include/ktap_types.h" ++#include "ktap.h" ++#include "kp_obj.h" ++#include "kp_str.h" ++#include "kp_transport.h" ++#include "kp_vm.h" ++ ++static void ktap_call_probe_closure(ktap_state *mainthread, ktap_closure *cl, ++ struct ktap_event *e) ++{ ++ ktap_state *ks; ++ ktap_value *func; ++ ++ ks = kp_newthread(mainthread); ++ set_closure(ks->top, cl); ++ func = ks->top; ++ incr_top(ks); ++ ++ ks->current_event = e; ++ ++ kp_call(ks, func, 0); ++ ++ ks->current_event = NULL; ++ kp_exitthread(ks); ++} ++ ++void kp_event_tostring(ktap_state *ks, struct trace_seq *seq) ++{ ++ struct ktap_event *e = ks->current_event; ++ struct trace_iterator *iter; ++ struct trace_event *ev; ++ enum print_line_t ret = TRACE_TYPE_NO_CONSUME; ++ ++ /* Simulate the iterator */ ++ ++ /* ++ * use temp percpu buffer as trace_iterator ++ * we cannot use same temp buffer as printf. ++ */ ++ iter = kp_percpu_data(ks, KTAP_PERCPU_DATA_BUFFER2); ++ ++ trace_seq_init(&iter->seq); ++ iter->ent = e->entry; ++ ++ ev = &(e->call->event); ++ if (ev) ++ ret = ev->funcs->trace(iter, 0, ev); ++ ++ if (ret != TRACE_TYPE_NO_CONSUME) { ++ struct trace_seq *s = &iter->seq; ++ int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len; ++ ++ s->buffer[len] = '\0'; ++ _trace_seq_puts(seq, s->buffer); ++ } ++} ++ ++/* This definition should keep update with kernel/trace/trace.h */ ++struct ftrace_event_field { ++ struct list_head link; ++ const char *name; ++ const char *type; ++ int filter_type; ++ int offset; ++ int size; ++ int is_signed; ++}; ++ ++static struct list_head *ktap_get_fields(struct ftrace_event_call *event_call) ++{ ++ if (!event_call->class->get_fields) ++ return &event_call->class->fields; ++ return event_call->class->get_fields(event_call); ++} ++ ++static void get_field_value(ktap_state *ks, struct ktap_event *e, ++ struct ftrace_event_field *field, ktap_value *ra) ++{ ++ void *value = (unsigned char *)e->entry + field->offset; ++ ++ if (field->size == 4) { ++ int n = *(int *)value; ++ set_number(ra, n); ++ return; ++ } else if (field->size == 8) { ++ long n = *(long *)value; ++ set_number(ra, n); ++ return; ++ } ++ ++ if (!strncmp(field->type, "char", 4)) { ++ set_string(ra, kp_tstring_new(ks, (char *)value)); ++ return; ++ } ++} ++ ++void kp_event_getarg(ktap_state *ks, ktap_value *ra, int n) ++{ ++ struct ktap_event *e = ks->current_event; ++ int index = n; ++ struct ftrace_event_field *field; ++ struct list_head *head; ++ ++ /* this is very slow and not safe, fix it in future */ ++ head = ktap_get_fields(e->call); ++ list_for_each_entry_reverse(field, head, link) { ++ if (--index == 0) { ++ get_field_value(ks, e, field, ra); ++ return; ++ } ++ } ++ ++ set_nil(ra); ++ return; ++} ++ ++/* Callback function for perf event subsystem ++ * make ktap reentrant, don't disable irq in callback function, ++ * same as perf and ftrace. to make reentrant, we need some ++ * percpu data to be context isolation(irq/sirq/nmi/process) ++ * ++ * The recursion checking in here is mainly purpose for avoiding ++ * corrupt ktap_state with timer closure callback. For tracepoint ++ * recusion, perf core already handle it. ++ * ++ * Note tracepoint handler is calling with rcu_read_lock. ++ */ ++static void ktap_overflow_callback(struct perf_event *event, ++ struct perf_sample_data *data, ++ struct pt_regs *regs) ++{ ++ struct ktap_probe_event *ktap_pevent; ++ struct ktap_event e; ++ ktap_state *ks; ++ int rctx; ++ ++ ktap_pevent = event->overflow_handler_context; ++ ks = ktap_pevent->ks; ++ ++ if (unlikely(ks->stop)) ++ return; ++ ++ rctx = get_recursion_context(ks); ++ if (rctx < 0) ++ return; ++ ++ KTAP_STATS(ks)->events_hits += 1; ++ ++ /* profile perf event don't have valid associated tp_event */ ++ if (event->tp_event) { ++ e.call = event->tp_event; ++ e.entry = data->raw->data; ++ e.entry_size = data->raw->size; ++ } ++ e.pevent = ktap_pevent; ++ e.regs = regs; ++ ++ ktap_call_probe_closure(ks, ktap_pevent->cl, &e); ++ ++ put_recursion_context(ks, rctx); ++} ++ ++static void perf_destructor(struct ktap_probe_event *ktap_pevent) ++{ ++ perf_event_release_kernel(ktap_pevent->perf); ++} ++ ++static int (*kp_ftrace_profile_set_filter)(struct perf_event *event, ++ int event_id, char *filter_str); ++ ++/* ++ * Generic perf event register function ++ * used by tracepoints/kprobe/uprobe/profile-timer/hw_breakpoint. ++ */ ++void kp_perf_event_register(ktap_state *ks, struct perf_event_attr *attr, ++ struct task_struct *task, char *filter, ++ ktap_closure *cl) ++{ ++ struct ktap_probe_event *ktap_pevent; ++ struct kmem_cache *pevent_cache = G(ks)->pevent_cache; ++ struct perf_event *event; ++ int cpu, ret; ++ ++ kp_verbose_printf(ks, "enable perf event id: %d, filter: %s " ++ "pid: %d\n", attr->config, filter, ++ task ? task_tgid_vnr(task) : -1); ++ ++ /* ++ * don't tracing until ktap_wait, the reason is: ++ * 1). some event may hit before apply filter ++ * 2). more simple to manage tracing thread ++ * 3). avoid race with mainthread. ++ * ++ * Another way to do this is make attr.disabled as 1, then use ++ * perf_event_enable after filter apply, however, perf_event_enable ++ * was not exported in kernel older than 3.3, so we drop this method. ++ */ ++ ks->stop = 1; ++ ++ for_each_cpu(cpu, G(ks)->cpumask) { ++ ktap_pevent = kmem_cache_zalloc(pevent_cache, GFP_KERNEL); ++ if (!ktap_pevent) ++ return; ++ ++ ktap_pevent->ks = ks; ++ ktap_pevent->cl = cl; ++ event = perf_event_create_kernel_counter(attr, cpu, task, ++ ktap_overflow_callback, ++ ktap_pevent); ++ if (IS_ERR(event)) { ++ int err = PTR_ERR(event); ++ kp_error(ks, "unable register perf event %d on cpu %d, " ++ "err: %d\n", attr->config, cpu, err); ++ kp_free(ks, ktap_pevent); ++ return; ++ } ++ ++ ktap_pevent->perf = event; ++ INIT_LIST_HEAD(&ktap_pevent->list); ++ list_add_tail(&ktap_pevent->list, &G(ks)->probe_events_head); ++ ++ if (!filter) ++ continue; ++ ++ ret = kp_ftrace_profile_set_filter(event, attr->config, filter); ++ if (ret) { ++ kp_error(ks, "unable set filter %s for event id %d, " ++ "ret: %d\n", filter, attr->config, ret); ++ perf_destructor(ktap_pevent); ++ list_del(&ktap_pevent->list); ++ kp_free(ks, ktap_pevent); ++ return; ++ } ++ } ++} ++ ++static void end_probes(struct ktap_state *ks) ++{ ++ struct ktap_probe_event *ktap_pevent; ++ struct list_head *tmp, *pos; ++ struct list_head *head = &G(ks)->probe_events_head; ++ ++ list_for_each(pos, head) { ++ ktap_pevent = container_of(pos, struct ktap_probe_event, ++ list); ++ perf_destructor(ktap_pevent); ++ } ++ /* ++ * Ensure our callback won't be called anymore. The buffers ++ * will be freed after that. ++ */ ++ tracepoint_synchronize_unregister(); ++ ++ list_for_each_safe(pos, tmp, head) { ++ ktap_pevent = container_of(pos, struct ktap_probe_event, ++ list); ++ list_del(&ktap_pevent->list); ++ kp_free(ks, ktap_pevent); ++ } ++} ++ ++static int ktap_lib_probe_by_id(ktap_state *ks) ++{ ++ ktap_closure *cl; ++ struct task_struct *task = G(ks)->trace_task; ++ ktap_eventdef_info evdef_info; ++ char *filter = NULL; ++ int *id_arr; ++ int ret, i; ++ ++ /* the number is userspace address refer to ktap_eventdef_info */ ++ kp_arg_check(ks, 1, KTAP_TNUMBER); ++ kp_arg_check(ks, 2, KTAP_TFUNCTION); ++ ++ ret = copy_from_user(&evdef_info, (void *)nvalue(kp_arg(ks, 1)), ++ sizeof(evdef_info)); ++ if (ret < 0) ++ return -1; ++ ++ if (evdef_info.filter) { ++ int len; ++ ++ len = strlen_user(evdef_info.filter); ++ if (len > 0x1000) ++ return -1; ++ ++ filter = kmalloc(len + 1, GFP_KERNEL); ++ if (!filter) ++ return -1; ++ ++ if (strncpy_from_user(filter, evdef_info.filter, len) < 0) { ++ kfree(filter); ++ return -1; ++ } ++ } ++ ++ id_arr = kmalloc(evdef_info.nr * sizeof(int), GFP_KERNEL); ++ if (!id_arr) { ++ kfree(filter); ++ return -1; ++ } ++ ++ ret = copy_from_user(id_arr, evdef_info.id_arr, ++ evdef_info.nr * sizeof(int)); ++ if (ret < 0) { ++ kfree(filter); ++ kfree(id_arr); ++ return -1; ++ } ++ ++ cl = clvalue(kp_arg(ks, 2)); ++ ++ for (i = 0; i < evdef_info.nr; i++) { ++ struct perf_event_attr attr; ++ ++ memset(&attr, 0, sizeof(attr)); ++ attr.type = PERF_TYPE_TRACEPOINT; ++ attr.config = id_arr[i]; ++ attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | ++ PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD; ++ attr.sample_period = 1; ++ attr.size = sizeof(attr); ++ attr.disabled = 0; ++ ++ kp_perf_event_register(ks, &attr, task, filter, cl); ++ } ++ ++ kfree(filter); ++ kfree(id_arr); ++ return 0; ++} ++ ++static int ktap_lib_probe_end(ktap_state *ks) ++{ ++ kp_arg_check(ks, 1, KTAP_TFUNCTION); ++ ++ G(ks)->trace_end_closure = clvalue(kp_arg(ks, 1)); ++ return 0; ++} ++ ++static int ktap_lib_traceoff(ktap_state *ks) ++{ ++ end_probes(ks); ++ ++ /* call trace_end_closure after probed end */ ++ if (G(ks)->trace_end_closure) { ++ set_closure(ks->top, G(ks)->trace_end_closure); ++ incr_top(ks); ++ kp_call(ks, ks->top - 1, 0); ++ G(ks)->trace_end_closure = NULL; ++ } ++ ++ return 0; ++} ++ ++void kp_probe_exit(ktap_state *ks) ++{ ++ if (!G(ks)->trace_enabled) ++ return; ++ ++ end_probes(ks); ++ ++ /* call trace_end_closure after probed end */ ++ if (!G(ks)->error && G(ks)->trace_end_closure) { ++ set_closure(ks->top, G(ks)->trace_end_closure); ++ incr_top(ks); ++ kp_call(ks, ks->top - 1, 0); ++ G(ks)->trace_end_closure = NULL; ++ } ++ ++ kmem_cache_destroy(G(ks)->pevent_cache); ++ G(ks)->trace_enabled = 0; ++} ++ ++int kp_probe_init(ktap_state *ks) ++{ ++ G(ks)->pevent_cache = KMEM_CACHE(ktap_probe_event, SLAB_PANIC); ++ G(ks)->trace_enabled = 1; ++ return 0; ++} ++ ++static const ktap_Reg kdebuglib_funcs[] = { ++ {"probe_by_id", ktap_lib_probe_by_id}, ++ {"probe_end", ktap_lib_probe_end}, ++ {"traceoff", ktap_lib_traceoff}, ++ {NULL} ++}; ++ ++void kp_init_kdebuglib(ktap_state *ks) ++{ ++ kp_ftrace_profile_set_filter = ++ (void *)kallsyms_lookup_name("ftrace_profile_set_filter"); ++ if (!kp_ftrace_profile_set_filter) { ++ kp_error(ks, "ktap: cannot lookup ftrace_profile_set_filter " ++ "in kallsyms\n"); ++ return; ++ } ++ ++ kp_register_lib(ks, "kdebug", kdebuglib_funcs); ++} ++ +diff --git a/drivers/staging/ktap/runtime/lib_timer.c b/drivers/staging/ktap/runtime/lib_timer.c +new file mode 100644 +index 0000000..2fa4575 +--- /dev/null ++++ b/drivers/staging/ktap/runtime/lib_timer.c +@@ -0,0 +1,193 @@ ++/* ++ * timer.c - timer library support for ktap ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "../include/ktap_types.h" ++#include "ktap.h" ++#include "kp_obj.h" ++#include "kp_vm.h" ++ ++struct hrtimer_ktap { ++ struct hrtimer timer; ++ ktap_state *ks; ++ ktap_closure *cl; ++ u64 ns; ++ struct list_head list; ++}; ++ ++/* ++ * Currently ktap disallow tracing event in timer callback closure, ++ * that will corrupt ktap_state and ktap stack, because timer closure ++ * and event closure use same irq percpu ktap_state and stack. ++ * We can use a different percpu ktap_state and stack for timer purpuse, ++ * but that's don't bring any big value with cost on memory consuming. ++ * ++ * So just simply disable tracing in timer closure, ++ * get_recursion_context()/put_recursion_context() is used for this purpose. ++ */ ++static enum hrtimer_restart hrtimer_ktap_fn(struct hrtimer *timer) ++{ ++ struct hrtimer_ktap *t; ++ ktap_state *ks; ++ int rctx; ++ ++ rcu_read_lock_sched_notrace(); ++ ++ t = container_of(timer, struct hrtimer_ktap, timer); ++ rctx = get_recursion_context(t->ks); ++ ++ ks = kp_newthread(t->ks); ++ set_closure(ks->top, t->cl); ++ incr_top(ks); ++ kp_call(ks, ks->top - 1, 0); ++ kp_exitthread(ks); ++ ++ hrtimer_add_expires_ns(timer, t->ns); ++ ++ put_recursion_context(ks, rctx); ++ rcu_read_unlock_sched_notrace(); ++ ++ return HRTIMER_RESTART; ++} ++ ++static void set_tick_timer(ktap_state *ks, u64 period, ktap_closure *cl) ++{ ++ struct hrtimer_ktap *t; ++ ++ t = kp_malloc(ks, sizeof(*t)); ++ t->ks = ks; ++ t->cl = cl; ++ t->ns = period; ++ ++ INIT_LIST_HEAD(&t->list); ++ list_add(&t->list, &(G(ks)->timers)); ++ ++ hrtimer_init(&t->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ t->timer.function = hrtimer_ktap_fn; ++ hrtimer_start(&t->timer, ns_to_ktime(period), HRTIMER_MODE_REL); ++} ++ ++static void set_profile_timer(ktap_state *ks, u64 period, ktap_closure *cl) ++{ ++ struct perf_event_attr attr; ++ ++ memset(&attr, 0, sizeof(attr)); ++ attr.type = PERF_TYPE_SOFTWARE; ++ attr.config = PERF_COUNT_SW_CPU_CLOCK; ++ attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | ++ PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD; ++ attr.sample_period = period; ++ attr.size = sizeof(attr); ++ attr.disabled = 0; ++ ++ kp_perf_event_register(ks, &attr, NULL, NULL, cl); ++} ++ ++static int do_tick_profile(ktap_state *ks, int is_tick) ++{ ++ const char *str, *tmp; ++ char interval_str[32] = {0}; ++ char suffix[10] = {0}; ++ int n, i = 0; ++ int factor; ++ ++ kp_arg_check(ks, 1, KTAP_TSTRING); ++ kp_arg_check(ks, 2, KTAP_TFUNCTION); ++ ++ str = svalue(kp_arg(ks, 1)); ++ tmp = str; ++ while (isdigit(*tmp)) ++ tmp++; ++ ++ strncpy(interval_str, str, tmp - str); ++ if (kstrtoint(interval_str, 10, &n)) ++ goto error; ++ ++ strncpy(suffix, tmp, 9); ++ while (suffix[i] != ' ' && suffix[i] != '\0') ++ i++; ++ ++ suffix[i] = '\0'; ++ ++ if (!strcmp(suffix, "s") || !strcmp(suffix, "sec")) ++ factor = NSEC_PER_SEC; ++ else if (!strcmp(suffix, "ms") || !strcmp(suffix, "msec")) ++ factor = NSEC_PER_MSEC; ++ else if (!strcmp(suffix, "us") || !strcmp(suffix, "usec")) ++ factor = NSEC_PER_USEC; ++ else ++ goto error; ++ ++ if (is_tick) ++ set_tick_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2))); ++ else ++ set_profile_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2))); ++ ++ return 0; ++ ++ error: ++ kp_error(ks, "cannot parse timer interval: %s\n", str); ++ return -1; ++} ++ ++/* ++ * tick-n probes fire on only one CPU per interval. ++ * valid time suffixes: sec/s, msec/ms, usec/us ++ */ ++static int ktap_lib_tick(ktap_state *ks) ++{ ++ return do_tick_profile(ks, 1); ++} ++ ++/* ++ * A profile-n probe fires every fixed interval on every CPU ++ * valid time suffixes: sec/s, msec/ms, usec/us ++ */ ++static int ktap_lib_profile(ktap_state *ks) ++{ ++ return do_tick_profile(ks, 0); ++} ++ ++void kp_exit_timers(ktap_state *ks) ++{ ++ struct hrtimer_ktap *t, *tmp; ++ struct list_head *timers_list = &(G(ks)->timers); ++ ++ list_for_each_entry_safe(t, tmp, timers_list, list) { ++ hrtimer_cancel(&t->timer); ++ kp_free(ks, t); ++ } ++} ++ ++static const ktap_Reg timerlib_funcs[] = { ++ {"profile", ktap_lib_profile}, ++ {"tick", ktap_lib_tick}, ++ {NULL} ++}; ++ ++void kp_init_timerlib(ktap_state *ks) ++{ ++ kp_register_lib(ks, "timer", timerlib_funcs); ++} ++ +diff --git a/drivers/staging/ktap/samples/ansi/ansi_color_demo.kp b/drivers/staging/ktap/samples/ansi/ansi_color_demo.kp +new file mode 100644 +index 0000000..2998fe0 +--- /dev/null ++++ b/drivers/staging/ktap/samples/ansi/ansi_color_demo.kp +@@ -0,0 +1,22 @@ ++#!/usr/bin/env ktap ++ ++#this script demonstrate how to use ktap to output color text. ++ ++ansi.clear_screen() ++ ++ansi.set_color(32) ++printf("this line should be Green color\n") ++ ++ansi.set_color(31) ++printf("this line should be Red color\n") ++ ++ansi.set_color2(34, 43) ++printf("this line should be Blue color, with Yellow background\n") ++ ++ansi.reset_color() ++ansi.set_color3(34, 46, 4) ++printf("this line should be Blue color, with Cyan background, underline single attribute\n") ++ ++ansi.reset_color() ++ansi.new_line() ++ +diff --git a/drivers/staging/ktap/samples/basic/backtrace.kp b/drivers/staging/ktap/samples/basic/backtrace.kp +new file mode 100644 +index 0000000..39b8c39 +--- /dev/null ++++ b/drivers/staging/ktap/samples/basic/backtrace.kp +@@ -0,0 +1,6 @@ ++#!/usr/bin/env ktap ++ ++trace sched:sched_switch { ++ print_backtrace() ++} ++ +diff --git a/drivers/staging/ktap/samples/basic/event_trigger.kp b/drivers/staging/ktap/samples/basic/event_trigger.kp +new file mode 100644 +index 0000000..3cc8b04 +--- /dev/null ++++ b/drivers/staging/ktap/samples/basic/event_trigger.kp +@@ -0,0 +1,24 @@ ++#!/usr/bin/env ktap ++ ++soft_disabled = 1 ++this_cpu = 0 ++ ++trace syscalls:sys_enter_open { ++ print(argevent) ++ soft_disabled = 0 ++ this_cpu = cpu() ++} ++ ++trace *:* { ++ if (soft_disabled == 0 && cpu() == this_cpu) { ++ print(argevent) ++ } ++} ++ ++trace syscalls:sys_exit_open { ++ print(argevent) ++ if (cpu() == this_cpu) { ++ exit() ++ } ++} ++ +diff --git a/drivers/staging/ktap/samples/basic/event_trigger_ftrace.kp b/drivers/staging/ktap/samples/basic/event_trigger_ftrace.kp +new file mode 100644 +index 0000000..7e0d7d3 +--- /dev/null ++++ b/drivers/staging/ktap/samples/basic/event_trigger_ftrace.kp +@@ -0,0 +1,28 @@ ++#!/usr/bin/env ktap ++ ++ ++#This ktap script will output all function calling between ++#sys_enter_open and sys_exit_open, in one cpu. ++ ++soft_disabled = 1 ++this_cpu = 0 ++ ++trace syscalls:sys_enter_open { ++ print(argevent) ++ soft_disabled = 0 ++ this_cpu = cpu() ++} ++ ++trace ftrace:function { ++ if (soft_disabled == 0 && cpu() == this_cpu) { ++ print(argevent) ++ } ++} ++ ++trace syscalls:sys_exit_open { ++ print(argevent) ++ if (cpu() == this_cpu) { ++ exit() ++ } ++} ++ +diff --git a/drivers/staging/ktap/samples/basic/ftrace.kp b/drivers/staging/ktap/samples/basic/ftrace.kp +new file mode 100644 +index 0000000..9feca2b +--- /dev/null ++++ b/drivers/staging/ktap/samples/basic/ftrace.kp +@@ -0,0 +1,6 @@ ++#!/usr/bin/env ktap ++ ++trace ftrace:function /ip==mutex*/ { ++ print(cpu(), pid(), execname(), argevent) ++} ++ +diff --git a/drivers/staging/ktap/samples/basic/function_time.kp b/drivers/staging/ktap/samples/basic/function_time.kp +new file mode 100644 +index 0000000..e7859a3 +--- /dev/null ++++ b/drivers/staging/ktap/samples/basic/function_time.kp +@@ -0,0 +1,57 @@ ++#!/usr/bin/env ktap ++ ++#Demo for thread-local variable ++# ++#Note this kind of function time tracing already handled concurrent issue, ++#but not aware on the recursion problem, user need to aware this limitation, ++#so don't use this script to trace function which could be called recursive. ++ ++self = {} ++count_max = 0 ++count_min = 0 ++count_num = 0 ++total_time = 0 ++ ++printf("measure time(us) of function vfs_read\n"); ++ ++trace probe:vfs_read { ++ if (execname() == "ktap") { ++ return ++ } ++ ++ self[tid()] = gettimeofday_us() ++} ++ ++trace probe:vfs_read%return { ++ if (execname() == "ktap") { ++ return ++ } ++ ++ if (self[tid()] == nil) { ++ return ++ } ++ ++ local durtion = gettimeofday_us() - self[tid()] ++ if (durtion > count_max) { ++ count_max = durtion ++ } ++ local min = count_min ++ if (min == 0 || durtion < min) { ++ count_min = durtion ++ } ++ ++ count_num = count_num + 1 ++ total_time = total_time + durtion ++ ++ self[tid()] = nil ++} ++ ++trace_end { ++ printf("avg\tmax\tmin\n"); ++ printf("-------------------\n") ++ ++ printf("%d\t%d\t%d\n", total_time/count_num, ++ count_max, count_min) ++} ++ ++ +diff --git a/drivers/staging/ktap/samples/basic/kretprobe.kp b/drivers/staging/ktap/samples/basic/kretprobe.kp +new file mode 100644 +index 0000000..82de3cf +--- /dev/null ++++ b/drivers/staging/ktap/samples/basic/kretprobe.kp +@@ -0,0 +1,6 @@ ++#!/usr/bin/env ktap ++ ++trace probe:vfs_read%return fd=$retval { ++ print(execname(), argevent); ++} ++ +diff --git a/drivers/staging/ktap/samples/ffi/ffi_kmalloc.kp b/drivers/staging/ktap/samples/ffi/ffi_kmalloc.kp +new file mode 100644 +index 0000000..d8f0898 +--- /dev/null ++++ b/drivers/staging/ktap/samples/ffi/ffi_kmalloc.kp +@@ -0,0 +1,19 @@ ++#!/usr/bin/env ktap ++ ++cdef[[ ++ typedef unsigned gfp_t; ++ typedef unsigned long size_t; ++ void *__kmalloc( size_t size, gfp_t flags); ++ void kfree(const void *objp); ++]] ++ ++t1 = gettimeofday_us() ++ ++for (i = 1, 1000, 1) { ++ local object = C.__kmalloc(128, 208) #GFP_KERNEL is 208 ++ C.kfree(object) ++} ++ ++t2 = gettimeofday_us() ++ ++printf("execution time: %d us\n", t2 - t1) +diff --git a/drivers/staging/ktap/samples/ffi/printk.kp b/drivers/staging/ktap/samples/ffi/printk.kp +new file mode 100644 +index 0000000..dab9049 +--- /dev/null ++++ b/drivers/staging/ktap/samples/ffi/printk.kp +@@ -0,0 +1,10 @@ ++cdef[[ ++ int printk(char *fmt, ...); ++]] ++ ++ ++C.printk("This is printed out by ffi\n") ++C.printk("Show me the %s\n", "code") ++C.printk("%s should be at %02d/%02d %02d:%02d:%02d\n", "New Year", 1, 1, 0, 0, 0) ++C.printk("\'a\' + 5 = \'%c\'\n", 95 + 5) ++C.printk("The string is located at 0x%p\n", "str") +diff --git a/drivers/staging/ktap/samples/ffi/sched_clock.kp b/drivers/staging/ktap/samples/ffi/sched_clock.kp +new file mode 100644 +index 0000000..2a94587 +--- /dev/null ++++ b/drivers/staging/ktap/samples/ffi/sched_clock.kp +@@ -0,0 +1,6 @@ ++cdef[[ ++ unsigned long long sched_clock(); ++]] ++ ++ret = C.sched_clock() ++print("C.sched_clock returned, value: ", ret) +diff --git a/drivers/staging/ktap/samples/game/tetris.kp b/drivers/staging/ktap/samples/game/tetris.kp +new file mode 100644 +index 0000000..245dd8a +--- /dev/null ++++ b/drivers/staging/ktap/samples/game/tetris.kp +@@ -0,0 +1,293 @@ ++#!/usr/bin/env ktap ++ ++# ++# Tetris KTAP Script ++# ++# Copyright (C) 2013/OCT/05 Tadaki SAKAI ++# ++# based on stapgames (Systemtap Game Collection) ++# https://github.com/mhiramat/stapgames/blob/master/games/tetris.stp ++# ++# - Requirements ++# Kernel Configuration: CONFIG_KPROBE_EVENT=y ++# CONFIG_EVENT_TRACING=y ++# CONFIG_PERF_EVENTS=y ++# CONFIG_DEBUG_FS=y ++# CPU Architecture : x86_64 ++# ++# - Setup ++# $ sudo mount -t debugfs none /sys/kernel/debug/ ++# ++# $ git clone https://github.com/ktap/ktap ++# $ cd ktap ++# $ make 2>&1 | tee ../make.log ++# $ sudo make load ++# $ sudo sh -c 'echo 50000 > /sys/module/ktapvm/parameters/max_exec_count' ++# ++# - Run Tetris ++# $ sudo ./ktap samples/game/tetris.kp ++# ++ ++ ++# ++# utils ++# ++ ++function rand(max) { ++ r = gettimeofday_us() ++ if (r < 0) { ++ r = r * -1 ++ } ++ return r % max ++} ++ ++function update_display() { ++ for (i = 0, 239, 1) { ++ if ((i % 12 - 11) != 0) { ++ tmp = "" ++ } else { ++ tmp = "\n" ++ } ++ ++ if (display_buffer[240 + i] == empty) { ++ printf(" %s", tmp) ++ } else { ++ color = display_buffer[240 + i] + 40 ++ ansi.set_color2(color, color) ++ printf(" %s", tmp) ++ ansi.reset_color() ++ } ++ ++ # clear the display buffer ++ display_buffer[240 + i] = display_buffer[i] ++ } ++ ++ printf("%d\n",point) ++} ++ ++ ++# ++# global value ++# ++ ++empty = -1 ++ ++key_code = 0 ++point = 0 ++block_number = 0 ++height = 0 ++height_update = 0 ++ ++destination_position = {} ++display_buffer = {} ++ ++block_data0 = {} ++block_data1 = {} ++block_data2 = {} ++block_data3 = {} ++block_data4 = {} ++block_data5 = {} ++block_data6 = {} ++block_table = {} ++ ++# ++# Initialize ++# ++ ++# Create blocks ++# block is represented by the position from the center. ++# Every block has "L" part in the center except for a bar. ++block_data0[0] = -11 # non-"L" part for each block ++block_data1[0] = -24 ++block_data2[0] = 2 ++block_data3[0] = 13 ++block_data4[0] = -13 ++block_data5[0] = -1 ++block_data6[0] = 2 ++ ++block_table[0] = block_data0 ++block_table[1] = block_data1 ++block_table[2] = block_data2 ++block_table[3] = block_data3 ++block_table[4] = block_data4 ++block_table[5] = block_data5 ++block_table[6] = block_data6 ++ ++for (i = 0, len(block_table) - 1, 1) { ++ # common "L" part ++ block_table[i][1] = 0 ++ block_table[i][2] = 1 ++ block_table[i][3] = -12 ++} ++ ++block_table[6][3] = -1 # bar is not common ++# Position: 1 row has 12 columns, ++# and (x, y) is represented by h = x + y * 12.p ++height = 17 # First block position (center) ++ ++for (i = 0, 240, 1) { ++ # Wall and Floor (sentinel) ++ if (((i % 12) < 2) || (i > 228)) { ++ tmp = 7 # White ++ } else { ++ tmp = empty ++ } ++ display_buffer[i - 1] = tmp ++ display_buffer[240 + i - 1] = tmp ++} ++ ++block_number = rand(7) ++ ++ansi.clear_screen() ++ ++ ++# ++# Key Input ++# ++ ++trace probe:kbd_event handle=%di event_type=%si event_code=%dx value=%cx { ++ # Only can run it in x86_64 ++ # ++ # Register follow x86_64 call conversion: ++ # ++ # x86_64: ++ # %rcx 4 argument ++ # %rdx 3 argument ++ # %rsi 2 argument ++ # %rdi 1 argument ++ ++ local event_code = arg4 ++ local value = arg5 ++ ++ if (value != 0) { ++ if ((event_code - 4) != 0) { ++ key_code = event_code ++ } ++ } ++} ++ ++ ++# ++# timer ++# ++ ++tick-200ms { ++ ansi.clear_screen() ++ ++ f = 0 # move/rotate flag ++ ++ if (key_code != 0) { # if key is pressed ++ if(key_code != 103) { #move left or right ++ # d: movement direction ++ if ((key_code - 105) != 0) { ++ if ((key_code - 106) != 0) { ++ d = 0 ++ } else { ++ d = 1 ++ } ++ } else { ++ d = -1 ++ } ++ ++ for (i = 0, 3, 1) { # check if the block can be moved ++ # destination is free ++ if (display_buffer[height + ++ block_table[block_number][i] + d] ++ != empty) { ++ f = 1 ++ } ++ } ++ # move if destinations of every block are free ++ if (f == 0) { ++ height = height + d ++ } ++ } else { # rotate ++ for (i = 0, 3, 1) { # check if block can be rotated ++ # each block position ++ p = block_table[block_number][i] ++ ++ # destination x pos(p/12 rounded) ++ v = (p * 2 + 252) / 24 - 10 ++ w = p - v * 12 # destination y pos ++ ++ # destination position ++ destination_position[i] = w * 12 - v ++ ++ # check if desetination is free ++ if (display_buffer[height + ++ destination_position[i]] != empty) { ++ f = 1 ++ } ++ } ++ ++ if (f == 0) { ++ # rotate if destinations of every block ++ # are free ++ for (i = 0, 3, 1) { ++ block_table[block_number][i] = ++ destination_position[i] ++ } ++ } ++ } ++ } ++ key_code = 0 # clear the input key ++ ++ f = 0 ++ for (i = 0, 3, 1) { # drop 1 row ++ # check if destination is free ++ p = height + block_table[block_number][i] ++ if (display_buffer[12 + p] != empty) { ++ f = 1 ++ } ++ ++ # copy the moving block to display buffer ++ display_buffer[240 + p] = block_number ++ } ++ ++ if ((f == 1) && (height == 17)) { ++ update_display() ++ exit() # exit if there are block at initial position ++ } ++ ++ height_update = !height_update ++ if (height_update != 0) { ++ if(f != 0) { # the block can't drop anymore ++ for (i = 0, 3, 1) { ++ # fix the block ++ display_buffer[height + ++ block_table[block_number][i]] = block_number ++ } ++ # determin the next block ++ block_number = rand(7) ++ height = 17 # make the block to initial position ++ } else { ++ height = height + 12 # drop the block 1 row ++ } ++ } ++ ++ k = 1 ++ for (i = 18, 0, -1) { #check if line is filled ++ # search for filled line ++ j = 10 ++ while ((j > 0) && ++ (display_buffer[i * 12 + j] != empty)) { ++ j = j - 1 ++ } ++ ++ if (j == 0) { # filled! ++ # add a point: 1 line - 1 point, ..., tetris - 10points ++ point = point + k ++ k = k + 1 ++ ++ # drop every upper block ++ j = (i + 1) * 12 ++ i = i + 1 ++ while (j > 2 * 12) { ++ j = j - 1 ++ display_buffer[j] = display_buffer[j - 12] ++ } ++ } ++ } ++ ++ update_display() ++} +diff --git a/drivers/staging/ktap/samples/helloworld.kp b/drivers/staging/ktap/samples/helloworld.kp +new file mode 100644 +index 0000000..5673c15 +--- /dev/null ++++ b/drivers/staging/ktap/samples/helloworld.kp +@@ -0,0 +1,3 @@ ++#!/usr/bin/env ktap ++ ++print("Hello World! I am ktap") +diff --git a/drivers/staging/ktap/samples/interrupt/hardirq_time.kp b/drivers/staging/ktap/samples/interrupt/hardirq_time.kp +new file mode 100644 +index 0000000..e570f15 +--- /dev/null ++++ b/drivers/staging/ktap/samples/interrupt/hardirq_time.kp +@@ -0,0 +1,24 @@ ++#!/usr/bin/env ktap ++ ++#this script output each average consumimg time of each hardirq ++s = ptable() ++map = {} ++ ++trace irq:irq_handler_entry { ++ map[cpu()] = gettimeofday_us() ++} ++ ++trace irq:irq_handler_exit { ++ local entry_time = map[cpu()] ++ if (entry_time == nil) { ++ return; ++ } ++ ++ s[arg1] <<< gettimeofday_us() - entry_time ++ map[cpu()] = nil ++} ++ ++trace_end { ++ print(s) ++} ++ +diff --git a/drivers/staging/ktap/samples/interrupt/softirq_time.kp b/drivers/staging/ktap/samples/interrupt/softirq_time.kp +new file mode 100644 +index 0000000..5aec09a +--- /dev/null ++++ b/drivers/staging/ktap/samples/interrupt/softirq_time.kp +@@ -0,0 +1,24 @@ ++#!/usr/bin/env ktap ++ ++#this script output each average consumimg time of each softirq line ++s = ptable() ++map = {} ++ ++trace irq:softirq_entry { ++ map[cpu()] = gettimeofday_us() ++} ++ ++trace irq:softirq_exit { ++ local entry_time = map[cpu()] ++ if (entry_time == nil) { ++ return; ++ } ++ ++ s[arg1] <<< gettimeofday_us() - entry_time ++ map[cpu()] = nil ++} ++ ++trace_end { ++ print(s) ++} ++ +diff --git a/drivers/staging/ktap/samples/io/kprobes-do-sys-open.kp b/drivers/staging/ktap/samples/io/kprobes-do-sys-open.kp +new file mode 100644 +index 0000000..a15f911 +--- /dev/null ++++ b/drivers/staging/ktap/samples/io/kprobes-do-sys-open.kp +@@ -0,0 +1,20 @@ ++#!/usr/bin/env ktap ++ ++#Only can run it in x86_64 ++# ++#Register follow x86_64 call conversion: ++# ++#x86_64: ++# %rcx 4 argument ++# %rdx 3 argument ++# %rsi 2 argument ++# %rdi 1 argument ++ ++trace probe:do_sys_open dfd=%di filename=%si flags=%dx mode=%cx { ++ printf("[do_sys_open entry]: (%s) open file (%s)\n", ++ execname(), user_string(arg3)) ++} ++ ++trace probe:do_sys_open%return fd=$retval { ++ printf("[do_sys_open exit]: return fd (%d)\n", arg3) ++} +diff --git a/drivers/staging/ktap/samples/io/traceio.kp b/drivers/staging/ktap/samples/io/traceio.kp +new file mode 100644 +index 0000000..89f979b +--- /dev/null ++++ b/drivers/staging/ktap/samples/io/traceio.kp +@@ -0,0 +1,54 @@ ++#! /usr/bin/env ktap ++ ++# Based on systemtap traceio.stp ++ ++reads = ptable() ++writes = ptable() ++total_io = ptable() ++ ++trace syscalls:sys_exit_read { ++ reads[execname()] <<< arg2 ++ total_io[execname()] <<< arg2 ++} ++ ++trace syscalls:sys_exit_write { ++ writes[execname()] <<< arg2 ++ total_io[execname()] <<< arg2 ++} ++ ++function humanread_digit(bytes) { ++ if (bytes > 1024*1024*1024) { ++ return bytes/1024/1024/1024 ++ } elseif (bytes > 1024*1024) { ++ return bytes/1024/1024 ++ } elseif (bytes > 1024) { ++ return bytes/1024 ++ } else { ++ return bytes ++ } ++} ++ ++function humanread_x(bytes) { ++ if (bytes > 1024*1024*1024) { ++ return " GiB" ++ } elseif (bytes > 1024*1024) { ++ return " MiB" ++ } elseif (bytes > 1024) { ++ return " KiB" ++ } else { ++ return " B" ++ } ++} ++ ++tick-1s { ++ ansi.clear_screen() ++ for (exec, _ in pairs(total_io)) { ++ local readnum = sum(reads[exec]) ++ local writenum = sum(writes[exec]) ++ printf("%15s r: %12d%s w: %12d%s\n", exec, ++ humanread_digit(readnum), humanread_x(readnum), ++ humanread_digit(writenum), humanread_x(writenum)) ++ } ++ printf("\n") ++} ++ +diff --git a/drivers/staging/ktap/samples/mem/kmalloc-top.kp b/drivers/staging/ktap/samples/mem/kmalloc-top.kp +new file mode 100644 +index 0000000..f18ed9f +--- /dev/null ++++ b/drivers/staging/ktap/samples/mem/kmalloc-top.kp +@@ -0,0 +1,17 @@ ++#!/usr/bin/env ktap ++ ++kmalloc_stack = {} ++ ++trace kmem:kmalloc { ++ kmalloc_stack[backtrace()] += 1 ++} ++ ++tick-60s { ++ for (k, v in pairs(kmalloc_stack)) { ++ print(k) ++ printf("%d\n\n", v) ++ } ++ ++ exit() ++} ++ +diff --git a/drivers/staging/ktap/samples/mem/kmem.kp b/drivers/staging/ktap/samples/mem/kmem.kp +new file mode 100644 +index 0000000..c6f2c99 +--- /dev/null ++++ b/drivers/staging/ktap/samples/mem/kmem.kp +@@ -0,0 +1,30 @@ ++#!/usr/bin/env ktap ++ ++count1 = 0 ++trace kmem:kmalloc { ++ count1 = count1 + 1 ++} ++ ++count2 = 0 ++trace kmem:kfree { ++ count2 = count2 + 1 ++} ++ ++count3 = 0 ++trace kmem:mm_page_alloc { ++ count3 = count3 + 1 ++} ++ ++count4 = 0 ++trace kmem:mm_page_free { ++ count4 = count4 + 1 ++} ++ ++trace_end { ++ print("\n") ++ print("kmem:kmalloc:\t", count1) ++ print("kmem:kfree:\t", count2) ++ print("kmem:mm_page_alloc:", count3) ++ print("kmem:mm_page_free:", count4) ++ print("trace ending\n") ++} +diff --git a/drivers/staging/ktap/samples/profiling/function_profiler.kp b/drivers/staging/ktap/samples/profiling/function_profiler.kp +new file mode 100644 +index 0000000..28f1fa6 +--- /dev/null ++++ b/drivers/staging/ktap/samples/profiling/function_profiler.kp +@@ -0,0 +1,41 @@ ++#!/usr/bin/env ktap ++ ++#kernel function profile ++#You can use this script to know what function is called frequently, ++#without enable CONFIG_FUNCTION_PROFILER in kernel. ++ ++s = ptable() ++ ++trace ftrace:function { ++ s[arg1] <<< 1 ++} ++ ++trace_end { ++ histogram(s) ++} ++ ++#sample output ++#^C ++# value ------------- Distribution ------------- count ++# sub_preempt_count | @@@@@ 34904 ++# add_preempt_count | @@@@@ 33435 ++# nsecs_to_jiffies64 | @@@ 19919 ++# irqtime_account_process_tick... | @ 9970 ++# account_idle_time | @ 9880 ++# _raw_spin_lock | 5100 ++# _raw_spin_unlock | 5021 ++# _raw_spin_unlock_irqrestore | 4235 ++# _raw_spin_lock_irqsave | 4232 ++# __rcu_read_lock | 3373 ++# __rcu_read_unlock | 3373 ++# lookup_address | 2392 ++# pfn_range_is_mapped | 2384 ++# update_cfs_rq_blocked_load | 1983 ++# idle_cpu | 1808 ++# ktime_get | 1394 ++# _raw_spin_unlock_irq | 1270 ++# _raw_spin_lock_irq | 1091 ++# update_curr | 950 ++# irqtime_account_irq | 950 ++# ... | ++# +diff --git a/drivers/staging/ktap/samples/profiling/stack_profile.kp b/drivers/staging/ktap/samples/profiling/stack_profile.kp +new file mode 100644 +index 0000000..c88f850 +--- /dev/null ++++ b/drivers/staging/ktap/samples/profiling/stack_profile.kp +@@ -0,0 +1,30 @@ ++#!/usr/bin/env ktap ++ ++# This ktap script samples stacktrace of system per 10us, ++# you can use generated output to make a flame graph. ++# ++# Flame Graphs: ++# http://dtrace.org/blogs/brendan/2012/03/17/linux-kernel-performance-flame-graphs/ ++ ++s = ptable() ++ ++profile-10us { ++ #skip 12 stack entries, and dump all remain entries. ++ s[backtrace(12, -1)] <<< 1 ++} ++ ++tick-60s { ++ exit() ++} ++ ++trace_end { ++ function cmp(v1, v2) { ++ return (count(v1) < count(v2)) ++ } ++ for (k, v in sort_pairs(s, cmp)) { ++ print(k) ++ print(count(v)) ++ print() ++ } ++} ++ +diff --git a/drivers/staging/ktap/samples/schedule/sched_transition.kp b/drivers/staging/ktap/samples/schedule/sched_transition.kp +new file mode 100644 +index 0000000..eb54b09 +--- /dev/null ++++ b/drivers/staging/ktap/samples/schedule/sched_transition.kp +@@ -0,0 +1,5 @@ ++#!/usr/bin/env ktap ++ ++trace sched:sched_switch { ++ printf("%s ... ", arg1) ++} +diff --git a/drivers/staging/ktap/samples/schedule/schedtimes.kp b/drivers/staging/ktap/samples/schedule/schedtimes.kp +new file mode 100644 +index 0000000..acef904 +--- /dev/null ++++ b/drivers/staging/ktap/samples/schedule/schedtimes.kp +@@ -0,0 +1,125 @@ ++#!/usr/vin/env ktap ++ ++#schedtimer.kp ++#Initially inspired by Systemtap schedtimes.stp ++#and more bugfree compare with Systemtap's version ++# ++#Note that the time value is associate with pid, not with execname strictly, ++#sometime you will found there have sleep time for command "ls", the reason ++#is that sleep time is belong to parent process bash, so clear on this. ++ ++RUNNING = 0 ++QUEUED = 1 ++SLEEPING = 2 ++DEAD = 64 ++ ++run_time = {} ++queued_time = {} ++sleep_time = {} ++io_wait_time = {} ++ ++pid_state = {} ++pid_names = {} ++prev_timestamp = {} ++io_wait = {} ++ ++trace sched:sched_switch { ++ local prev_comm = arg1 ++ local prev_pid = arg2 ++ local prev_state = arg4 ++ local next_comm = arg5 ++ local next_pid = arg6 ++ local t = gettimeofday_us() ++ ++ if (pid_state[prev_pid] == nil) { ++ #do nothing ++ } elseif (pid_state[prev_pid] == RUNNING) { ++ run_time[prev_pid] += t - prev_timestamp[prev_pid] ++ } elseif (pid_state[prev_pid] == QUEUED) { ++ #found this: ++ #sched_wakeup comm=foo ++ #sched_switch prev_comm=foo ++ run_time[prev_pid] += t - prev_timestamp[prev_pid] ++ } ++ ++ pid_names[prev_pid] = prev_comm ++ prev_timestamp[prev_pid] = t ++ ++ if (prev_state == DEAD) { ++ pid_state[prev_pid] = DEAD ++ } elseif (prev_state > 0) { ++ if (in_iowait() == 1) { ++ io_wait[prev_pid] = 1 ++ } ++ pid_state[prev_pid] = SLEEPING ++ } elseif (prev_state == 0) { ++ pid_state[prev_pid] = QUEUED ++ } ++ ++ if (pid_state[next_pid] == nil) { ++ pid_state[next_pid] = RUNNING ++ } elseif (pid_state[next_pid] == QUEUED) { ++ queued_time[next_pid] += t - prev_timestamp[next_pid] ++ pid_state[next_pid] = RUNNING ++ } ++ ++ pid_names[next_pid] = next_comm ++ prev_timestamp[next_pid] = t ++} ++ ++trace sched:sched_wakeup, sched:sched_wakeup_new { ++ local comm = arg1 ++ local wakeup_pid = arg2 ++ local success = arg4 ++ local t = gettimeofday_us() ++ ++ if (pid_state[wakeup_pid] == nil) { ++ #do nothing ++ } elseif (pid_state[wakeup_pid] == SLEEPING) { ++ local durtion = t - prev_timestamp[wakeup_pid] ++ ++ sleep_time[wakeup_pid] += durtion ++ if (io_wait[wakeup_pid] == 1) { ++ io_wait_time[wakeup_pid] += durtion ++ io_wait[wakeup_pid] = 0 ++ } ++ } elseif (pid_state[wakeup_pid] == RUNNING) { ++ return ++ } ++ ++ pid_names[wakeup_pid] = comm ++ prev_timestamp[wakeup_pid] = t ++ pid_state[wakeup_pid] = QUEUED ++} ++ ++trace_end { ++ local t = gettimeofday_us() ++ ++ for (pid, state in pairs(pid_state)) { ++ local durtion = t - prev_timestamp[pid] ++ if (state == SLEEPING) { ++ sleep_time[pid] += durtion ++ } elseif (state == QUEUED) { ++ queued_time[pid] += durtion ++ } elseif (state == RUNNING) { ++ run_time[pid] += durtion ++ } ++ } ++ ++ printf ("%16s: %6s %10s %10s %10s %10s %10s\n\n", ++ "execname", "pid", "run(us)", "sleep(us)", "io_wait(us)", ++ "queued(us)", "total(us)") ++ ++ for (pid, time in pairs(run_time)) { ++ if (sleep_time[pid] == nil) { ++ sleep_time[pid] = 0 ++ } ++ if (queued_time[pid] == nil) { ++ queue_time[pid] = 0 ++ } ++ printf("%16s: %6d %10d %10d %10d %10d %10d\n", ++ pid_names[pid], pid, run_time[pid], sleep_time[pid], ++ io_wait_time[pid], queued_time[pid], ++ run_time[pid] + sleep_time[pid] + queued_time[pid]); ++ } ++} +diff --git a/drivers/staging/ktap/samples/syscalls/errinfo.kp b/drivers/staging/ktap/samples/syscalls/errinfo.kp +new file mode 100644 +index 0000000..049031b +--- /dev/null ++++ b/drivers/staging/ktap/samples/syscalls/errinfo.kp +@@ -0,0 +1,145 @@ ++#!/usr/bin/env ktap ++ ++#errdesc get from include/uapi/asm-generic/errno*.h ++errdesc = { ++ [1] = "Operation not permitted", #EPERM ++ [2] = "No such file or directory", #ENOENT ++ [3] = "No such process", #ESRCH ++ [4] = "Interrupted system call", #EINRT ++ [5] = "I/O error", #EIO ++ [6] = "No such device or address", #ENXIO ++ [7] = "Argument list too long", #E2BIG ++ [8] = "Exec format error", #ENOEXEC ++ [9] = "Bad file number", #EBADF ++ [10] = "No child processes", #ECHILD ++ [11] = "Try again", #EAGAIN ++ [12] = "Out of memory", #ENOMEM ++ [13] = "Permission denied", #EACCES ++ [14] = "Bad address", #EFAULT ++ [15] = "Block device required", #ENOTBLK ++ [16] = "Device or resource busy", #EBUSY ++ [17] = "File exists", #EEXIST ++ [18] = "Cross-device link", #EXDEV ++ [19] = "No such device", #ENODEV ++ [20] = "Not a directory", #ENOTDIR ++ [21] = "Is a directory", #EISDIR ++ [22] = "Invalid argument", #EINVAL ++ [23] = "File table overflow", #ENFILE ++ [24] = "Too many open files", #EMFILE ++ [25] = "Not a typewriter", #ENOTTY ++ [26] = "Text file busy", #ETXTBSY ++ [27] = "File too large", #EFBIG ++ [28] = "No space left on device", #ENOSPC ++ [29] = "Illegal seek", #ESPIPE ++ [30] = "Read-only file system", #EROFS ++ [31] = "Too many links", #EMLINK ++ [32] = "Broken pipe", #EPIPE ++ [33] = "Math argument out of domain of func", #EDOM ++ [34] = "Math result not representable", #ERANGE ++ ++ [35] = "Resource deadlock would occur", #EDEADLK ++ [36] = "File name too long", #ENAMETOOLONG ++ [37] = "No record locks available", #ENOLCK ++ [38] = "Function not implemented", #ENOSYS ++ [39] = "Directory not empty", #ENOTEMPTY ++ [40] = "Too many symbolic links encountered", #ELOOP ++ [42] = "No message of desired type", #ENOMSG ++ [43] = "Identifier removed", #EIDRM ++ [44] = "Channel number out of range", #ECHRNG ++ [45] = "Level 2 not synchronized", #EL2NSYNC ++ [46] = "Level 3 halted", #EL3HLT ++ [47] = "Level 3 reset", #EL3RST ++ [48] = "Link number out of range", #ELNRNG ++ [49] = "Protocol driver not attached", #EUNATCH ++ [50] = "No CSI structure available", #ENOCSI ++ [51] = "Level 2 halted", #EL2HLT ++ [52] = "Invalid exchange", #EBADE ++ [53] = "Invalid request descriptor", #EBADR ++ [54] = "Exchange full", #EXFULL ++ [55] = "No anode", #ENOANO ++ [56] = "Invalid request code", #EBADRQC ++ [57] = "Invalid slot", #EBADSLT ++ ++ [59] = "Bad font file format", #EBFONT ++ [60] = "Device not a stream", #ENOSTR ++ [61] = "No data available", #ENODATA ++ [62] = "Timer expired", #ETIME ++ [63] = "Out of streams resources", #ENOSR ++ [64] = "Machine is not on the network", #ENONET ++ [65] = "Package not installed", #ENOPKG ++ [66] = "Object is remote", #EREMOTE ++ [67] = "Link has been severed", #ENOLINK ++ [68] = "Advertise error", #EADV ++ [69] = "Srmount error", #ESRMNT ++ [70] = "Communication error on send", #ECOMM ++ [71] = "Protocol error", #EPROTO ++ [72] = "Multihop attempted", #EMULTIHOP ++ [73] = "RFS specific error", #EDOTDOT ++ [74] = "Not a data message", #EBADMSG ++ [75] = "Value too large for defined data type", #EOVERFLOW ++ [76] = "Name not unique on network", #ENOTUNIQ ++ [77] = "File descriptor in bad state", #EBADFD ++ [78] = "Remote address changed", #EREMCHG ++ [79] = "Can not access a needed shared library", #ELIBACC ++ [80] = "Accessing a corrupted shared library", #ELIBBAD ++ [81] = ".lib section in a.out corrupted", #ELIBSCN ++ [82] = "Attempting to link in too many shared libraries", #ELIBMAX ++ [83] = "Cannot exec a shared library directly", #ELIBEXEC ++ [84] = "Illegal byte sequence", #EILSEQ ++ [85] = "Interrupted system call should be restarted", #ERESTART ++ [86] = "Streams pipe error", #ESTRPIPE ++ [87] = "Too many users", #EUSERS ++ [88] = "Socket operation on non-socket", #ENOTSOCK ++ [89] = "Destination address required", #EDESTADDRREQ ++ [90] = "Message too long", #EMSGSIZE ++ [91] = "Protocol wrong type for socket", #EPROTOTYPE ++ [92] = "Protocol not available", #ENOPROTOOPT ++ [93] = "Protocol not supported", #EPROTONOSUPPORT ++ [94] = "Socket type not supported", #ESOCKTNOSUPPORT ++ [95] = "Operation not supported on transport endpoint", #EOPNOTSUPP ++ [96] = "Protocol family not supported", #EPFNOSUPPORT ++ [97] = "Address family not supported by protocol", #EAFNOSUPPORT ++ [98] = "Address already in use", #EADDRINUSE ++ [99] = "Cannot assign requested address", #EADDRNOTAVAIL ++ [100] = "Network is down", #ENETDOWN ++ [101] = "Network is unreachable", #ENETUNREACH ++ [102] = "Network dropped connection because of reset", #ENETRESET ++ [103] = "Software caused connection abort", #ECONNABORTED ++ [104] = "Connection reset by peer", #ECONNRESET ++ [105] = "No buffer space available", #ENOBUFS ++ [106] = "Transport endpoint is already connected", #EISCONN ++ [107] = "Transport endpoint is not connected", #ENOTCONN ++ [108] = " Cannot send after transport endpoint shutdown", #ESHUTDOWN ++ [109] = "Too many references: cannot splice", #ETOOMANYREFS ++ [110] = "Connection timed out", #ETIMEDOUT ++ [111] = "Connection refused", #ECONNREFUSED ++ [112] = "Host is down", #EHOSTDOWN ++ [113] = "No route to host", #EHOSTUNREACH ++ [114] = "Operation already in progress", #EALREADY ++ [115] = "Operation now in progress", #EINPROGRESS ++ [116] = "Stale NFS file handle", #ESTALE ++ [117] = "Structure needs cleaning", #EUCLEAN ++ [118] = "Not a XENIX named type file", #ENOTNAM ++ [119] = "No XENIX semaphores available", #ENAVAIL ++ [120] = "Is a named type file", #EISNAM ++ [121] = "Remote I/O error", #EREMOTEIO ++ [122] = "Quota exceeded", #EDQUOT ++ [123] = "No medium found", #ENOMEDIUM ++ [124] = "Wrong medium type", #EMEDIUMTYPE ++ [125] = "Operation Canceled", #ECANCELED ++ [126] = "Required key not available", #ENOKEY ++ [127] = "Key has expired", #EKEYEXPIRED ++ [128] = "Key has been revoked", #EKEYREVOKED ++ [129] = "Key was rejected by service", #EKEYREJECTED ++ [130] = "Owner died", #EOWNERDEAD ++ [131] = "State not recoverable", #ENOTRECOVERABLE ++ ++} ++ ++trace syscalls:sys_exit_* { ++ if (arg2 < 0) { ++ local errno = -arg2 ++ printf("%-15s%-20s\t%d\t%-30s\n", ++ execname(), argname, errno, errdesc[errno]) ++ } ++} +diff --git a/drivers/staging/ktap/samples/syscalls/execve.kp b/drivers/staging/ktap/samples/syscalls/execve.kp +new file mode 100644 +index 0000000..e7019d6 +--- /dev/null ++++ b/drivers/staging/ktap/samples/syscalls/execve.kp +@@ -0,0 +1,8 @@ ++#!/usr/bin/env ktap ++ ++#This script trace filename of process execution ++#only tested in x86-64 ++ ++trace probe:do_execve filename=%di { ++ printf("[do_execve entry]: (%s) name=%s\n", execname(), kernel_string(arg2)) ++} +diff --git a/drivers/staging/ktap/samples/syscalls/opensnoop.kp b/drivers/staging/ktap/samples/syscalls/opensnoop.kp +new file mode 100644 +index 0000000..7369ebe +--- /dev/null ++++ b/drivers/staging/ktap/samples/syscalls/opensnoop.kp +@@ -0,0 +1,31 @@ ++#!/usr/local/bin/ktap -q ++# ++# opensnoop.kp trace open syscalls with pathnames and basic info ++# ++# 23-Nov-2013 Brendan Gregg Created this ++ ++path = {} ++ ++printf("%5s %6s %-12s %3s %3s %s\n", "UID", "PID", "COMM", "FD", "ERR", "PATH"); ++ ++trace syscalls:sys_enter_open { ++ path[tid()] = user_string(arg2) ++} ++ ++trace syscalls:sys_exit_open { ++ local fd ++ local errno ++ ++ if (arg2 < 0) { ++ fd = 0 ++ errno = -arg2 ++ } else { ++ fd = arg2 ++ errno = 0 ++ } ++ ++ printf("%5d %6d %-12s %3d %3d %s\n", uid(), pid(), execname(), fd, ++ errno, path[tid()]) ++ ++ path[tid()] = 0 ++} +diff --git a/drivers/staging/ktap/samples/syscalls/sctop.kp b/drivers/staging/ktap/samples/syscalls/sctop.kp +new file mode 100644 +index 0000000..a45b4cb +--- /dev/null ++++ b/drivers/staging/ktap/samples/syscalls/sctop.kp +@@ -0,0 +1,13 @@ ++#! /usr/bin/env ktap ++ ++s = {} ++ ++trace syscalls:sys_enter_* { ++ s[argname] += 1 ++} ++ ++tick-5s { ++ ansi.clear_screen() ++ histogram(s) ++ delete(s) ++} +diff --git a/drivers/staging/ktap/samples/syscalls/syscalls.kp b/drivers/staging/ktap/samples/syscalls/syscalls.kp +new file mode 100644 +index 0000000..8bbaaca +--- /dev/null ++++ b/drivers/staging/ktap/samples/syscalls/syscalls.kp +@@ -0,0 +1,6 @@ ++#!/usr/bin/env ktap ++ ++trace syscalls:* { ++ print(cpu(), pid(), execname(), argevent) ++} ++ +diff --git a/drivers/staging/ktap/samples/syscalls/syscalls_count.kp b/drivers/staging/ktap/samples/syscalls/syscalls_count.kp +new file mode 100644 +index 0000000..ed7d989 +--- /dev/null ++++ b/drivers/staging/ktap/samples/syscalls/syscalls_count.kp +@@ -0,0 +1,54 @@ ++#!/usr/bin/env ktap ++ ++s = ptable() ++ ++trace syscalls:sys_enter_* { ++ s[argname] <<< 1 ++} ++ ++trace_end { ++ histogram(s) ++} ++ ++#Result: ++# ++#[root@jovi ktap]# ./ktap samples/syscalls_histogram.kp ++#^C ++# value ------------- Distribution ------------- count ++# sys_enter_rt_sigprocmask |@@@@@@ 326 ++# sys_enter_read |@@@@@ 287 ++# sys_enter_close |@@@@ 236 ++# sys_enter_open |@@@@ 222 ++# sys_enter_stat64 |@@ 132 ++# sys_enter_select |@@ 123 ++# sys_enter_rt_sigaction |@@ 107 ++# sys_enter_poll |@ 72 ++# sys_enter_write |@ 70 ++# sys_enter_mmap_pgoff |@ 58 ++# sys_enter_fstat64 | 41 ++# sys_enter_nanosleep | 23 ++# sys_enter_access | 20 ++# sys_enter_mprotect | 18 ++# sys_enter_geteuid | 17 ++# sys_enter_getegid | 16 ++# sys_enter_getuid | 16 ++# sys_enter_getgid | 16 ++# sys_enter_brk | 15 ++# sys_enter_waitpid | 11 ++# sys_enter_time | 10 ++# sys_enter_ioctl | 9 ++# sys_enter_munmap | 9 ++# sys_enter_fcntl64 | 7 ++# sys_enter_dup2 | 7 ++# sys_enter_clone | 6 ++# sys_enter_exit_group | 6 ++# sys_enter_execve | 4 ++# sys_enter_pipe | 3 ++# sys_enter_gettimeofday | 3 ++# sys_enter_getdents | 2 ++# sys_enter_getgroups | 2 ++# sys_enter_statfs64 | 2 ++# sys_enter_lseek | 2 ++# sys_enter_openat | 1 ++# sys_enter_newuname | 1 ++ +diff --git a/drivers/staging/ktap/samples/syscalls/syscalls_count_by_proc.kp b/drivers/staging/ktap/samples/syscalls/syscalls_count_by_proc.kp +new file mode 100644 +index 0000000..67d7bee +--- /dev/null ++++ b/drivers/staging/ktap/samples/syscalls/syscalls_count_by_proc.kp +@@ -0,0 +1,22 @@ ++#!/usr/bin/env ktap ++ ++s = ptable() ++ ++trace syscalls:sys_enter_* { ++ s[execname()] <<< 1 ++} ++ ++trace_end { ++ histogram(s) ++} ++ ++#Result: ++# ++#[root@jovi ktap]# ./ktap samples/syscalls_histogram2.kp ++#^C ++# value ------------- Distribution ------------- count ++# sshd |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 196 ++# iscsid |@@@@ 24 ++# sendmail |@ 9 ++ ++ +diff --git a/drivers/staging/ktap/samples/syscalls/syslatl.kp b/drivers/staging/ktap/samples/syscalls/syslatl.kp +new file mode 100644 +index 0000000..5ba3219 +--- /dev/null ++++ b/drivers/staging/ktap/samples/syscalls/syslatl.kp +@@ -0,0 +1,30 @@ ++#!/usr/bin/env ktap ++# ++# syslatl.kp syscall latency linear aggregation ++# ++# 10-Nov-2013 Brendan Gregg Created this ++ ++step = 10 # number of ms per step ++ ++self = {} ++lats = {} ++max = 0 ++ ++trace syscalls:sys_enter_* { ++ self[tid()] = gettimeofday_us() ++} ++ ++trace syscalls:sys_exit_* { ++ if (self[tid()] == nil) { return } ++ delta = (gettimeofday_us() - self[tid()]) / (step * 1000) ++ if (delta > max) { max = delta } ++ lats[delta] += 1 ++ self[tid()] = nil ++} ++ ++trace_end { ++ printf(" %8s %8s\n", "LAT(ms)+", "COUNT"); ++ for (i = 0, max, 1) { ++ printf(" %8d %8d\n", i * step, lats[i]); ++ } ++} +diff --git a/drivers/staging/ktap/samples/syscalls/syslist.kp b/drivers/staging/ktap/samples/syscalls/syslist.kp +new file mode 100644 +index 0000000..a4a3cb0 +--- /dev/null ++++ b/drivers/staging/ktap/samples/syscalls/syslist.kp +@@ -0,0 +1,31 @@ ++#!/usr/bin/env ktap ++# ++# syslist.kp syscall latency as a list with counts ++# ++# 10-Nov-2013 Brendan Gregg Created this ++ ++self = {} ++lats = {} ++order = {} # a workaround for key sorting ++ ++trace syscalls:sys_enter_* { ++ self[tid()] = gettimeofday_us() ++} ++ ++trace syscalls:sys_exit_* { ++ if (self[tid()] == nil) { return } ++ delta = gettimeofday_us() - self[tid()] ++ lats[delta] += 1 ++ order[delta] = delta ++ self[tid()] = nil ++} ++ ++trace_end { ++ printf(" %8s %8s\n", "LAT(us)", "COUNT"); ++ function cmp(v1, v2) { ++ return (v1 < v2) ++ } ++ for (lat, dummy in sort_pairs(order, cmp)) { ++ printf(" %8d %8d\n", lat, lats[lat]); ++ } ++} +diff --git a/drivers/staging/ktap/samples/tracepoints/eventcount.kp b/drivers/staging/ktap/samples/tracepoints/eventcount.kp +new file mode 100644 +index 0000000..1bd37eb +--- /dev/null ++++ b/drivers/staging/ktap/samples/tracepoints/eventcount.kp +@@ -0,0 +1,210 @@ ++#!/usr/bin/env ktap ++ ++# showing all tracepoints in histogram style ++ ++s = ptable() ++ ++trace *:* { ++ s[argname] <<< 1 ++} ++ ++trace_end { ++ histogram(s) ++} ++ ++#Results: ++#^C ++# ++# value ------------- Distribution ------------- count ++# rcu_utilization |@@@@@ 225289 ++# cpu_idle |@@@ 120168 ++# sched_wakeup |@@ 91950 ++# timer_cancel |@@ 91232 ++# timer_start |@@ 91201 ++# sched_stat_sleep |@@ 90981 ++# timer_expire_exit |@@ 90634 ++# timer_expire_entry |@@ 90625 ++# hrtimer_cancel |@ 75411 ++# hrtimer_start |@ 74946 ++# softirq_raise |@ 63117 ++# softirq_exit |@ 63109 ++# softirq_entry |@ 63094 ++# sched_switch |@ 62331 ++# sched_stat_wait |@ 60491 ++# hrtimer_expire_exit |@ 47538 ++# hrtimer_expire_entry |@ 47530 ++# sched_stat_runtime | 2780 ++# kmem_cache_free | 2684 ++# kmem_cache_alloc | 2415 ++# kfree | 2288 ++# sys_exit | 2145 ++# sys_enter | 2145 ++# sys_exit_rt_sigprocmask | 1000 ++# sys_enter_rt_sigprocmask | 1000 ++# timer_init | 912 ++# sched_stat_blocked | 685 ++# kmalloc | 667 ++# workqueue_execute_end | 621 ++# workqueue_execute_start | 621 ++# sys_enter_select | 566 ++# sys_exit_select | 566 ++# sys_enter_read | 526 ++# sys_exit_read | 526 ++# mm_page_free | 478 ++# mm_page_alloc | 427 ++# mm_page_free_batched | 382 ++# net_dev_queue | 296 ++# net_dev_xmit | 296 ++# consume_skb | 296 ++# sys_exit_write | 290 ++# sys_enter_write | 290 ++# kfree_skb | 289 ++# kmem_cache_alloc_node | 269 ++# kmalloc_node | 263 ++# sys_enter_close | 249 ++# sys_exit_close | 249 ++# hrtimer_init | 248 ++# netif_receive_skb | 242 ++# sys_enter_open | 237 ++# sys_exit_open | 237 ++# napi_poll | 226 ++# sched_migrate_task | 207 ++# sys_exit_poll | 173 ++# sys_enter_poll | 173 ++# workqueue_queue_work | 152 ++# workqueue_activate_work | 152 ++# sys_enter_stat64 | 133 ++# sys_exit_stat64 | 133 ++# sys_exit_rt_sigaction | 133 ++# sys_enter_rt_sigaction | 133 ++# irq_handler_entry | 125 ++# irq_handler_exit | 125 ++# mm_page_alloc_zone_locked | 99 ++# sys_exit_mmap_pgoff | 66 ++# sys_enter_mmap_pgoff | 66 ++# sys_exit_fstat64 | 54 ++# sys_enter_fstat64 | 54 ++# sys_enter_nanosleep | 51 ++# sys_exit_nanosleep | 51 ++# block_bio_queue | 46 ++# block_bio_remap | 46 ++# block_bio_complete | 46 ++# mix_pool_bytes | 44 ++# mm_page_pcpu_drain | 31 ++# sys_exit_time | 23 ++# sys_enter_time | 23 ++# sys_exit_access | 20 ++# sys_enter_access | 20 ++# mix_pool_bytes_nolock | 18 ++# sys_enter_mprotect | 18 ++# sys_exit_mprotect | 18 ++# sys_enter_geteuid | 17 ++# sys_exit_geteuid | 17 ++# sys_enter_munmap | 17 ++# sys_exit_munmap | 17 ++# block_getrq | 16 ++# sys_enter_getuid | 16 ++# sys_enter_getgid | 16 ++# sys_exit_getgid | 16 ++# sys_exit_getuid | 16 ++# block_rq_issue | 16 ++# scsi_dispatch_cmd_start | 16 ++# block_rq_complete | 16 ++# scsi_dispatch_cmd_done | 16 ++# sys_enter_getegid | 16 ++# sys_exit_getegid | 16 ++# block_rq_insert | 16 ++# skb_copy_datagram_iovec | 15 ++# sys_enter_brk | 15 ++# sys_exit_brk | 15 ++# credit_entropy_bits | 14 ++# wbc_writepage | 14 ++# sys_exit_clone | 12 ++# block_touch_buffer | 12 ++# sched_process_wait | 11 ++# sys_enter_waitpid | 11 ++# sys_exit_waitpid | 11 ++# writeback_written | 10 ++# writeback_start | 10 ++# writeback_queue_io | 10 ++# ext4_es_lookup_extent_enter | 9 ++# sys_enter_ioctl | 9 ++# sys_exit_ioctl | 9 ++# ext4_ext_map_blocks_enter | 9 ++# ext4_ext_map_blocks_exit | 9 ++# ext4_es_lookup_extent_exit | 9 ++# ext4_es_insert_extent | 9 ++# ext4_ext_show_extent | 8 ++# extract_entropy | 8 ++#ext4_es_find_delayed_extent_exit | 8 ++# ext4_es_find_delayed_extent_... | 8 ++# writeback_pages_written | 7 ++# sys_exit_dup2 | 7 ++# sys_enter_dup2 | 7 ++# signal_generate | 7 ++# sys_enter_fcntl64 | 7 ++# sys_exit_fcntl64 | 7 ++# global_dirty_state | 7 ++# writeback_dirty_inode_start | 7 ++# block_bio_backmerge | 7 ++# writeback_dirty_inode | 7 ++# sched_wakeup_new | 6 ++# sched_process_free | 6 ++# sys_enter_exit_group | 6 ++# task_newtask | 6 ++# sys_enter_clone | 6 ++# sched_process_fork | 6 ++# sched_process_exit | 6 ++# sys_exit_gettimeofday | 5 ++# signal_deliver | 5 ++# sys_enter_gettimeofday | 5 ++# writeback_single_inode | 4 ++# sys_enter_execve | 4 ++# task_rename | 4 ++# sched_process_exec | 4 ++# block_dirty_buffer | 4 ++# sys_exit_execve | 4 ++# block_unplug | 4 ++# sched_stat_iowait | 4 ++# writeback_single_inode_start | 4 ++# block_plug | 4 ++# writeback_write_inode | 3 ++# sys_enter_pipe | 3 ++# writeback_dirty_page | 3 ++# writeback_write_inode_start | 3 ++# ext4_mark_inode_dirty | 3 ++# ext4_journal_start | 3 ++# sys_exit_pipe | 3 ++# jbd2_drop_transaction | 2 ++# jbd2_commit_locking | 2 ++# jbd2_commit_flushing | 2 ++# jbd2_handle_start | 2 ++# jbd2_run_stats | 2 ++# sys_exit_getdents | 2 ++# jbd2_checkpoint_stats | 2 ++# sys_enter_getgroups | 2 ++# jbd2_start_commit | 2 ++# jbd2_end_commit | 2 ++# ext4_da_writepages | 2 ++# jbd2_handle_stats | 2 ++# sys_enter_statfs64 | 2 ++# sys_exit_statfs64 | 2 ++# sys_exit_getgroups | 2 ++# sys_exit_lseek | 2 ++# sys_enter_lseek | 2 ++# sys_enter_getdents | 2 ++# ext4_da_write_pages | 2 ++# jbd2_commit_logging | 2 ++# ext4_request_blocks | 1 ++# sys_exit_openat | 1 ++# ext4_discard_preallocations | 1 ++# ext4_mballoc_alloc | 1 ++# sys_enter_openat | 1 ++# ext4_da_writepages_result | 1 ++# ext4_allocate_blocks | 1 ++# sys_enter_newuname | 1 ++# ext4_da_update_reserve_space | 1 ++# ext4_get_reserved_cluster_alloc | 1 ++# sys_exit_newuname | 1 ++# writeback_wake_thread | 1 ++ +diff --git a/drivers/staging/ktap/samples/tracepoints/eventcount_by_proc.kp b/drivers/staging/ktap/samples/tracepoints/eventcount_by_proc.kp +new file mode 100644 +index 0000000..5b9547e +--- /dev/null ++++ b/drivers/staging/ktap/samples/tracepoints/eventcount_by_proc.kp +@@ -0,0 +1,57 @@ ++#!/usr/bin/env ktap ++ ++# showing all tracepoints in histogram style ++ ++s = ptable() ++ ++trace *:* { ++ s[execname()] <<< 1 ++} ++ ++trace_end { ++ histogram(s) ++} ++ ++#Results: ++#^C ++# value ------------- Distribution ------------- count ++# swapper/0 |@@@@@@@@@@@@ 354378 ++# swapper/1 |@@@@@@@@@@ 284984 ++# ps |@@@@ 115697 ++# ksmtuned |@@@ 95857 ++# iscsid |@@ 80008 ++# awk |@ 30354 ++# irqbalance | 16530 ++# rcu_sched | 15892 ++# sendmail | 14463 ++# kworker/0:1 | 10540 ++# kworker/u4:2 | 9250 ++# kworker/1:2 | 7943 ++# sleep | 7555 ++# crond | 3911 ++# ksoftirqd/0 | 3817 ++# sshd | 2849 ++# systemd-journal | 2209 ++# migration/1 | 1601 ++# migration/0 | 1350 ++# dhclient | 1343 ++# nm-dhcp-client. | 1208 ++# ksoftirqd/1 | 1064 ++# watchdog/1 | 966 ++# watchdog/0 | 964 ++# khugepaged | 776 ++# dbus-daemon | 611 ++# rpcbind | 607 ++# gdbus | 529 ++# NetworkManager | 399 ++# jbd2/dm-1-8 | 378 ++# modem-manager | 184 ++# abrt-watch-log | 157 ++# polkitd | 156 ++# rs:main Q:Reg | 153 ++# avahi-daemon | 151 ++# rsyslogd | 102 ++# systemd | 96 ++# kworker/0:1H | 45 ++# smartd | 30 ++ +diff --git a/drivers/staging/ktap/samples/tracepoints/tracepoints.kp b/drivers/staging/ktap/samples/tracepoints/tracepoints.kp +new file mode 100644 +index 0000000..5d08869 +--- /dev/null ++++ b/drivers/staging/ktap/samples/tracepoints/tracepoints.kp +@@ -0,0 +1,6 @@ ++#!/usr/bin/env ktap ++ ++trace *:* { ++ print(cpu(), pid(), execname(), argevent) ++} ++ +diff --git a/drivers/staging/ktap/samples/userspace/gcc_unwind.kp b/drivers/staging/ktap/samples/userspace/gcc_unwind.kp +new file mode 100644 +index 0000000..0890872 +--- /dev/null ++++ b/drivers/staging/ktap/samples/userspace/gcc_unwind.kp +@@ -0,0 +1,9 @@ ++#!/usr/bin/env ktap ++ ++#only tested in x86-64 system, ++#if you run this script in x86_32, change the libc path. ++ ++trace sdt:/lib/x86_64-linux-gnu/libgcc_s.so.1:unwind { ++ print(execname(), argevent) ++} ++ +diff --git a/drivers/staging/ktap/samples/userspace/glibc_func_hist.kp b/drivers/staging/ktap/samples/userspace/glibc_func_hist.kp +new file mode 100644 +index 0000000..5c30b3f +--- /dev/null ++++ b/drivers/staging/ktap/samples/userspace/glibc_func_hist.kp +@@ -0,0 +1,44 @@ ++#!/usr/bin/env ktap ++ ++#This ktap script trace all glibc functions in histogram output ++ ++#only tested in x86-64 system, ++#if you run this script in x86_32, change the libc path. ++ ++s = {} ++ ++trace probe:/lib64/libc.so.6:* { ++ s[argname] += 1 ++} ++ ++trace_end { ++ histogram(s) ++} ++ ++# Example result: ++#[root@localhost ktap]# ./ktap ./glibc_func_hist.kp ++#Tracing... Ctrl-C to end. ++#^C ++# value ------------- Distribution ------------- count ++# _IO_sputbackc | 1536 ++# __strncmp_sse2 | 1522 ++# __GI_strncmp | 1522 ++# __GI_memcpy | 1446 ++# __memcpy_sse2 | 1446 ++# _dl_mcount_wrapper_check | 1433 ++# __GI__dl_mcount_wrapper_check | 1433 ++# __gconv_transform_utf8_internal | 1429 ++# __mbrtowc | 1425 ++# mbrtoc32 | 1425 ++# __GI___mbrtowc | 1425 ++# mbrtowc | 1425 ++# __GI_mbrtowc | 1425 ++# strtouq | 1274 ++# strtoull | 1274 ++# strtoul | 1274 ++# __ctype_get_mb_cur_max | 984 ++# ____strtoull_l_internal | 970 ++# __GI_____strtoul_l_internal | 970 ++# __GI__IO_sputbackc | 960 ++# ... | ++ +diff --git a/drivers/staging/ktap/samples/userspace/glibc_sdt.kp b/drivers/staging/ktap/samples/userspace/glibc_sdt.kp +new file mode 100644 +index 0000000..626151a +--- /dev/null ++++ b/drivers/staging/ktap/samples/userspace/glibc_sdt.kp +@@ -0,0 +1,11 @@ ++#!/usr/bin/env ktap ++ ++#This ktap script trace all sdt notes in glibc ++ ++#only tested in x86-64 system, ++#if you run this script in x86_32, change the libc path. ++ ++trace sdt:/lib64/libc.so.6:* { ++ print(execname(), argevent) ++} ++ +diff --git a/drivers/staging/ktap/samples/userspace/glibc_trace.kp b/drivers/staging/ktap/samples/userspace/glibc_trace.kp +new file mode 100644 +index 0000000..ca03089 +--- /dev/null ++++ b/drivers/staging/ktap/samples/userspace/glibc_trace.kp +@@ -0,0 +1,11 @@ ++#!/usr/bin/env ktap ++ ++#This ktap script trace all functions in glibc ++ ++#only tested in x86-64 system, ++#if you run this script in x86_32, change the libc path. ++ ++trace probe:/lib64/libc.so.6:* { ++ print(execname(), argevent) ++} ++ +diff --git a/drivers/staging/ktap/samples/userspace/malloc_free.kp b/drivers/staging/ktap/samples/userspace/malloc_free.kp +new file mode 100644 +index 0000000..7c7da25 +--- /dev/null ++++ b/drivers/staging/ktap/samples/userspace/malloc_free.kp +@@ -0,0 +1,20 @@ ++#!/usr/bin/env ktap ++ ++#only tested in x86-64 system, ++#if you run this script in x86_32, change the libc path. ++ ++trace probe:/lib64/libc.so.6:malloc { ++ print("malloc entry:", execname()) ++} ++ ++trace probe:/lib64/libc.so.6:malloc%return { ++ print("malloc exit:", execname()) ++} ++ ++trace probe:/lib64/libc.so.6:free { ++ print("free entry:", execname()) ++} ++ ++trace probe:/lib64/libc.so.6:free%return { ++ print("free exit:", execname()) ++} +diff --git a/drivers/staging/ktap/samples/userspace/malloc_size_hist.kp b/drivers/staging/ktap/samples/userspace/malloc_size_hist.kp +new file mode 100644 +index 0000000..9df89e6 +--- /dev/null ++++ b/drivers/staging/ktap/samples/userspace/malloc_size_hist.kp +@@ -0,0 +1,22 @@ ++#!/usr/bin/env ktap ++ ++# Aggregate system or process malloc size ++ ++# only tested in x86-64 system, ++# if you run this script in x86_32, change the libc path and register name. ++# ++# Examples: ++# ++# ktap malloc_size_hist.kp ++# ktap malloc_size_hist.kp -- ls ++ ++m = {} ++ ++trace probe:/lib64/libc.so.6:malloc size=%di { ++ #arg2 is argument "size" of malloc function ++ m[arg2] += 1 ++} ++ ++trace_end { ++ histogram(m) ++} +diff --git a/drivers/staging/ktap/test/arg.kp b/drivers/staging/ktap/test/arg.kp +new file mode 100644 +index 0000000..0cf16eb +--- /dev/null ++++ b/drivers/staging/ktap/test/arg.kp +@@ -0,0 +1,24 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#-----------------------------------------# ++ ++if (!arg[0]) { ++ failed() ++} ++ ++if (arg[1] != 1) { ++ failed() ++} ++ ++if (arg[2] != "testing") { ++ failed() ++} ++ ++if (arg[3] != "2 3 4") { ++ failed() ++} +diff --git a/drivers/staging/ktap/test/arithmetic.kp b/drivers/staging/ktap/test/arithmetic.kp +new file mode 100644 +index 0000000..5063e50 +--- /dev/null ++++ b/drivers/staging/ktap/test/arithmetic.kp +@@ -0,0 +1,50 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#-----------------------------------------# ++ ++if (1 > 2) { ++ failed() ++} ++ ++if (200 < 100) { ++ failed() ++} ++ ++a = 4 ++b = 5 ++ ++if ((a + b) != 9) { ++ failed() ++} ++ ++if ((a - b) != -1) { ++ failed() ++} ++ ++if ((a % b) != 4) { ++ failed() ++} ++ ++if ((a / b) != 0) { ++ failed() ++} ++ ++ ++#below checking only valid for 64-bit system ++ ++c = 0x1234567812345678 ++d = 0x2 ++ ++if (c + d != 0x123456781234567a) { ++ failed() ++} ++ ++if (-1 != 0xffffffffffffffff) { ++ failed() ++} ++ +diff --git a/drivers/staging/ktap/test/benchmark/sembench.c b/drivers/staging/ktap/test/benchmark/sembench.c +new file mode 100644 +index 0000000..0811934 +--- /dev/null ++++ b/drivers/staging/ktap/test/benchmark/sembench.c +@@ -0,0 +1,556 @@ ++/* ++ * copyright Oracle 2007. Licensed under GPLv2 ++ * To compile: gcc -Wall -o sembench sembench.c -lpthread ++ * ++ * usage: sembench -t thread count -w wakenum -r runtime -o op ++ * op can be: 0 (ipc sem) 1 (nanosleep) 2 (futexes) ++ * ++ * example: ++ * sembench -t 1024 -w 512 -r 60 -o 2 ++ * runs 1024 threads, waking up 512 at a time, running for 60 seconds using ++ * futex locking. ++ * ++ */ ++#define _GNU_SOURCE ++#define _POSIX_C_SOURCE 199309 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define VERSION "0.2" ++ ++/* futexes have been around since 2.5.something, but it still seems I ++ * need to make my own syscall. Sigh. ++ */ ++#define FUTEX_WAIT 0 ++#define FUTEX_WAKE 1 ++#define FUTEX_FD 2 ++#define FUTEX_REQUEUE 3 ++#define FUTEX_CMP_REQUEUE 4 ++#define FUTEX_WAKE_OP 5 ++static inline int futex (int *uaddr, int op, int val, ++ const struct timespec *timeout, ++ int *uaddr2, int val3) ++{ ++ return syscall(__NR_futex, uaddr, op, val, timeout, uaddr2, val3); ++} ++ ++static void smp_mb(void) ++{ ++ __sync_synchronize(); ++} ++ ++static int all_done = 0; ++static int timeout_test = 0; ++ ++#define SEMS_PERID 250 ++ ++struct sem_operations; ++ ++struct lockinfo { ++ unsigned long id; ++ unsigned long index; ++ int data; ++ pthread_t tid; ++ struct lockinfo *next; ++ struct sem_operations *ops; ++ unsigned long ready; ++}; ++ ++struct sem_wakeup_info { ++ int wakeup_count; ++ struct sembuf sb[SEMS_PERID]; ++}; ++ ++struct sem_operations { ++ void (*wait)(struct lockinfo *l); ++ int (*wake)(struct sem_wakeup_info *wi, int num_semids, int num); ++ void (*setup)(struct sem_wakeup_info **wi, int num_semids); ++ void (*cleanup)(int num_semids); ++ char *name; ++}; ++ ++int *semid_lookup = NULL; ++ ++pthread_mutex_t worklist_mutex = PTHREAD_MUTEX_INITIALIZER; ++static unsigned long total_burns = 0; ++static unsigned long min_burns = ~0UL; ++static unsigned long max_burns = 0; ++ ++/* currently running threads */ ++static int thread_count = 0; ++ ++struct lockinfo *worklist = NULL; ++static int workers_started = 0; ++ ++/* total threads started */ ++static int num_threads = 2048; ++ ++static void worklist_add(struct lockinfo *l) ++{ ++ smp_mb(); ++ l->ready = 1; ++} ++ ++static struct lockinfo *worklist_rm(void) ++{ ++ static int last_index = 0; ++ int i; ++ struct lockinfo *l; ++ ++ for (i = 0; i < num_threads; i++) { ++ int test = (last_index + i) % num_threads; ++ ++ l = worklist + test; ++ smp_mb(); ++ if (l->ready) { ++ l->ready = 0; ++ last_index = test; ++ return l; ++ } ++ } ++ return NULL; ++} ++ ++/* ipc semaphore post& wait */ ++void wait_ipc_sem(struct lockinfo *l) ++{ ++ struct sembuf sb; ++ int ret; ++ struct timespec *tvp = NULL; ++ struct timespec tv = { 0, 1 }; ++ ++ sb.sem_num = l->index; ++ sb.sem_flg = 0; ++ ++ sb.sem_op = -1; ++ l->data = 1; ++ ++ if (timeout_test && (l->id % 5) == 0) ++ tvp = &tv; ++ ++ worklist_add(l); ++ ret = semtimedop(semid_lookup[l->id], &sb, 1, tvp); ++ ++ while(l->data != 0 && tvp) { ++ struct timespec tv2 = { 0, 500 }; ++ nanosleep(&tv2, NULL); ++ } ++ ++ if (l->data != 0) { ++ if (tvp) ++ return; ++ fprintf(stderr, "wakeup without data update\n"); ++ exit(1); ++ } ++ if (ret) { ++ if (errno == EAGAIN && tvp) ++ return; ++ perror("semtimed op"); ++ exit(1); ++ } ++} ++ ++int ipc_wake_some(struct sem_wakeup_info *wi, int num_semids, int num) ++{ ++ int i; ++ int ret; ++ struct lockinfo *l; ++ int found = 0; ++ ++ for (i = 0; i < num_semids; i++) { ++ wi[i].wakeup_count = 0; ++ } ++ while(num > 0) { ++ struct sembuf *sb; ++ l = worklist_rm(); ++ if (!l) ++ break; ++ if (l->data != 1) ++ fprintf(stderr, "warning, lockinfo data was %d\n", ++ l->data); ++ l->data = 0; ++ sb = wi[l->id].sb + wi[l->id].wakeup_count; ++ sb->sem_num = l->index; ++ sb->sem_op = 1; ++ sb->sem_flg = IPC_NOWAIT; ++ wi[l->id].wakeup_count++; ++ found++; ++ num--; ++ } ++ if (!found) ++ return 0; ++ for (i = 0; i < num_semids; i++) { ++ int wakeup_total; ++ int cur; ++ int offset = 0; ++ if (!wi[i].wakeup_count) ++ continue; ++ wakeup_total = wi[i].wakeup_count; ++ while(wakeup_total > 0) { ++ cur = wakeup_total > 64 ? 64 : wakeup_total; ++ ret = semtimedop(semid_lookup[i], wi[i].sb + offset, ++ cur, NULL); ++ if (ret) { ++ perror("semtimedop"); ++ exit(1); ++ } ++ offset += cur; ++ wakeup_total -= cur; ++ } ++ } ++ return found; ++} ++ ++void setup_ipc_sems(struct sem_wakeup_info **wi, int num_semids) ++{ ++ int i; ++ *wi = malloc(sizeof(**wi) * num_semids); ++ semid_lookup = malloc(num_semids * sizeof(int)); ++ for(i = 0; i < num_semids; i++) { ++ semid_lookup[i] = semget(IPC_PRIVATE, SEMS_PERID, ++ IPC_CREAT | 0777); ++ if (semid_lookup[i] < 0) { ++ perror("semget"); ++ exit(1); ++ } ++ } ++ sleep(10); ++} ++ ++void cleanup_ipc_sems(int num) ++{ ++ int i; ++ for (i = 0; i < num; i++) { ++ semctl(semid_lookup[i], 0, IPC_RMID); ++ } ++} ++ ++struct sem_operations ipc_sem_ops = { ++ .wait = wait_ipc_sem, ++ .wake = ipc_wake_some, ++ .setup = setup_ipc_sems, ++ .cleanup = cleanup_ipc_sems, ++ .name = "ipc sem operations", ++}; ++ ++/* futex post & wait */ ++void wait_futex_sem(struct lockinfo *l) ++{ ++ int ret; ++ l->data = 1; ++ worklist_add(l); ++ while(l->data == 1) { ++ ret = futex(&l->data, FUTEX_WAIT, 1, NULL, NULL, 0); ++ /* ++ if (ret && ret != EWOULDBLOCK) { ++ perror("futex wait"); ++ exit(1); ++ }*/ ++ } ++} ++ ++int futex_wake_some(struct sem_wakeup_info *wi, int num_semids, int num) ++{ ++ int i; ++ int ret; ++ struct lockinfo *l; ++ int found = 0; ++ ++ for (i = 0; i < num; i++) { ++ l = worklist_rm(); ++ if (!l) ++ break; ++ if (l->data != 1) ++ fprintf(stderr, "warning, lockinfo data was %d\n", ++ l->data); ++ l->data = 0; ++ ret = futex(&l->data, FUTEX_WAKE, 1, NULL, NULL, 0); ++ if (ret < 0) { ++ perror("futex wake"); ++ exit(1); ++ } ++ found++; ++ } ++ return found; ++} ++ ++void setup_futex_sems(struct sem_wakeup_info **wi, int num_semids) ++{ ++ return; ++} ++ ++void cleanup_futex_sems(int num) ++{ ++ return; ++} ++ ++struct sem_operations futex_sem_ops = { ++ .wait = wait_futex_sem, ++ .wake = futex_wake_some, ++ .setup = setup_futex_sems, ++ .cleanup = cleanup_futex_sems, ++ .name = "futex sem operations", ++}; ++ ++/* nanosleep sems here */ ++void wait_nanosleep_sem(struct lockinfo *l) ++{ ++ int ret; ++ struct timespec tv = { 0, 1000000 }; ++ int count = 0; ++ ++ l->data = 1; ++ worklist_add(l); ++ while(l->data) { ++ ret = nanosleep(&tv, NULL); ++ if (ret) { ++ perror("nanosleep"); ++ exit(1); ++ } ++ count++; ++ } ++} ++ ++int nanosleep_wake_some(struct sem_wakeup_info *wi, int num_semids, int num) ++{ ++ int i; ++ struct lockinfo *l; ++ ++ for (i = 0; i < num; i++) { ++ l = worklist_rm(); ++ if (!l) ++ break; ++ if (l->data != 1) ++ fprintf(stderr, "warning, lockinfo data was %d\n", ++ l->data); ++ l->data = 0; ++ } ++ return i; ++} ++ ++void setup_nanosleep_sems(struct sem_wakeup_info **wi, int num_semids) ++{ ++ return; ++} ++ ++void cleanup_nanosleep_sems(int num) ++{ ++ return; ++} ++ ++struct sem_operations nanosleep_sem_ops = { ++ .wait = wait_nanosleep_sem, ++ .wake = nanosleep_wake_some, ++ .setup = setup_nanosleep_sems, ++ .cleanup = cleanup_nanosleep_sems, ++ .name = "nano sleep sem operations", ++}; ++ ++void *worker(void *arg) ++{ ++ struct lockinfo *l = (struct lockinfo *)arg; ++ int burn_count = 0; ++ pthread_t tid = pthread_self(); ++ size_t pagesize = getpagesize(); ++ char *buf = malloc(pagesize); ++ ++ if (!buf) { ++ perror("malloc"); ++ exit(1); ++ } ++ ++ l->tid = tid; ++ workers_started = 1; ++ smp_mb(); ++ ++ while(!all_done) { ++ l->ops->wait(l); ++ if (all_done) ++ break; ++ burn_count++; ++ } ++ pthread_mutex_lock(&worklist_mutex); ++ total_burns += burn_count; ++ if (burn_count < min_burns) ++ min_burns = burn_count; ++ if (burn_count > max_burns) ++ max_burns = burn_count; ++ thread_count--; ++ pthread_mutex_unlock(&worklist_mutex); ++ return (void *)0; ++} ++ ++void print_usage(void) ++{ ++ printf("usage: sembench [-t threads] [-w wake incr] [-r runtime]"); ++ printf(" [-o num] (0=ipc, 1=nanosleep, 2=futex)\n"); ++ exit(1); ++} ++ ++#define NUM_OPERATIONS 3 ++struct sem_operations *allops[NUM_OPERATIONS] = { &ipc_sem_ops, ++ &nanosleep_sem_ops, ++ &futex_sem_ops}; ++ ++int main(int ac, char **av) { ++ int ret; ++ int i; ++ int semid = 0; ++ int sem_num = 0; ++ int burn_count = 0; ++ struct sem_wakeup_info *wi = NULL; ++ struct timeval start; ++ struct timeval now; ++ int num_semids = 0; ++ int wake_num = 256; ++ int run_secs = 30; ++ int pagesize = getpagesize(); ++ char *buf = malloc(pagesize); ++ struct sem_operations *ops = allops[0]; ++ cpu_set_t cpu_mask; ++ cpu_set_t target_mask; ++ int target_cpu = 0; ++ int max_cpu = -1; ++ ++ if (!buf) { ++ perror("malloc"); ++ exit(1); ++ } ++ for (i = 1; i < ac; i++) { ++ if (strcmp(av[i], "-t") == 0) { ++ if (i == ac -1) ++ print_usage(); ++ num_threads = atoi(av[i+1]); ++ i++; ++ } else if (strcmp(av[i], "-w") == 0) { ++ if (i == ac -1) ++ print_usage(); ++ wake_num = atoi(av[i+1]); ++ i++; ++ } else if (strcmp(av[i], "-r") == 0) { ++ if (i == ac -1) ++ print_usage(); ++ run_secs = atoi(av[i+1]); ++ i++; ++ } else if (strcmp(av[i], "-o") == 0) { ++ int index; ++ if (i == ac -1) ++ print_usage(); ++ index = atoi(av[i+1]); ++ if (index >= NUM_OPERATIONS) { ++ fprintf(stderr, "invalid operations %d\n", ++ index); ++ exit(1); ++ } ++ ops = allops[index]; ++ i++; ++ } else if (strcmp(av[i], "-T") == 0) { ++ timeout_test = 1; ++ } else if (strcmp(av[i], "-h") == 0) { ++ print_usage(); ++ } ++ } ++ num_semids = (num_threads + SEMS_PERID - 1) / SEMS_PERID; ++ ops->setup(&wi, num_semids); ++ ++ ret = sched_getaffinity(0, sizeof(cpu_set_t), &cpu_mask); ++ if (ret) { ++ perror("sched_getaffinity"); ++ exit(1); ++ } ++ for (i = 0; i < CPU_SETSIZE; i++) ++ if (CPU_ISSET(i, &cpu_mask)) ++ max_cpu = i; ++ if (max_cpu == -1) { ++ fprintf(stderr, "sched_getaffinity returned empty mask\n"); ++ exit(1); ++ } ++ ++ CPU_ZERO(&target_mask); ++ ++ worklist = malloc(sizeof(*worklist) * num_threads); ++ memset(worklist, 0, sizeof(*worklist) * num_threads); ++ ++ for (i = 0; i < num_threads; i++) { ++ struct lockinfo *l; ++ pthread_t tid; ++ thread_count++; ++ l = worklist + i; ++ if (!l) { ++ perror("malloc"); ++ exit(1); ++ } ++ l->id = semid; ++ l->index = sem_num++; ++ l->ops = ops; ++ if (sem_num >= SEMS_PERID) { ++ semid++; ++ sem_num = 0; ++ } ++ ret = pthread_create(&tid, NULL, worker, (void *)l); ++ if (ret) { ++ perror("pthread_create"); ++ exit(1); ++ } ++ ++ while (!CPU_ISSET(target_cpu, &cpu_mask)) { ++ target_cpu++; ++ if (target_cpu > max_cpu) ++ target_cpu = 0; ++ } ++ CPU_SET(target_cpu, &target_mask); ++ ret = pthread_setaffinity_np(tid, sizeof(cpu_set_t), ++ &target_mask); ++ CPU_CLR(target_cpu, &target_mask); ++ target_cpu++; ++ ++ ret = pthread_detach(tid); ++ if (ret) { ++ perror("pthread_detach"); ++ exit(1); ++ } ++ } ++ while(!workers_started) { ++ smp_mb(); ++ usleep(200); ++ } ++ gettimeofday(&start, NULL); ++ fprintf(stderr, "main loop going\n"); ++ while(1) { ++ ops->wake(wi, num_semids, wake_num); ++ burn_count++; ++ gettimeofday(&now, NULL); ++ if (now.tv_sec - start.tv_sec >= run_secs) ++ break; ++ } ++ fprintf(stderr, "all done\n"); ++ all_done = 1; ++ while(thread_count > 0) { ++ ops->wake(wi, num_semids, wake_num); ++ usleep(200); ++ } ++ printf("%d threads, waking %d at a time\n", num_threads, wake_num); ++ printf("using %s\n", ops->name); ++ printf("main thread burns: %d\n", burn_count); ++ printf("worker burn count total %lu min %lu max %lu avg %lu\n", ++ total_burns, min_burns, max_burns, total_burns / num_threads); ++ printf("run time %d seconds %lu worker burns per second\n", ++ (int)(now.tv_sec - start.tv_sec), ++ total_burns / (now.tv_sec - start.tv_sec)); ++ ops->cleanup(num_semids); ++ return 0; ++} ++ +diff --git a/drivers/staging/ktap/test/benchmark/test.sh b/drivers/staging/ktap/test/benchmark/test.sh +new file mode 100644 +index 0000000..f29a882 +--- /dev/null ++++ b/drivers/staging/ktap/test/benchmark/test.sh +@@ -0,0 +1,26 @@ ++#!/bin/sh ++ ++gcc -o sembench sembench.c -O2 -lpthread ++ ++COMMAND="./sembench -t 200 -w 20 -r 30 -o 2" ++ ++echo -e "\n\t\tPass 1 without tracing" ++$COMMAND ++echo -e "\n\t\tPass 2 without tracing" ++$COMMAND ++echo -e "\n\t\tPass 3 without tracing" ++$COMMAND ++ ++echo "" ++ ++../../ktap -e 'trace syscalls:sys_*_futex {}' & ++ ++echo -e "\n\t\tPass 1 with tracing" ++$COMMAND ++echo -e "\n\t\tPass 2 with tracing" ++$COMMAND ++echo -e "\n\t\tPass 3 with tracing" ++$COMMAND ++ ++pkill ktap ++rm -rf ./sembench +diff --git a/drivers/staging/ktap/test/concat.kp b/drivers/staging/ktap/test/concat.kp +new file mode 100644 +index 0000000..be77bb7 +--- /dev/null ++++ b/drivers/staging/ktap/test/concat.kp +@@ -0,0 +1,15 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#----------------------------------------# ++ ++a = "123" ++b = "456" ++ ++if (a..b != "123456") { ++ failed() ++} +diff --git a/drivers/staging/ktap/test/count.kp b/drivers/staging/ktap/test/count.kp +new file mode 100644 +index 0000000..26f962c +--- /dev/null ++++ b/drivers/staging/ktap/test/count.kp +@@ -0,0 +1,20 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#---------------------------------------# ++ ++t = {} ++ ++t["key"] += 1 ++if (t["key"] != 1) { ++ failed() ++} ++ ++t["key"] += 1 ++if (t["key"] != 2) { ++ failed() ++} +diff --git a/drivers/staging/ktap/test/ffi/.gitignore b/drivers/staging/ktap/test/ffi/.gitignore +new file mode 100644 +index 0000000..3fdfdc1 +--- /dev/null ++++ b/drivers/staging/ktap/test/ffi/.gitignore +@@ -0,0 +1,2 @@ ++cparser_test ++Module.symvers +diff --git a/drivers/staging/ktap/test/ffi/Makefile b/drivers/staging/ktap/test/ffi/Makefile +new file mode 100644 +index 0000000..fd7016f +--- /dev/null ++++ b/drivers/staging/ktap/test/ffi/Makefile +@@ -0,0 +1,46 @@ ++obj-m += ktap_ffi_test.o ++ ++all: funct_mod cparser_test ++ ++funct_mod: ++ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules ++ ++INC=../../include ++U_DIR=../../userspace ++RUNTIME=../../runtime ++U_FFI_DIR=$(U_DIR)/ffi ++CPARSER_FILES=cparser.o ctype.o ffi_type.o ++KTAPC_CFLAGS = -Wall -O2 ++ ++cparser.o: $(U_FFI_DIR)/cparser.c $(INC)/* ++ $(QUIET_CC)$(CC) -DCONFIG_KTAP_FFI -o $@ -c $< ++ ++ctype.o: $(U_FFI_DIR)/ctype.c $(INC)/* ++ $(QUIET_CC)$(CC) -DCONFIG_KTAP_FFI -o $@ -c $< ++ ++ffi_type.o: $(RUNTIME)/ffi/ffi_type.c $(INC)/* ++ $(QUIET_CC)$(CC) -DCONFIG_KTAP_FFI -o $@ -c $< ++ ++cparser_test: cparser_test.c $(CPARSER_FILES) $(INC)/* ++ $(QUIET_CC)$(CC) -DCONFIG_KTAP_FFI -I$(INC) -I$(U_DIR) $(KTAPC_CFLAGS) \ ++ -o $@ $< $(CPARSER_FILES) ++ ++load: ++ insmod ktap_ffi_test.ko ++ ++unload: ++ rmmod ktap_ffi_test ++ ++test: all ++ @echo "testing cparser:" ++ ./cparser_test ++ @echo "testing ffi module:" ++ rmmod ktap_ffi_test > /dev/null 2>&1 || true ++ insmod ktap_ffi_test.ko ++ ../../ktap ffi_test.kp ++ rmmod ktap_ffi_test.ko ++ @echo "[*] all ffi tests passed." ++ ++clean: ++ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean ++ rm -rf cparser_test +diff --git a/drivers/staging/ktap/test/ffi/cparser_test.c b/drivers/staging/ktap/test/ffi/cparser_test.c +new file mode 100644 +index 0000000..21a7814 +--- /dev/null ++++ b/drivers/staging/ktap/test/ffi/cparser_test.c +@@ -0,0 +1,322 @@ ++#include ++#include ++#include ++#include ++ ++#include "ktap_types.h" ++#include "ktap_opcodes.h" ++#include "../../userspace/ktapc.h" ++#include "cparser.h" ++ ++void ffi_cparser_init(void); ++void ffi_cparser_free(void); ++int ffi_cdef(const char *s); ++ ++static cp_csymbol_state *csym_state; ++ ++#define cs_nr (csym_state->cs_nr) ++#define cs_arr_size (csym_state->cs_arr_size) ++#define cs_arr (csym_state->cs_arr) ++ ++ ++#define DO_TEST(name) do { \ ++ ffi_cparser_init(); \ ++ int ret; \ ++ printf("[*] start "#name" test... "); \ ++ ret = test_##name(); \ ++ if (ret) \ ++ fprintf(stderr, "\n[!] "#name" test failed.\n");\ ++ else \ ++ printf(" passed.\n"); \ ++ ffi_cparser_free(); \ ++} while (0) ++ ++#define assert_csym_arr_type(cs_arr, n, t) do { \ ++ csymbol *ncs; \ ++ ncs = &cs_arr[n]; \ ++ assert(ncs->type == t); \ ++} while (0) ++ ++#define assert_fret_type(fcs, t) do { \ ++ csymbol *ncs; \ ++ ncs = &cs_arr[fcs->ret_id]; \ ++ assert(ncs->type == t); \ ++} while (0) ++ ++#define assert_farg_type(fcs, n, t) do { \ ++ csymbol *ncs; \ ++ ncs = &cs_arr[fcs->arg_ids[n]]; \ ++ assert(ncs->type == t); \ ++} while (0) ++ ++ ++ ++ ++/* mock find_kernel_symbol */ ++unsigned long find_kernel_symbol(const char *symbol) ++{ ++ return 0xdeadbeef; ++} ++ ++int lookup_csymbol_id_by_name(char *name) ++{ ++ int i; ++ ++ for (i = 0; i < cs_nr; i++) { ++ if (!strcmp(name, cs_arr[i].name)) { ++ return i; ++ } ++ } ++ ++ return -1; ++} ++ ++int test_func_sched_clock() ++{ ++ int idx; ++ csymbol *cs; ++ csymbol_func *fcs; ++ ++ ffi_cdef("unsigned long long sched_clock();"); ++ ++ csym_state = ctype_get_csym_state(); ++ assert(cs_arr); ++ ++ idx = lookup_csymbol_id_by_name("sched_clock"); ++ assert(idx >= 0); ++ cs = &cs_arr[idx]; ++ assert(cs->type == FFI_FUNC); ++ ++ fcs = csym_func(cs); ++ ++ /* check return type */ ++ assert_fret_type(fcs, FFI_UINT64); ++ ++ /* check arguments */ ++ assert(fcs->arg_nr == 0); ++ ++ return 0; ++} ++ ++int test_func_funct_module() ++{ ++ int idx; ++ csymbol *cs; ++ csymbol_func *fcs; ++ ++ ffi_cdef("void funct_void();"); ++ ffi_cdef("int funct_int1(unsigned char a, char b, unsigned short c, " ++ "short d);"); ++ ffi_cdef("long long funct_int2(unsigned int a, int b, " ++ "unsigned long c, long d, unsigned long long e, " ++ "long long f, long long g);"); ++ ffi_cdef("void *funct_pointer1(char *a);"); ++ ++ csym_state = ctype_get_csym_state(); ++ assert(cs_arr); ++ ++ /* check funct_void function */ ++ idx = lookup_csymbol_id_by_name("funct_void"); ++ assert(idx >= 0); ++ cs = &cs_arr[idx]; ++ assert(cs->type == FFI_FUNC); ++ fcs = csym_func(cs); ++ ++ /* check return type */ ++ assert_fret_type(fcs, FFI_VOID); ++ ++ /* check arguments */ ++ assert(fcs->arg_nr == 0); ++ ++ ++ ++ /* check funct_int1 function */ ++ idx = lookup_csymbol_id_by_name("funct_int1"); ++ assert(idx >= 0); ++ cs = &cs_arr[idx]; ++ assert(cs); ++ assert(cs->type == FFI_FUNC); ++ fcs = csym_func(cs); ++ ++ /* check return type */ ++ assert_fret_type(fcs, FFI_INT32); ++ ++ /* check arguments */ ++ assert(fcs->arg_nr == 4); ++ assert_farg_type(fcs, 0, FFI_UINT8); ++ assert_farg_type(fcs, 1, FFI_INT8); ++ assert_farg_type(fcs, 2, FFI_UINT16); ++ assert_farg_type(fcs, 3, FFI_INT16); ++ ++ ++ ++ /* check funct_int2 function */ ++ idx = lookup_csymbol_id_by_name("funct_int2"); ++ assert(idx >= 0); ++ cs = &cs_arr[idx]; ++ assert(cs); ++ assert(cs->type == FFI_FUNC); ++ fcs = csym_func(cs); ++ ++ /* check return type */ ++ assert_fret_type(fcs, FFI_INT64); ++ ++ /* check arguments */ ++ assert(fcs->arg_nr == 7); ++ assert_farg_type(fcs, 0, FFI_UINT32); ++ assert_farg_type(fcs, 1, FFI_INT32); ++ assert_farg_type(fcs, 2, FFI_UINT64); ++ assert_farg_type(fcs, 3, FFI_INT64); ++ assert_farg_type(fcs, 4, FFI_UINT64); ++ assert_farg_type(fcs, 5, FFI_INT64); ++ assert_farg_type(fcs, 6, FFI_INT64); ++ ++ ++ ++ /* check funct_pointer1 function */ ++ idx = lookup_csymbol_id_by_name("funct_pointer1"); ++ assert(idx >= 0); ++ cs = &cs_arr[idx]; ++ assert(cs); ++ assert(cs->type == FFI_FUNC); ++ fcs = csym_func(cs); ++ ++ /* check return type */ ++ assert_fret_type(fcs, FFI_PTR); ++ ++ /* check arguments */ ++ assert(fcs->arg_nr == 1); ++ assert_farg_type(fcs, 0, FFI_PTR); ++ /*@TODO check pointer dereference type 18.11 2013 (houqp)*/ ++ ++ return 0; ++} ++ ++int test_struct_timespec() ++{ ++ int idx; ++ csymbol *cs; ++ csymbol_struct *stcs; ++ ++ ffi_cdef("struct timespec { long ts_sec; long ts_nsec; };"); ++ ++ csym_state = ctype_get_csym_state(); ++ assert(cs_arr); ++ ++ idx = lookup_csymbol_id_by_name("struct timespec"); ++ assert(idx >= 0); ++ cs = &cs_arr[idx]; ++ assert(cs); ++ assert(cs->type == FFI_STRUCT); ++ ++ stcs = csym_struct(cs); ++ assert(stcs->memb_nr == 2); ++ ++ return 0; ++} ++ ++int test_func_time_to_tm() ++{ ++ int idx; ++ csymbol *cs, *arg_cs; ++ csymbol_struct *stcs; ++ csymbol_func *fcs; ++ ++ ffi_cdef("typedef long time_t;"); ++ ffi_cdef("struct tm { " ++ "int tm_sec;" ++ "int tm_min;" ++ "int tm_hour;" ++ "int tm_mday;" ++ "int tm_mon;" ++ "long tm_year;" ++ "int tm_wday;" ++ "int tm_yday;" ++ "};"); ++ ffi_cdef("void time_to_tm(time_t totalsecs, int offset, struct tm *result);"); ++ ++ csym_state = ctype_get_csym_state(); ++ assert(cs_arr); ++ ++ idx = lookup_csymbol_id_by_name("struct tm"); ++ assert(idx >= 0); ++ cs = cp_id_to_csym(idx); ++ assert(cs); ++ assert(cs->type == FFI_STRUCT); ++ ++ stcs = csym_struct(cs); ++ assert(stcs->memb_nr == 8); ++ ++ ++ idx = lookup_csymbol_id_by_name("time_to_tm"); ++ assert(idx >= 0); ++ cs = cp_id_to_csym(idx); ++ assert(cs); ++ assert(cs->type == FFI_FUNC); ++ ++ fcs = csym_func(cs); ++ assert(csymf_arg_nr(fcs) == 3); ++ /* check first argument */ ++ assert_farg_type(fcs, 0, FFI_INT64); ++ ++ /* check second argument */ ++ assert_farg_type(fcs, 1, FFI_INT32); ++ /* check third argument */ ++ assert_farg_type(fcs, 2, FFI_PTR); ++ arg_cs = cp_csymf_arg(fcs, 2); ++ assert(!strcmp(csym_name(arg_cs), "struct tm *")); ++ assert(csym_ptr_deref_id(arg_cs) == ++ lookup_csymbol_id_by_name("struct tm")); ++ ++ return 0; ++} ++ ++int test_pointer_symbols() ++{ ++ csymbol_func *fcs_foo, *fcs_bar; ++ ++ /* int pointer symbol should be resolved to the same id */ ++ ffi_cdef("void foo(int *a);"); ++ ffi_cdef("int *bar(void);"); ++ ++ csym_state = ctype_get_csym_state(); ++ assert(cs_arr); ++ ++ fcs_foo = csym_func(cp_id_to_csym(lookup_csymbol_id_by_name("foo"))); ++ fcs_bar = csym_func(cp_id_to_csym(lookup_csymbol_id_by_name("bar"))); ++ ++ assert(csymf_arg_ids(fcs_foo)[0] == csymf_ret_id(fcs_bar)); ++ assert(cp_csymf_arg(fcs_foo, 0) == cp_csymf_ret(fcs_bar)); ++ ++ return 0; ++} ++ ++int test_var_arg_function() ++{ ++ csymbol_func *fcs; ++ ++ ffi_cdef("int printk(char *fmt, ...);"); ++ ++ fcs = csym_func(cp_id_to_csym(lookup_csymbol_id_by_name("printk"))); ++ ++ /* var arg function needs void * type argument type checking */ ++ assert(lookup_csymbol_id_by_name("void *") >= 0); ++ ++ assert_fret_type(fcs, FFI_INT32); ++ assert_farg_type(fcs, 0, FFI_PTR); ++ assert(fcs->has_var_arg); ++ ++ return 0; ++} ++ ++int main (int argc, char *argv[]) ++{ ++ DO_TEST(func_sched_clock); ++ DO_TEST(func_funct_module); ++ DO_TEST(struct_timespec); ++ DO_TEST(func_time_to_tm); ++ DO_TEST(pointer_symbols); ++ DO_TEST(var_arg_function); ++ ++ return 0; ++} +diff --git a/drivers/staging/ktap/test/ffi/ffi_test.kp b/drivers/staging/ktap/test/ffi/ffi_test.kp +new file mode 100644 +index 0000000..20a9a5e +--- /dev/null ++++ b/drivers/staging/ktap/test/ffi/ffi_test.kp +@@ -0,0 +1,47 @@ ++function failed(msg) { ++ printf("failed: " .. msg); ++ printf("\n") ++ exit(-1); ++} ++ ++ ++cdef[[ ++ void ffi_test_void(); ++ int ffi_test_int1(unsigned char a, char b, unsigned short c, short d); ++ long long ffi_test_int2(unsigned int a, int b, unsigned long c, long d, ++ unsigned long long e, long long f, long long g); ++ void *ffi_test_pointer1(char *a); ++ long long ffi_test_var_arg(int n, ...); ++ unsigned long long ffi_test_sched_clock(void); ++]] ++ ++ ++ret = C.ffi_test_void() ++if (ret != nil) { ++ failed("ffi_test_void should return nil") ++} ++ ++ret = C.ffi_test_int1(1111, 1111, 1111, 1111) ++if (ret != 2396) { ++ failed("ffi_test_int1(1111, 1111, 1111, 1111) should return 2396") ++} ++ ++ret = C.ffi_test_int2(90, 7800, 560000, 34000000, 1200000000, 900000000000, 78000000000000) ++if (ret != 78901234567890) { ++ failed("ffi_test_int2 should return 78901234567890") ++} ++ ++ret = C.ffi_test_pointer1("") ++if (ret == nil) { ++ failed("ffi_test_pointer1 shoudl return address around 0xffff8800--------") ++} ++ ++ret = C.ffi_test_var_arg(7, 90, 7800, 560000, 34000000, 1200000000, 900000000000, 78000000000000) ++if (ret != 78901234567890) { ++ failed("ffi_test_var_arg should return 78901234567890") ++} ++ ++ret = C.ffi_test_sched_clock() ++if (ret == nil) { ++ failed("ffi_test_clock should not return nil") ++} +diff --git a/drivers/staging/ktap/test/ffi/ktap_ffi_test.c b/drivers/staging/ktap/test/ffi/ktap_ffi_test.c +new file mode 100644 +index 0000000..9434244 +--- /dev/null ++++ b/drivers/staging/ktap/test/ffi/ktap_ffi_test.c +@@ -0,0 +1,64 @@ ++#include ++#include ++#include ++#include ++ ++void ffi_test_void(void) ++{ ++} ++EXPORT_SYMBOL(ffi_test_void); ++ ++int ffi_test_int1(unsigned char a, char b, unsigned short c, short d) ++{ ++ return a + b + c + d; ++} ++EXPORT_SYMBOL(ffi_test_int1); ++ ++long long ffi_test_int2(unsigned int a, int b, unsigned long c, long d, ++ unsigned long long e, long long f, long long g) ++{ ++ return a + b + c + d + e + f + g; ++} ++EXPORT_SYMBOL(ffi_test_int2); ++ ++void *ffi_test_pointer1(char *a) { ++ return a; ++} ++EXPORT_SYMBOL(ffi_test_pointer1); ++ ++long long ffi_test_var_arg(int n, ...) { ++ va_list ap; ++ int i; ++ long long sum = 0; ++ va_start(ap, n); ++ for (i = 0; i < n; i++) { ++ sum += va_arg(ap, long long); ++ } ++ va_end(ap); ++ return sum; ++} ++EXPORT_SYMBOL(ffi_test_var_arg); ++ ++unsigned long long ffi_test_sched_clock(void) ++{ ++ return sched_clock(); ++} ++EXPORT_SYMBOL(ffi_test_sched_clock); ++ ++ ++ ++static int __init ffi_test_init(void) ++{ ++ return 0; ++} ++ ++static void __exit ffi_test_exit(void) ++{ ++} ++ ++ ++MODULE_DESCRIPTION("ktap ffi test module"); ++MODULE_LICENSE("GPL"); ++ ++module_init(ffi_test_init); ++module_exit(ffi_test_exit); +diff --git a/drivers/staging/ktap/test/fibonacci.kp b/drivers/staging/ktap/test/fibonacci.kp +new file mode 100644 +index 0000000..7e141da0 +--- /dev/null ++++ b/drivers/staging/ktap/test/fibonacci.kp +@@ -0,0 +1,36 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#---------------fibonacci---------------- ++ ++ ++#regular recursive fibonacci ++function fib(n) { ++ if (n < 2) { ++ return n ++ } ++ return fib(n-1) + fib(n-2) ++} ++ ++if (fib(20) != 6765) { ++ failed() ++} ++ ++#tail recursive fibonacci ++function fib(n) { ++ f = function (iter, res, next) { ++ if (iter == 0) { ++ return res; ++ } ++ return f(iter-1, next, res+next) ++ } ++ return f(n, 0, 1) ++} ++ ++if (fib(20) != 6765) { ++ failed() ++} +diff --git a/drivers/staging/ktap/test/function.kp b/drivers/staging/ktap/test/function.kp +new file mode 100644 +index 0000000..bfbff26 +--- /dev/null ++++ b/drivers/staging/ktap/test/function.kp +@@ -0,0 +1,88 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++### basic function call ### ++function f1(a, b) { ++ return a + b ++} ++ ++if (f1(2, 3) != 5) { ++ failed(); ++} ++ ++### return string ### ++function f2() { ++ return "function return" ++} ++ ++if (f2() != "function return") { ++ failed(); ++} ++ ++### mutli-value return ### ++function f3(a, b) { ++ return a+b, a-b; ++} ++ ++c, d = f3(2, 3); ++if(c != 5 || d != -1) { ++ failed(); ++} ++ ++ ++### closure testing ### ++function f4() { ++ f5 = function(a, b) { ++ return a * b ++ } ++ return f5 ++} ++ ++local f = f4() ++if (f(9, 9) != 81) { ++ failed(); ++} ++ ++### closure with lexcial variable ### ++# issue: variable cannot be local ++i = 1 ++function f6() { ++ i = 5 ++ f7 = function(a, b) { ++ return a * b + i ++ } ++ return f7 ++} ++ ++f = f6() ++if (f(9, 9) != 81 + i) { ++ failed(); ++} ++ ++i = 6 ++if (f(9, 9) != 81 + i) { ++ failed(); ++} ++ ++### tail call ++### stack should not overflow in tail call mechanism ++a = 0 ++function f8(i) { ++ if (i == 1000000) { ++ a = 1000000 ++ return ++ } ++ # must add return here, otherwise stack overflow ++ return f8(i+1) ++} ++ ++f8(0) ++if (a != 1000000) { ++ failed(); ++} ++ ++ +diff --git a/drivers/staging/ktap/test/if.kp b/drivers/staging/ktap/test/if.kp +new file mode 100644 +index 0000000..3122084 +--- /dev/null ++++ b/drivers/staging/ktap/test/if.kp +@@ -0,0 +1,24 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#-----------------------------------------# ++ ++if (false) { ++ failed() ++} ++ ++if (nil) { ++ failed() ++} ++ ++# ktap only think false and nil is "real false", number 0 is true ++# it's same as lua ++# Might change it in future, to make similar with C ++if (0) { ++ #failed() ++} ++ +diff --git a/drivers/staging/ktap/test/kprobe.kp b/drivers/staging/ktap/test/kprobe.kp +new file mode 100644 +index 0000000..483bccd +--- /dev/null ++++ b/drivers/staging/ktap/test/kprobe.kp +@@ -0,0 +1,19 @@ ++#!/usr/bin/env ktap ++ ++n = 0 ++trace probe:schedule { ++ n = n + 1 ++} ++ ++# share same event id with previous one ++trace probe:schedule { ++} ++ ++ ++tick-1s { ++ if (n == 0) { ++ printf("failed\n"); ++ } ++ exit() ++} ++ +diff --git a/drivers/staging/ktap/test/kretprobe.kp b/drivers/staging/ktap/test/kretprobe.kp +new file mode 100644 +index 0000000..e311e84 +--- /dev/null ++++ b/drivers/staging/ktap/test/kretprobe.kp +@@ -0,0 +1,14 @@ ++#!/usr/bin/env ktap ++ ++n = 0 ++trace probe:__schedule%return { ++ n = n + 1 ++} ++ ++tick-1s { ++ if (n == 0) { ++ printf("failed\n"); ++ } ++ exit() ++} ++ +diff --git a/drivers/staging/ktap/test/ksym.kp b/drivers/staging/ktap/test/ksym.kp +new file mode 100644 +index 0000000..736acfe +--- /dev/null ++++ b/drivers/staging/ktap/test/ksym.kp +@@ -0,0 +1,17 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#-----------------------------------------# ++ ++a = `generic_file_buffered_write` ++b = `generic_file_mmap` ++ ++printf("generic_file_buffered_write: 0x%x\n", a); ++printf("generic_file_mmap: 0x%x\n", b); ++ ++# test read symbol in kernel module ++printf("kp_call: 0x%x\n", `kp_call`) +diff --git a/drivers/staging/ktap/test/len.kp b/drivers/staging/ktap/test/len.kp +new file mode 100644 +index 0000000..697d915 +--- /dev/null ++++ b/drivers/staging/ktap/test/len.kp +@@ -0,0 +1,25 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#-----------------------------------------# ++ ++a = "123456789" ++ ++if (len(a) != 9) { ++ failed() ++} ++ ++b = {} ++b[0] = 0 ++b[1] = 1 ++b["keys"] = "values" ++ ++if (len(b) != 3) { ++ failed() ++} ++ ++ +diff --git a/drivers/staging/ktap/test/looping.kp b/drivers/staging/ktap/test/looping.kp +new file mode 100644 +index 0000000..fe48051 +--- /dev/null ++++ b/drivers/staging/ktap/test/looping.kp +@@ -0,0 +1,40 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++### basic while-loop testing ++a = 1 ++while (a < 1000) { ++ a = a + 1 ++} ++ ++if (a != 1000) { ++ failed() ++} ++ ++### break testing ++### Note that ktap don't have continue keyword ++a = 1 ++while (a < 1000) { ++ if (a == 10) { ++ break ++ } ++ a = a + 1 ++} ++ ++if (a != 10) { ++ failed() ++} ++ ++### for-loop testing ++b=0 ++for (c = 0, 1000, 1) { ++ b = b + 1 ++} ++ ++if (b != 1001) { ++ failed() ++} +diff --git a/drivers/staging/ktap/test/pairs.kp b/drivers/staging/ktap/test/pairs.kp +new file mode 100644 +index 0000000..c062f1628 +--- /dev/null ++++ b/drivers/staging/ktap/test/pairs.kp +@@ -0,0 +1,84 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#-----------------------------------------# ++ ++t = {} ++t[1] = 101 ++t[2] = 102 ++t[3] = 103 ++t["key_1"] = "value_1" ++t["key_2"] = "value_2" ++t["key_3"] = "value_3" ++ ++local n = 0 ++ ++for (k, v in pairs(t)) { ++ n = n + 1 ++ ++ if (k == 1 && v != 101) { ++ failed() ++ } ++ if (k == 2 && v != 102) { ++ failed() ++ } ++ if (k == 3 && v != 103) { ++ failed() ++ } ++ if (k == "key_1" && v != "value_1") { ++ failed() ++ } ++ if (k == "key_2" && v != "value_2") { ++ failed() ++ } ++ if (k == "key_3" && v != "value_3") { ++ failed() ++ } ++} ++ ++if (n != len(t)) { ++ failed() ++} ++ ++ ++#-------------------------------------------------# ++ ++s = {} ++s[1] = 12 ++s[2] = 2 ++s[3] = 3 ++s["124"] = 100 ++s["125"] = -1 ++ ++ordered = {} ++ ++number = 0 ++ ++function cmp(v1, v2) { ++ return (v1 > v2) ++} ++ ++for (k, v in sort_pairs(s, cmp)) { ++ number += 1 ++ ordered[number] = v ++} ++ ++if (ordered[1] != 100) { ++ failed() ++} ++if (ordered[2] != 12) { ++ failed() ++} ++if (ordered[3] != 3) { ++ failed() ++} ++if (ordered[4] != 2) { ++ failed() ++} ++if (ordered[5] != -1) { ++ failed() ++} +diff --git a/drivers/staging/ktap/test/ptable.kp b/drivers/staging/ktap/test/ptable.kp +new file mode 100644 +index 0000000..0f394cf +--- /dev/null ++++ b/drivers/staging/ktap/test/ptable.kp +@@ -0,0 +1,46 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#---------------------------------# ++ ++s = ptable() ++ ++for (i = 1, 100, 1) { ++ s["k"] <<< i ++} ++ ++if (count(s["k"]) != 100) { ++ failed() ++} ++if (sum(s["k"]) != 5050) { ++ failed() ++} ++if (max(s["k"]) != 100) { ++ failed() ++} ++if (min(s["k"]) != 1) { ++ failed() ++} ++ ++for (i = 1, 10000, 1) { ++ s[i] <<< i ++} ++ ++if (min(s[1]) != 1) { ++ failed() ++} ++ ++if (sum(s[10]) != 10) { ++ failed() ++} ++ ++if (max(s[100]) != 100) { ++ failed() ++} ++ ++ ++ +diff --git a/drivers/staging/ktap/test/run_test.sh b/drivers/staging/ktap/test/run_test.sh +new file mode 100644 +index 0000000..d33105e +--- /dev/null ++++ b/drivers/staging/ktap/test/run_test.sh +@@ -0,0 +1,62 @@ ++#!/bin/sh ++ ++rmmod ktapvm > /dev/null 2>&1 ++insmod ../ktapvm.ko ++if test $? -ne 0; then ++ echo "Cannot insmod ../ktapvm.ko" ++ exit -1 ++fi ++ ++KTAP=../ktap ++ktaprun() { ++ echo "$KTAP $@" ++ $KTAP $@ ++} ++ ++ ++ ++####################################################### ++# Use $ktap directly if the arguments contains strings ++$KTAP arg.kp 1 testing "2 3 4" ++$KTAP -e 'print("one-liner testing")' ++$KTAP -e 'exit()' ++$KTAP -o /dev/null -e 'trace syscalls:* { print(argevent) }' \ ++ -- ls > /dev/null ++ ++$KTAP -o /dev/null -e 'trace syscalls:* { print(argevent) }' \ ++ -- $KTAP -e 'print("trace ktap by self")' ++ ++ktaprun arithmetic.kp ++ktaprun -o /dev/null stack_overflow.kp ++ktaprun concat.kp ++ktaprun count.kp ++ktaprun fibonacci.kp ++ktaprun function.kp ++ktaprun if.kp ++ktaprun -q kprobe.kp ++ktaprun -q kretprobe.kp ++ktaprun len.kp ++ktaprun looping.kp ++ktaprun pairs.kp ++ktaprun table.kp ++ktaprun ptable.kp ++ktaprun -q timer.kp ++ktaprun -q tracepoint.kp ++ktaprun -o /dev/null zerodivide.kp ++ktaprun -o /dev/null ksym.kp ++ ++echo "testing kill deadloop ktap script" ++$KTAP -e 'while (1) {}' & ++sleep 1 ++pkill ktap ++sleep 1 ++ ++cd ffi && make --quiet --no-print-directory test && cd - ++ ++##################################################### ++rmmod ktapvm ++if test $? -ne 0; then ++ echo "Error in rmmod ../ktapvm.ko, leak module refcount?" ++ exit -1 ++fi ++ +diff --git a/drivers/staging/ktap/test/stack_overflow.kp b/drivers/staging/ktap/test/stack_overflow.kp +new file mode 100644 +index 0000000..ce33e62 +--- /dev/null ++++ b/drivers/staging/ktap/test/stack_overflow.kp +@@ -0,0 +1,9 @@ ++#!/usr/bin/env ktap ++ ++#this script check overflow in ktap ++ ++function f(a) { ++ return 1 + f(a+1) ++} ++ ++print(f(0)) +diff --git a/drivers/staging/ktap/test/table.kp b/drivers/staging/ktap/test/table.kp +new file mode 100644 +index 0000000..1f6d6e4 +--- /dev/null ++++ b/drivers/staging/ktap/test/table.kp +@@ -0,0 +1,71 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++### table testing ### ++x = {} ++x[1] = "1" ++if (x[1] != "1") { ++ failed() ++} ++ ++x[1] = 22222222222222222222222222222222222222222 ++if (x[1] != 22222222222222222222222222222222222222222) { ++ failed() ++} ++ ++x[1] = "jovi" ++if (x[1] != "jovi") { ++ failed() ++} ++ ++x[11111111111111111111111111111111] = "jovi" ++if (x[11111111111111111111111111111111] != "jovi") { ++ failed() ++} ++ ++x["jovi"] = 1 ++if (x["jovi"] != 1) { ++ failed() ++} ++ ++x["long string....................................."] = 1 ++if (x["long string....................................."] != 1) { ++ failed() ++} ++ ++# issue: subx must declare firstly, otherwise kernel will oops ++subx = {} ++subx["test"] = "this is test" ++x["test"] = subx ++if (x["test"]["test"] != "this is test") { ++ failed() ++} ++ ++tbl = {} ++i = 1 ++while (i < 100000) { ++ tbl[i] = i ++ i = i + 1 ++} ++ ++i = 1 ++while (i < 100000) { ++ if (tbl[i] != i) { ++ failed() ++ } ++ i = i + 1 ++} ++ ++#### table initization ++days = {"Sunday", "Monday", "Tuesday", "Wednesday", ++ "Thursday", "Friday", "Saturday"} ++ ++if (days[2] != "Monday") { ++ failed() ++} ++ ++ +diff --git a/drivers/staging/ktap/test/timer.kp b/drivers/staging/ktap/test/timer.kp +new file mode 100644 +index 0000000..02e8a61 +--- /dev/null ++++ b/drivers/staging/ktap/test/timer.kp +@@ -0,0 +1,28 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#---------------------------------------# ++ ++n1 = 0 ++n2 = 0 ++ ++tick-1s { ++ n1 = n1 + 1 ++} ++ ++tick-1s { ++ n2 = n2 + 1 ++} ++ ++tick-4s { ++ if (n1 == 0 || n2 == 0) { ++ failed() ++ } ++ exit() ++} ++ ++ +diff --git a/drivers/staging/ktap/test/tracepoint.kp b/drivers/staging/ktap/test/tracepoint.kp +new file mode 100644 +index 0000000..fb036e6 +--- /dev/null ++++ b/drivers/staging/ktap/test/tracepoint.kp +@@ -0,0 +1,22 @@ ++#!/usr/bin/env ktap ++ ++function failed() { ++ printf("failed\n"); ++ exit(-1); ++} ++ ++#----------------------------------------# ++ ++n = 0 ++ ++trace sched:* { ++ n = n + 1 ++} ++ ++tick-1s { ++ if (n == 0) { ++ failed() ++ } ++ exit() ++} ++ +diff --git a/drivers/staging/ktap/test/zerodivide.kp b/drivers/staging/ktap/test/zerodivide.kp +new file mode 100644 +index 0000000..abb1eae +--- /dev/null ++++ b/drivers/staging/ktap/test/zerodivide.kp +@@ -0,0 +1,5 @@ ++#!/usr/bin/env ktap ++ ++a = 1/0 ++#should not go here ++printf("Failed\n") +diff --git a/drivers/staging/ktap/userspace/code.c b/drivers/staging/ktap/userspace/code.c +new file mode 100644 +index 0000000..e7317f6 +--- /dev/null ++++ b/drivers/staging/ktap/userspace/code.c +@@ -0,0 +1,998 @@ ++/* ++ * code.c - Code generator for ktap ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * Copyright (C) 1994-2013 Lua.org, PUC-Rio. ++ * - The part of code in this file is copied from lua initially. ++ * - lua's MIT license is compatible with GPL. ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++ ++#include "../include/ktap_types.h" ++#include "../include/ktap_opcodes.h" ++#include "ktapc.h" ++#include "../runtime/kp_obj.h" ++ ++ ++#define hasjumps(e) ((e)->t != (e)->f) ++ ++void codegen_patchtohere (ktap_funcstate *fs, int list); ++ ++static int isnumeral(ktap_expdesc *e) ++{ ++ return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP); ++} ++ ++void codegen_nil(ktap_funcstate *fs, int from, int n) ++{ ++ ktap_instruction *previous; ++ int l = from + n - 1; /* last register to set nil */ ++ ++ if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ ++ previous = &fs->f->code[fs->pc-1]; ++ if (GET_OPCODE(*previous) == OP_LOADNIL) { ++ int pfrom = GETARG_A(*previous); ++ int pl = pfrom + GETARG_B(*previous); ++ ++ if ((pfrom <= from && from <= pl + 1) || ++ (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ ++ if (pfrom < from) ++ from = pfrom; /* from = min(from, pfrom) */ ++ if (pl > l) ++ l = pl; /* l = max(l, pl) */ ++ SETARG_A(*previous, from); ++ SETARG_B(*previous, l - from); ++ return; ++ } ++ } /* else go through */ ++ } ++ codegen_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */ ++} ++ ++int codegen_jump(ktap_funcstate *fs) ++{ ++ int jpc = fs->jpc; /* save list of jumps to here */ ++ int j; ++ ++ fs->jpc = NO_JUMP; ++ j = codegen_codeAsBx(fs, OP_JMP, 0, NO_JUMP); ++ codegen_concat(fs, &j, jpc); /* keep them on hold */ ++ return j; ++} ++ ++void codegen_ret(ktap_funcstate *fs, int first, int nret) ++{ ++ codegen_codeABC(fs, OP_RETURN, first, nret+1, 0); ++} ++ ++static int condjump(ktap_funcstate *fs, OpCode op, int A, int B, int C) ++{ ++ codegen_codeABC(fs, op, A, B, C); ++ return codegen_jump(fs); ++} ++ ++static void fixjump(ktap_funcstate *fs, int pc, int dest) ++{ ++ ktap_instruction *jmp = &fs->f->code[pc]; ++ int offset = dest-(pc+1); ++ ++ ktap_assert(dest != NO_JUMP); ++ if (abs(offset) > MAXARG_sBx) ++ lex_syntaxerror(fs->ls, "control structure too long"); ++ SETARG_sBx(*jmp, offset); ++} ++ ++/* ++ * returns current `pc' and marks it as a jump target (to avoid wrong ++ * optimizations with consecutive instructions not in the same basic block). ++ */ ++int codegen_getlabel(ktap_funcstate *fs) ++{ ++ fs->lasttarget = fs->pc; ++ return fs->pc; ++} ++ ++static int getjump(ktap_funcstate *fs, int pc) ++{ ++ int offset = GETARG_sBx(fs->f->code[pc]); ++ ++ if (offset == NO_JUMP) /* point to itself represents end of list */ ++ return NO_JUMP; /* end of list */ ++ else ++ return (pc+1)+offset; /* turn offset into absolute position */ ++} ++ ++static ktap_instruction *getjumpcontrol(ktap_funcstate *fs, int pc) ++{ ++ ktap_instruction *pi = &fs->f->code[pc]; ++ if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) ++ return pi-1; ++ else ++ return pi; ++} ++ ++/* ++ * check whether list has any jump that do not produce a value ++ * (or produce an inverted value) ++ */ ++static int need_value(ktap_funcstate *fs, int list) ++{ ++ for (; list != NO_JUMP; list = getjump(fs, list)) { ++ ktap_instruction i = *getjumpcontrol(fs, list); ++ if (GET_OPCODE(i) != OP_TESTSET) ++ return 1; ++ } ++ return 0; /* not found */ ++} ++ ++static int patchtestreg(ktap_funcstate *fs, int node, int reg) ++{ ++ ktap_instruction *i = getjumpcontrol(fs, node); ++ if (GET_OPCODE(*i) != OP_TESTSET) ++ return 0; /* cannot patch other instructions */ ++ if (reg != NO_REG && reg != GETARG_B(*i)) ++ SETARG_A(*i, reg); ++ else /* no register to put value or register already has the value */ ++ *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); ++ ++ return 1; ++} ++ ++static void removevalues(ktap_funcstate *fs, int list) ++{ ++ for (; list != NO_JUMP; list = getjump(fs, list)) ++ patchtestreg(fs, list, NO_REG); ++} ++ ++static void patchlistaux(ktap_funcstate *fs, int list, int vtarget, int reg, ++ int dtarget) ++{ ++ while (list != NO_JUMP) { ++ int next = getjump(fs, list); ++ if (patchtestreg(fs, list, reg)) ++ fixjump(fs, list, vtarget); ++ else ++ fixjump(fs, list, dtarget); /* jump to default target */ ++ list = next; ++ } ++} ++ ++static void dischargejpc(ktap_funcstate *fs) ++{ ++ patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); ++ fs->jpc = NO_JUMP; ++} ++ ++void codegen_patchlist(ktap_funcstate *fs, int list, int target) ++{ ++ if (target == fs->pc) ++ codegen_patchtohere(fs, list); ++ else { ++ ktap_assert(target < fs->pc); ++ patchlistaux(fs, list, target, NO_REG, target); ++ } ++} ++ ++void codegen_patchclose(ktap_funcstate *fs, int list, int level) ++{ ++ level++; /* argument is +1 to reserve 0 as non-op */ ++ while (list != NO_JUMP) { ++ int next = getjump(fs, list); ++ ktap_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP && ++ (GETARG_A(fs->f->code[list]) == 0 || ++ GETARG_A(fs->f->code[list]) >= level)); ++ SETARG_A(fs->f->code[list], level); ++ list = next; ++ } ++} ++ ++void codegen_patchtohere(ktap_funcstate *fs, int list) ++{ ++ codegen_getlabel(fs); ++ codegen_concat(fs, &fs->jpc, list); ++} ++ ++void codegen_concat(ktap_funcstate *fs, int *l1, int l2) ++{ ++ if (l2 == NO_JUMP) ++ return; ++ else if (*l1 == NO_JUMP) ++ *l1 = l2; ++ else { ++ int list = *l1; ++ int next; ++ while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ ++ list = next; ++ fixjump(fs, list, l2); ++ } ++} ++ ++static int codegen_code(ktap_funcstate *fs, ktap_instruction i) ++{ ++ ktap_proto *f = fs->f; ++ ++ dischargejpc(fs); /* `pc' will change */ ++ ++ /* put new instruction in code array */ ++ ktapc_growvector(f->code, fs->pc, f->sizecode, ktap_instruction, ++ MAX_INT, "opcodes"); ++ f->code[fs->pc] = i; ++ ++ /* save corresponding line information */ ++ ktapc_growvector(f->lineinfo, fs->pc, f->sizelineinfo, int, ++ MAX_INT, "opcodes"); ++ f->lineinfo[fs->pc] = fs->ls->lastline; ++ return fs->pc++; ++} ++ ++int codegen_codeABC(ktap_funcstate *fs, OpCode o, int a, int b, int c) ++{ ++ ktap_assert(getOpMode(o) == iABC); ++ //ktap_assert(getBMode(o) != OpArgN || b == 0); ++ //ktap_assert(getCMode(o) != OpArgN || c == 0); ++ //ktap_assert(a <= MAXARG_A && b <= MAXARG_B && c <= MAXARG_C); ++ return codegen_code(fs, CREATE_ABC(o, a, b, c)); ++} ++ ++int codegen_codeABx(ktap_funcstate *fs, OpCode o, int a, unsigned int bc) ++{ ++ ktap_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); ++ ktap_assert(getCMode(o) == OpArgN); ++ ktap_assert(a <= MAXARG_A && bc <= MAXARG_Bx); ++ return codegen_code(fs, CREATE_ABx(o, a, bc)); ++} ++ ++static int codeextraarg(ktap_funcstate *fs, int a) ++{ ++ ktap_assert(a <= MAXARG_Ax); ++ return codegen_code(fs, CREATE_Ax(OP_EXTRAARG, a)); ++} ++ ++int codegen_codek(ktap_funcstate *fs, int reg, int k) ++{ ++ if (k <= MAXARG_Bx) ++ return codegen_codeABx(fs, OP_LOADK, reg, k); ++ else { ++ int p = codegen_codeABx(fs, OP_LOADKX, reg, 0); ++ codeextraarg(fs, k); ++ return p; ++ } ++} ++ ++void codegen_checkstack(ktap_funcstate *fs, int n) ++{ ++ int newstack = fs->freereg + n; ++ ++ if (newstack > fs->f->maxstacksize) { ++ if (newstack >= MAXSTACK) ++ lex_syntaxerror(fs->ls, "function or expression too complex"); ++ fs->f->maxstacksize = (u8)(newstack); ++ } ++} ++ ++void codegen_reserveregs(ktap_funcstate *fs, int n) ++{ ++ codegen_checkstack(fs, n); ++ fs->freereg += n; ++} ++ ++static void freereg(ktap_funcstate *fs, int reg) ++{ ++ if (!ISK(reg) && reg >= fs->nactvar) { ++ fs->freereg--; ++ ktap_assert(reg == fs->freereg); ++ } ++} ++ ++static void freeexp(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ if (e->k == VNONRELOC) ++ freereg(fs, e->u.info); ++} ++ ++static int addk(ktap_funcstate *fs, ktap_value *key, ktap_value *v) ++{ ++ const ktap_value *idx = ktapc_table_get(fs->h, key); ++ ktap_proto *f = fs->f; ++ ktap_value kn; ++ int k, oldsize; ++ ++ if (is_number(idx)) { ++ ktap_number n = nvalue(idx); ++ kp_number2int(k, n); ++ if (ktapc_equalobj(&f->k[k], v)) ++ return k; ++ /* else may be a collision (e.g., between 0.0 and "\0\0\0\0\0\0\0\0"); ++ go through and create a new entry for this value */ ++ } ++ /* constant not found; create a new entry */ ++ oldsize = f->sizek; ++ k = fs->nk; ++ ++ /* numerical value does not need GC barrier; ++ table has no metatable, so it does not need to invalidate cache */ ++ set_number(&kn, (ktap_number)k); ++ ktapc_table_setvalue(fs->h, key, &kn); ++ ktapc_growvector(f->k, k, f->sizek, ktap_value, MAXARG_Ax, "constants"); ++ while (oldsize < f->sizek) ++ set_nil(&f->k[oldsize++]); ++ set_obj(&f->k[k], v); ++ fs->nk++; ++ return k; ++} ++ ++int codegen_stringK(ktap_funcstate *fs, ktap_string *s) ++{ ++ ktap_value o; ++ ++ set_string(&o, s); ++ return addk(fs, &o, &o); ++} ++ ++int codegen_numberK(ktap_funcstate *fs, ktap_number r) ++{ ++ int n; ++ ktap_value o, s; ++ ++ set_number(&o, r); ++ if (r == 0 || ktap_numisnan(NULL, r)) { /* handle -0 and NaN */ ++ /* use raw representation as key to avoid numeric problems */ ++ set_string(&s, ktapc_ts_newlstr((char *)&r, sizeof(r))); ++ // incr_top(L); ++ n = addk(fs, &s, &o); ++ // L->top--; ++ } else ++ n = addk(fs, &o, &o); /* regular case */ ++ return n; ++} ++ ++static int boolK(ktap_funcstate *fs, int b) ++{ ++ ktap_value o; ++ set_boolean(&o, b); ++ return addk(fs, &o, &o); ++} ++ ++static int nilK(ktap_funcstate *fs) ++{ ++ ktap_value k, v; ++ set_nil(&v); ++ /* cannot use nil as key; instead use table itself to represent nil */ ++ set_table(&k, fs->h); ++ return addk(fs, &k, &v); ++} ++ ++void codegen_setreturns(ktap_funcstate *fs, ktap_expdesc *e, int nresults) ++{ ++ if (e->k == VCALL) { /* expression is an open function call? */ ++ SETARG_C(getcode(fs, e), nresults+1); ++ } ++ else if (e->k == VVARARG) { ++ SETARG_B(getcode(fs, e), nresults+1); ++ SETARG_A(getcode(fs, e), fs->freereg); ++ codegen_reserveregs(fs, 1); ++ } ++} ++ ++void codegen_setoneret(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ if (e->k == VCALL) { /* expression is an open function call? */ ++ e->k = VNONRELOC; ++ e->u.info = GETARG_A(getcode(fs, e)); ++ } else if (e->k == VVARARG) { ++ SETARG_B(getcode(fs, e), 2); ++ e->k = VRELOCABLE; /* can relocate its simple result */ ++ } ++} ++ ++void codegen_dischargevars(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ switch (e->k) { ++ case VLOCAL: { ++ e->k = VNONRELOC; ++ break; ++ } ++ case VUPVAL: { ++ e->u.info = codegen_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); ++ e->k = VRELOCABLE; ++ break; ++ } ++ case VINDEXED: { ++ OpCode op = OP_GETTABUP; /* assume 't' is in an upvalue */ ++ freereg(fs, e->u.ind.idx); ++ if (e->u.ind.vt == VLOCAL) { /* 't' is in a register? */ ++ freereg(fs, e->u.ind.t); ++ op = OP_GETTABLE; ++ } ++ e->u.info = codegen_codeABC(fs, op, 0, e->u.ind.t, e->u.ind.idx); ++ e->k = VRELOCABLE; ++ break; ++ } ++ case VVARARG: ++ case VCALL: { ++ codegen_setoneret(fs, e); ++ break; ++ } ++ default: ++ break; /* there is one value available (somewhere) */ ++ } ++} ++ ++static int code_label(ktap_funcstate *fs, int A, int b, int jump) ++{ ++ codegen_getlabel(fs); /* those instructions may be jump targets */ ++ return codegen_codeABC(fs, OP_LOADBOOL, A, b, jump); ++} ++ ++static void discharge2reg(ktap_funcstate *fs, ktap_expdesc *e, int reg) ++{ ++ codegen_dischargevars(fs, e); ++ switch (e->k) { ++ case VNIL: { ++ codegen_nil(fs, reg, 1); ++ break; ++ } ++ case VFALSE: case VTRUE: { ++ codegen_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); ++ break; ++ } ++ case VEVENT: ++ codegen_codeABC(fs, OP_EVENT, reg, 0, 0); ++ break; ++ case VEVENTNAME: ++ codegen_codeABC(fs, OP_EVENTNAME, reg, 0, 0); ++ break; ++ case VEVENTARG: ++ codegen_codeABC(fs, OP_EVENTARG, reg, e->u.info, 0); ++ break; ++ case VK: { ++ codegen_codek(fs, reg, e->u.info); ++ break; ++ } ++ case VKNUM: { ++ codegen_codek(fs, reg, codegen_numberK(fs, e->u.nval)); ++ break; ++ } ++ case VRELOCABLE: { ++ ktap_instruction *pc = &getcode(fs, e); ++ SETARG_A(*pc, reg); ++ break; ++ } ++ case VNONRELOC: { ++ if (reg != e->u.info) ++ codegen_codeABC(fs, OP_MOVE, reg, e->u.info, 0); ++ break; ++ } ++ default: ++ ktap_assert(e->k == VVOID || e->k == VJMP); ++ return; /* nothing to do... */ ++ } ++ ++ e->u.info = reg; ++ e->k = VNONRELOC; ++} ++ ++static void discharge2anyreg(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ if (e->k != VNONRELOC) { ++ codegen_reserveregs(fs, 1); ++ discharge2reg(fs, e, fs->freereg-1); ++ } ++} ++ ++static void exp2reg(ktap_funcstate *fs, ktap_expdesc *e, int reg) ++{ ++ discharge2reg(fs, e, reg); ++ if (e->k == VJMP) ++ codegen_concat(fs, &e->t, e->u.info); /* put this jump in `t' list */ ++ if (hasjumps(e)) { ++ int final; /* position after whole expression */ ++ int p_f = NO_JUMP; /* position of an eventual LOAD false */ ++ int p_t = NO_JUMP; /* position of an eventual LOAD true */ ++ ++ if (need_value(fs, e->t) || need_value(fs, e->f)) { ++ int fj = (e->k == VJMP) ? NO_JUMP : codegen_jump(fs); ++ ++ p_f = code_label(fs, reg, 0, 1); ++ p_t = code_label(fs, reg, 1, 0); ++ codegen_patchtohere(fs, fj); ++ } ++ final = codegen_getlabel(fs); ++ patchlistaux(fs, e->f, final, reg, p_f); ++ patchlistaux(fs, e->t, final, reg, p_t); ++ } ++ e->f = e->t = NO_JUMP; ++ e->u.info = reg; ++ e->k = VNONRELOC; ++} ++ ++void codegen_exp2nextreg(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ codegen_dischargevars(fs, e); ++ freeexp(fs, e); ++ codegen_reserveregs(fs, 1); ++ exp2reg(fs, e, fs->freereg - 1); ++} ++ ++int codegen_exp2anyreg(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ codegen_dischargevars(fs, e); ++ if (e->k == VNONRELOC) { ++ if (!hasjumps(e)) ++ return e->u.info; /* exp is already in a register */ ++ if (e->u.info >= fs->nactvar) { /* reg. is not a local? */ ++ exp2reg(fs, e, e->u.info); /* put value on it */ ++ return e->u.info; ++ } ++ } ++ codegen_exp2nextreg(fs, e); /* default */ ++ return e->u.info; ++} ++ ++void codegen_exp2anyregup(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ if (e->k != VUPVAL || hasjumps(e)) ++ codegen_exp2anyreg(fs, e); ++} ++ ++void codegen_exp2val(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ if (hasjumps(e)) ++ codegen_exp2anyreg(fs, e); ++ else ++ codegen_dischargevars(fs, e); ++} ++ ++int codegen_exp2RK(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ codegen_exp2val(fs, e); ++ switch (e->k) { ++ case VTRUE: ++ case VFALSE: ++ case VNIL: { ++ if (fs->nk <= MAXINDEXRK) { /* constant fits in RK operand? */ ++ e->u.info = (e->k == VNIL) ? nilK(fs) : ++ boolK(fs, (e->k == VTRUE)); ++ e->k = VK; ++ return RKASK(e->u.info); ++ } ++ else ++ break; ++ } ++ case VKNUM: { ++ e->u.info = codegen_numberK(fs, e->u.nval); ++ e->k = VK; ++ /* go through */ ++ } ++ case VK: { ++ if (e->u.info <= MAXINDEXRK) /* constant fits in argC? */ ++ return RKASK(e->u.info); ++ else ++ break; ++ } ++ default: ++ break; ++ } ++ /* not a constant in the right range: put it in a register */ ++ return codegen_exp2anyreg(fs, e); ++} ++ ++void codegen_storevar(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex) ++{ ++ switch (var->k) { ++ case VLOCAL: { ++ freeexp(fs, ex); ++ exp2reg(fs, ex, var->u.info); ++ return; ++ } ++ case VUPVAL: { ++ int e = codegen_exp2anyreg(fs, ex); ++ codegen_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); ++ break; ++ } ++ case VINDEXED: { ++ OpCode op = (var->u.ind.vt == VLOCAL) ? OP_SETTABLE : OP_SETTABUP; ++ int e = codegen_exp2RK(fs, ex); ++ codegen_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e); ++ break; ++ } ++ default: ++ ktap_assert(0); /* invalid var kind to store */ ++ break; ++ } ++ ++ freeexp(fs, ex); ++} ++ ++void codegen_storeincr(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex) ++{ ++ switch (var->k) { ++#if 0 /*current not supported */ ++ case VLOCAL: { ++ freeexp(fs, ex); ++ exp2reg(fs, ex, var->u.info); ++ return; ++ } ++ case VUPVAL: { ++ int e = codegen_exp2anyreg(fs, ex); ++ codegen_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); ++ break; ++ } ++#endif ++ case VINDEXED: { ++ OpCode op = (var->u.ind.vt == VLOCAL) ? OP_SETTABLE_INCR : ++ OP_SETTABUP_INCR; ++ int e = codegen_exp2RK(fs, ex); ++ codegen_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e); ++ break; ++ } ++ default: ++ ktap_assert(0); /* invalid var kind to store */ ++ break; ++ } ++ ++ freeexp(fs, ex); ++} ++ ++void codegen_store_aggr(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex) ++{ ++ switch (var->k) { ++#if 0 /*current not supported */ ++ case VLOCAL: { ++ freeexp(fs, ex); ++ exp2reg(fs, ex, var->u.info); ++ return; ++ } ++ case VUPVAL: { ++ int e = codegen_exp2anyreg(fs, ex); ++ codegen_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); ++ break; ++ } ++#endif ++ case VINDEXED: { ++ OpCode op = (var->u.ind.vt == VLOCAL) ? OP_SETTABLE_AGGR : ++ OP_SETTABUP_AGGR; ++ int e = codegen_exp2RK(fs, ex); ++ codegen_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e); ++ break; ++ } ++ default: ++ ktap_assert(0); /* invalid var kind to store */ ++ break; ++ } ++ ++ freeexp(fs, ex); ++} ++ ++void codegen_self(ktap_funcstate *fs, ktap_expdesc *e, ktap_expdesc *key) ++{ ++ int ereg; ++ ++ codegen_exp2anyreg(fs, e); ++ ereg = e->u.info; /* register where 'e' was placed */ ++ freeexp(fs, e); ++ e->u.info = fs->freereg; /* base register for op_self */ ++ e->k = VNONRELOC; ++ codegen_reserveregs(fs, 2); /* function and 'self' produced by op_self */ ++ codegen_codeABC(fs, OP_SELF, e->u.info, ereg, codegen_exp2RK(fs, key)); ++ freeexp(fs, key); ++} ++ ++static void invertjump(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ ktap_instruction *pc = getjumpcontrol(fs, e->u.info); ++ ktap_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && ++ GET_OPCODE(*pc) != OP_TEST); ++ SETARG_A(*pc, !(GETARG_A(*pc))); ++} ++ ++static int jumponcond(ktap_funcstate *fs, ktap_expdesc *e, int cond) ++{ ++ if (e->k == VRELOCABLE) { ++ ktap_instruction ie = getcode(fs, e); ++ if (GET_OPCODE(ie) == OP_NOT) { ++ fs->pc--; /* remove previous OP_NOT */ ++ return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond); ++ } ++ /* else go through */ ++ } ++ discharge2anyreg(fs, e); ++ freeexp(fs, e); ++ return condjump(fs, OP_TESTSET, NO_REG, e->u.info, cond); ++} ++ ++void codegen_goiftrue(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ int pc; /* pc of last jump */ ++ ++ codegen_dischargevars(fs, e); ++ switch (e->k) { ++ case VJMP: { ++ invertjump(fs, e); ++ pc = e->u.info; ++ break; ++ } ++ case VK: case VKNUM: case VTRUE: { ++ pc = NO_JUMP; /* always true; do nothing */ ++ break; ++ } ++ default: ++ pc = jumponcond(fs, e, 0); ++ break; ++ } ++ ++ codegen_concat(fs, &e->f, pc); /* insert last jump in `f' list */ ++ codegen_patchtohere(fs, e->t); ++ e->t = NO_JUMP; ++} ++ ++void codegen_goiffalse(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ int pc; /* pc of last jump */ ++ codegen_dischargevars(fs, e); ++ ++ switch (e->k) { ++ case VJMP: { ++ pc = e->u.info; ++ break; ++ } ++ case VNIL: case VFALSE: { ++ pc = NO_JUMP; /* always false; do nothing */ ++ break; ++ } ++ default: ++ pc = jumponcond(fs, e, 1); ++ break; ++ } ++ codegen_concat(fs, &e->t, pc); /* insert last jump in `t' list */ ++ codegen_patchtohere(fs, e->f); ++ e->f = NO_JUMP; ++} ++ ++static void codenot(ktap_funcstate *fs, ktap_expdesc *e) ++{ ++ codegen_dischargevars(fs, e); ++ switch (e->k) { ++ case VNIL: case VFALSE: { ++ e->k = VTRUE; ++ break; ++ } ++ case VK: case VKNUM: case VTRUE: { ++ e->k = VFALSE; ++ break; ++ } ++ case VJMP: { ++ invertjump(fs, e); ++ break; ++ } ++ case VRELOCABLE: ++ case VNONRELOC: { ++ discharge2anyreg(fs, e); ++ freeexp(fs, e); ++ e->u.info = codegen_codeABC(fs, OP_NOT, 0, e->u.info, 0); ++ e->k = VRELOCABLE; ++ break; ++ } ++ default: ++ ktap_assert(0); /* cannot happen */ ++ break; ++ } ++ ++ /* interchange true and false lists */ ++ { int temp = e->f; e->f = e->t; e->t = temp; } ++ removevalues(fs, e->f); ++ removevalues(fs, e->t); ++} ++ ++void codegen_indexed(ktap_funcstate *fs, ktap_expdesc *t, ktap_expdesc *k) ++{ ++ ktap_assert(!hasjumps(t)); ++ t->u.ind.t = t->u.info; ++ t->u.ind.idx = codegen_exp2RK(fs, k); ++ t->u.ind.vt = (t->k == VUPVAL) ? VUPVAL ++ : check_exp(vkisinreg(t->k), VLOCAL); ++ t->k = VINDEXED; ++} ++ ++static int constfolding(OpCode op, ktap_expdesc *e1, ktap_expdesc *e2) ++{ ++ ktap_number r; ++ ++ if (!isnumeral(e1) || !isnumeral(e2)) ++ return 0; ++ ++ if ((op == OP_DIV || op == OP_MOD) && e2->u.nval == 0) ++ return 0; /* do not attempt to divide by 0 */ ++ ++ if (op == OP_POW) ++ return 0; /* ktap current do not suppor pow arith */ ++ ++ r = ktapc_arith(op - OP_ADD + KTAP_OPADD, e1->u.nval, e2->u.nval); ++ e1->u.nval = r; ++ return 1; ++} ++ ++static void codearith(ktap_funcstate *fs, OpCode op, ++ ktap_expdesc *e1, ktap_expdesc *e2, int line) ++{ ++ if (constfolding(op, e1, e2)) ++ return; ++ else { ++ int o2 = (op != OP_UNM && op != OP_LEN) ? codegen_exp2RK(fs, e2) : 0; ++ int o1 = codegen_exp2RK(fs, e1); ++ ++ if (o1 > o2) { ++ freeexp(fs, e1); ++ freeexp(fs, e2); ++ } else { ++ freeexp(fs, e2); ++ freeexp(fs, e1); ++ } ++ e1->u.info = codegen_codeABC(fs, op, 0, o1, o2); ++ e1->k = VRELOCABLE; ++ codegen_fixline(fs, line); ++ } ++} ++ ++static void codecomp(ktap_funcstate *fs, OpCode op, int cond, ktap_expdesc *e1, ++ ktap_expdesc *e2) ++{ ++ int o1 = codegen_exp2RK(fs, e1); ++ int o2 = codegen_exp2RK(fs, e2); ++ ++ freeexp(fs, e2); ++ freeexp(fs, e1); ++ if (cond == 0 && op != OP_EQ) { ++ int temp; /* exchange args to replace by `<' or `<=' */ ++ temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */ ++ cond = 1; ++ } ++ e1->u.info = condjump(fs, op, cond, o1, o2); ++ e1->k = VJMP; ++} ++ ++void codegen_prefix(ktap_funcstate *fs, UnOpr op, ktap_expdesc *e, int line) ++{ ++ ktap_expdesc e2; ++ ++ e2.t = e2.f = NO_JUMP; ++ e2.k = VKNUM; ++ e2.u.nval = 0; ++ ++ switch (op) { ++ case OPR_MINUS: { ++ if (isnumeral(e)) /* minus constant? */ ++ e->u.nval = ktap_numunm(e->u.nval); /* fold it */ ++ else { ++ codegen_exp2anyreg(fs, e); ++ codearith(fs, OP_UNM, e, &e2, line); ++ } ++ break; ++ } ++ case OPR_NOT: ++ codenot(fs, e); ++ break; ++ case OPR_LEN: { ++ codegen_exp2anyreg(fs, e); /* cannot operate on constants */ ++ codearith(fs, OP_LEN, e, &e2, line); ++ break; ++ } ++ default: ++ ktap_assert(0); ++ } ++} ++ ++void codegen_infix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *v) ++{ ++ switch (op) { ++ case OPR_AND: { ++ codegen_goiftrue(fs, v); ++ break; ++ } ++ case OPR_OR: { ++ codegen_goiffalse(fs, v); ++ break; ++ } ++ case OPR_CONCAT: { ++ codegen_exp2nextreg(fs, v); /* operand must be on the `stack' */ ++ break; ++ } ++ case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: ++ case OPR_MOD: case OPR_POW: { ++ if (!isnumeral(v)) codegen_exp2RK(fs, v); ++ break; ++ } ++ default: ++ codegen_exp2RK(fs, v); ++ break; ++ } ++} ++ ++void codegen_posfix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *e1, ktap_expdesc *e2, int line) ++{ ++ switch (op) { ++ case OPR_AND: { ++ ktap_assert(e1->t == NO_JUMP); /* list must be closed */ ++ codegen_dischargevars(fs, e2); ++ codegen_concat(fs, &e2->f, e1->f); ++ *e1 = *e2; ++ break; ++ } ++ case OPR_OR: { ++ ktap_assert(e1->f == NO_JUMP); /* list must be closed */ ++ codegen_dischargevars(fs, e2); ++ codegen_concat(fs, &e2->t, e1->t); ++ *e1 = *e2; ++ break; ++ } ++ case OPR_CONCAT: { ++ codegen_exp2val(fs, e2); ++ if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) { ++ ktap_assert(e1->u.info == GETARG_B(getcode(fs, e2))-1); ++ freeexp(fs, e1); ++ SETARG_B(getcode(fs, e2), e1->u.info); ++ e1->k = VRELOCABLE; e1->u.info = e2->u.info; ++ } else { ++ codegen_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ ++ codearith(fs, OP_CONCAT, e1, e2, line); ++ } ++ break; ++ } ++ case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: ++ case OPR_MOD: case OPR_POW: { ++ codearith(fs, (OpCode)(op - OPR_ADD + OP_ADD), e1, e2, line); ++ break; ++ } ++ case OPR_EQ: case OPR_LT: case OPR_LE: { ++ codecomp(fs, (OpCode)(op - OPR_EQ + OP_EQ), 1, e1, e2); ++ break; ++ } ++ case OPR_NE: case OPR_GT: case OPR_GE: { ++ codecomp(fs, (OpCode)(op - OPR_NE + OP_EQ), 0, e1, e2); ++ break; ++ } ++ default: ++ ktap_assert(0); ++ } ++} ++ ++void codegen_fixline(ktap_funcstate *fs, int line) ++{ ++ fs->f->lineinfo[fs->pc - 1] = line; ++} ++ ++void codegen_setlist(ktap_funcstate *fs, int base, int nelems, int tostore) ++{ ++ int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1; ++ int b = (tostore == KTAP_MULTRET) ? 0 : tostore; ++ ++ ktap_assert(tostore != 0); ++ if (c <= MAXARG_C) ++ codegen_codeABC(fs, OP_SETLIST, base, b, c); ++ else if (c <= MAXARG_Ax) { ++ codegen_codeABC(fs, OP_SETLIST, base, b, 0); ++ codeextraarg(fs, c); ++ } else ++ lex_syntaxerror(fs->ls, "constructor too long"); ++ fs->freereg = base + 1; /* free registers with list values */ ++} ++ +diff --git a/drivers/staging/ktap/userspace/cparser.h b/drivers/staging/ktap/userspace/cparser.h +new file mode 100644 +index 0000000..f2a9004 +--- /dev/null ++++ b/drivers/staging/ktap/userspace/cparser.h +@@ -0,0 +1,202 @@ ++#ifndef __KTAP_CPARSER_H__ ++#define __KTAP_CPARSER_H__ ++ ++/* ++ * Copyright (c) 2011 James R. McKaskill ++ * ++ * This software is licensed under the stock MIT license: ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * ---------------------------------------------------------------------------- ++ */ ++ ++/* ++ * Adapted from luaffi commit: abc638c9341025580099dcf77795c4b320ba0e63 ++ * ++ * Copyright (c) 2013 Yicheng Qin, Qingping Hou ++ */ ++ ++#ifdef CONFIG_KTAP_FFI ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../include/ktap_ffi.h" ++ ++#define PTR_ALIGN_MASK (sizeof(void*) - 1) ++#define FUNCTION_ALIGN_MASK (sizeof(void (*)()) - 1) ++#define DEFAULT_ALIGN_MASK 7 ++ ++struct parser { ++ int line; ++ const char *next; ++ const char *prev; ++ unsigned align_mask; ++}; ++ ++enum { ++ C_CALL, ++ STD_CALL, ++ FAST_CALL, ++}; ++ ++ ++#define MAX_TYPE_NAME_LEN CSYM_NAME_MAX_LEN ++ ++enum { ++ /* 0 - 4 */ ++ INVALID_TYPE, ++ VOID_TYPE, ++ BOOL_TYPE, ++ INT8_TYPE, ++ INT16_TYPE, ++ /* 5 - 9 */ ++ INT32_TYPE, ++ INT64_TYPE, ++ INTPTR_TYPE, ++ ENUM_TYPE, ++ UNION_TYPE, ++ /* 10 - 12 */ ++ STRUCT_TYPE, ++ FUNCTION_TYPE, ++ FUNCTION_PTR_TYPE, ++}; ++ ++ ++#define IS_CHAR_UNSIGNED (((char) -1) > 0) ++ ++#define POINTER_BITS 2 ++#define POINTER_MAX ((1 << POINTER_BITS) - 1) ++ ++#define ALIGNOF(S) ((int) ((char*) &S.v - (char*) &S - 1)) ++ ++ ++/* Note: if adding a new member that is associated with a struct/union ++ * definition then it needs to be copied over in ctype.c:set_defined for when ++ * we create types based off of the declaration alone. ++ * ++ * Since this is used as a header for every ctype and cdata, and we create a ++ * ton of them on the stack, we try and minimise its size. ++ */ ++struct cp_ctype { ++ size_t base_size; /* size of the base type in bytes */ ++ int ffi_cs_id; /* index for csymbol from ktap vm */ ++ union { ++ /* valid if is_bitfield */ ++ struct { ++ /* size of bitfield in bits */ ++ unsigned bit_size : 7; ++ /* offset within the current byte between 0-63 */ ++ unsigned bit_offset : 6; ++ }; ++ /* Valid if is_array */ ++ size_t array_size; ++ /* Valid for is_variable_struct or is_variable_array. If ++ * variable_size_known (only used for is_variable_struct) ++ * then this is the total increment otherwise this is the ++ * per element increment. ++ */ ++ size_t variable_increment; ++ }; ++ size_t offset; ++ /* as (align bytes - 1) eg 7 gives 8 byte alignment */ ++ unsigned align_mask : 4; ++ /* number of dereferences to get to the base type ++ * including +1 for arrays */ ++ unsigned pointers : POINTER_BITS; ++ /* const pointer mask, LSB is current pointer, +1 for the whether ++ * the base type is const */ ++ unsigned const_mask : POINTER_MAX + 1; ++ unsigned type : 5; /* value given by type enum above */ ++ unsigned is_reference : 1; ++ unsigned is_array : 1; ++ unsigned is_defined : 1; ++ unsigned is_null : 1; ++ unsigned has_member_name : 1; ++ unsigned calling_convention : 2; ++ unsigned has_var_arg : 1; ++ /* set for variable array types where we don't know ++ * the variable size yet */ ++ unsigned is_variable_array : 1; ++ unsigned is_variable_struct : 1; ++ /* used for variable structs after we know the variable size */ ++ unsigned variable_size_known : 1; ++ unsigned is_bitfield : 1; ++ unsigned has_bitfield : 1; ++ unsigned is_jitted : 1; ++ unsigned is_packed : 1; ++ unsigned is_unsigned : 1; ++}; ++ ++#define ALIGNED_DEFAULT (__alignof__(void* __attribute__((aligned))) - 1) ++ ++csymbol *cp_id_to_csym(int id); ++#define ct_ffi_cs(ct) (cp_id_to_csym((ct)->ffi_cs_id)) ++ ++size_t ctype_size(const struct cp_ctype* ct); ++int cp_ctype_init(); ++int cp_ctype_free(); ++struct cp_ctype *ctype_lookup_type(char *name); ++void cp_ctype_dump_stack(); ++void cp_error(const char *err_msg_fmt, ...); ++struct cp_ctype *cp_ctype_reg_type(char *name, struct cp_ctype *ct); ++ ++void cp_push_ctype_with_name(struct cp_ctype *ct, const char *name, int nlen); ++void cp_push_ctype(struct cp_ctype *ct); ++void cp_set_defined(struct cp_ctype *ct); ++ ++int cp_symbol_build_func(struct cp_ctype *type, ++ const char *fname, int fn_size); ++int cp_symbol_build_struct(const char *stname); ++int cp_symbol_build_pointer(struct cp_ctype *ct); ++ ++int ffi_cdef(const char *s); ++void ffi_cparser_init(void); ++void ffi_cparser_free(void); ++ ++ ++static inline csymbol *cp_csymf_ret(csymbol_func *csf) ++{ ++ return cp_id_to_csym(csf->ret_id); ++} ++ ++static inline csymbol *cp_csymf_arg(csymbol_func *csf, int idx) ++{ ++ return cp_id_to_csym(csf->arg_ids[idx]); ++} ++ ++ ++#else ++static void __maybe_unused ffi_cparser_init(void) ++{ ++ return; ++} ++static void __maybe_unused ffi_cparser_free(void) ++{ ++ return; ++} ++#endif /* CONFIG_KTAP_FFI */ ++ ++ ++#endif /* __KTAP_CPARSER_H__ */ +diff --git a/drivers/staging/ktap/userspace/dump.c b/drivers/staging/ktap/userspace/dump.c +new file mode 100644 +index 0000000..eb0acc1 +--- /dev/null ++++ b/drivers/staging/ktap/userspace/dump.c +@@ -0,0 +1,251 @@ ++/* ++ * dump.c - save precompiled ktap chunks ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * Copyright (C) 1994-2013 Lua.org, PUC-Rio. ++ * - The part of code in this file is copied from lua initially. ++ * - lua's MIT license is compatible with GPL. ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++ ++#include "../include/ktap_types.h" ++#include "../include/ktap_opcodes.h" ++#include "ktapc.h" ++#include "../runtime/kp_obj.h" ++#include "cparser.h" ++ ++ ++typedef struct { ++ ktap_writer writer; ++ void *data; ++ int strip; ++ int status; ++} DumpState; ++ ++#define DumpMem(b, n, size, D) DumpBlock(b, (n)*(size), D) ++#define DumpVar(x, D) DumpMem(&x, 1, sizeof(x), D) ++ ++static void DumpBlock(const void *b, size_t size, DumpState *D) ++{ ++ if (D->status == 0) ++ D->status = ((D->writer))(b, size, D->data); ++} ++ ++static void DumpChar(int y, DumpState *D) ++{ ++ char x = (char)y; ++ DumpVar(x, D); ++} ++ ++static void DumpInt(int x, DumpState *D) ++{ ++ DumpVar(x, D); ++} ++ ++static void DumpNumber(ktap_number x, DumpState *D) ++{ ++ DumpVar(x,D); ++} ++ ++static void DumpVector(const void *b, int n, size_t size, DumpState *D) ++{ ++ DumpInt(n, D); ++ DumpMem(b, n, size, D); ++} ++ ++static void DumpString(const ktap_string *s, DumpState *D) ++{ ++ if (s == NULL) { ++ int size = 0; ++ DumpVar(size, D); ++ } else { ++ int size = s->tsv.len + 1; /* include trailing '\0' */ ++ DumpVar(size, D); ++ DumpBlock(getstr(s), size * sizeof(char), D); ++ } ++} ++ ++#define DumpCode(f, D) DumpVector(f->code, f->sizecode, sizeof(ktap_instruction), D) ++ ++static void DumpFunction(const ktap_proto *f, DumpState *D); ++ ++static void DumpConstants(const ktap_proto *f, DumpState *D) ++{ ++ int i, n = f->sizek; ++ ++ DumpInt(n, D); ++ for (i = 0; i < n; i++) { ++ const ktap_value* o=&f->k[i]; ++ DumpChar(ttypenv(o), D); ++ switch (ttypenv(o)) { ++ case KTAP_TNIL: ++ break; ++ case KTAP_TBOOLEAN: ++ DumpChar(bvalue(o), D); ++ break; ++ case KTAP_TNUMBER: ++ DumpNumber(nvalue(o), D); ++ break; ++ case KTAP_TSTRING: ++ DumpString(rawtsvalue(o), D); ++ break; ++ default: ++ printf("ktap: DumpConstants with unknown vaule type %d\n", ttypenv(o)); ++ ktap_assert(0); ++ } ++ } ++ n = f->sizep; ++ DumpInt(n, D); ++ for (i = 0; i < n; i++) ++ DumpFunction(f->p[i], D); ++} ++ ++static void DumpUpvalues(const ktap_proto *f, DumpState *D) ++{ ++ int i, n = f->sizeupvalues; ++ ++ DumpInt(n, D); ++ for (i = 0; i < n; i++) { ++ DumpChar(f->upvalues[i].instack, D); ++ DumpChar(f->upvalues[i].idx, D); ++ } ++} ++ ++static void DumpDebug(const ktap_proto *f, DumpState *D) ++{ ++ int i,n; ++ ++ DumpString((D->strip) ? NULL : f->source, D); ++ n= (D->strip) ? 0 : f->sizelineinfo; ++ DumpVector(f->lineinfo, n, sizeof(int), D); ++ n = (D->strip) ? 0 : f->sizelocvars; ++ DumpInt(n, D); ++ ++ for (i = 0; i < n; i++) { ++ DumpString(f->locvars[i].varname, D); ++ DumpInt(f->locvars[i].startpc, D); ++ DumpInt(f->locvars[i].endpc, D); ++ } ++ n = (D->strip) ? 0 : f->sizeupvalues; ++ DumpInt(n, D); ++ for (i = 0; i < n; i++) ++ DumpString(f->upvalues[i].name, D); ++} ++ ++static void DumpFunction(const ktap_proto *f, DumpState *D) ++{ ++ DumpInt(f->linedefined, D); ++ DumpInt(f->lastlinedefined, D); ++ DumpChar(f->numparams, D); ++ DumpChar(f->is_vararg, D); ++ DumpChar(f->maxstacksize, D); ++ DumpCode(f, D); ++ DumpConstants(f, D); ++ DumpUpvalues(f, D); ++ DumpDebug(f, D); ++} ++ ++static void DumpHeader(DumpState *D) ++{ ++ u8 h[KTAPC_HEADERSIZE]; ++ ++ kp_header(h); ++ DumpBlock(h, KTAPC_HEADERSIZE, D); ++} ++ ++#ifdef CONFIG_KTAP_FFI ++static void DumpCSymbolFunc(csymbol *cs, DumpState *D) ++{ ++ csymbol_func *csf = csym_func(cs); ++ ++ DumpBlock(cs, sizeof(csymbol), D); ++ /* dump csymbol index for argument types */ ++ DumpBlock(csf->arg_ids, csf->arg_nr*sizeof(int), D); ++} ++ ++static void DumpCSymbolStruct(csymbol *cs, DumpState *D) ++{ ++ csymbol_struct *csst = csym_struct(cs); ++ ++ DumpBlock(cs, sizeof(csymbol), D); ++ /* dump csymbol index for argument types */ ++ DumpBlock(csst->members, csst->memb_nr*sizeof(struct_member), D); ++} ++ ++static void DumpCSymbols(DumpState *D) ++{ ++ int i, cs_nr; ++ cp_csymbol_state *cs_state; ++ csymbol *cs, *cs_arr; ++ ++ cs_state = ctype_get_csym_state(); ++ cs_arr = cs_state->cs_arr; ++ cs_nr = cs_state->cs_nr; ++ ++ if (!cs_arr || cs_nr == 0) { ++ DumpInt(0, D); ++ return; ++ } ++ ++ /* dump number of csymbols */ ++ DumpInt(cs_nr, D); ++ /* dump size of csymbol, for safty check in vm */ ++ DumpInt(sizeof(csymbol), D); ++ for (i = 0; i < cs_nr; i++) { ++ cs = &cs_arr[i]; ++ switch (cs->type) { ++ case FFI_FUNC: ++ DumpCSymbolFunc(cs, D); ++ break; ++ case FFI_STRUCT: ++ DumpCSymbolStruct(cs, D); ++ break; ++ default: ++ DumpBlock(cs, sizeof(csymbol), D); ++ break; ++ } ++ } ++} ++#else ++static void DumpCSymbols(DumpState *D) ++{ ++ /* always dump zero when FFI is disabled */ ++ DumpInt(0, D); ++} ++#endif /* CONFIG_KTAP_FFI */ ++ ++/* ++ * dump ktap function as precompiled chunk ++ */ ++int ktapc_dump(const ktap_proto *f, ktap_writer w, void *data, int strip) ++{ ++ DumpState D; ++ ++ D.writer = w; ++ D.data = data; ++ D.strip = strip; ++ D.status = 0; ++ DumpHeader(&D); ++ DumpCSymbols(&D); ++ DumpFunction(f, &D); ++ return D.status; ++} +diff --git a/drivers/staging/ktap/userspace/eventdef.c b/drivers/staging/ktap/userspace/eventdef.c +new file mode 100644 +index 0000000..9896dd3 +--- /dev/null ++++ b/drivers/staging/ktap/userspace/eventdef.c +@@ -0,0 +1,857 @@ ++/* ++ * eventdef.c - ktap eventdef parser ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../include/ktap_types.h" ++#include "../include/ktap_opcodes.h" ++#include "ktapc.h" ++#include "symbol.h" ++ ++#define TRACING_EVENTS_DIR "/sys/kernel/debug/tracing/events" ++ ++static u8 *idmap; ++static int idmap_size = 1024; /* set init size */ ++static int id_nr = 0; ++ ++static int idmap_init(void) ++{ ++ idmap = malloc(idmap_size); ++ if (!idmap) ++ return -1; ++ ++ memset(idmap, 0, idmap_size); ++ return 0; ++} ++ ++static void idmap_free(void) ++{ ++ free(idmap); ++} ++ ++static inline int idmap_is_set(int id) ++{ ++ return idmap[id / 8] & (1 << (id % 8)); ++} ++ ++static void idmap_set(int id) ++{ ++ if (id >= idmap_size * 8) { ++ int newsize = id + 100; /* allocate extra 800 id */ ++ idmap = realloc(idmap, newsize); ++ memset(idmap + idmap_size, 0, newsize - idmap_size); ++ idmap_size = newsize; ++ } ++ ++ if (!idmap_is_set(id)) ++ id_nr++; ++ ++ idmap[id / 8] = idmap[id / 8] | (1 << (id % 8)); ++} ++ ++static void idmap_clear(int id) ++{ ++ id_nr--; ++ idmap[id / 8] = idmap[id / 8] & ~ (1 << (id % 8)); ++} ++ ++static int idmap_get_max_id(void) ++{ ++ return idmap_size * 8; ++} ++ ++static int *get_id_array() ++{ ++ int *id_array; ++ int i, j = 0; ++ ++ id_array = malloc(sizeof(int) * id_nr); ++ if (!id_array) ++ return NULL; ++ ++ for (i = 0; i < idmap_get_max_id(); i++) { ++ if (idmap_is_set(i)) ++ id_array[j++] = i; ++ } ++ ++ return id_array; ++} ++ ++static int add_event(char *evtid_path) ++{ ++ char id_buf[24]; ++ int id, fd; ++ ++ fd = open(evtid_path, O_RDONLY); ++ if (fd < 0) { ++ /* ++ * some tracepoint doesn't have id file, like ftrace, ++ * return success in here, and don't print error. ++ */ ++ verbose_printf("warning: cannot open file %s\n", evtid_path); ++ return 0; ++ } ++ ++ if (read(fd, id_buf, sizeof(id_buf)) < 0) { ++ fprintf(stderr, "read file error %s\n", evtid_path); ++ close(fd); ++ return -1; ++ } ++ ++ id = atoll(id_buf); ++ ++ idmap_set(id); ++ ++ close(fd); ++ return 0; ++} ++ ++static int add_tracepoint(char *sys_name, char *evt_name) ++{ ++ char evtid_path[PATH_MAX] = {0}; ++ ++ ++ snprintf(evtid_path, PATH_MAX, "%s/%s/%s/id", TRACING_EVENTS_DIR, ++ sys_name, evt_name); ++ return add_event(evtid_path); ++} ++ ++static int add_tracepoint_multi_event(char *sys_name, char *evt_name) ++{ ++ char evt_path[PATH_MAX]; ++ struct dirent *evt_ent; ++ DIR *evt_dir; ++ int ret = 0; ++ ++ snprintf(evt_path, PATH_MAX, "%s/%s", TRACING_EVENTS_DIR, sys_name); ++ evt_dir = opendir(evt_path); ++ if (!evt_dir) { ++ perror("Can't open event dir"); ++ return -1; ++ } ++ ++ while (!ret && (evt_ent = readdir(evt_dir))) { ++ if (!strcmp(evt_ent->d_name, ".") ++ || !strcmp(evt_ent->d_name, "..") ++ || !strcmp(evt_ent->d_name, "enable") ++ || !strcmp(evt_ent->d_name, "filter")) ++ continue; ++ ++ if (!strglobmatch(evt_ent->d_name, evt_name)) ++ continue; ++ ++ ret = add_tracepoint(sys_name, evt_ent->d_name); ++ } ++ ++ closedir(evt_dir); ++ return ret; ++} ++ ++static int add_tracepoint_event(char *sys_name, char *evt_name) ++{ ++ return strpbrk(evt_name, "*?") ? ++ add_tracepoint_multi_event(sys_name, evt_name) : ++ add_tracepoint(sys_name, evt_name); ++} ++ ++static int add_tracepoint_multi_sys(char *sys_name, char *evt_name) ++{ ++ struct dirent *events_ent; ++ DIR *events_dir; ++ int ret = 0; ++ ++ events_dir = opendir(TRACING_EVENTS_DIR); ++ if (!events_dir) { ++ perror("Can't open event dir"); ++ return -1; ++ } ++ ++ while (!ret && (events_ent = readdir(events_dir))) { ++ if (!strcmp(events_ent->d_name, ".") ++ || !strcmp(events_ent->d_name, "..") ++ || !strcmp(events_ent->d_name, "enable") ++ || !strcmp(events_ent->d_name, "header_event") ++ || !strcmp(events_ent->d_name, "header_page")) ++ continue; ++ ++ if (!strglobmatch(events_ent->d_name, sys_name)) ++ continue; ++ ++ ret = add_tracepoint_event(events_ent->d_name, ++ evt_name); ++ } ++ ++ closedir(events_dir); ++ return ret; ++} ++ ++static int parse_events_add_tracepoint(char *sys, char *event) ++{ ++ if (strpbrk(sys, "*?")) ++ return add_tracepoint_multi_sys(sys, event); ++ else ++ return add_tracepoint_event(sys, event); ++} ++ ++enum { ++ KPROBE_EVENT, ++ UPROBE_EVENT, ++}; ++ ++struct probe_list { ++ struct probe_list *next; ++ int type; ++ char event[64]; ++}; ++ ++static struct probe_list *probe_list_head; /* for cleanup resources */ ++ ++/* ++ * Some symbol format cannot write to uprobe_events in debugfs, like: ++ * symbol "check_one_fd.part.0" in glibc. ++ * For those symbols, we change the format, get rid of invalid chars, ++ * "check_one_fd.part.0" -> "check_one_fd" ++ * ++ * This function copy is_good_name function in linux/kernel/trace/trace_probe.h ++ */ ++static char *format_symbol_name(const char *old_symbol) ++{ ++ char *new_name = strdup(old_symbol); ++ char *name = new_name; ++ ++ if (!isalpha(*name) && *name != '_') ++ *name = '\0'; ++ ++ while (*++name != '\0') { ++ if (!isalpha(*name) && !isdigit(*name) && *name != '_') { ++ *name = '\0'; ++ break; ++ } ++ } ++ ++ /* this is a good name */ ++ return new_name; ++} ++ ++ ++#define KPROBE_EVENTS_PATH "/sys/kernel/debug/tracing/kprobe_events" ++ ++/** ++ * @return 0 on success, otherwise -1 ++ */ ++static int ++write_kprobe_event(int fd, int ret_probe, const char *symbol, char *fetch_args) ++{ ++ char probe_event[128] = {0}; ++ char event[64] = {0}; ++ struct probe_list *pl; ++ char event_id_path[128] = {0}; ++ char *symbol_name; ++ int id_fd, ret; ++ ++ /* In case some symbols cannot write to uprobe_events debugfs file */ ++ symbol_name = format_symbol_name(symbol); ++ ++ if (!fetch_args) ++ fetch_args = " "; ++ ++ if (ret_probe) { ++ snprintf(event, 64, "ktap_kprobes_%d/ret_%s", ++ getpid(), symbol_name); ++ snprintf(probe_event, 128, "r:%s %s %s", ++ event, symbol, fetch_args); ++ } else { ++ snprintf(event, 64, "ktap_kprobes_%d/%s", ++ getpid(), symbol_name); ++ snprintf(probe_event, 128, "p:%s %s %s", ++ event, symbol, fetch_args); ++ } ++ ++ sprintf(event_id_path, "/sys/kernel/debug/tracing/events/%s/id", event); ++ /* if event id already exist, then don't write to kprobes_event again */ ++ id_fd = open(event_id_path, O_RDONLY); ++ if (id_fd > 0) { ++ close(id_fd); ++ ++ /* remember add event id to ids_array */ ++ ret = add_event(event_id_path); ++ if (ret) ++ goto error; ++ ++ goto out; ++ } ++ ++ verbose_printf("write kprobe event %s\n", probe_event); ++ ++ if (write(fd, probe_event, strlen(probe_event)) <= 0) { ++ fprintf(stderr, "Cannot write %s to %s\n", probe_event, ++ KPROBE_EVENTS_PATH); ++ goto error; ++ } ++ ++ /* add to cleanup list */ ++ pl = malloc(sizeof(struct probe_list)); ++ if (!pl) ++ goto error; ++ ++ pl->type = KPROBE_EVENT; ++ pl->next = probe_list_head; ++ memcpy(pl->event, event, 64); ++ probe_list_head = pl; ++ ++ ret = add_event(event_id_path); ++ if (ret < 0) ++ goto error; ++ ++ out: ++ free(symbol_name); ++ return 0; ++ ++ error: ++ free(symbol_name); ++ return -1; ++} ++ ++static unsigned long core_kernel_text_start; ++static unsigned long core_kernel_text_end; ++static unsigned long kprobes_text_start; ++static unsigned long kprobes_text_end; ++ ++static void init_kprobe_prohibited_area(void) ++{ ++ static int once = 0; ++ ++ if (once > 0) ++ return; ++ ++ once = 1; ++ ++ core_kernel_text_start = find_kernel_symbol("_stext"); ++ core_kernel_text_end = find_kernel_symbol("_etext"); ++ kprobes_text_start = find_kernel_symbol("__kprobes_text_start"); ++ kprobes_text_end = find_kernel_symbol("__kprobes_text_end"); ++} ++ ++static int check_kprobe_addr_prohibited(unsigned long addr) ++{ ++ if (addr <= core_kernel_text_start || addr >= core_kernel_text_end) ++ return -1; ++ ++ if (addr >= kprobes_text_start && addr <= kprobes_text_end) ++ return -1; ++ ++ return 0; ++} ++ ++struct probe_cb_base { ++ int fd; ++ int ret_probe; ++ const char *event; ++ char *binary; ++ char *symbol; ++ char *fetch_args; ++}; ++ ++static int kprobe_symbol_actor(void *arg, const char *name, char type, ++ unsigned long start) ++{ ++ struct probe_cb_base *base = (struct probe_cb_base *)arg; ++ ++ /* only can probe text function */ ++ if (type != 't' && type != 'T') ++ return 0; ++ ++ if (!strglobmatch(name, base->symbol)) ++ return 0; ++ ++ if (check_kprobe_addr_prohibited(start)) ++ return 0; ++ ++ return write_kprobe_event(base->fd, base->ret_probe, name, ++ base->fetch_args); ++} ++ ++static int parse_events_add_kprobe(char *event) ++{ ++ char *symbol, *end; ++ struct probe_cb_base base; ++ int fd, ret; ++ ++ fd = open(KPROBE_EVENTS_PATH, O_WRONLY); ++ if (fd < 0) { ++ fprintf(stderr, "Cannot open %s\n", KPROBE_EVENTS_PATH); ++ return -1; ++ } ++ ++ end = strpbrk(event, "% "); ++ if (end) ++ symbol = strndup(event, end - event); ++ else ++ symbol = strdup(event); ++ ++ base.fd = fd; ++ base.ret_probe = !!strstr(event, "%return"); ++ base.symbol = symbol; ++ base.fetch_args = strchr(event, ' '); ++ ++ init_kprobe_prohibited_area(); ++ ++ ret = kallsyms_parse(&base, kprobe_symbol_actor); ++ if (ret < 0) ++ fprintf(stderr, "cannot parse symbol \"%s\"\n", symbol); ++ ++ free(symbol); ++ close(fd); ++ ++ return ret; ++} ++ ++#define UPROBE_EVENTS_PATH "/sys/kernel/debug/tracing/uprobe_events" ++ ++/** ++ * @return 0 on success, otherwise -1 ++ */ ++static int ++write_uprobe_event(int fd, int ret_probe, const char *binary, ++ const char *symbol, unsigned long addr, ++ char *fetch_args) ++{ ++ char probe_event[128] = {0}; ++ char event[64] = {0}; ++ struct probe_list *pl; ++ char event_id_path[128] = {0}; ++ char *symbol_name; ++ int id_fd, ret; ++ ++ /* In case some symbols cannot write to uprobe_events debugfs file */ ++ symbol_name = format_symbol_name(symbol); ++ ++ if (!fetch_args) ++ fetch_args = " "; ++ ++ if (ret_probe) { ++ snprintf(event, 64, "ktap_uprobes_%d/ret_%s", ++ getpid(), symbol_name); ++ snprintf(probe_event, 128, "r:%s %s:0x%lx %s", ++ event, binary, addr, fetch_args); ++ } else { ++ snprintf(event, 64, "ktap_uprobes_%d/%s", ++ getpid(), symbol_name); ++ snprintf(probe_event, 128, "p:%s %s:0x%lx %s", ++ event, binary, addr, fetch_args); ++ } ++ ++ sprintf(event_id_path, "/sys/kernel/debug/tracing/events/%s/id", event); ++ /* if event id already exist, then don't write to uprobes_event again */ ++ id_fd = open(event_id_path, O_RDONLY); ++ if (id_fd > 0) { ++ close(id_fd); ++ ++ /* remember add event id to ids_array */ ++ ret = add_event(event_id_path); ++ if (ret) ++ goto error; ++ ++ goto out; ++ } ++ ++ verbose_printf("write uprobe event %s\n", probe_event); ++ ++ if (write(fd, probe_event, strlen(probe_event)) <= 0) { ++ fprintf(stderr, "Cannot write %s to %s\n", probe_event, ++ UPROBE_EVENTS_PATH); ++ goto error; ++ } ++ ++ /* add to cleanup list */ ++ pl = malloc(sizeof(struct probe_list)); ++ if (!pl) ++ goto error; ++ ++ pl->type = UPROBE_EVENT; ++ pl->next = probe_list_head; ++ memcpy(pl->event, event, 64); ++ probe_list_head = pl; ++ ++ ret = add_event(event_id_path); ++ if (ret < 0) ++ goto error; ++ ++ out: ++ free(symbol_name); ++ return 0; ++ ++ error: ++ free(symbol_name); ++ return -1; ++} ++ ++/** ++ * TODO: avoid copy-paste stuff ++ * ++ * @return 1 on success, otherwise 0 ++ */ ++#ifdef NO_LIBELF ++static int parse_events_resolve_symbol(int fd, char *event, int type) ++{ ++ char *colon, *binary, *fetch_args; ++ unsigned long symbol_address; ++ ++ colon = strchr(event, ':'); ++ if (!colon) ++ return -1; ++ ++ symbol_address = strtol(colon + 1 /* skip ":" */, NULL, 0); ++ ++ fetch_args = strchr(event, ' '); ++ ++ /** ++ * We already have address, no need in resolving. ++ */ ++ if (symbol_address) { ++ int ret; ++ ++ binary = strndup(event, colon - event); ++ ret = write_uprobe_event(fd, !!strstr(event, "%return"), binary, ++ "NULL", symbol_address, fetch_args); ++ free(binary); ++ return ret; ++ } ++ ++ fprintf(stderr, "error: cannot resolve event \"%s\" without libelf, " ++ "please recompile ktap with NO_LIBELF disabled\n", ++ event); ++ exit(EXIT_FAILURE); ++ return -1; ++} ++ ++#else ++static int uprobe_symbol_actor(const char *name, vaddr_t addr, void *arg) ++{ ++ struct probe_cb_base *base = (struct probe_cb_base *)arg; ++ int ret; ++ ++ if (!strglobmatch(name, base->symbol)) ++ return 0; ++ ++ verbose_printf("uprobe: binary: \"%s\" symbol \"%s\" " ++ "resolved to 0x%lx\n", ++ base->binary, base->symbol, addr); ++ ++ ret = write_uprobe_event(base->fd, base->ret_probe, base->binary, ++ name, addr, base->fetch_args); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int parse_events_resolve_symbol(int fd, char *event, int type) ++{ ++ char *colon, *end; ++ vaddr_t symbol_address; ++ int ret; ++ struct probe_cb_base base = { ++ .fd = fd, ++ .event = event ++ }; ++ ++ colon = strchr(event, ':'); ++ if (!colon) ++ return 0; ++ ++ base.ret_probe = !!strstr(event, "%return"); ++ symbol_address = strtol(colon + 1 /* skip ":" */, NULL, 0); ++ base.binary = strndup(event, colon - event); ++ ++ base.fetch_args = strchr(event, ' '); ++ ++ /* ++ * We already have address, no need in resolving. ++ */ ++ if (symbol_address) { ++ int ret; ++ ret = write_uprobe_event(fd, base.ret_probe, base.binary, ++ "NULL", symbol_address, ++ base.fetch_args); ++ free(base.binary); ++ return ret; ++ } ++ ++ end = strpbrk(event, "% "); ++ if (end) ++ base.symbol = strndup(colon + 1, end - 1 - colon); ++ else ++ base.symbol = strdup(colon + 1); ++ ++ ret = parse_dso_symbols(base.binary, type, uprobe_symbol_actor, ++ (void *)&base); ++ if (!ret) { ++ fprintf(stderr, "error: cannot find symbol %s in binary %s\n", ++ base.symbol, base.binary); ++ ret = -1; ++ } else if(ret > 0) { ++ /* no error found when parse symbols */ ++ ret = 0; ++ } ++ ++ free(base.binary); ++ free(base.symbol); ++ ++ return ret; ++} ++#endif ++ ++static int parse_events_add_uprobe(char *old_event, int type) ++{ ++ int ret; ++ int fd; ++ ++ fd = open(UPROBE_EVENTS_PATH, O_WRONLY); ++ if (fd < 0) { ++ fprintf(stderr, "Cannot open %s\n", UPROBE_EVENTS_PATH); ++ return -1; ++ } ++ ++ ret = parse_events_resolve_symbol(fd, old_event, type); ++ ++ close(fd); ++ return ret; ++} ++ ++static int parse_events_add_probe(char *old_event) ++{ ++ char *separator; ++ ++ separator = strchr(old_event, ':'); ++ if (!separator || (separator == old_event)) ++ return parse_events_add_kprobe(old_event); ++ else ++ return parse_events_add_uprobe(old_event, FIND_SYMBOL); ++} ++ ++static int parse_events_add_sdt(char *old_event) ++{ ++ return parse_events_add_uprobe(old_event, FIND_STAPSDT_NOTE); ++} ++ ++static void strim(char *s) ++{ ++ size_t size; ++ char *end; ++ ++ size = strlen(s); ++ if (!size) ++ return; ++ ++ end = s + size -1; ++ while (end >= s && isspace(*end)) ++ end--; ++ ++ *(end + 1) = '\0'; ++} ++ ++static int get_sys_event_filter_str(char *start, ++ char **sys, char **event, char **filter) ++{ ++ char *separator, *separator2, *ptr, *end; ++ ++ while (*start == ' ') ++ start++; ++ ++ /* find sys */ ++ separator = strchr(start, ':'); ++ if (!separator || (separator == start)) { ++ return -1; ++ } ++ ++ ptr = malloc(separator - start + 1); ++ if (!ptr) ++ return -1; ++ ++ strncpy(ptr, start, separator - start); ++ ptr[separator - start] = '\0'; ++ ++ strim(ptr); ++ *sys = ptr; ++ ++ if (!strcmp(*sys, "probe") && (*(separator + 1) == '/')) { ++ /* it's uprobe event */ ++ separator2 = strchr(separator + 1, ':'); ++ if (!separator2) ++ return -1; ++ } else ++ separator2 = separator; ++ ++ /* find filter */ ++ end = start + strlen(start); ++ while (*--end == ' ') { ++ } ++ ++ if (*end == '/') { ++ char *filter_start; ++ ++ filter_start = strchr(separator2, '/'); ++ if (filter_start == end) ++ return -1; ++ ++ ptr = malloc(end - filter_start + 2); ++ if (!ptr) ++ return -1; ++ ++ memcpy(ptr, filter_start, end - filter_start + 1); ++ ptr[end - filter_start + 1] = '\0'; ++ ++ *filter = ptr; ++ ++ end = filter_start; ++ } else { ++ *filter = NULL; ++ end++; ++ } ++ ++ /* find event */ ++ ptr = malloc(end - separator); ++ if (!ptr) ++ return -1; ++ ++ memcpy(ptr, separator + 1, end - separator - 1); ++ ptr[end - separator - 1] = '\0'; ++ ++ strim(ptr); ++ *event = ptr; ++ ++ return 0; ++} ++ ++static char *get_next_eventdef(char *str) ++{ ++ char *separator; ++ ++ separator = strchr(str, ','); ++ if (!separator) ++ return str + strlen(str); ++ ++ *separator = '\0'; ++ return separator + 1; ++} ++ ++ktap_eventdef_info *ktapc_parse_eventdef(const char *eventdef) ++{ ++ char *str = strdup(eventdef); ++ char *sys, *event, *filter, *next; ++ ktap_eventdef_info *evdef_info; ++ int ret; ++ ++ idmap_init(); ++ ++ parse_next_eventdef: ++ next = get_next_eventdef(str); ++ ++ if (get_sys_event_filter_str(str, &sys, &event, &filter)) ++ goto error; ++ ++ verbose_printf("parse_eventdef: sys[%s], event[%s], filter[%s]\n", ++ sys, event, filter); ++ ++ if (!strcmp(sys, "probe")) ++ ret = parse_events_add_probe(event); ++ else if (!strcmp(sys, "sdt")) ++ ret = parse_events_add_sdt(event); ++ else ++ ret = parse_events_add_tracepoint(sys, event); ++ ++ if (ret) ++ goto error; ++ ++ /* don't trace ftrace:function when all tracepoints enabled */ ++ if (!strcmp(sys, "*")) ++ idmap_clear(1); ++ ++ ++ if (filter && *next != '\0') { ++ fprintf(stderr, "Error: eventdef only can append one filter\n"); ++ goto error; ++ } ++ ++ str = next; ++ if (*next != '\0') ++ goto parse_next_eventdef; ++ ++ evdef_info = malloc(sizeof(*evdef_info)); ++ if (!evdef_info) ++ goto error; ++ ++ evdef_info->nr = id_nr; ++ evdef_info->id_arr = get_id_array(); ++ evdef_info->filter = filter; ++ ++ idmap_free(); ++ return evdef_info; ++ error: ++ idmap_free(); ++ cleanup_event_resources(); ++ return NULL; ++} ++ ++void cleanup_event_resources(void) ++{ ++ struct probe_list *pl; ++ const char *path; ++ char probe_event[128] = {0}; ++ int fd, ret; ++ ++ for (pl = probe_list_head; pl; pl = pl->next) { ++ if (pl->type == KPROBE_EVENT) ++ path = KPROBE_EVENTS_PATH; ++ else if (pl->type == UPROBE_EVENT) ++ path = UPROBE_EVENTS_PATH; ++ else { ++ fprintf(stderr, "Cannot cleanup event type %d\n", ++ pl->type); ++ continue; ++ } ++ ++ snprintf(probe_event, 128, "-:%s", pl->event); ++ ++ fd = open(path, O_WRONLY); ++ if (fd < 0) { ++ fprintf(stderr, "Cannot open %s\n", UPROBE_EVENTS_PATH); ++ continue; ++ } ++ ++ ret = write(fd, probe_event, strlen(probe_event)); ++ if (ret <= 0) { ++ fprintf(stderr, "Cannot write %s to %s\n", probe_event, ++ path); ++ close(fd); ++ continue; ++ } ++ ++ close(fd); ++ } ++} ++ +diff --git a/drivers/staging/ktap/userspace/ffi/cparser.c b/drivers/staging/ktap/userspace/ffi/cparser.c +new file mode 100644 +index 0000000..9f9a429 +--- /dev/null ++++ b/drivers/staging/ktap/userspace/ffi/cparser.c +@@ -0,0 +1,1755 @@ ++#include ++#include "../cparser.h" ++ ++#define IS_CONST(tok) (IS_LITERAL(tok, "const") || IS_LITERAL(tok, "__const") \ ++ || IS_LITERAL(tok, "__const__")) ++#define IS_VOLATILE(tok) (IS_LITERAL(tok, "volatile") || \ ++ IS_LITERAL(tok, "__volatile") || \ ++ IS_LITERAL(tok, "__volatile__")) ++#define IS_RESTRICT(tok) (IS_LITERAL(tok, "restrict") || \ ++ IS_LITERAL(tok, "__restrict") || \ ++ IS_LITERAL(tok, "__restrict__")) ++ ++#define max(a,b) ((a) < (b) ? (b) : (a)) ++#define min(a,b) ((a) < (b) ? (a) : (b)) ++ ++ ++enum etoken { ++ /* 0 - 3 */ ++ TOK_NIL, ++ TOK_NUMBER, ++ TOK_STRING, ++ TOK_TOKEN, ++ ++ /* the order of these values must match the token strings in lex.c */ ++ ++ /* 4 - 5 */ ++ TOK_3_BEGIN, ++ TOK_VA_ARG, ++ ++ /* 6 - 14 */ ++ TOK_2_BEGIN, ++ TOK_LEFT_SHIFT, TOK_RIGHT_SHIFT, TOK_LOGICAL_AND, TOK_LOGICAL_OR, ++ TOK_LESS_EQUAL, TOK_GREATER_EQUAL, TOK_EQUAL, TOK_NOT_EQUAL, ++ ++ /* 15 - 20 */ ++ TOK_1_BEGIN, ++ TOK_OPEN_CURLY, TOK_CLOSE_CURLY, TOK_SEMICOLON, TOK_COMMA, TOK_COLON, ++ /* 21 - 30 */ ++ TOK_ASSIGN, TOK_OPEN_PAREN, TOK_CLOSE_PAREN, TOK_OPEN_SQUARE, TOK_CLOSE_SQUARE, ++ TOK_DOT, TOK_AMPERSAND, TOK_LOGICAL_NOT, TOK_BITWISE_NOT, TOK_MINUS, ++ /* 31 - 40 */ ++ TOK_PLUS, TOK_STAR, TOK_DIVIDE, TOK_MODULUS, TOK_LESS, ++ TOK_GREATER, TOK_BITWISE_XOR, TOK_BITWISE_OR, TOK_QUESTION, TOK_POUND, ++ ++ /* 41 - 43 */ ++ TOK_REFERENCE = TOK_AMPERSAND, ++ TOK_MULTIPLY = TOK_STAR, ++ TOK_BITWISE_AND = TOK_AMPERSAND, ++}; ++ ++struct token { ++ enum etoken type; ++ int64_t integer; ++ const char *str; ++ size_t size; ++}; ++ ++#define IS_LITERAL(TOK, STR) \ ++ (((TOK).size == sizeof(STR) - 1) && \ ++ 0 == memcmp((TOK).str, STR, sizeof(STR) - 1)) ++ ++ ++static int parse_type_name(struct parser *P, char *type_name); ++static void parse_argument(struct parser *P, struct cp_ctype *ct, ++ struct token *pname, struct parser *asmname); ++static int parse_attribute(struct parser *P, struct token *tok, ++ struct cp_ctype *ct, struct parser *asmname); ++static int parse_record(struct parser *P, struct cp_ctype *ct); ++static void instantiate_typedef(struct parser *P, struct cp_ctype *tt, ++ const struct cp_ctype *ft); ++ ++ ++/* the order of tokens _must_ match the order of the enum etoken enum */ ++ ++static char tok3[][4] = { ++ "...", /* unused ">>=", "<<=", */ ++}; ++ ++static char tok2[][3] = { ++ "<<", ">>", "&&", "||", "<=", ++ ">=", "==", "!=", ++ /* unused "+=", "-=", "*=", "/=", "%=", "&=", "^=", ++ * "|=", "++", "--", "->", "::", */ ++}; ++ ++static char tok1[] = { ++ '{', '}', ';', ',', ':', ++ '=', '(', ')', '[', ']', ++ '.', '&', '!', '~', '-', ++ '+', '*', '/', '%', '<', ++ '>', '^', '|', '?', '#' ++}; ++ ++ ++/* this function never returns, but it's an idiom to use it in C functions ++ * as return cp_error */ ++void cp_error(const char *err_msg_fmt, ...) ++{ ++ va_list ap; ++ ++ fprintf(stderr, "cparser error:\n"); ++ ++ va_start(ap, err_msg_fmt); ++ vfprintf(stderr, err_msg_fmt, ap); ++ va_end(ap); ++ ++ exit(EXIT_FAILURE); ++} ++ ++static int set_struct_type_name(char *dst, const char *src, int len) ++{ ++ int prefix_len = sizeof("struct "); ++ ++ if (len + prefix_len > MAX_TYPE_NAME_LEN) ++ return -1; ++ ++ memset(dst, 0, MAX_TYPE_NAME_LEN); ++ strcpy(dst, "struct "); ++ strncat(dst, src, len); ++ ++ return 0; ++} ++ ++static void increase_ptr_deref_level(struct parser *P, struct cp_ctype *ct) ++{ ++ if (ct->pointers == POINTER_MAX) { ++ cp_error("maximum number of pointer derefs reached - use a " ++ "struct to break up the pointers on line %d", P->line); ++ } else { ++ ct->pointers++; ++ ct->const_mask <<= 1; ++ } ++} ++ ++static int next_token(struct parser *P, struct token *tok) ++{ ++ size_t i; ++ const char *s = P->next; ++ ++ /* UTF8 BOM */ ++ if (s[0] == '\xEF' && s[1] == '\xBB' && s[2] == '\xBF') { ++ s += 3; ++ } ++ ++ /* consume whitespace and comments */ ++ for (;;) { ++ /* consume whitespace */ ++ while (*s == '\t' || *s == '\n' || *s == ' ' ++ || *s == '\v' || *s == '\r') { ++ if (*s == '\n') { ++ P->line++; ++ } ++ s++; ++ } ++ ++ /* consume comments */ ++ if (*s == '/' && *(s+1) == '/') { ++ ++ s = strchr(s, '\n'); ++ if (!s) { ++ cp_error("non-terminated comment"); ++ } ++ ++ } else if (*s == '/' && *(s+1) == '*') { ++ s += 2; ++ ++ for (;;) { ++ if (s[0] == '\0') { ++ cp_error("non-terminated comment"); ++ } else if (s[0] == '*' && s[1] == '/') { ++ s += 2; ++ break; ++ } else if (s[0] == '\n') { ++ P->line++; ++ } ++ s++; ++ } ++ ++ } else if (*s == '\0') { ++ tok->type = TOK_NIL; ++ return 0; ++ ++ } else { ++ break; ++ } ++ } ++ ++ P->prev = s; ++ ++ for (i = 0; i < sizeof(tok3) / sizeof(tok3[0]); i++) { ++ if (s[0] == tok3[i][0] && s[1] == tok3[i][1] && s[2] == tok3[i][2]) { ++ tok->type = (enum etoken) (TOK_3_BEGIN + 1 + i); ++ P->next = s + 3; ++ goto end; ++ } ++ } ++ ++ for (i = 0; i < sizeof(tok2) / sizeof(tok2[0]); i++) { ++ if (s[0] == tok2[i][0] && s[1] == tok2[i][1]) { ++ tok->type = (enum etoken) (TOK_2_BEGIN + 1 + i); ++ P->next = s + 2; ++ goto end; ++ } ++ } ++ ++ for (i = 0; i < sizeof(tok1) / sizeof(tok1[0]); i++) { ++ if (s[0] == tok1[i]) { ++ tok->type = (enum etoken) (TOK_1_BEGIN + 1 + i); ++ P->next = s + 1; ++ goto end; ++ } ++ } ++ ++ if (*s == '.' || *s == '-' || ('0' <= *s && *s <= '9')) { ++ /* number */ ++ tok->type = TOK_NUMBER; ++ ++ /* split out the negative case so we get the full range of ++ * bits for unsigned (eg to support 0xFFFFFFFF where ++ * sizeof(long) == 4 */ ++ if (*s == '-') { ++ tok->integer = strtol(s, (char**) &s, 0); ++ } else { ++ tok->integer = strtoul(s, (char**) &s, 0); ++ } ++ ++ while (*s == 'u' || *s == 'U' || *s == 'l' || *s == 'L') { ++ s++; ++ } ++ ++ P->next = s; ++ goto end; ++ ++ } else if (*s == '\'' || *s == '\"') { ++ /* "..." or '...' */ ++ char quote = *s; ++ s++; /* jump over " */ ++ ++ tok->type = TOK_STRING; ++ tok->str = s; ++ ++ while (*s != quote) { ++ if (*s == '\0' || (*s == '\\' && *(s+1) == '\0')) { ++ cp_error("string not finished\n"); ++ } ++ if (*s == '\\') { ++ s++; ++ } ++ s++; ++ } ++ ++ tok->size = s - tok->str; ++ s++; /* jump over " */ ++ P->next = s; ++ goto end; ++ ++ } else if (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z') ++ || *s == '_') { ++ /* tokens */ ++ tok->type = TOK_TOKEN; ++ tok->str = s; ++ ++ while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z') ++ || *s == '_' || ('0' <= *s && *s <= '9')) { ++ s++; ++ } ++ ++ tok->size = s - tok->str; ++ P->next = s; ++ goto end; ++ } else { ++ cp_error("invalid character %d", P->line); ++ } ++ ++end: ++ return 1; ++} ++ ++static void require_token(struct parser *P, struct token *tok) ++{ ++ if (!next_token(P, tok)) { ++ cp_error("unexpected end"); ++ } ++} ++ ++static void check_token(struct parser *P, int type, const char *str, ++ const char *err, ...) ++{ ++ va_list ap; ++ struct token tok; ++ if (!next_token(P, &tok) || tok.type != type ++ || (tok.type == TOK_TOKEN && (tok.size != strlen(str) ++ || memcmp(tok.str, str, tok.size) != 0))) { ++ ++ va_start(ap, err); ++ vfprintf(stderr, err, ap); ++ va_end(ap); ++ ++ exit(EXIT_FAILURE); ++ } ++} ++ ++static void put_back(struct parser *P) { ++ P->next = P->prev; ++} ++ ++int64_t calculate_constant(struct parser *P); ++ ++/* parses out the base type of a type expression in a function declaration, ++ * struct definition, typedef etc ++ * ++ * leaves the usr value of the type on the stack ++ */ ++int parse_type(struct parser *P, struct cp_ctype *ct) ++{ ++ struct token tok; ++ ++ memset(ct, 0, sizeof(*ct)); ++ ++ require_token(P, &tok); ++ ++ /* get function attributes before the return type */ ++ while (parse_attribute(P, &tok, ct, NULL)) { ++ require_token(P, &tok); ++ } ++ ++ /* get const/volatile before the base type */ ++ for (;;) { ++ if (tok.type != TOK_TOKEN) { ++ cp_error("unexpected value before type name on line %d", ++ P->line); ++ return 0; ++ } else if (IS_CONST(tok)) { ++ ct->const_mask = 1; ++ require_token(P, &tok); ++ } else if (IS_VOLATILE(tok) || IS_RESTRICT(tok)) { ++ /* ignored for now */ ++ require_token(P, &tok); ++ } else { ++ break; ++ } ++ } ++ ++ /* get base type */ ++ if (tok.type != TOK_TOKEN) { ++ cp_error("unexpected value before type name on line %d", P->line); ++ return 0; ++ } else if (IS_LITERAL(tok, "struct")) { ++ ct->type = STRUCT_TYPE; ++ parse_record(P, ct); ++ } else if (IS_LITERAL(tok, "union")) { ++ ct->type = UNION_TYPE; ++ parse_record(P, ct); ++ } else if (IS_LITERAL(tok, "enum")) { ++ ct->type = ENUM_TYPE; ++ parse_record(P, ct); ++ } else { ++ put_back(P); ++ ++ /* lookup type */ ++ struct cp_ctype *lct; ++ char cur_type_name[MAX_TYPE_NAME_LEN]; ++ ++ memset(cur_type_name, 0, MAX_TYPE_NAME_LEN); ++ parse_type_name(P, cur_type_name); ++ lct = ctype_lookup_type(cur_type_name); ++ if (!lct) ++ cp_error("unknow type: \"%s\"\n", cur_type_name); ++ ++ instantiate_typedef(P, ct, lct); ++ } ++ ++ while (next_token(P, &tok)) { ++ if (tok.type != TOK_TOKEN) { ++ put_back(P); ++ break; ++ } else if (IS_CONST(tok) || IS_VOLATILE(tok)) { ++ /* ignore for now */ ++ } else { ++ put_back(P); ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++enum test {TEST}; ++ ++/* Parses an enum definition from after the open curly through to the close ++ * curly. Expects the user table to be on the top of the stack ++ */ ++static int parse_enum(struct parser *P, struct cp_ctype *type) ++{ ++ struct token tok; ++ int value = -1; ++ ++ /*@TODO clean up this function when enum support is added*/ ++ cp_error("TODO: enum not supported!\n"); ++ ++ for (;;) { ++ require_token(P, &tok); ++ ++ if (tok.type == TOK_CLOSE_CURLY) { ++ break; ++ } else if (tok.type != TOK_TOKEN) { ++ cp_error("unexpected token in enum at line %d", P->line); ++ return 0; ++ } ++ require_token(P, &tok); ++ ++ if (tok.type == TOK_COMMA || tok.type == TOK_CLOSE_CURLY) { ++ /* we have an auto calculated enum value */ ++ value++; ++ } else if (tok.type == TOK_ASSIGN) { ++ /* we have an explicit enum value */ ++ value = (int) calculate_constant(P); ++ require_token(P, &tok); ++ } else { ++ cp_error("unexpected token in enum at line %d", P->line); ++ return 0; ++ } ++ ++ if (tok.type == TOK_CLOSE_CURLY) { ++ break; ++ } else if (tok.type != TOK_COMMA) { ++ cp_error("unexpected token in enum at line %d", P->line); ++ return 0; ++ } ++ } ++ ++ type->base_size = sizeof(enum test); ++ type->align_mask = sizeof(enum test) - 1; ++ ++ return 0; ++} ++ ++/* Parses a struct from after the open curly through to the close curly. */ ++static int parse_struct(struct parser *P, const struct cp_ctype *ct) ++{ ++ struct token tok; ++ ++ /* parse members */ ++ for (;;) { ++ struct cp_ctype mbase; ++ ++ /* see if we're at the end of the struct */ ++ require_token(P, &tok); ++ if (tok.type == TOK_CLOSE_CURLY) { ++ break; ++ } else if (ct->is_variable_struct) { ++ cp_error("can't have members after a variable sized " ++ "member on line %d", P->line); ++ return -1; ++ } else { ++ put_back(P); ++ } ++ ++ /* members are of the form ++ * , , ; ++ * eg struct foo bar, *bar2[2]; ++ * mbase is 'struct foo' ++ * mtype is '' then '*[2]' ++ * mname is 'bar' then 'bar2' ++ */ ++ ++ parse_type(P, &mbase); ++ ++ for (;;) { ++ struct token mname; ++ struct cp_ctype mt = mbase; ++ ++ memset(&mname, 0, sizeof(mname)); ++ ++ if (ct->is_variable_struct) { ++ cp_error("can't have members after a variable " ++ "sized member on line %d", P->line); ++ return -1; ++ } ++ ++ parse_argument(P, &mt, &mname, NULL); ++ ++ if (!mt.is_defined && (mt.pointers - mt.is_array) == 0) { ++ cp_error("member type is undefined on line %d", ++ P->line); ++ return -1; ++ } ++ ++ if (mt.type == VOID_TYPE ++ && (mt.pointers - mt.is_array) == 0) { ++ cp_error("member type can not be void on line %d", ++ P->line); ++ return -1; ++ } ++ ++ mt.has_member_name = (mname.size > 0); ++ if (mt.has_member_name) { ++ cp_push_ctype_with_name(&mt, ++ mname.str, mname.size); ++ } else { ++ /* @TODO handle unnamed member (houqp) */ ++ cp_error("TODO: unnamed member not supported."); ++ cp_push_ctype(&mt); ++ } ++ ++ require_token(P, &tok); ++ if (tok.type == TOK_SEMICOLON) { ++ break; ++ } else if (tok.type != TOK_COMMA) { ++ cp_error("unexpected token in struct " ++ "definition on line %d", P->line); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/* copy over attributes that could be specified before the typedef eg ++ * __attribute__(packed) const type_t */ ++static void instantiate_typedef(struct parser *P, struct cp_ctype *tt, ++ const struct cp_ctype *ft) ++{ ++ struct cp_ctype pt = *tt; ++ *tt = *ft; ++ ++ tt->const_mask |= pt.const_mask; ++ tt->is_packed = pt.is_packed; ++ ++ if (tt->is_packed) { ++ tt->align_mask = 0; ++ } else { ++ /* Instantiate the typedef in the current packing. This may be ++ * further updated if a pointer is added or another alignment ++ * attribute is applied. If pt.align_mask is already non-zero ++ * than an increased alignment via __declspec(aligned(#)) has ++ * been set. */ ++ tt->align_mask = max(min(P->align_mask, tt->align_mask), ++ pt.align_mask); ++ } ++} ++ ++/* this parses a struct or union starting with the optional ++ * name before the opening brace ++ * leaves the type usr value on the stack */ ++static int parse_record(struct parser *P, struct cp_ctype *ct) ++{ ++ struct token tok; ++ char cur_type_name[MAX_TYPE_NAME_LEN]; ++ ++ require_token(P, &tok); ++ ++ /* name is optional */ ++ if (tok.type == TOK_TOKEN) { ++ /* declaration */ ++ struct cp_ctype *lct; ++ ++ memset(cur_type_name, 0, MAX_TYPE_NAME_LEN); ++ set_struct_type_name(cur_type_name, tok.str, tok.size); ++; ++ /* lookup the name to see if we've seen this type before */ ++ lct = ctype_lookup_type(cur_type_name); ++ ++ if (!lct) { ++ /* new type, delay type registration to the end ++ * of this function */ ++ } else { ++ /* get the exsting declared type */ ++ if (lct->type != ct->type) { ++ cp_error("type '%s' previously declared as '%s'", ++ cur_type_name, ++ csym_name(ct_ffi_cs(lct))); ++ } ++ ++ instantiate_typedef(P, ct, lct); ++ } ++ ++ /* if a name is given then we may be at the end of the string ++ * eg for ffi.new('struct foo') */ ++ if (!next_token(P, &tok)) { ++ return 0; ++ } ++ } else { ++ /* create a new unnamed record */ ++ ++ /*@TODO clean this up after unnamed record support is added */ ++ cp_error("TODO: support unnamed record.\n"); ++ } ++ ++ if (tok.type != TOK_OPEN_CURLY) { ++ /* this may just be a declaration or use of the type as an ++ * argument or member */ ++ put_back(P); ++ return 0; ++ } ++ ++ if (ct->is_defined) { ++ cp_error("redefinition in line %d", P->line); ++ return 0; ++ } ++ ++ if (ct->type == ENUM_TYPE) { ++ parse_enum(P, ct); ++ } else { ++ /* we do a two stage parse, where we parse the content first ++ * and build up the temp user table. We then iterate over that ++ * to calculate the offsets and fill out ct_usr. This is so we ++ * can handle out of order members (eg vtable) and attributes ++ * specified at the end of the struct. */ ++ parse_struct(P, ct); ++ /* build symbol for vm */ ++ ct->ffi_cs_id = cp_symbol_build_struct(cur_type_name); ++ /* save cp_ctype for parser */ ++ cp_ctype_reg_type(cur_type_name, ct); ++ } ++ ++ cp_set_defined(ct); ++ return 0; ++} ++ ++/* parses single or multi work built in types, and pushes it onto the stack */ ++static int parse_type_name(struct parser *P, char *type_name) ++{ ++ struct token tok; ++ int flags = 0; ++ ++ enum { ++ UNSIGNED = 0x01, ++ SIGNED = 0x02, ++ LONG = 0x04, ++ SHORT = 0x08, ++ INT = 0x10, ++ CHAR = 0x20, ++ LONG_LONG = 0x40, ++ INT8 = 0x80, ++ INT16 = 0x100, ++ INT32 = 0x200, ++ INT64 = 0x400, ++ }; ++ ++ require_token(P, &tok); ++ ++ /* we have to manually decode the builtin types since they can take up ++ * more then one token */ ++ for (;;) { ++ if (tok.type != TOK_TOKEN) { ++ break; ++ } else if (IS_LITERAL(tok, "unsigned")) { ++ flags |= UNSIGNED; ++ } else if (IS_LITERAL(tok, "signed")) { ++ flags |= SIGNED; ++ } else if (IS_LITERAL(tok, "short")) { ++ flags |= SHORT; ++ } else if (IS_LITERAL(tok, "char")) { ++ flags |= CHAR; ++ } else if (IS_LITERAL(tok, "long")) { ++ flags |= (flags & LONG) ? LONG_LONG : LONG; ++ } else if (IS_LITERAL(tok, "int")) { ++ flags |= INT; ++ } else if (IS_LITERAL(tok, "__int8")) { ++ flags |= INT8; ++ } else if (IS_LITERAL(tok, "__int16")) { ++ flags |= INT16; ++ } else if (IS_LITERAL(tok, "__int32")) { ++ flags |= INT32; ++ } else if (IS_LITERAL(tok, "__int64")) { ++ flags |= INT64; ++ } else if (IS_LITERAL(tok, "register")) { ++ /* ignore */ ++ } else { ++ break; ++ } ++ ++ if (!next_token(P, &tok)) { ++ break; ++ } ++ } ++ ++ if (flags) { ++ put_back(P); ++ } ++ ++ if (flags & CHAR) { ++ if (flags & SIGNED) { ++ strcpy(type_name, "int8_t"); ++ } else if (flags & UNSIGNED) { ++ strcpy(type_name, "uint8_t"); ++ } else { ++ if (((char) -1) > 0) { ++ strcpy(type_name, "uint8_t"); ++ } else { ++ strcpy(type_name, "int8_t"); ++ } ++ } ++ } else if (flags & INT8) { ++ strcpy(type_name, (flags & UNSIGNED) ? "uint8_t" : "int8_t"); ++ } else if (flags & INT16) { ++ strcpy(type_name, (flags & UNSIGNED) ? "uint16_t" : "int16_t"); ++ } else if (flags & INT32) { ++ strcpy(type_name, (flags & UNSIGNED) ? "uint32_t" : "int32_t"); ++ } else if (flags & INT64) { ++ strcpy(type_name, (flags & UNSIGNED) ? "uint64_t" : "int64_t"); ++ } else if (flags & LONG_LONG) { ++ strcpy(type_name, (flags & UNSIGNED) ? "uint64_t" : "int64_t"); ++ } else if (flags & SHORT) { ++#define SHORT_TYPE(u) (sizeof(short) == sizeof(int64_t) ? \ ++ u "int64_t" : sizeof(short) == sizeof(int32_t) ? \ ++ u "int32_t" : u "int16_t") ++ if (flags & UNSIGNED) { ++ strcpy(type_name, SHORT_TYPE("u")); ++ } else { ++ strcpy(type_name, SHORT_TYPE("")); ++ } ++#undef SHORT_TYPE ++ } else if (flags & LONG) { ++#define LONG_TYPE(u) (sizeof(long) == sizeof(int64_t) ? \ ++ u "int64_t" : u "int32_t") ++ if (flags & UNSIGNED) { ++ strcpy(type_name, LONG_TYPE("u")); ++ } else { ++ strcpy(type_name, LONG_TYPE("")); ++ } ++#undef LONG_TYPE ++ } else if (flags) { ++#define INT_TYPE(u) (sizeof(int) == sizeof(int64_t) ? \ ++ u "int64_t" : sizeof(int) == sizeof(int32_t) ? \ ++ u "int32_t" : u "int16_t") ++ if (flags & UNSIGNED) { ++ strcpy(type_name, INT_TYPE("u")); ++ } else { ++ strcpy(type_name, INT_TYPE("")); ++ } ++#undef INT_TYPE ++ } else { ++ strncpy(type_name, tok.str, tok.size); ++ } ++ ++ return 0; ++} ++ ++/* parse_attribute parses a token to see if it is an attribute. It may then ++ * parse some following tokens to decode the attribute setting the appropriate ++ * fields in ct. It will return 1 if the token was used (and possibly some ++ * more following it) or 0 if not. If the token was used, the next token must ++ * be retrieved using next_token/require_token. */ ++static int parse_attribute(struct parser *P, struct token *tok, ++ struct cp_ctype *ct, struct parser *asmname) ++{ ++ if (tok->type != TOK_TOKEN) { ++ return 0; ++ } else if (asmname && (IS_LITERAL(*tok, "__asm__") ++ || IS_LITERAL(*tok, "__asm"))) { ++ check_token(P, TOK_OPEN_PAREN, NULL, ++ "unexpected token after __asm__ on line %d", ++ P->line); ++ *asmname = *P; ++ ++ require_token(P, tok); ++ while (tok->type == TOK_STRING) { ++ require_token(P, tok); ++ } ++ ++ if (tok->type != TOK_CLOSE_PAREN) { ++ cp_error("unexpected token after __asm__ on line %d", ++ P->line); ++ } ++ return 1; ++ ++ } else if (IS_LITERAL(*tok, "__attribute__") ++ || IS_LITERAL(*tok, "__declspec")) { ++ int parens = 1; ++ check_token(P, TOK_OPEN_PAREN, NULL, ++ "expected parenthesis after __attribute__ or " ++ "__declspec on line %d", P->line); ++ ++ for (;;) { ++ require_token(P, tok); ++ if (tok->type == TOK_OPEN_PAREN) { ++ parens++; ++ } else if (tok->type == TOK_CLOSE_PAREN) { ++ if (--parens == 0) { ++ break; ++ } ++ ++ } else if (tok->type != TOK_TOKEN) { ++ /* ignore unknown symbols within parentheses */ ++ ++ } else if (IS_LITERAL(*tok, "align") || ++ IS_LITERAL(*tok, "aligned") || ++ IS_LITERAL(*tok, "__aligned__")) { ++ unsigned align = 0; ++ require_token(P, tok); ++ ++ switch (tok->type) { ++ case TOK_CLOSE_PAREN: ++ align = ALIGNED_DEFAULT; ++ put_back(P); ++ break; ++ ++ case TOK_OPEN_PAREN: ++ require_token(P, tok); ++ ++ if (tok->type != TOK_NUMBER) { ++ cp_error("expected align(#) " ++ "on line %d", P->line); ++ } ++ ++ switch (tok->integer) { ++ case 1: align = 0; break; ++ case 2: align = 1; break; ++ case 4: align = 3; break; ++ case 8: align = 7; break; ++ case 16: align = 15; break; ++ default: ++ cp_error("unsupported align " ++ "size on line %d", ++ P->line); ++ } ++ ++ check_token(P, TOK_CLOSE_PAREN, NULL, ++ "expected align(#) on line %d", ++ P->line); ++ break; ++ ++ default: ++ cp_error("expected align(#) on line %d", ++ P->line); ++ } ++ ++ /* __attribute__(aligned(#)) is only supposed ++ * to increase alignment */ ++ ct->align_mask = max(align, ct->align_mask); ++ ++ } else if (IS_LITERAL(*tok, "packed") ++ || IS_LITERAL(*tok, "__packed__")) { ++ ct->align_mask = 0; ++ ct->is_packed = 1; ++ ++ } else if (IS_LITERAL(*tok, "mode") ++ || IS_LITERAL(*tok, "__mode__")) { ++ ++ check_token(P, TOK_OPEN_PAREN, NULL, ++ "expected mode(MODE) on line %d", ++ P->line); ++ ++ require_token(P, tok); ++ if (tok->type != TOK_TOKEN) { ++ cp_error("expected mode(MODE) on line %d", ++ P->line); ++ } ++ ++ ++ struct {char ch; uint16_t v;} a16; ++ struct {char ch; uint32_t v;} a32; ++ struct {char ch; uint64_t v;} a64; ++ ++ if (IS_LITERAL(*tok, "QI") ++ || IS_LITERAL(*tok, "__QI__") ++ || IS_LITERAL(*tok, "byte") ++ || IS_LITERAL(*tok, "__byte__") ++ ) { ++ ct->type = INT8_TYPE; ++ ct->base_size = sizeof(uint8_t); ++ ct->align_mask = 0; ++ ++ } else if (IS_LITERAL(*tok, "HI") ++ || IS_LITERAL(*tok, "__HI__")) { ++ ct->type = INT16_TYPE; ++ ct->base_size = sizeof(uint16_t); ++ ct->align_mask = ALIGNOF(a16); ++ ++ } else if (IS_LITERAL(*tok, "SI") ++ || IS_LITERAL(*tok, "__SI__") ++#if defined ARCH_X86 || defined ARCH_ARM ++ || IS_LITERAL(*tok, "word") ++ || IS_LITERAL(*tok, "__word__") ++ || IS_LITERAL(*tok, "pointer") ++ || IS_LITERAL(*tok, "__pointer__") ++#endif ++ ) { ++ ct->type = INT32_TYPE; ++ ct->base_size = sizeof(uint32_t); ++ ct->align_mask = ALIGNOF(a32); ++ ++ } else if (IS_LITERAL(*tok, "DI") ++ || IS_LITERAL(*tok, "__DI__") ++#if defined ARCH_X64 ++ || IS_LITERAL(*tok, "word") ++ || IS_LITERAL(*tok, "__word__") ++ || IS_LITERAL(*tok, "pointer") ++ || IS_LITERAL(*tok, "__pointer__") ++#endif ++ ) { ++ ct->type = INT64_TYPE; ++ ct->base_size = sizeof(uint64_t); ++ ct->align_mask = ALIGNOF(a64); ++ ++ } else { ++ cp_error("unexpected mode on line %d", ++ P->line); ++ } ++ ++ check_token(P, TOK_CLOSE_PAREN, NULL, ++ "expected mode(MODE) on line %d", P->line); ++ ++ } else if (IS_LITERAL(*tok, "cdecl") ++ || IS_LITERAL(*tok, "__cdecl__")) { ++ ct->calling_convention = C_CALL; ++ ++ } else if (IS_LITERAL(*tok, "fastcall") ++ || IS_LITERAL(*tok, "__fastcall__")) { ++ ct->calling_convention = FAST_CALL; ++ ++ } else if (IS_LITERAL(*tok, "stdcall") ++ || IS_LITERAL(*tok, "__stdcall__")) { ++ ct->calling_convention = STD_CALL; ++ } ++ /* ignore unknown tokens within parentheses */ ++ } ++ return 1; ++ ++ } else if (IS_LITERAL(*tok, "__cdecl")) { ++ ct->calling_convention = C_CALL; ++ return 1; ++ ++ } else if (IS_LITERAL(*tok, "__fastcall")) { ++ ct->calling_convention = FAST_CALL; ++ return 1; ++ ++ } else if (IS_LITERAL(*tok, "__stdcall")) { ++ ct->calling_convention = STD_CALL; ++ return 1; ++ ++ } else if (IS_LITERAL(*tok, "__extension__") ++ || IS_LITERAL(*tok, "extern")) { ++ /* ignore */ ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++/* parses from after the opening paranthesis to after the closing parenthesis */ ++static void parse_function_arguments(struct parser* P, struct cp_ctype* ct) ++{ ++ struct token tok; ++ int args = 0; ++ ++ for (;;) { ++ require_token(P, &tok); ++ ++ if (tok.type == TOK_CLOSE_PAREN) ++ break; ++ ++ if (args) { ++ if (tok.type != TOK_COMMA) { ++ cp_error("unexpected token in function " ++ "argument %d on line %d", ++ args, P->line); ++ } ++ require_token(P, &tok); ++ } ++ ++ if (tok.type == TOK_VA_ARG) { ++ ct->has_var_arg = true; ++ check_token(P, TOK_CLOSE_PAREN, "", ++ "unexpected token after ... in " ++ "function on line %d", ++ P->line); ++ break; ++ } else if (tok.type == TOK_TOKEN) { ++ struct cp_ctype at; ++ ++ put_back(P); ++ parse_type(P, &at); ++ parse_argument(P, &at, NULL, NULL); ++ ++ /* array arguments are just treated as their ++ * base pointer type */ ++ at.is_array = 0; ++ ++ /* check for the c style int func(void) and error ++ * on other uses of arguments of type void */ ++ if (at.type == VOID_TYPE && at.pointers == 0) { ++ if (args) { ++ cp_error("can't have argument of type " ++ "void on line %d", ++ P->line); ++ } ++ ++ check_token(P, TOK_CLOSE_PAREN, "", ++ "unexpected void in function on line %d", ++ P->line); ++ break; ++ } ++ cp_push_ctype(&at); ++ args++; ++ } else { ++ cp_error("unexpected token in function argument %d " ++ "on line %d", args+1, P->line); ++ } ++ } ++} ++ ++static int max_bitfield_size(int type) ++{ ++ switch (type) { ++ case BOOL_TYPE: ++ return 1; ++ case INT8_TYPE: ++ return 8; ++ case INT16_TYPE: ++ return 16; ++ case INT32_TYPE: ++ case ENUM_TYPE: ++ return 32; ++ case INT64_TYPE: ++ return 64; ++ default: ++ return -1; ++ } ++} ++ ++static struct cp_ctype *parse_argument2(struct parser *P, struct cp_ctype *ct, ++ struct token *name, struct parser *asmname); ++ ++/* parses from after the first ( in a function declaration or function pointer ++ * can be one of: ++ * void foo(...) before ... ++ * void (foo)(...) before foo ++ * void (* <>)(...) before <> which is the inner type */ ++static struct cp_ctype *parse_function(struct parser *P, struct cp_ctype *ct, ++ struct token *name, struct parser *asmname) ++{ ++ /* We have a function pointer or a function. The usr table will ++ * get replaced by the canonical one (if there is one) in ++ * find_canonical_usr after all the arguments and returns have ++ * been parsed. */ ++ struct token tok; ++ struct cp_ctype *ret = ct; ++ ++ cp_push_ctype(ct); ++ ++ memset(ct, 0, sizeof(*ct)); ++ ct->base_size = sizeof(void (*)()); ++ ct->align_mask = min(FUNCTION_ALIGN_MASK, P->align_mask); ++ ct->type = FUNCTION_TYPE; ++ ct->is_defined = 1; ++ ++ if (name->type == TOK_NIL) { ++ for (;;) { ++ require_token(P, &tok); ++ ++ if (tok.type == TOK_STAR) { ++ if (ct->type == FUNCTION_TYPE) { ++ ct->type = FUNCTION_PTR_TYPE; ++ } else { ++ increase_ptr_deref_level(P, ct); ++ } ++ } else if (parse_attribute(P, &tok, ct, asmname)) { ++ /* parse_attribute sets the appropriate fields */ ++ } else { ++ /* call parse_argument to handle the inner ++ * contents e.g. the <> in "void (* <>) ++ * (...)". Note that the inner contents can ++ * itself be a function, a function ptr, ++ * array, etc (e.g. "void (*signal(int sig, ++ * void (*func)(int)))(int)" ). */ ++ cp_error("TODO: inner function not supported for now."); ++ put_back(P); ++ ct = parse_argument2(P, ct, name, asmname); ++ break; ++ } ++ } ++ ++ check_token(P, TOK_CLOSE_PAREN, NULL, ++ "unexpected token in function on line %d", P->line); ++ check_token(P, TOK_OPEN_PAREN, NULL, ++ "unexpected token in function on line %d", P->line); ++ } ++ ++ parse_function_arguments(P, ct); ++ ++ /*@TODO support for inner function 24.11 2013 (houqp)*/ ++ /* if we have an inner function then set the outer function ptr as its ++ * return type and return the inner function ++ * e.g. for void (* )(int) inner is ++ * surrounded by <>, return type is void (*)(int) */ ++ ++ return ret; ++} ++ ++static struct cp_ctype *parse_argument2(struct parser *P, struct cp_ctype *ct, ++ struct token *name, struct parser *asmname) ++{ ++ struct token tok; ++ ++ for (;;) { ++ if (!next_token(P, &tok)) { ++ /* we've reached the end of the string */ ++ break; ++ } else if (tok.type == TOK_STAR) { ++ increase_ptr_deref_level(P, ct); ++ ++ /* __declspec(align(#)) may come before the type in a ++ * member */ ++ if (!ct->is_packed) { ++ ct->align_mask = max(min(PTR_ALIGN_MASK, P->align_mask), ++ ct->align_mask); ++ } ++ } else if (tok.type == TOK_REFERENCE) { ++ cp_error("NYI: c++ reference types"); ++ return 0; ++ } else if (parse_attribute(P, &tok, ct, asmname)) { ++ /* parse attribute has filled out appropriate fields in type */ ++ ++ } else if (tok.type == TOK_OPEN_PAREN) { ++ ct = parse_function(P, ct, name, asmname); ++ } else if (tok.type == TOK_OPEN_SQUARE) { ++ /* array */ ++ if (ct->pointers == POINTER_MAX) { ++ cp_error("maximum number of pointer derefs " ++ "reached - use a struct to break up " ++ "the pointers"); ++ } ++ ct->is_array = 1; ++ ct->pointers++; ++ ct->const_mask <<= 1; ++ require_token(P, &tok); ++ ++ if (ct->pointers == 1 && !ct->is_defined) { ++ cp_error("array of undefined type on line %d", ++ P->line); ++ } ++ ++ if (ct->is_variable_struct || ct->is_variable_array) { ++ cp_error("can't have an array of a variably " ++ "sized type on line %d", P->line); ++ } ++ ++ if (tok.type == TOK_QUESTION) { ++ ct->is_variable_array = 1; ++ ct->variable_increment = (ct->pointers > 1) ? ++ sizeof(void*) : ct->base_size; ++ check_token(P, TOK_CLOSE_SQUARE, "", ++ "invalid character in array on line %d", ++ P->line); ++ ++ } else if (tok.type == TOK_CLOSE_SQUARE) { ++ ct->array_size = 0; ++ ++ } else if (tok.type == TOK_TOKEN && IS_RESTRICT(tok)) { ++ /* odd gcc extension foo[__restrict] for arguments */ ++ ct->array_size = 0; ++ check_token(P, TOK_CLOSE_SQUARE, "", ++ "invalid character in array on line %d", ++ P->line); ++ } else { ++ int64_t asize; ++ put_back(P); ++ asize = calculate_constant(P); ++ if (asize < 0) { ++ cp_error("array size can not be " ++ "negative on line %d", P->line); ++ return 0; ++ } ++ ct->array_size = (size_t) asize; ++ check_token(P, TOK_CLOSE_SQUARE, "", ++ "invalid character in array on line %d", ++ P->line); ++ } ++ ++ } else if (tok.type == TOK_COLON) { ++ int64_t bsize = calculate_constant(P); ++ ++ if (ct->pointers || bsize < 0 ++ || bsize > max_bitfield_size(ct->type)) { ++ cp_error("invalid bitfield on line %d", P->line); ++ } ++ ++ ct->is_bitfield = 1; ++ ct->bit_size = (unsigned) bsize; ++ ++ } else if (tok.type != TOK_TOKEN) { ++ /* we've reached the end of the declaration */ ++ put_back(P); ++ break; ++ ++ } else if (IS_CONST(tok)) { ++ ct->const_mask |= 1; ++ ++ } else if (IS_VOLATILE(tok) || IS_RESTRICT(tok)) { ++ /* ignored for now */ ++ ++ } else { ++ *name = tok; ++ } ++ } ++ ++ return ct; ++} ++ ++ ++ ++/* parses after the main base type of a typedef, function argument or ++ * struct/union member ++ * eg for const void* bar[3] the base type is void with the subtype so far of ++ * const, this parses the "* bar[3]" and updates the type argument ++ * ++ * type must be as filled out by parse_type ++ * ++ * pushes the updated user value on the top of the stack ++ */ ++void parse_argument(struct parser *P, struct cp_ctype *ct, struct token *pname, ++ struct parser *asmname) ++{ ++ struct token tok, name; ++ ++ memset(&name, 0, sizeof(name)); ++ parse_argument2(P, ct, &name, asmname); ++ ++ for (;;) { ++ if (!next_token(P, &tok)) { ++ break; ++ } else if (parse_attribute(P, &tok, ct, asmname)) { ++ /* parse_attribute sets the appropriate fields */ ++ } else { ++ put_back(P); ++ break; ++ } ++ } ++ ++ if (pname) { ++ *pname = name; ++ } ++} ++ ++static void parse_typedef(struct parser *P) ++{ ++ struct token tok; ++ struct cp_ctype base_type; ++ char typedef_name[MAX_TYPE_NAME_LEN]; ++ ++ parse_type(P, &base_type); ++ ++ for (;;) { ++ struct cp_ctype arg_type = base_type; ++ struct token name; ++ ++ memset(&name, 0, sizeof(name)); ++ ++ parse_argument(P, &arg_type, &name, NULL); ++ ++ if (!name.size) { ++ cp_error("Can't have a typedef without a name on line %d", ++ P->line); ++ } else if (arg_type.is_variable_array) { ++ cp_error("Can't typedef a variable length array on line %d", ++ P->line); ++ } ++ ++ memset(typedef_name, 0, sizeof(typedef_name)); ++ strncpy(typedef_name, name.str, name.size); ++ /* link typedef name with ctype for parser */ ++ cp_ctype_reg_type(typedef_name, &arg_type); ++ ++ require_token(P, &tok); ++ ++ if (tok.type == TOK_SEMICOLON) { ++ break; ++ } else if (tok.type != TOK_COMMA) { ++ cp_error("Unexpected character in typedef on line %d", ++ P->line); ++ } ++ } ++} ++ ++#define END 0 ++#define PRAGMA_POP 1 ++ ++static int parse_root(struct parser *P) ++{ ++ struct token tok; ++ ++ while (next_token(P, &tok)) { ++ /* we can have: ++ * struct definition ++ * enum definition ++ * union definition ++ * struct/enum/union declaration ++ * typedef ++ * function declaration ++ * pragma pack ++ */ ++ ++ if (tok.type == TOK_SEMICOLON) { ++ /* empty semicolon in root continue on */ ++ ++ } else if (tok.type == TOK_POUND) { ++ ++ check_token(P, TOK_TOKEN, "pragma", ++ "unexpected pre processor directive on line %d", ++ P->line); ++ check_token(P, TOK_TOKEN, "pack", ++ "unexpected pre processor directive on line %d", ++ P->line); ++ check_token(P, TOK_OPEN_PAREN, "", ++ "invalid pack directive on line %d", ++ P->line); ++ require_token(P, &tok); ++ ++ if (tok.type == TOK_NUMBER) { ++ if (tok.integer != 1 && tok.integer != 2 ++ && tok.integer != 4 ++ && tok.integer != 8 ++ && tok.integer != 16) { ++ cp_error("pack directive with invalid " ++ "pack size on line %d", ++ P->line); ++ return 0; ++ } ++ ++ P->align_mask = (unsigned) (tok.integer - 1); ++ check_token(P, TOK_CLOSE_PAREN, "", ++ "invalid pack directive on line %d", ++ P->line); ++ } else if (tok.type == TOK_TOKEN && IS_LITERAL(tok, "push")) { ++ /*int line = P->line;*/ ++ unsigned previous_alignment = P->align_mask; ++ ++ check_token(P, TOK_CLOSE_PAREN, "", ++ "invalid pack directive on line %d", ++ P->line); ++ ++ if (parse_root(P) != PRAGMA_POP) { ++ cp_error("reached end of string " ++ "without a pragma pop to " ++ "match the push on line %d", ++ P->line); ++ return 0; ++ } ++ ++ P->align_mask = previous_alignment; ++ ++ } else if (tok.type == TOK_TOKEN && IS_LITERAL(tok, "pop")) { ++ check_token(P, TOK_CLOSE_PAREN, "", ++ "invalid pack directive on line %d", ++ P->line); ++ return PRAGMA_POP; ++ } else { ++ cp_error("invalid pack directive on line %d", ++ P->line); ++ return 0; ++ } ++ } else if (tok.type != TOK_TOKEN) { ++ cp_error("unexpected character on line %d", P->line); ++ return 0; ++ } else if (IS_LITERAL(tok, "__extension__")) { ++ /* ignore */ ++ continue; ++ } else if (IS_LITERAL(tok, "extern")) { ++ /* ignore extern as data and functions can only be ++ * extern */ ++ continue; ++ } else if (IS_LITERAL(tok, "typedef")) { ++ parse_typedef(P); ++ } else if (IS_LITERAL(tok, "static")) { ++ /*@TODO we haven't tested static so far */ ++ cp_error("TODO: support static keyword.\n"); ++ } else { ++ /* type declaration, type definition, or function ++ * declaration */ ++ struct cp_ctype type; ++ struct token name; ++ struct parser asmname; ++ ++ memset(&name, 0, sizeof(name)); ++ memset(&asmname, 0, sizeof(asmname)); ++ ++ put_back(P); ++ parse_type(P, &type); ++ ++ for (;;) { ++ parse_argument(P, &type, &name, &asmname); ++ ++ if (name.size) { ++ /* global/function declaration */ ++ cp_symbol_build_func(&type, name.str, name.size); ++ /* @TODO asmname is not used for now ++ * since we are not supporting __asm__ ++ * as this point. ++ * might need to bind it with function ++ * name later. */ ++ } else { ++ /* type declaration/definition - ++ * already been processed */ ++ } ++ require_token(P, &tok); ++ ++ if (tok.type == TOK_SEMICOLON) { ++ break; ++ } else if (tok.type != TOK_COMMA) { ++ cp_error("missing semicolon on line %d", ++ P->line); ++ } ++ } ++ } ++ } ++ ++ return END; ++} ++ ++static int64_t calculate_constant2(struct parser *P, struct token *tok); ++ ++/* () */ ++static int64_t calculate_constant1(struct parser *P, struct token *tok) ++{ ++ int64_t ret; ++ ++ if (tok->type == TOK_NUMBER) { ++ ret = tok->integer; ++ next_token(P, tok); ++ return ret; ++ ++ } else if (tok->type == TOK_TOKEN) { ++ /* look up name in constants table */ ++ cp_error("TODO: support name lookup in constant table\n"); ++ next_token(P, tok); ++ return ret; ++ ++ } else if (tok->type == TOK_OPEN_PAREN) { ++ struct parser before_cast = *P; ++ cp_error("TODO: handle open parent token in constant1\n"); ++ *P = before_cast; ++ ret = calculate_constant(P); ++ ++ require_token(P, tok); ++ if (tok->type != TOK_CLOSE_PAREN) { ++ cp_error("error whilst parsing constant at line %d", ++ P->line); ++ } ++ ++ next_token(P, tok); ++ return ret; ++ } else { ++ cp_error("unexpected token whilst parsing constant at line %d", ++ P->line); ++ return 0; ++ } ++} ++ ++/* ! and ~, unary + and -, and sizeof */ ++static int64_t calculate_constant2(struct parser *P, struct token *tok) ++{ ++ if (tok->type == TOK_LOGICAL_NOT) { ++ require_token(P, tok); ++ return !calculate_constant2(P, tok); ++ ++ } else if (tok->type == TOK_BITWISE_NOT) { ++ require_token(P, tok); ++ return ~calculate_constant2(P, tok); ++ ++ } else if (tok->type == TOK_PLUS) { ++ require_token(P, tok); ++ return calculate_constant2(P, tok); ++ ++ } else if (tok->type == TOK_MINUS) { ++ require_token(P, tok); ++ return -calculate_constant2(P, tok); ++ ++ } else if (tok->type == TOK_TOKEN && ++ (IS_LITERAL(*tok, "sizeof") ++ || IS_LITERAL(*tok, "alignof") ++ || IS_LITERAL(*tok, "__alignof__") ++ || IS_LITERAL(*tok, "__alignof"))) { ++ cp_error("TODO: support sizeof\n"); ++ bool issize = IS_LITERAL(*tok, "sizeof"); ++ struct cp_ctype type; ++ ++ require_token(P, tok); ++ if (tok->type != TOK_OPEN_PAREN) { ++ cp_error("invalid sizeof at line %d", P->line); ++ } ++ ++ parse_type(P, &type); ++ parse_argument(P, &type, NULL, NULL); ++ ++ require_token(P, tok); ++ if (tok->type != TOK_CLOSE_PAREN) { ++ cp_error("invalid sizeof at line %d", P->line); ++ } ++ ++ next_token(P, tok); ++ ++ return issize ? ctype_size(&type) : type.align_mask + 1; ++ ++ } else { ++ return calculate_constant1(P, tok); ++ } ++} ++ ++/* binary * / and % (left associative) */ ++static int64_t calculate_constant3(struct parser *P, struct token *tok) ++{ ++ int64_t left = calculate_constant2(P, tok); ++ ++ for (;;) { ++ if (tok->type == TOK_MULTIPLY) { ++ require_token(P, tok); ++ left *= calculate_constant2(P, tok); ++ ++ } else if (tok->type == TOK_DIVIDE) { ++ require_token(P, tok); ++ left /= calculate_constant2(P, tok); ++ ++ } else if (tok->type == TOK_MODULUS) { ++ require_token(P, tok); ++ left %= calculate_constant2(P, tok); ++ ++ } else { ++ return left; ++ } ++ } ++} ++ ++/* binary + and - (left associative) */ ++static int64_t calculate_constant4(struct parser *P, struct token *tok) ++{ ++ int64_t left = calculate_constant3(P, tok); ++ ++ for (;;) { ++ if (tok->type == TOK_PLUS) { ++ require_token(P, tok); ++ left += calculate_constant3(P, tok); ++ ++ } else if (tok->type == TOK_MINUS) { ++ require_token(P, tok); ++ left -= calculate_constant3(P, tok); ++ ++ } else { ++ return left; ++ } ++ } ++} ++ ++/* binary << and >> (left associative) */ ++static int64_t calculate_constant5(struct parser *P, struct token *tok) ++{ ++ int64_t left = calculate_constant4(P, tok); ++ ++ for (;;) { ++ if (tok->type == TOK_LEFT_SHIFT) { ++ require_token(P, tok); ++ left <<= calculate_constant4(P, tok); ++ ++ } else if (tok->type == TOK_RIGHT_SHIFT) { ++ require_token(P, tok); ++ left >>= calculate_constant4(P, tok); ++ ++ } else { ++ return left; ++ } ++ } ++} ++ ++/* binary <, <=, >, and >= (left associative) */ ++static int64_t calculate_constant6(struct parser *P, struct token *tok) ++{ ++ int64_t left = calculate_constant5(P, tok); ++ ++ for (;;) { ++ if (tok->type == TOK_LESS) { ++ require_token(P, tok); ++ left = (left < calculate_constant5(P, tok)); ++ ++ } else if (tok->type == TOK_LESS_EQUAL) { ++ require_token(P, tok); ++ left = (left <= calculate_constant5(P, tok)); ++ ++ } else if (tok->type == TOK_GREATER) { ++ require_token(P, tok); ++ left = (left > calculate_constant5(P, tok)); ++ ++ } else if (tok->type == TOK_GREATER_EQUAL) { ++ require_token(P, tok); ++ left = (left >= calculate_constant5(P, tok)); ++ ++ } else { ++ return left; ++ } ++ } ++} ++ ++/* binary ==, != (left associative) */ ++static int64_t calculate_constant7(struct parser *P, struct token *tok) ++{ ++ int64_t left = calculate_constant6(P, tok); ++ ++ for (;;) { ++ if (tok->type == TOK_EQUAL) { ++ require_token(P, tok); ++ left = (left == calculate_constant6(P, tok)); ++ ++ } else if (tok->type == TOK_NOT_EQUAL) { ++ require_token(P, tok); ++ left = (left != calculate_constant6(P, tok)); ++ ++ } else { ++ return left; ++ } ++ } ++} ++ ++/* binary & (left associative) */ ++static int64_t calculate_constant8(struct parser *P, struct token *tok) ++{ ++ int64_t left = calculate_constant7(P, tok); ++ ++ for (;;) { ++ if (tok->type == TOK_BITWISE_AND) { ++ require_token(P, tok); ++ left = (left & calculate_constant7(P, tok)); ++ ++ } else { ++ return left; ++ } ++ } ++} ++ ++/* binary ^ (left associative) */ ++static int64_t calculate_constant9(struct parser *P, struct token *tok) ++{ ++ int64_t left = calculate_constant8(P, tok); ++ ++ for (;;) { ++ if (tok->type == TOK_BITWISE_XOR) { ++ require_token(P, tok); ++ left = (left ^ calculate_constant8(P, tok)); ++ ++ } else { ++ return left; ++ } ++ } ++} ++ ++/* binary | (left associative) */ ++static int64_t calculate_constant10(struct parser *P, struct token *tok) ++{ ++ int64_t left = calculate_constant9(P, tok); ++ ++ for (;;) { ++ if (tok->type == TOK_BITWISE_OR) { ++ require_token(P, tok); ++ left = (left | calculate_constant9(P, tok)); ++ ++ } else { ++ return left; ++ } ++ } ++} ++ ++/* binary && (left associative) */ ++static int64_t calculate_constant11(struct parser *P, struct token *tok) ++{ ++ int64_t left = calculate_constant10(P, tok); ++ ++ for (;;) { ++ if (tok->type == TOK_LOGICAL_AND) { ++ require_token(P, tok); ++ left = (left && calculate_constant10(P, tok)); ++ ++ } else { ++ return left; ++ } ++ } ++} ++ ++/* binary || (left associative) */ ++static int64_t calculate_constant12(struct parser *P, struct token *tok) ++{ ++ int64_t left = calculate_constant11(P, tok); ++ ++ for (;;) { ++ if (tok->type == TOK_LOGICAL_OR) { ++ require_token(P, tok); ++ left = (left || calculate_constant11(P, tok)); ++ ++ } else { ++ return left; ++ } ++ } ++} ++ ++/* ternary ?: (right associative) */ ++static int64_t calculate_constant13(struct parser *P, struct token *tok) ++{ ++ int64_t left = calculate_constant12(P, tok); ++ ++ if (tok->type == TOK_QUESTION) { ++ int64_t middle, right; ++ require_token(P, tok); ++ middle = calculate_constant13(P, tok); ++ if (tok->type != TOK_COLON) { ++ cp_error("invalid ternery (? :) in constant on line %d", ++ P->line); ++ } ++ require_token(P, tok); ++ right = calculate_constant13(P, tok); ++ return left ? middle : right; ++ ++ } else { ++ return left; ++ } ++} ++ ++int64_t calculate_constant(struct parser* P) ++{ ++ struct token tok; ++ int64_t ret; ++ require_token(P, &tok); ++ ret = calculate_constant13(P, &tok); ++ ++ if (tok.type != TOK_NIL) { ++ put_back(P); ++ } ++ ++ return ret; ++} ++ ++int ffi_cdef(const char *s) ++{ ++ struct parser P; ++ ++ memset(&P, 0, sizeof(struct parser)); ++ P.line = 1; ++ P.prev = P.next = s; ++ P.align_mask = DEFAULT_ALIGN_MASK; ++ ++ if (parse_root(&P) == PRAGMA_POP) { ++ cp_error("pragma pop without an associated push on line %d", ++ P.line); ++ } ++ ++ return 0; ++} ++ ++void ffi_cparser_init(void) ++{ ++ cp_ctype_init(); ++} ++ ++void ffi_cparser_free(void) ++{ ++ cp_ctype_free(); ++} +diff --git a/drivers/staging/ktap/userspace/ffi/ctype.c b/drivers/staging/ktap/userspace/ffi/ctype.c +new file mode 100644 +index 0000000..40d1d9d +--- /dev/null ++++ b/drivers/staging/ktap/userspace/ffi/ctype.c +@@ -0,0 +1,551 @@ ++#include "../../include/ktap_types.h" ++#include "../../include/ktap_opcodes.h" ++#include "../ktapc.h" ++#include "../cparser.h" ++ ++ ++/* for ktap vm */ ++cp_csymbol_state csym_state; ++ ++#define cs_nr (csym_state.cs_nr) ++#define cs_arr_size (csym_state.cs_arr_size) ++#define cs_arr (csym_state.cs_arr) ++ ++csymbol *cp_id_to_csym(int id) ++{ ++ return &cs_arr[id]; ++} ++ ++ ++typedef struct cp_ctype_entry { ++ char name[MAX_TYPE_NAME_LEN]; ++ struct cp_ctype ct; ++} cp_ctype_entry; ++ ++#define DEFAULT_CTYPE_ARR_SIZE 100 ++static int cte_nr; ++static int cte_arr_size; ++static cp_ctype_entry *cte_arr; ++ ++ ++/* stack to help maintain state during parsing */ ++typedef struct cp_ctype_stack { ++ int size; ++ int top; ++ cp_ctype_entry *stack; ++} ctype_stack; ++ ++ ++static ctype_stack cts; ++ ++#define ct_stack(id) (&(cts.stack[id])) ++#define ct_stack_ct(id) (&(cts.stack[id].ct)) ++ ++ ++ ++int cp_ctype_reg_csymbol(csymbol *cs); ++ ++ ++size_t ctype_size(const struct cp_ctype *ct) ++{ ++ if (ct->pointers - ct->is_array) { ++ return sizeof(void*) * (ct->is_array ? ct->array_size : 1); ++ ++ } else if (!ct->is_defined || ct->type == VOID_TYPE) { ++ cp_error("can't calculate size of an undefined type"); ++ return 0; ++ } else if (ct->variable_size_known) { ++ assert(ct->is_variable_struct && !ct->is_array); ++ return ct->base_size + ct->variable_increment; ++ } else if (ct->is_variable_array || ct->is_variable_struct) { ++ cp_error("internal error: calc size of variable type with " ++ "unknown size"); ++ return 0; ++ } else { ++ return ct->base_size * (ct->is_array ? ct->array_size : 1); ++ } ++} ++ ++#define MAX_STACK_SIZE 100 ++int ctype_stack_grow(int size) ++{ ++ struct cp_ctype_entry *new_st; ++ ++ assert(cts.size + size < MAX_STACK_SIZE); ++ ++ new_st = realloc(cts.stack, (cts.size+size)*sizeof(cp_ctype_entry)); ++ if (new_st) ++ cts.stack = new_st; ++ else ++ return -1; ++ ++ cts.size += size; ++ ++ return size; ++} ++ ++int ctype_stack_free_space() ++{ ++ return cts.size - cts.top; ++} ++ ++void ctype_stack_reset() ++{ ++ cts.top = 0; ++} ++ ++/* push ctype to stack, create new csymbol if needed */ ++void cp_push_ctype_with_name(struct cp_ctype *ct, const char *name, int nlen) ++{ ++ int i; ++ struct cp_ctype *nct; ++ ++ if (ctype_stack_free_space() < 1) ++ ctype_stack_grow(4); ++ ++ /* we have to check pointer here because does type lookup by name ++ * before parsing '*', and for pointers, ct will always be the ++ * original type */ ++ if (ct->pointers) { ++ for (i = 0; i < cte_nr; i++) { ++ nct = &(cte_arr[i].ct); ++ if (nct->type == ct->type && ++ nct->pointers == ct->pointers) { ++ break; ++ } ++ } ++ ++ if (i == cte_nr) { ++ /* pointer type not found ++ * create a new pointer symbol for this type */ ++ /* associate ctype with new csymbol */ ++ ct->ffi_cs_id = cp_symbol_build_pointer(ct); ++ /* register wit new pointer name */ ++ cp_ctype_reg_type(csym_name(ct_ffi_cs(ct)), ct); ++ } else { ++ /* pointer type already registered, reinstantiate ct */ ++ *ct = cte_arr[i].ct; ++ } ++ } ++ memset(ct_stack(cts.top), 0, sizeof(cp_ctype_entry)); ++ ct_stack(cts.top)->ct = *ct; ++ if (name) ++ strncpy(ct_stack(cts.top)->name, name, nlen); ++ cts.top++; ++} ++ ++void cp_push_ctype(struct cp_ctype *ct) ++{ ++ cp_push_ctype_with_name(ct, NULL, 0); ++} ++ ++void cp_set_defined(struct cp_ctype *ct) ++{ ++ ct->is_defined = 1; ++ ++ /* @TODO: update ctypes and cdatas that were created before the ++ * definition came in */ ++} ++ ++void cp_ctype_dump_stack() ++{ ++ int i; ++ struct cp_ctype *ct; ++ ++ printf("---------------------------\n"); ++ printf("start of ctype stack (%d) dump: \n", cts.top); ++ for (i = 0; i < cts.top; i++) { ++ ct = ct_stack_ct(i); ++ printf("[%d] -> cp_ctype: %d, sym_type: %d, pointer: %d " ++ "symbol_id: %d, name: %s\n", ++ i, ct->type, ++ csym_type(ct_ffi_cs(ct)), ct->pointers, ct->ffi_cs_id, ++ ct_stack(i)->name); ++ } ++} ++ ++int ctype_reg_table_grow() ++{ ++ cp_ctype_entry *new_arr; ++ ++ new_arr = realloc(cte_arr, sizeof(cp_ctype_entry)*cte_arr_size*2); ++ if (!new_arr) ++ cp_error("failed to allocate memory for ctype array\n"); ++ ++ cte_arr_size = cte_arr_size * 2; ++ return 0; ++} ++ ++/* return index in csymbol array */ ++int cp_ctype_reg_csymbol(csymbol *cs) ++{ ++ if (cs_nr >= cs_arr_size) { ++ cs_arr_size *= 2; ++ cs_arr = realloc(cs_arr, cs_arr_size*sizeof(csymbol)); ++ if (!cs_arr) ++ cp_error("failed to extend csymbol array!\n"); ++ } ++ ++ cs_arr[cs_nr] = *cs; ++ cs_nr++; ++ ++ return cs_nr-1; ++} ++ ++void __cp_symbol_dump_struct(csymbol *cs) ++{ ++ int i; ++ csymbol *ncs; ++ csymbol_struct *stcs = csym_struct(cs); ++ ++ printf("=== [%s] definition ==================\n", csym_name(cs)); ++ for (i = 0; i < stcs->memb_nr; i++) { ++ printf("\t(%d) ", i); ++ printf("csym_id: %d, ", stcs->members[i].id); ++ ncs = &cs_arr[stcs->members[i].id]; ++ printf("name: %s, ffi_ctype: %d, %s\n", ++ stcs->members[i].name, ncs->type, csym_name(ncs)); ++ } ++} ++ ++void cp_symbol_dump_struct(int id) ++{ ++ __cp_symbol_dump_struct(&cs_arr[id]); ++} ++ ++int cp_symbol_build_struct(const char *stname) ++{ ++ int i, id, memb_size; ++ cp_ctype_entry *cte; ++ csymbol nst; ++ struct_member *st_membs; ++ csymbol_struct *stcs; ++ ++ if (cts.top <= 0 || !stname) { ++ cp_error("invalid struct definition.\n"); ++ } ++ ++ memb_size = cts.top; ++ st_membs = malloc(memb_size*sizeof(struct_member)); ++ if (!st_membs) ++ cp_error("failed to allocate memory for struct members.\n"); ++ memset(st_membs, 0, memb_size*sizeof(struct_member)); ++ ++ nst.type = FFI_STRUCT; ++ strcpy(nst.name, stname); ++ ++ stcs = csym_struct(&nst); ++ stcs->memb_nr = memb_size; ++ stcs->members = st_membs; ++ ++ for (i = 0; i < memb_size; i++) { ++ assert(i < cts.top); ++ cte = ct_stack(i); ++ if (cte->name) ++ strcpy(st_membs[i].name, cte->name); ++ st_membs[i].id = ct_stack_ct(i)->ffi_cs_id; ++ } ++ ++ id = cp_ctype_reg_csymbol(&nst); ++ ++ ctype_stack_reset(); ++ ++ return id; ++} ++ ++/* build pointer symbol from given csymbol */ ++int cp_symbol_build_pointer(struct cp_ctype *ct) ++{ ++ int id, ret; ++ csymbol ncspt; ++ csymbol *ref_cs = ct_ffi_cs(ct); ++ ++ /* TODO: Check correctness of multi-level pointer 24.11.2013(unihorn) */ ++ memset(&ncspt, 0, sizeof(csymbol)); ++ ncspt.type = FFI_PTR; ++ ret = sprintf(ncspt.name, "%s *", csym_name(ref_cs)); ++ assert(ret < MAX_TYPE_NAME_LEN); ++ ++ csym_set_ptr_deref_id(&ncspt, ct->ffi_cs_id); ++ id = cp_ctype_reg_csymbol(&ncspt); ++ ++ return id; ++} ++ ++void __cp_symbol_dump_func(csymbol *cs) ++{ ++ int i; ++ csymbol *ncs; ++ csymbol_func *fcs = csym_func(cs); ++ ++ printf("=== [%s] function definition =============\n", csym_name(cs)); ++ ncs = cp_csymf_ret(fcs); ++ printf("address: %p\n", fcs->addr); ++ printf("return type: \n"); ++ printf("\tcsym_id: %d, ffi_ctype: %d, %s\n", ++ fcs->ret_id, ncs->type, csym_name(ncs)); ++ printf("args type (%d): \n", fcs->arg_nr); ++ for (i = 0; i < csymf_arg_nr(fcs); i++) { ++ printf("\t (%d) ", i); ++ printf("csym_id: %d, ", fcs->arg_ids[i]); ++ ncs = cp_csymf_arg(fcs, i); ++ printf("ffi_ctype: %d, %s\n", ncs->type, csym_name(ncs)); ++ } ++} ++ ++void cp_symbol_dump_func(int id) ++{ ++ __cp_symbol_dump_func(&cs_arr[id]); ++} ++ ++int cp_symbol_build_func(struct cp_ctype *type, const char *fname, int fn_size) ++{ ++ int i = 1, arg_nr, id; ++ int *argsym_id_arr; ++ csymbol nfcs; ++ csymbol_func *fcs; ++ ++ if (cts.top == 0 || fn_size < 0 || !fname) { ++ cp_error("invalid function definition.\n"); ++ } ++ ++ argsym_id_arr = NULL; ++ memset(&nfcs, 0, sizeof(csymbol)); ++ csym_type(&nfcs) = FFI_FUNC; ++ ++ strncpy(csym_name(&nfcs), fname, fn_size); ++ ++ fcs = csym_func(&nfcs); ++ fcs->has_var_arg = type->has_var_arg; ++ /* Type needed for handling variable args handle */ ++ if (fcs->has_var_arg && !ctype_lookup_type("void *")) ++ cp_symbol_build_pointer(ctype_lookup_type("void")); ++ ++ /* Fetch start address of function */ ++ fcs->addr = (void *)find_kernel_symbol(csym_name(&nfcs)); ++ if (!fcs->addr) ++ cp_error("wrong function address for %s\n", csym_name(&nfcs)); ++ ++ /* bottom of the stack is return type */ ++ fcs->ret_id = ct_stack_ct(0)->ffi_cs_id; ++ ++ /* the rest is argument type */ ++ if (cts.top == 1) { ++ /* function takes no argument */ ++ arg_nr = 0; ++ } else { ++ arg_nr = cts.top - 1; ++ argsym_id_arr = malloc(arg_nr * sizeof(int)); ++ if (!argsym_id_arr) ++ cp_error("failed to allocate memory for function args.\n"); ++ for (i = 0; i < arg_nr; i++) { ++ argsym_id_arr[i] = ct_stack_ct(i+1)->ffi_cs_id; ++ } ++ } ++ fcs->arg_nr = arg_nr; ++ fcs->arg_ids = argsym_id_arr; ++ ++ id = cp_ctype_reg_csymbol(&nfcs); ++ ++ /* clear stack since we have consumed all the ctypes */ ++ ctype_stack_reset(); ++ ++ return id; ++} ++ ++struct cp_ctype *cp_ctype_reg_type(char *name, struct cp_ctype *ct) ++{ ++ if (cte_nr >= cte_arr_size) ++ ctype_reg_table_grow(); ++ ++ memset(cte_arr[cte_nr].name, 0, MAX_TYPE_NAME_LEN); ++ strcpy(cte_arr[cte_nr].name, name); ++ ++ cte_arr[cte_nr].ct = *ct; ++ cte_nr++; ++ ++ return &(cte_arr[cte_nr-1].ct); ++} ++ ++#if 0 ++/* TODO: used for size calculation */ ++static ffi_type ffi_int_type(ktap_state *ks, int size, bool sign) ++{ ++ switch(size) { ++ case 1: ++ if (!sign) ++ return FFI_UINT8; ++ else ++ return FFI_INT8; ++ case 2: ++ if (!sign) ++ return FFI_UINT16; ++ else ++ return FFI_INT16; ++ case 4: ++ if (!sign) ++ return FFI_UINT32; ++ else ++ return FFI_INT32; ++ case 8: ++ if (!sign) ++ return FFI_UINT64; ++ else ++ return FFI_INT64; ++ default: ++ kp_error(ks, "Error: Have not support int type of size %d\n", size); ++ return FFI_UNKNOWN; ++ } ++ ++ /* NEVER reach here, silence compiler */ ++ return -1; ++} ++#endif ++ ++ ++static inline void ct_set_type(struct cp_ctype *ct, int type, int is_unsigned) ++{ ++ ct->type = type; ++ ct->is_unsigned = is_unsigned; ++} ++ ++static void init_builtin_type(struct cp_ctype *ct, ffi_type ftype) ++{ ++ csymbol cs; ++ int cs_id; ++ ++ csym_type(&cs) = ftype; ++ strncpy(csym_name(&cs), ffi_type_name(ftype), CSYM_NAME_MAX_LEN); ++ cs_id = cp_ctype_reg_csymbol(&cs); ++ ++ memset(ct, 0, sizeof(*ct)); ++ ct->ffi_cs_id = cs_id; ++ switch (ftype) { ++ case FFI_VOID: ct_set_type(ct, VOID_TYPE, 0); break; ++ case FFI_UINT8: ct_set_type(ct, INT8_TYPE, 1); break; ++ case FFI_INT8: ct_set_type(ct, INT8_TYPE, 0); break; ++ case FFI_UINT16: ct_set_type(ct, INT16_TYPE, 1); break; ++ case FFI_INT16: ct_set_type(ct, INT16_TYPE, 0); break; ++ case FFI_UINT32: ct_set_type(ct, INT32_TYPE, 1); break; ++ case FFI_INT32: ct_set_type(ct, INT32_TYPE, 0); break; ++ case FFI_UINT64: ct_set_type(ct, INT64_TYPE, 1); break; ++ case FFI_INT64: ct_set_type(ct, INT64_TYPE, 0); break; ++ default: break; ++ } ++ ct->base_size = ffi_type_size(ftype); ++ ct->align_mask = ffi_type_align(ftype) - 1; ++ ct->is_defined = 1; ++} ++ ++/* ++ * lookup and register builtin C type on demand ++ * You should ensure that the type with name doesn't appear in ++ * csymbol table before calling. ++ */ ++struct cp_ctype *ctype_lookup_builtin_type(char *name) ++{ ++ struct cp_ctype ct; ++ ++ if (!strncmp(name, "void", sizeof("void"))) { ++ init_builtin_type(&ct, FFI_VOID); ++ return cp_ctype_reg_type("void", &ct); ++ } else if (!strncmp(name, "int8_t", sizeof("int8_t"))) { ++ init_builtin_type(&ct, FFI_INT8); ++ return cp_ctype_reg_type("int8_t", &ct); ++ } else if (!strncmp(name, "uint8_t", sizeof("uint8_t"))) { ++ init_builtin_type(&ct, FFI_UINT8); ++ return cp_ctype_reg_type("uint8_t", &ct); ++ } else if (!strncmp(name, "int16_t", sizeof("int16_t"))) { ++ init_builtin_type(&ct, FFI_INT16); ++ return cp_ctype_reg_type("int16_t", &ct); ++ } else if (!strncmp(name, "uint16_t", sizeof("uint16_t"))) { ++ init_builtin_type(&ct, FFI_UINT16); ++ return cp_ctype_reg_type("uint16_t", &ct); ++ } else if (!strncmp(name, "int32_t", sizeof("int32_t"))) { ++ init_builtin_type(&ct, FFI_INT32); ++ return cp_ctype_reg_type("int32_t", &ct); ++ } else if (!strncmp(name, "uint32_t", sizeof("uint32_t"))) { ++ init_builtin_type(&ct, FFI_UINT32); ++ return cp_ctype_reg_type("uint32_t", &ct); ++ } else if (!strncmp(name, "int64_t", sizeof("int64_t"))) { ++ init_builtin_type(&ct, FFI_INT64); ++ return cp_ctype_reg_type("int64_t", &ct); ++ } else if (!strncmp(name, "uint64_t", sizeof("uint64_t"))) { ++ init_builtin_type(&ct, FFI_UINT64); ++ return cp_ctype_reg_type("uint64_t", &ct); ++ } else { ++ /* no builtin type matched */ ++ return NULL; ++ } ++} ++ ++/* start ctype reg table */ ++struct cp_ctype *ctype_lookup_type(char *name) ++{ ++ int i; ++ struct cp_ctype *ct; ++ ++ for (i = 0; i < cte_nr; i++) { ++ ct = &cte_arr[i].ct; ++ if (!strcmp(name, cte_arr[i].name)) ++ return ct; ++ } ++ ++ /* see if it's a builtin C type ++ * return NULL if still no match */ ++ return ctype_lookup_builtin_type(name); ++} ++ ++cp_csymbol_state *ctype_get_csym_state(void) ++{ ++ return &csym_state; ++} ++ ++#define DEFAULT_STACK_SIZE 20 ++#define DEFAULT_SYM_ARR_SIZE 20 ++int cp_ctype_init() ++{ ++ cts.size = DEFAULT_STACK_SIZE; ++ cts.top = 0; ++ cts.stack = malloc(sizeof(cp_ctype_entry)*DEFAULT_STACK_SIZE); ++ ++ cs_nr = 0; ++ cs_arr_size = DEFAULT_SYM_ARR_SIZE; ++ cs_arr = malloc(sizeof(csymbol)*DEFAULT_SYM_ARR_SIZE); ++ memset(cs_arr, 0, sizeof(csymbol)*DEFAULT_SYM_ARR_SIZE); ++ ++ cte_nr = 0; ++ cte_arr_size = DEFAULT_CTYPE_ARR_SIZE; ++ cte_arr = malloc(sizeof(cp_ctype_entry)*DEFAULT_CTYPE_ARR_SIZE); ++ ++ return 0; ++} ++ ++int cp_ctype_free() ++{ ++ int i; ++ csymbol *cs; ++ ++ if (cts.stack) ++ free(cts.stack); ++ ++ if (cs_arr) { ++ for (i = 0; i < cs_nr; i++) { ++ cs = &cs_arr[i]; ++ if (csym_type(cs) == FFI_FUNC) { ++ if (csym_func(cs)->arg_ids) ++ free(csym_func(cs)->arg_ids); ++ } else if (csym_type(cs) == FFI_STRUCT) { ++ if (csym_struct(cs)->members) ++ free(csym_struct(cs)->members); ++ } ++ } ++ free(cs_arr); ++ } ++ ++ if (cte_arr) { ++ free(cte_arr); ++ } ++ ++ return 0; ++} +diff --git a/drivers/staging/ktap/userspace/ktapc.h b/drivers/staging/ktap/userspace/ktapc.h +new file mode 100644 +index 0000000..76348df +--- /dev/null ++++ b/drivers/staging/ktap/userspace/ktapc.h +@@ -0,0 +1,393 @@ ++/* ++ * ktapc.h ++ * only can be included by userspace compiler ++ */ ++ ++#include ++ ++typedef int bool; ++#define false 0 ++#define true 1 ++ ++#define MAX_INT ((int)(~0U>>1)) ++#define UCHAR_MAX 255 ++ ++#define MAX_SIZET ((size_t)(~(size_t)0)-2) ++ ++#define KTAP_ERRSYNTAX 3 ++ ++/* ++ * KTAP_IDSIZE gives the maximum size for the description of the source ++ * of a function in debug information. ++ * CHANGE it if you want a different size. ++ */ ++#define KTAP_IDSIZE 60 ++ ++ ++#define FIRST_RESERVED 257 ++ ++/* ++ * maximum depth for nested C calls and syntactical nested non-terminals ++ * in a program. (Value must fit in an unsigned short int.) ++ */ ++#define KTAP_MAXCCALLS 200 ++ ++#define KTAP_MULTRET (-1) ++ ++ ++#define SHRT_MAX UCHAR_MAX ++ ++#define MAXUPVAL UCHAR_MAX ++ ++ ++/* maximum stack for a ktap function */ ++#define MAXSTACK 250 ++ ++#define islalpha(c) (isalpha(c) || (c) == '_') ++#define islalnum(c) (isalnum(c) || (c) == '_') ++ ++#define isreserved(s) ((s)->tsv.tt == KTAP_TSHRSTR && (s)->tsv.extra > 0) ++ ++#define ktap_numeq(a,b) ((a)==(b)) ++#define ktap_numisnan(L,a) (!ktap_numeq((a), (a))) ++ ++#define ktap_numunm(a) (-(a)) ++ ++/* ++ * ** Comparison and arithmetic functions ++ * */ ++ ++#define KTAP_OPADD 0 /* ORDER TM */ ++#define KTAP_OPSUB 1 ++#define KTAP_OPMUL 2 ++#define KTAP_OPDIV 3 ++#define KTAP_OPMOD 4 ++#define KTAP_OPPOW 5 ++#define KTAP_OPUNM 6 ++ ++#define KTAP_OPEQ 0 ++#define KTAP_OPLT 1 ++#define KTAP_OPLE 2 ++ ++ ++/* ++ * WARNING: if you change the order of this enumeration, ++ * grep "ORDER RESERVED" ++ */ ++enum RESERVED { ++ /* terminal symbols denoted by reserved words */ ++ TK_TRACE = FIRST_RESERVED, TK_TRACE_END, ++ TK_ARGEVENT, TK_ARGNAME, ++ TK_FFI_CDEF, ++ TK_ARG1, TK_ARG2, TK_ARG3, TK_ARG4, TK_ARG5, TK_ARG6, TK_ARG7, TK_ARG8, ++ TK_ARG9, TK_PROFILE, TK_TICK, TK_AGGR_ASSIGN, ++ TK_AND, TK_BREAK, ++ TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, ++ TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, ++ TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, ++ /* other terminal symbols */ ++ TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_INCR, TK_DBCOLON, ++ TK_EOS, TK_NUMBER, TK_NAME, TK_STRING, TK_KSYM ++}; ++ ++/* number of reserved words */ ++#define NUM_RESERVED ((int)(TK_WHILE-FIRST_RESERVED + 1)) ++ ++#define EOZ (0) /* end of stream */ ++ ++typedef union { ++ ktap_number r; ++ ktap_string *ts; ++} ktap_seminfo; /* semantics information */ ++ ++ ++typedef struct ktap_token { ++ int token; ++ ktap_seminfo seminfo; ++} ktap_token; ++ ++typedef struct ktap_mbuffer { ++ char *buffer; ++ size_t n; ++ size_t buffsize; ++} ktap_mbuffer; ++ ++#define mbuff_init(buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) ++#define mbuff(buff) ((buff)->buffer) ++#define mbuff_reset(buff) ((buff)->n = 0, memset((buff)->buffer, 0, (buff)->buffsize)) ++#define mbuff_len(buff) ((buff)->n) ++#define mbuff_size(buff) ((buff)->buffsize) ++ ++#define mbuff_resize(buff, size) \ ++ (ktapc_realloc((buff)->buffer, (buff)->buffsize, size, char), \ ++ (buff)->buffsize = size) ++ ++#define mbuff_free(buff) mbuff_resize(buff, 0) ++ ++ ++/* ++ * state of the lexer plus state of the parser when shared by all ++ * functions ++ */ ++typedef struct ktap_lexstate { ++ char *ptr; /* source file reading position */ ++ int current; /* current character (charint) */ ++ int linenumber; /* input line counter */ ++ int lastline; /* line of last token `consumed' */ ++ ktap_token t; /* current token */ ++ ktap_token lookahead; /* look ahead token */ ++ struct ktap_funcstate *fs; /* current function (parser) */ ++ ktap_mbuffer *buff; /* buffer for tokens */ ++ struct ktap_dyndata *dyd; /* dynamic structures used by the parser */ ++ ktap_string *source; /* current source name */ ++ ktap_string *envn; /* environment variable name */ ++ char decpoint; /* locale decimal point */ ++ int nCcalls; ++} ktap_lexstate; ++ ++ ++/* ++ * Expression descriptor ++ */ ++typedef enum { ++ VVOID, /* no value */ ++ VNIL, ++ VTRUE, ++ VFALSE, ++ VK, /* info = index of constant in `k' */ ++ VKNUM, /* nval = numerical value */ ++ VNONRELOC, /* info = result register */ ++ VLOCAL, /* info = local register */ ++ VUPVAL, /* info = index of upvalue in 'upvalues' */ ++ VINDEXED, /* t = table register/upvalue; idx = index R/K */ ++ VJMP, /* info = instruction pc */ ++ VRELOCABLE, /* info = instruction pc */ ++ VCALL, /* info = instruction pc */ ++ VVARARG, /* info = instruction pc */ ++ VEVENT, ++ VEVENTNAME, ++ VEVENTARG, ++} expkind; ++ ++ ++#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXED) ++#define vkisinreg(k) ((k) == VNONRELOC || (k) == VLOCAL) ++ ++typedef struct ktap_expdesc { ++ expkind k; ++ union { ++ struct { /* for indexed variables (VINDEXED) */ ++ short idx; /* index (R/K) */ ++ u8 t; /* table (register or upvalue) */ ++ u8 vt; /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */ ++ } ind; ++ int info; /* for generic use */ ++ ktap_number nval; /* for VKNUM */ ++ } u; ++ int t; /* patch list of `exit when true' */ ++ int f; /* patch list of `exit when false' */ ++} ktap_expdesc; ++ ++ ++typedef struct ktap_vardesc { ++ short idx; /* variable index in stack */ ++} ktap_vardesc; ++ ++ ++/* description of pending goto statements and label statements */ ++typedef struct ktap_labeldesc { ++ ktap_string *name; /* label identifier */ ++ int pc; /* position in code */ ++ int line; /* line where it appeared */ ++ u8 nactvar; /* local level where it appears in current block */ ++} ktap_labeldesc; ++ ++ ++/* list of labels or gotos */ ++typedef struct ktap_labellist { ++ ktap_labeldesc *arr; /* array */ ++ int n; /* number of entries in use */ ++ int size; /* array size */ ++} ktap_labellist; ++ ++ ++/* dynamic structures used by the parser */ ++typedef struct ktap_dyndata { ++ struct { /* list of active local variables */ ++ ktap_vardesc *arr; ++ int n; ++ int size; ++ } actvar; ++ ktap_labellist gt; /* list of pending gotos */ ++ ktap_labellist label; /* list of active labels */ ++} ktap_dyndata; ++ ++ ++/* control of blocks */ ++struct ktap_blockcnt; /* defined in lparser.c */ ++ ++ ++/* state needed to generate code for a given function */ ++typedef struct ktap_funcstate { ++ ktap_proto *f; /* current function header */ ++ ktap_tab *h; /* table to find (and reuse) elements in `k' */ ++ struct ktap_funcstate *prev; /* enclosing function */ ++ struct ktap_lexstate *ls; /* lexical state */ ++ struct ktap_blockcnt *bl; /* chain of current blocks */ ++ int pc; /* next position to code (equivalent to `ncode') */ ++ int lasttarget; /* 'label' of last 'jump label' */ ++ int jpc; /* list of pending jumps to `pc' */ ++ int nk; /* number of elements in `k' */ ++ int np; /* number of elements in `p' */ ++ int firstlocal; /* index of first local var (in ktap_dyndata array) */ ++ short nlocvars; /* number of elements in 'f->locvars' */ ++ u8 nactvar; /* number of active local variables */ ++ u8 nups; /* number of upvalues */ ++ u8 freereg; /* first free register */ ++} ktap_funcstate; ++ ++ ++/* ++ * Marks the end of a patch list. It is an invalid value both as an absolute ++ * address, and as a list link (would link an element to itself). ++ */ ++#define NO_JUMP (-1) ++ ++ ++/* ++ * grep "ORDER OPR" if you change these enums (ORDER OP) ++ */ ++typedef enum BinOpr { ++ OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, ++ OPR_CONCAT, ++ OPR_EQ, OPR_LT, OPR_LE, ++ OPR_NE, OPR_GT, OPR_GE, ++ OPR_AND, OPR_OR, ++ OPR_NOBINOPR ++} BinOpr; ++ ++ ++typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; ++ ++ ++#define getcode(fs,e) ((fs)->f->code[(e)->u.info]) ++ ++#define codegen_codeAsBx(fs,o,A,sBx) codegen_codeABx(fs,o,A,(sBx)+MAXARG_sBx) ++ ++#define codegen_setmultret(fs,e) codegen_setreturns(fs, e, KTAP_MULTRET) ++ ++#define codegen_jumpto(fs,t) codegen_patchlist(fs, codegen_jump(fs), t) ++ ++ ++#define ktapc_realloc(v, osize, nsize, t) \ ++ ((v) = (t *)ktapc_reallocv(v, osize * sizeof(t), nsize * sizeof(t))) ++ ++#define ktapc_reallocvector(v,oldn,n,t) ktapc_realloc(v,oldn,n,t) ++ ++ ++#define ktapc_growvector(v,nelems,size,t,limit,e) \ ++ if ((nelems)+1 > (size)) \ ++ ((v)=(t *)ktapc_growaux(v,&(size),sizeof(t),limit,e)) ++ ++ ++void lex_init(); ++ktap_string *lex_newstring(ktap_lexstate *ls, const char *str, size_t l); ++const char *lex_token2str(ktap_lexstate *ls, int token); ++void lex_syntaxerror(ktap_lexstate *ls, const char *msg); ++void lex_setinput(ktap_lexstate *ls, char *ptr, ktap_string *source, int firstchar); ++void lex_next(ktap_lexstate *ls); ++int lex_lookahead(ktap_lexstate *ls); ++void lex_read_string_until(ktap_lexstate *ls, int c); ++ktap_closure *ktapc_parser(char *pos, const char *name); ++ktap_string *ktapc_ts_new(const char *str); ++int ktapc_ts_eqstr(ktap_string *a, ktap_string *b); ++ktap_string *ktapc_ts_newlstr(const char *str, size_t l); ++ktap_proto *ktapc_newproto(); ++ktap_tab *ktapc_table_new(); ++const ktap_value *ktapc_table_get(ktap_tab *t, const ktap_value *key); ++void ktapc_table_setvalue(ktap_tab *t, const ktap_value *key, ktap_value *val); ++ktap_closure *ktapc_newclosure(int n); ++char *ktapc_sprintf(const char *fmt, ...); ++ ++void *ktapc_reallocv(void *block, size_t osize, size_t nsize); ++void *ktapc_growaux(void *block, int *size, size_t size_elems, int limit, ++ const char *what); ++ ++void ktapio_exit(void); ++int ktapio_create(const char *output_filename); ++ ++ktap_eventdef_info *ktapc_parse_eventdef(const char *eventdef); ++void cleanup_event_resources(void); ++ ++extern int verbose; ++#define verbose_printf(...) \ ++ if (verbose) \ ++ printf("[verbose] " __VA_ARGS__); ++ ++#define ktapc_equalobj(t1, t2) kp_equalobjv(NULL, t1, t2) ++ ++int codegen_stringK(ktap_funcstate *fs, ktap_string *s); ++void codegen_indexed(ktap_funcstate *fs, ktap_expdesc *t, ktap_expdesc *k); ++void codegen_setreturns(ktap_funcstate *fs, ktap_expdesc *e, int nresults); ++void codegen_reserveregs(ktap_funcstate *fs, int n); ++void codegen_exp2nextreg(ktap_funcstate *fs, ktap_expdesc *e); ++void codegen_nil(ktap_funcstate *fs, int from, int n); ++void codegen_patchlist(ktap_funcstate *fs, int list, int target); ++void codegen_patchclose(ktap_funcstate *fs, int list, int level); ++int codegen_jump(ktap_funcstate *fs); ++void codegen_patchtohere(ktap_funcstate *fs, int list); ++int codegen_codeABx(ktap_funcstate *fs, OpCode o, int a, unsigned int bc); ++void codegen_ret(ktap_funcstate *fs, int first, int nret); ++void codegen_exp2anyregup(ktap_funcstate *fs, ktap_expdesc *e); ++void codegen_exp2val(ktap_funcstate *fs, ktap_expdesc *e); ++int codegen_exp2RK(ktap_funcstate *fs, ktap_expdesc *e); ++int codegen_codeABC(ktap_funcstate *fs, OpCode o, int a, int b, int c); ++void codegen_setlist(ktap_funcstate *fs, int base, int nelems, int tostore); ++void codegen_fixline (ktap_funcstate *fs, int line); ++void codegen_dischargevars(ktap_funcstate *fs, ktap_expdesc *e); ++void codegen_self(ktap_funcstate *fs, ktap_expdesc *e, ktap_expdesc *key); ++void codegen_prefix(ktap_funcstate *fs, UnOpr op, ktap_expdesc *e, int line); ++void codegen_infix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *v); ++void codegen_posfix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *e1, ktap_expdesc *e2, int line); ++void codegen_setoneret(ktap_funcstate *fs, ktap_expdesc *e); ++void codegen_storevar(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex); ++void codegen_storeincr(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex); ++void codegen_store_aggr(ktap_funcstate *fs, ktap_expdesc *var, ++ ktap_expdesc *ex); ++void codegen_goiftrue(ktap_funcstate *fs, ktap_expdesc *e); ++int codegen_getlabel(ktap_funcstate *fs); ++int codegen_codek(ktap_funcstate *fs, int reg, int k); ++int codegen_numberK(ktap_funcstate *fs, ktap_number r); ++void codegen_checkstack(ktap_funcstate *fs, int n); ++void codegen_goiffalse(ktap_funcstate *fs, ktap_expdesc *e); ++void codegen_concat(ktap_funcstate *fs, int *l1, int l2); ++int codegen_exp2anyreg(ktap_funcstate *fs, ktap_expdesc *e); ++ ++typedef int (*ktap_writer)(const void* p, size_t sz, void* ud); ++int ktapc_dump(const ktap_proto *f, ktap_writer w, void *data, int strip); ++ ++void ktapc_chunkid(char *out, const char *source, size_t bufflen); ++int ktapc_str2d(const char *s, size_t len, ktap_number *result); ++int ktapc_hexavalue(int c); ++ktap_number ktapc_arith(int op, ktap_number v1, ktap_number v2); ++int ktapc_int2fb(unsigned int x); ++bool strglobmatch(const char *str, const char *pat); ++int kallsyms_parse(void *arg, ++ int(*process_symbol)(void *arg, const char *name, ++ char type, unsigned long start)); ++ ++unsigned long find_kernel_symbol(const char *symbol); ++void list_available_events(const char *match); ++ ++ ++#ifdef CONFIG_KTAP_FFI ++#include "../include/ktap_ffi.h" ++ ++typedef struct cp_csymbol_state { ++ int cs_nr; /* number of c symbols */ ++ int cs_arr_size; /* size of current symbol arrays */ ++ csymbol *cs_arr; ++} cp_csymbol_state; ++ ++cp_csymbol_state *ctype_get_csym_state(void); ++#endif +diff --git a/drivers/staging/ktap/userspace/ktapio.c b/drivers/staging/ktap/userspace/ktapio.c +new file mode 100644 +index 0000000..a883fdc +--- /dev/null ++++ b/drivers/staging/ktap/userspace/ktapio.c +@@ -0,0 +1,106 @@ ++/* ++ * ktapio.c - ring buffer transport in userspace ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MAX_BUFLEN 131072 ++#define PATH_MAX 128 ++ ++#define handle_error(str) do { perror(str); exit(-1); } while(0) ++ ++void sigfunc(int signo) ++{ ++ /* should not not reach here */ ++} ++ ++static void block_sigint() ++{ ++ sigset_t mask; ++ ++ sigemptyset(&mask); ++ sigaddset(&mask, SIGINT); ++ ++ pthread_sigmask(SIG_BLOCK, &mask, NULL); ++} ++ ++static void *reader_thread(void *data) ++{ ++ char buf[MAX_BUFLEN]; ++ char filename[PATH_MAX]; ++ const char *output = data; ++ int failed = 0, fd, out_fd, len; ++ ++ block_sigint(); ++ ++ if (output) { ++ out_fd = open(output, O_CREAT | O_WRONLY | O_TRUNC, ++ S_IRUSR|S_IWUSR); ++ if (out_fd < 0) { ++ fprintf(stderr, "Cannot open output file %s\n", output); ++ return NULL; ++ } ++ } else ++ out_fd = 2; ++ ++ sprintf(filename, "/sys/kernel/debug/ktap/trace_pipe_%d", getpid()); ++ ++ open_again: ++ fd = open(filename, O_RDONLY); ++ if (fd < 0) { ++ usleep(10000); ++ ++ if (failed++ == 10) { ++ fprintf(stderr, "Cannot open file %s\n", filename); ++ return NULL; ++ } ++ goto open_again; ++ } ++ ++ while ((len = read(fd, buf, sizeof(buf))) > 0) ++ write(out_fd, buf, len); ++ ++ close(fd); ++ close(out_fd); ++ ++ return NULL; ++} ++ ++int ktapio_create(const char *output) ++{ ++ pthread_t reader; ++ ++ signal(SIGINT, sigfunc); ++ ++ if (pthread_create(&reader, NULL, reader_thread, (void *)output) < 0) ++ handle_error("pthread_create reader_thread failed\n"); ++ ++ return 0; ++} ++ +diff --git a/drivers/staging/ktap/userspace/lex.c b/drivers/staging/ktap/userspace/lex.c +new file mode 100644 +index 0000000..2cfcd92 +--- /dev/null ++++ b/drivers/staging/ktap/userspace/lex.c +@@ -0,0 +1,632 @@ ++/* ++ * lex.c - ktap lexical analyzer ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * Copyright (C) 1994-2013 Lua.org, PUC-Rio. ++ * - The part of code in this file is copied from lua initially. ++ * - lua's MIT license is compatible with GPL. ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "../include/ktap_types.h" ++#include "../include/ktap_opcodes.h" ++#include "ktapc.h" ++ ++#define next(ls) (ls->current = *ls->ptr++) ++ ++#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') ++ ++#define KTAP_MINBUFFER 32 ++ ++/* ORDER RESERVED */ ++static const char *const ktap_tokens [] = { ++ "trace", "trace_end", "argevent", "argname", "cdef", ++ "arg1", "arg2", "arg3", "arg4", "arg5", "arg6", "arg7", "arg9", "arg9", ++ "profile", "tick", "<<<", ++ "and", "break", "do", "else", "elseif", ++ "end", "false", "for", "function", "goto", "if", ++ "in", "local", "nil", "not", "or", "repeat", ++ "return", "then", "true", "until", "while", ++ "..", "...", "==", ">=", "<=", "!=", "+=", "::", "", ++ "", "", "", "" ++}; ++ ++#define save_and_next(ls) (save(ls, ls->current), next(ls)) ++ ++static void lexerror(ktap_lexstate *ls, const char *msg, int token); ++ ++static void save(ktap_lexstate *ls, int c) ++{ ++ ktap_mbuffer *b = ls->buff; ++ if (mbuff_len(b) + 1 > mbuff_size(b)) { ++ size_t newsize; ++ if (mbuff_size(b) >= MAX_SIZET / 2) ++ lexerror(ls, "lexical element too long", 0); ++ newsize = mbuff_size(b) * 2; ++ mbuff_resize(b, newsize); ++ } ++ b->buffer[mbuff_len(b)++] = (char)c; ++} ++ ++void lex_init() ++{ ++ int i; ++ for (i = 0; i < NUM_RESERVED; i++) { ++ ktap_string *ts = ktapc_ts_new(ktap_tokens[i]); ++ ts->tsv.extra = (u8)(i+1); /* reserved word */ ++ } ++} ++ ++const char *lex_token2str(ktap_lexstate *ls, int token) ++{ ++ if (token < FIRST_RESERVED) { ++ ktap_assert(token == (unsigned char)token); ++ return (isprint(token)) ? ktapc_sprintf(KTAP_QL("%c"), token) : ++ ktapc_sprintf("char(%d)", token); ++ } else { ++ const char *s = ktap_tokens[token - FIRST_RESERVED]; ++ if (token < TK_EOS) ++ return ktapc_sprintf(KTAP_QS, s); ++ else ++ return s; ++ } ++} ++ ++static const char *txtToken(ktap_lexstate *ls, int token) ++{ ++ switch (token) { ++ case TK_NAME: ++ case TK_STRING: ++ case TK_NUMBER: ++ save(ls, '\0'); ++ return ktapc_sprintf(KTAP_QS, mbuff(ls->buff)); ++ default: ++ return lex_token2str(ls, token); ++ } ++} ++ ++static void lexerror(ktap_lexstate *ls, const char *msg, int token) ++{ ++ char buff[KTAP_IDSIZE]; ++ char *newmsg; ++ ++ ktapc_chunkid(buff, getstr(ls->source), KTAP_IDSIZE); ++ newmsg = ktapc_sprintf("%s:%d: %s", buff, ls->linenumber, msg); ++ if (token) ++ newmsg = ktapc_sprintf("%s near %s", newmsg, txtToken(ls, token)); ++ printf("lexerror: %s\n", newmsg); ++ exit(EXIT_FAILURE); ++} ++ ++void lex_syntaxerror(ktap_lexstate *ls, const char *msg) ++{ ++ lexerror(ls, msg, ls->t.token); ++} ++ ++/* ++ * creates a new string and anchors it in function's table so that ++ * it will not be collected until the end of the function's compilation ++ * (by that time it should be anchored in function's prototype) ++ */ ++ktap_string *lex_newstring(ktap_lexstate *ls, const char *str, size_t l) ++{ ++ const ktap_value *o; /* entry for `str' */ ++ ktap_value val; /* entry for `str' */ ++ ktap_value tsv; ++ ktap_string *ts = ktapc_ts_newlstr(str, l); /* create new string */ ++ set_string(&tsv, ts); ++ o = ktapc_table_get(ls->fs->h, &tsv); ++ if (is_nil(o)) { /* not in use yet? (see 'addK') */ ++ /* boolean value does not need GC barrier; ++ table has no metatable, so it does not need to invalidate cache */ ++ set_boolean(&val, 1); /* t[string] = true */ ++ ktapc_table_setvalue(ls->fs->h, &tsv, &val); ++ } ++ return ts; ++} ++ ++/* ++ * increment line number and skips newline sequence (any of ++ * \n, \r, \n\r, or \r\n) ++ */ ++static void inclinenumber(ktap_lexstate *ls) ++{ ++ int old = ls->current; ++ ktap_assert(currIsNewline(ls)); ++ next(ls); /* skip `\n' or `\r' */ ++ if (currIsNewline(ls) && ls->current != old) ++ next(ls); /* skip `\n\r' or `\r\n' */ ++ if (++ls->linenumber >= MAX_INT) ++ lex_syntaxerror(ls, "chunk has too many lines"); ++} ++ ++void lex_setinput(ktap_lexstate *ls, char *ptr, ktap_string *source, int firstchar) ++{ ++ ls->decpoint = '.'; ++ ls->current = firstchar; ++ ls->lookahead.token = TK_EOS; /* no look-ahead token */ ++ ls->ptr = ptr; ++ ls->fs = NULL; ++ ls->linenumber = 1; ++ ls->lastline = 1; ++ ls->source = source; ++ ls->envn = ktapc_ts_new(KTAP_ENV); /* create env name */ ++ mbuff_resize(ls->buff, KTAP_MINBUFFER); /* initialize buffer */ ++} ++ ++/* ++ * ======================================================= ++ * LEXICAL ANALYZER ++ * ======================================================= ++ */ ++static int check_next(ktap_lexstate *ls, const char *set) ++{ ++ if (ls->current == '\0' || !strchr(set, ls->current)) ++ return 0; ++ save_and_next(ls); ++ return 1; ++} ++ ++/* ++ * change all characters 'from' in buffer to 'to' ++ */ ++static void buffreplace(ktap_lexstate *ls, char from, char to) ++{ ++ size_t n = mbuff_len(ls->buff); ++ char *p = mbuff(ls->buff); ++ while (n--) ++ if (p[n] == from) p[n] = to; ++} ++ ++#if !defined(getlocaledecpoint) ++#define getlocaledecpoint() (localeconv()->decimal_point[0]) ++#endif ++ ++#define mbuff2d(b,e) ktapc_str2d(mbuff(b), mbuff_len(b) - 1, e) ++ ++/* ++ * in case of format error, try to change decimal point separator to ++ * the one defined in the current locale and check again ++ */ ++static void trydecpoint(ktap_lexstate *ls, ktap_seminfo *seminfo) ++{ ++ char old = ls->decpoint; ++ ls->decpoint = getlocaledecpoint(); ++ buffreplace(ls, old, ls->decpoint); /* try new decimal separator */ ++ if (!mbuff2d(ls->buff, &seminfo->r)) { ++ /* format error with correct decimal point: no more options */ ++ buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ ++ lexerror(ls, "malformed number", TK_NUMBER); ++ } ++} ++ ++/* ++ * this function is quite liberal in what it accepts, as 'ktapc_str2d' ++ * will reject ill-formed numerals. ++ */ ++static void read_numeral(ktap_lexstate *ls, ktap_seminfo *seminfo) ++{ ++ const char *expo = "Ee"; ++ int first = ls->current; ++ ++ ktap_assert(isdigit(ls->current)); ++ save_and_next(ls); ++ if (first == '0' && check_next(ls, "Xx")) /* hexadecimal? */ ++ expo = "Pp"; ++ for (;;) { ++ if (check_next(ls, expo)) /* exponent part? */ ++ check_next(ls, "+-"); /* optional exponent sign */ ++ if (isxdigit(ls->current) || ls->current == '.') ++ save_and_next(ls); ++ else ++ break; ++ } ++ save(ls, '\0'); ++ buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ ++ if (!mbuff2d(ls->buff, &seminfo->r)) /* format error? */ ++ trydecpoint(ls, seminfo); /* try to update decimal point separator */ ++} ++ ++/* ++ * skip a sequence '[=*[' or ']=*]' and return its number of '='s or ++ * -1 if sequence is malformed ++ */ ++static int skip_sep(ktap_lexstate *ls) ++{ ++ int count = 0; ++ int s = ls->current; ++ ++ ktap_assert(s == '[' || s == ']'); ++ save_and_next(ls); ++ while (ls->current == '=') { ++ save_and_next(ls); ++ count++; ++ } ++ return (ls->current == s) ? count : (-count) - 1; ++} ++ ++static void read_long_string(ktap_lexstate *ls, ktap_seminfo *seminfo, int sep) ++{ ++ save_and_next(ls); /* skip 2nd `[' */ ++ if (currIsNewline(ls)) /* string starts with a newline? */ ++ inclinenumber(ls); /* skip it */ ++ for (;;) { ++ switch (ls->current) { ++ case EOZ: ++ lexerror(ls, (seminfo) ? "unfinished long string" : ++ "unfinished long comment", TK_EOS); ++ break; /* to avoid warnings */ ++ case ']': { ++ if (skip_sep(ls) == sep) { ++ save_and_next(ls); /* skip 2nd `]' */ ++ goto endloop; ++ } ++ break; ++ } ++ case '\n': ++ case '\r': { ++ save(ls, '\n'); ++ inclinenumber(ls); ++ /* avoid wasting space */ ++ if (!seminfo) ++ mbuff_reset(ls->buff); ++ break; ++ } ++ default: { ++ if (seminfo) ++ save_and_next(ls); ++ else ++ next(ls); ++ } ++ } ++ } ++ ++ endloop: ++ if (seminfo) ++ seminfo->ts = lex_newstring(ls, mbuff(ls->buff) + (2 + sep), ++ mbuff_len(ls->buff) - 2*(2 + sep)); ++} ++ ++static void escerror(ktap_lexstate *ls, int *c, int n, const char *msg) ++{ ++ int i; ++ mbuff_reset(ls->buff); /* prepare error message */ ++ save(ls, '\\'); ++ for (i = 0; i < n && c[i] != EOZ; i++) ++ save(ls, c[i]); ++ lexerror(ls, msg, TK_STRING); ++} ++ ++static int readhexaesc(ktap_lexstate *ls) ++{ ++ int c[3], i; /* keep input for error message */ ++ int r = 0; /* result accumulator */ ++ c[0] = 'x'; /* for error message */ ++ for (i = 1; i < 3; i++) { /* read two hexa digits */ ++ c[i] = next(ls); ++ if (!isxdigit(c[i])) ++ escerror(ls, c, i + 1, "hexadecimal digit expected"); ++ r = (r << 4) + ktapc_hexavalue(c[i]); ++ } ++ return r; ++} ++ ++static int readdecesc(ktap_lexstate *ls) ++{ ++ int c[3], i; ++ int r = 0; /* result accumulator */ ++ for (i = 0; i < 3 && isdigit(ls->current); i++) { /* read up to 3 digits */ ++ c[i] = ls->current; ++ r = 10*r + c[i] - '0'; ++ next(ls); ++ } ++ if (r > UCHAR_MAX) ++ escerror(ls, c, i, "decimal escape too large"); ++ return r; ++} ++ ++static void read_string(ktap_lexstate *ls, int del, ktap_seminfo *seminfo) ++{ ++ save_and_next(ls); /* keep delimiter (for error messages) */ ++ while (ls->current != del) { ++ switch (ls->current) { ++ case EOZ: ++ lexerror(ls, "unfinished string", TK_EOS); ++ break; /* to avoid warnings */ ++ case '\n': ++ case '\r': ++ lexerror(ls, "unfinished string", TK_STRING); ++ break; /* to avoid warnings */ ++ case '\\': { /* escape sequences */ ++ int c; /* final character to be saved */ ++ next(ls); /* do not save the `\' */ ++ switch (ls->current) { ++ case 'a': c = '\a'; goto read_save; ++ case 'b': c = '\b'; goto read_save; ++ case 'f': c = '\f'; goto read_save; ++ case 'n': c = '\n'; goto read_save; ++ case 'r': c = '\r'; goto read_save; ++ case 't': c = '\t'; goto read_save; ++ case 'v': c = '\v'; goto read_save; ++ case 'x': c = readhexaesc(ls); goto read_save; ++ case '\n': case '\r': ++ inclinenumber(ls); c = '\n'; goto only_save; ++ case '\\': case '\"': case '\'': ++ c = ls->current; goto read_save; ++ case EOZ: goto no_save; /* will raise an error next loop */ ++ case 'z': { /* zap following span of spaces */ ++ next(ls); /* skip the 'z' */ ++ while (isspace(ls->current)) { ++ if (currIsNewline(ls)) ++ inclinenumber(ls); ++ else ++ next(ls); ++ } ++ goto no_save; ++ } ++ default: { ++ if (!isdigit(ls->current)) ++ escerror(ls, &ls->current, 1, "invalid escape sequence"); ++ /* digital escape \ddd */ ++ c = readdecesc(ls); ++ goto only_save; ++ } ++ } ++ read_save: ++ next(ls); /* read next character */ ++ only_save: ++ save(ls, c); /* save 'c' */ ++ no_save: ++ break; ++ } ++ default: ++ save_and_next(ls); ++ } ++ } ++ save_and_next(ls); /* skip delimiter */ ++ seminfo->ts = lex_newstring(ls, mbuff(ls->buff) + 1, mbuff_len(ls->buff) - 2); ++} ++ ++static int llex(ktap_lexstate *ls, ktap_seminfo *seminfo) ++{ ++ mbuff_reset(ls->buff); ++ ++ for (;;) { ++ switch (ls->current) { ++ case '\n': case '\r': { /* line breaks */ ++ inclinenumber(ls); ++ break; ++ } ++ case ' ': case '\f': case '\t': case '\v': { /* spaces */ ++ next(ls); ++ break; ++ } ++ case '#': { ++ while (!currIsNewline(ls) && ls->current != EOZ) ++ next(ls); /* skip until end of line (or end of file) */ ++ break; ++ } ++ #if 0 ++ case '-': { /* '-' or '--' (comment) */ ++ next(ls); ++ if (ls->current != '-') ++ return '-'; ++ /* else is a comment */ ++ next(ls); ++ if (ls->current == '[') { /* long comment? */ ++ int sep = skip_sep(ls); ++ mbuff_reset(ls->buff); /* `skip_sep' may dirty the buffer */ ++ if (sep >= 0) { ++ read_long_string(ls, NULL, sep); /* skip long comment */ ++ mbuff_reset(ls->buff); /* previous call may dirty the buff. */ ++ break; ++ } ++ } ++ /* else short comment */ ++ while (!currIsNewline(ls) && ls->current != EOZ) ++ next(ls); /* skip until end of line (or end of file) */ ++ break; ++ } ++ #endif ++ case '[': { /* long string or simply '[' */ ++ int sep = skip_sep(ls); ++ if (sep >= 0) { ++ read_long_string(ls, seminfo, sep); ++ return TK_STRING; ++ } ++ else if (sep == -1) ++ return '['; ++ else ++ lexerror(ls, "invalid long string delimiter", TK_STRING); ++ } ++ case '+': { ++ next(ls); ++ if (ls->current != '=') ++ return '+'; ++ else { ++ next(ls); ++ return TK_INCR; ++ } ++ } ++ case '=': { ++ next(ls); ++ if (ls->current != '=') ++ return '='; ++ else { ++ next(ls); ++ return TK_EQ; ++ } ++ } ++ case '<': { ++ next(ls); ++ if (ls->current == '=') ++ return TK_LE; ++ else if (ls->current == '<') { ++ next(ls); ++ if (ls->current == '<') { ++ next(ls); ++ return TK_AGGR_ASSIGN; ++ } ++ } else { ++ return '<'; ++ } ++ } ++ case '>': { ++ next(ls); ++ if (ls->current != '=') ++ return '>'; ++ else { ++ next(ls); ++ return TK_GE; ++ } ++ } ++ case '!': { ++ next(ls); ++ if (ls->current != '=') ++ return TK_NOT; ++ else { ++ next(ls); ++ return TK_NE; ++ } ++ } ++ case ':': { ++ next(ls); ++ if (ls->current != ':') ++ return ':'; ++ else { ++ next(ls); ++ return TK_DBCOLON; ++ } ++ } ++ case '"': case '\'': { /* short literal strings */ ++ read_string(ls, ls->current, seminfo); ++ return TK_STRING; ++ } ++ case '`': { /* short literal kernel symbol */ ++ read_string(ls, ls->current, seminfo); ++ return TK_KSYM; ++ } ++ case '.': { /* '.', '..', '...', or number */ ++ save_and_next(ls); ++ if (check_next(ls, ".")) { ++ if (check_next(ls, ".")) ++ return TK_DOTS; /* '...' */ ++ else ++ return TK_CONCAT; /* '..' */ ++ } ++ else if (!isdigit(ls->current)) ++ return '.'; ++ /* else go through */ ++ } ++ case '0': case '1': case '2': case '3': case '4': ++ case '5': case '6': case '7': case '8': case '9': { ++ read_numeral(ls, seminfo); ++ return TK_NUMBER; ++ } ++ case EOZ: { ++ return TK_EOS; ++ } ++ case '&': { ++ next(ls); ++ if (ls->current != '&') ++ return '&'; ++ else { ++ next(ls); ++ return TK_AND; ++ } ++ } ++ case '|': { ++ next(ls); ++ if (ls->current != '|') ++ return '|'; ++ else { ++ next(ls); ++ return TK_OR; ++ } ++ } ++ default: { ++ if (islalpha(ls->current)) { ++ /* identifier or reserved word? */ ++ ktap_string *ts; ++ do { ++ save_and_next(ls); ++ } while (islalnum(ls->current)); ++ ts = lex_newstring(ls, mbuff(ls->buff), ++ mbuff_len(ls->buff)); ++ seminfo->ts = ts; ++ if (isreserved(ts)) /* reserved word? */ ++ return ts->tsv.extra - 1 + ++ FIRST_RESERVED; ++ else { ++ return TK_NAME; ++ } ++ } else { /* single-char tokens (+ - / ...) */ ++ int c = ls->current; ++ next(ls); ++ return c; ++ } ++ } ++ } ++ } ++} ++ ++void lex_read_string_until(ktap_lexstate *ls, int c) ++{ ++ ktap_string *ts; ++ char errmsg[32]; ++ ++ mbuff_reset(ls->buff); ++ ++ while (ls->current == ' ') ++ next(ls); ++ ++ do { ++ save_and_next(ls); ++ } while (ls->current != c && ls->current != EOZ); ++ ++ if (ls->current != c) { ++ sprintf(errmsg, "expect %c", c); ++ lexerror(ls, errmsg, 0); ++ } ++ ++ ts = lex_newstring(ls, mbuff(ls->buff), mbuff_len(ls->buff)); ++ ls->t.seminfo.ts = ts; ++ ls->t.token = TK_STRING; ++} ++ ++void lex_next(ktap_lexstate *ls) ++{ ++ ls->lastline = ls->linenumber; ++ if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ ++ ls->t = ls->lookahead; /* use this one */ ++ ls->lookahead.token = TK_EOS; /* and discharge it */ ++ } else ++ ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ ++} ++ ++int lex_lookahead(ktap_lexstate *ls) ++{ ++ ktap_assert(ls->lookahead.token == TK_EOS); ++ ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); ++ return ls->lookahead.token; ++} ++ +diff --git a/drivers/staging/ktap/userspace/main.c b/drivers/staging/ktap/userspace/main.c +new file mode 100644 +index 0000000..82fbab2 +--- /dev/null ++++ b/drivers/staging/ktap/userspace/main.c +@@ -0,0 +1,727 @@ ++/* ++ * main.c - ktap compiler and loader entry ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../include/ktap_types.h" ++#include "../include/ktap_opcodes.h" ++#include "ktapc.h" ++#include "../runtime/kp_obj.h" ++#include "../runtime/kp_str.h" ++#include "../runtime/kp_tab.h" ++#include "symbol.h" ++#include "cparser.h" ++ ++ ++/*******************************************************************/ ++ ++void *ktapc_reallocv(void *block, size_t osize, size_t nsize) ++{ ++ return kp_reallocv(NULL, block, osize, nsize); ++} ++ ++ktap_closure *ktapc_newclosure(int n) ++{ ++ return kp_newclosure(NULL, n); ++} ++ ++ktap_proto *ktapc_newproto() ++{ ++ return kp_newproto(NULL); ++} ++ ++const ktap_value *ktapc_table_get(ktap_tab *t, const ktap_value *key) ++{ ++ return kp_tab_get(t, key); ++} ++ ++void ktapc_table_setvalue(ktap_tab *t, const ktap_value *key, ktap_value *val) ++{ ++ kp_tab_setvalue(NULL, t, key, val); ++} ++ ++ktap_tab *ktapc_table_new() ++{ ++ return kp_tab_new(NULL); ++} ++ ++ktap_string *ktapc_ts_newlstr(const char *str, size_t l) ++{ ++ return kp_tstring_newlstr(NULL, str, l); ++} ++ ++ktap_string *ktapc_ts_new(const char *str) ++{ ++ return kp_tstring_new(NULL, str); ++} ++ ++int ktapc_ts_eqstr(ktap_string *a, ktap_string *b) ++{ ++ return kp_tstring_eqstr(a, b); ++} ++ ++static void ktapc_runerror(const char *err_msg_fmt, ...) ++{ ++ va_list ap; ++ ++ fprintf(stderr, "ktapc_runerror\n"); ++ ++ va_start(ap, err_msg_fmt); ++ vfprintf(stderr, err_msg_fmt, ap); ++ va_end(ap); ++ ++ exit(EXIT_FAILURE); ++} ++ ++/* ++ * todo: memory leak here ++ */ ++char *ktapc_sprintf(const char *fmt, ...) ++{ ++ char *msg = malloc(128); ++ ++ va_list argp; ++ va_start(argp, fmt); ++ vsprintf(msg, fmt, argp); ++ va_end(argp); ++ return msg; ++} ++ ++ ++#define MINSIZEARRAY 4 ++ ++void *ktapc_growaux(void *block, int *size, size_t size_elems, int limit, ++ const char *what) ++{ ++ void *newblock; ++ int newsize; ++ ++ if (*size >= limit/2) { /* cannot double it? */ ++ if (*size >= limit) /* cannot grow even a little? */ ++ ktapc_runerror("too many %s (limit is %d)\n", ++ what, limit); ++ newsize = limit; /* still have at least one free place */ ++ } else { ++ newsize = (*size) * 2; ++ if (newsize < MINSIZEARRAY) ++ newsize = MINSIZEARRAY; /* minimum size */ ++ } ++ ++ newblock = ktapc_reallocv(block, (*size) * size_elems, newsize * size_elems); ++ *size = newsize; /* update only when everything else is OK */ ++ return newblock; ++} ++ ++/*************************************************************************/ ++ ++#define print_base(i) \ ++ do { \ ++ if (i < f->sizelocvars) /* it's a localvars */ \ ++ printf("%s", getstr(f->locvars[i].varname)); \ ++ else \ ++ printf("base + %d", i); \ ++ } while (0) ++ ++#define print_RK(instr, _field) \ ++ do { \ ++ if (ISK(GETARG_##_field(instr))) \ ++ kp_showobj(NULL, k + INDEXK(GETARG_##_field(instr))); \ ++ else \ ++ print_base(GETARG_##_field(instr)); \ ++ } while (0) ++ ++#define print_RKA(instr) print_RK(instr, A) ++#define print_RKB(instr) print_RK(instr, B) ++#define print_RKC(instr) print_RK(instr, C) ++ ++#define print_upvalue(idx) \ ++ do { \ ++ if ((idx) == 0) \ ++ printf("global"); \ ++ else \ ++ printf("upvalues[%d]", (idx)); \ ++ } while (0) ++ ++static void decode_instruction(ktap_proto *f, int instr) ++{ ++ int opcode = GET_OPCODE(instr); ++ ktap_value *k; ++ ++ k = f->k; ++ ++ printf("%.8x\t", instr); ++ printf("%s\t", ktap_opnames[opcode]); ++ ++ switch (opcode) { ++ case OP_MOVE: ++ printf("\t"); ++ print_base(GETARG_A(instr)); ++ printf(" <- "); ++ print_base(GETARG_B(instr)); ++ break; ++ case OP_GETTABUP: ++ print_base(GETARG_A(instr)); ++ printf(" <- "); ++ print_upvalue(GETARG_B(instr)); ++ printf("{"); print_RKC(instr); printf("}"); ++ ++ break; ++ case OP_GETTABLE: ++ print_base(GETARG_A(instr)); ++ printf(" <- "); ++ ++ print_base(GETARG_B(instr)); ++ ++ printf("{"); ++ print_RKC(instr); ++ printf("}"); ++ break; ++ case OP_SETTABLE: ++ print_base(GETARG_A(instr)); ++ printf("{"); ++ print_RKB(instr); ++ printf("}"); ++ printf(" <- "); ++ print_RKC(instr); ++ break; ++ case OP_LOADK: ++ printf("\t"); ++ print_base(GETARG_A(instr)); ++ printf(" <- "); ++ ++ kp_showobj(NULL, k + GETARG_Bx(instr)); ++ break; ++ case OP_CALL: ++ printf("\t"); ++ print_base(GETARG_A(instr)); ++ break; ++ case OP_JMP: ++ printf("\t%d", GETARG_sBx(instr)); ++ break; ++ case OP_CLOSURE: ++ printf("\t"); ++ print_base(GETARG_A(instr)); ++ printf(" <- closure(func starts from line %d)", ++ f->p[GETARG_Bx(instr)]->lineinfo[0]); ++ break; ++ case OP_SETTABUP: ++ print_upvalue(GETARG_A(instr)); ++ printf("{"); ++ print_RKB(instr); ++ printf("} <- "); ++ ++ print_RKC(instr); ++ break; ++ case OP_GETUPVAL: ++ print_base(GETARG_A(instr)); ++ printf(" <- "); ++ ++ print_upvalue(GETARG_B(instr)); ++ break; ++ case OP_NEWTABLE: ++ print_base(GETARG_A(instr)); ++ printf(" <- {}"); ++ default: ++ break; ++ } ++ ++ printf("\n"); ++} ++ ++static int function_nr = 0; ++ ++/* this is a debug function used for check bytecode chunk file */ ++static void dump_function(int level, ktap_proto *f) ++{ ++ int i; ++ ++ printf("\n----------------------------------------------------\n"); ++ printf("function %d [level %d]:\n", function_nr++, level); ++ printf("linedefined: %d\n", f->linedefined); ++ printf("lastlinedefined: %d\n", f->lastlinedefined); ++ printf("numparams: %d\n", f->numparams); ++ printf("is_vararg: %d\n", f->is_vararg); ++ printf("maxstacksize: %d\n", f->maxstacksize); ++ printf("source: %s\n", getstr(f->source)); ++ printf("sizelineinfo: %d \t", f->sizelineinfo); ++ for (i = 0; i < f->sizelineinfo; i++) ++ printf("%d ", f->lineinfo[i]); ++ printf("\n"); ++ ++ printf("sizek: %d\n", f->sizek); ++ for (i = 0; i < f->sizek; i++) { ++ switch(f->k[i].type) { ++ case KTAP_TNIL: ++ printf("\tNIL\n"); ++ break; ++ case KTAP_TBOOLEAN: ++ printf("\tBOOLEAN: "); ++ printf("%d\n", f->k[i].val.b); ++ break; ++ case KTAP_TNUMBER: ++ printf("\tTNUMBER: "); ++ printf("%ld\n", f->k[i].val.n); ++ break; ++ case KTAP_TSHRSTR: ++ case KTAP_TLNGSTR: ++ printf("\tTSTRING: "); ++ printf("%s\n", svalue(&(f->k[i]))); ++ break; ++ default: ++ printf("\tUnknow constant type %d: ", f->k[i].type); ++ kp_showobj(NULL, &(f->k[i])); ++ printf("\n"); ++ } ++ } ++ ++ printf("sizelocvars: %d\n", f->sizelocvars); ++ for (i = 0; i < f->sizelocvars; i++) { ++ printf("\tlocvars: %s startpc: %d endpc: %d\n", ++ getstr(f->locvars[i].varname), f->locvars[i].startpc, ++ f->locvars[i].endpc); ++ } ++ ++ printf("sizeupvalues: %d\n", f->sizeupvalues); ++ for (i = 0; i < f->sizeupvalues; i++) { ++ printf("\tname: %s instack: %d idx: %d\n", ++ getstr(f->upvalues[i].name), f->upvalues[i].instack, ++ f->upvalues[i].idx); ++ } ++ ++ printf("\n"); ++ printf("sizecode: %d\n", f->sizecode); ++ for (i = 0; i < f->sizecode; i++) ++ decode_instruction(f, f->code[i]); ++ ++ printf("sizep: %d\n", f->sizep); ++ for (i = 0; i < f->sizep; i++) ++ dump_function(level + 1, f->p[i]); ++ ++} ++ ++static void usage(const char *msg_fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, msg_fmt); ++ vfprintf(stderr, msg_fmt, ap); ++ va_end(ap); ++ ++ fprintf(stderr, ++"Usage: ktap [options] file [script args] -- cmd [args]\n" ++" or: ktap [options] -e one-liner -- cmd [args]\n" ++"\n" ++"Options and arguments:\n" ++" -o file : send script output to file, instead of stderr\n" ++" -p pid : specific tracing pid\n" ++" -C cpu : cpu to monitor in system-wide\n" ++" -T : show timestamp for event\n" ++" -V : show version\n" ++" -v : enable verbose mode\n" ++" -q : suppress start tracing message\n" ++" -s : simple event tracing\n" ++" -b : list byte codes\n" ++" -le [glob] : list pre-defined events in system\n" ++#ifndef NO_LIBELF ++" -lf DSO : list available functions from DSO\n" ++" -lm DSO : list available sdt notes from DSO\n" ++#endif ++" file : program read from script file\n" ++" -- cmd [args] : workload to tracing\n"); ++ ++ exit(EXIT_FAILURE); ++} ++ ++ktap_global_state dummy_global_state; ++ ++static void init_dummy_global_state() ++{ ++ memset(&dummy_global_state, 0, sizeof(ktap_global_state)); ++ dummy_global_state.seed = 201236; ++ ++ kp_tstring_resize(NULL, 32); /* set inital string hashtable size */ ++} ++ ++#define handle_error(str) do { perror(str); exit(-1); } while(0) ++ ++static struct ktap_parm uparm; ++static int ktap_trunk_mem_size = 1024; ++ ++static int ktapc_writer(const void* p, size_t sz, void* ud) ++{ ++ if (uparm.trunk_len + sz > ktap_trunk_mem_size) { ++ int new_size = (uparm.trunk_len + sz) * 2; ++ uparm.trunk = realloc(uparm.trunk, new_size); ++ ktap_trunk_mem_size = new_size; ++ } ++ ++ memcpy(uparm.trunk + uparm.trunk_len, p, sz); ++ uparm.trunk_len += sz; ++ ++ return 0; ++} ++ ++ ++static int forks; ++static char **workload_argv; ++ ++static int fork_workload(int ktap_fd) ++{ ++ int pid; ++ ++ pid = fork(); ++ if (pid < 0) ++ handle_error("failed to fork"); ++ ++ if (pid > 0) ++ return pid; ++ ++ signal(SIGTERM, SIG_DFL); ++ ++ execvp("", workload_argv); ++ ++ /* ++ * waiting ktapvm prepare all tracing event ++ * make it more robust in future. ++ */ ++ pause(); ++ ++ execvp(workload_argv[0], workload_argv); ++ ++ perror(workload_argv[0]); ++ exit(-1); ++ ++ return -1; ++} ++ ++#define KTAPVM_PATH "/sys/kernel/debug/ktap/ktapvm" ++ ++static char *output_filename; ++ ++static int run_ktapvm() ++{ ++ int ktapvm_fd, ktap_fd; ++ int ret; ++ ++ ktapvm_fd = open(KTAPVM_PATH, O_RDONLY); ++ if (ktapvm_fd < 0) ++ handle_error("open " KTAPVM_PATH " failed"); ++ ++ ktap_fd = ioctl(ktapvm_fd, 0, NULL); ++ if (ktap_fd < 0) ++ handle_error("ioctl ktapvm failed"); ++ ++ ktapio_create(output_filename); ++ ++ if (forks) { ++ uparm.trace_pid = fork_workload(ktap_fd); ++ uparm.workload = 1; ++ } ++ ++ ret = ioctl(ktap_fd, KTAP_CMD_IOC_RUN, &uparm); ++ ++ close(ktap_fd); ++ close(ktapvm_fd); ++ ++ return ret; ++} ++ ++int verbose; ++static int quiet; ++static int dump_bytecode; ++static char oneline_src[1024]; ++static int trace_pid = -1; ++static int trace_cpu = -1; ++static int print_timestamp; ++ ++#define SIMPLE_ONE_LINER_FMT \ ++ "trace %s { print(cpu(), tid(), execname(), argevent) }" ++ ++static const char *script_file; ++static int script_args_start; ++static int script_args_end; ++ ++#ifndef NO_LIBELF ++struct binary_base ++{ ++ int type; ++ const char *binary; ++}; ++static int print_symbol(const char *name, vaddr_t addr, void *arg) ++{ ++ struct binary_base *base = (struct binary_base *)arg; ++ const char *type = base->type == FIND_SYMBOL ? ++ "probe" : "sdt"; ++ ++ printf("%s:%s:%s\n", type, base->binary, name); ++ return 0; ++} ++#endif ++ ++static void parse_option(int argc, char **argv) ++{ ++ char pid[32] = {0}; ++ char cpu_str[32] = {0}; ++ char *next_arg; ++ int i, j; ++ ++ for (i = 1; i < argc; i++) { ++ if (argv[i][0] != '-') { ++ script_file = argv[i]; ++ if (!script_file) ++ usage(""); ++ ++ script_args_start = i + 1; ++ script_args_end = argc; ++ ++ for (j = i + 1; j < argc; j++) { ++ if (argv[j][0] == '-' && argv[j][1] == '-') ++ goto found_cmd; ++ } ++ ++ return; ++ } ++ ++ if (argv[i][0] == '-' && argv[i][1] == '-') { ++ j = i; ++ goto found_cmd; ++ } ++ ++ next_arg = argv[i + 1]; ++ ++ switch (argv[i][1]) { ++ case 'o': ++ output_filename = malloc(strlen(next_arg) + 1); ++ if (!output_filename) ++ return; ++ ++ strncpy(output_filename, next_arg, strlen(next_arg)); ++ i++; ++ break; ++ case 'e': ++ strncpy(oneline_src, next_arg, strlen(next_arg)); ++ i++; ++ break; ++ case 'p': ++ strncpy(pid, next_arg, strlen(next_arg)); ++ trace_pid = atoi(pid); ++ i++; ++ break; ++ case 'C': ++ strncpy(cpu_str, next_arg, strlen(next_arg)); ++ trace_cpu = atoi(cpu_str); ++ i++; ++ break; ++ case 'T': ++ print_timestamp = 1; ++ break; ++ case 'v': ++ verbose = 1; ++ break; ++ case 'q': ++ quiet = 1; ++ break; ++ case 's': ++ sprintf(oneline_src, SIMPLE_ONE_LINER_FMT, next_arg); ++ i++; ++ break; ++ case 'b': ++ dump_bytecode = 1; ++ break; ++ case 'l': /* list available events */ ++ switch (argv[i][2]) { ++ case 'e': /* tracepoints */ ++ list_available_events(next_arg); ++ exit(EXIT_SUCCESS); ++#ifndef NO_LIBELF ++ case 'f': /* functions in DSO */ ++ case 'm': /* static marks in DSO */ { ++ const char *binary = next_arg; ++ int type = argv[i][2] == 'f' ? ++ FIND_SYMBOL : FIND_STAPSDT_NOTE; ++ struct binary_base base = { ++ .type = type, ++ .binary = binary, ++ }; ++ int ret; ++ ++ ret = parse_dso_symbols(binary, type, ++ print_symbol, ++ (void *)&base); ++ if (ret <= 0) { ++ fprintf(stderr, ++ "error: no symbols in binary %s\n", ++ binary); ++ exit(EXIT_FAILURE); ++ } ++ exit(EXIT_SUCCESS); ++ } ++#endif ++ default: ++ exit(EXIT_FAILURE); ++ } ++ break; ++ case 'V': ++#ifdef CONFIG_KTAP_FFI ++ usage("%s (with FFI)\n\n", KTAP_VERSION); ++#else ++ usage("%s\n\n", KTAP_VERSION); ++#endif ++ break; ++ case '?': ++ case 'h': ++ usage(""); ++ break; ++ default: ++ usage("wrong argument\n"); ++ break; ++ } ++ } ++ ++ return; ++ ++ found_cmd: ++ script_args_end = j; ++ forks = 1; ++ workload_argv = &argv[j + 1]; ++} ++ ++static void compile(const char *input) ++{ ++ ktap_closure *cl; ++ char *buff; ++ struct stat sb; ++ int fdin; ++ ++ if (oneline_src[0] != '\0') { ++ init_dummy_global_state(); ++ ffi_cparser_init(); ++ cl = ktapc_parser(oneline_src, input); ++ goto dump; ++ } ++ ++ fdin = open(input, O_RDONLY); ++ if (fdin < 0) { ++ fprintf(stderr, "open file %s failed\n", input); ++ exit(-1); ++ } ++ ++ if (fstat(fdin, &sb) == -1) ++ handle_error("fstat failed"); ++ ++ buff = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fdin, 0); ++ if (buff == MAP_FAILED) ++ handle_error("mmap failed"); ++ ++ init_dummy_global_state(); ++ ffi_cparser_init(); ++ cl = ktapc_parser(buff, input); ++ ++ munmap(buff, sb.st_size); ++ close(fdin); ++ ++ dump: ++ if (dump_bytecode) { ++ dump_function(1, cl->p); ++ exit(0); ++ } ++ ++ /* ktapc output */ ++ uparm.trunk = malloc(ktap_trunk_mem_size); ++ if (!uparm.trunk) ++ handle_error("malloc failed"); ++ ++ ktapc_dump(cl->p, ktapc_writer, NULL, 0); ++ ffi_cparser_free(); ++} ++ ++int main(int argc, char **argv) ++{ ++ char **ktapvm_argv; ++ int new_index, i; ++ int ret; ++ ++ if (argc == 1) ++ usage(""); ++ ++ parse_option(argc, argv); ++ ++ if (oneline_src[0] != '\0') ++ script_file = "one-liner"; ++ ++ compile(script_file); ++ ++ ktapvm_argv = (char **)malloc(sizeof(char *)*(script_args_end - ++ script_args_start + 1)); ++ if (!ktapvm_argv) { ++ fprintf(stderr, "canno allocate ktapvm_argv\n"); ++ return -1; ++ } ++ ++ ktapvm_argv[0] = malloc(strlen(script_file) + 1); ++ if (!ktapvm_argv[0]) { ++ fprintf(stderr, "canno allocate memory\n"); ++ return -1; ++ } ++ strcpy(ktapvm_argv[0], script_file); ++ ktapvm_argv[0][strlen(script_file)] = '\0'; ++ ++ /* pass rest argv into ktapvm */ ++ new_index = 1; ++ for (i = script_args_start; i < script_args_end; i++) { ++ ktapvm_argv[new_index] = malloc(strlen(argv[i]) + 1); ++ if (!ktapvm_argv[new_index]) { ++ fprintf(stderr, "canno allocate memory\n"); ++ return -1; ++ } ++ strcpy(ktapvm_argv[new_index], argv[i]); ++ ktapvm_argv[new_index][strlen(argv[i])] = '\0'; ++ new_index++; ++ } ++ ++ uparm.argv = ktapvm_argv; ++ uparm.argc = new_index; ++ uparm.verbose = verbose; ++ uparm.trace_pid = trace_pid; ++ uparm.trace_cpu = trace_cpu; ++ uparm.print_timestamp = print_timestamp; ++ uparm.quiet = quiet; ++ ++ /* start running into kernel ktapvm */ ++ ret = run_ktapvm(); ++ ++ cleanup_event_resources(); ++ return ret; ++} ++ ++ +diff --git a/drivers/staging/ktap/userspace/parser.c b/drivers/staging/ktap/userspace/parser.c +new file mode 100644 +index 0000000..f47c0c0 +--- /dev/null ++++ b/drivers/staging/ktap/userspace/parser.c +@@ -0,0 +1,1963 @@ ++/* ++ * parser.c - ktap parser ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * Copyright (C) 1994-2013 Lua.org, PUC-Rio. ++ * - The part of code in this file is copied from lua initially. ++ * - lua's MIT license is compatible with GPL. ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++ ++#include "../include/ktap_types.h" ++#include "../include/ktap_opcodes.h" ++#include "ktapc.h" ++#include "cparser.h" ++ ++/* maximum number of local variables per function (must be smaller ++ than 250, due to the bytecode format) */ ++#define MAXVARS 200 ++ ++#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) ++ ++/* ++ * nodes for block list (list of active blocks) ++ */ ++typedef struct ktap_blockcnt { ++ struct ktap_blockcnt *previous; /* chain */ ++ short firstlabel; /* index of first label in this block */ ++ short firstgoto; /* index of first pending goto in this block */ ++ u8 nactvar; /* # active locals outside the block */ ++ u8 upval; /* true if some variable in the block is an upvalue */ ++ u8 isloop; /* true if `block' is a loop */ ++} ktap_blockcnt; ++ ++/* ++ * prototypes for recursive non-terminal functions ++ */ ++static void statement (ktap_lexstate *ls); ++static void expr (ktap_lexstate *ls, ktap_expdesc *v); ++ ++static void anchor_token(ktap_lexstate *ls) ++{ ++ /* last token from outer function must be EOS */ ++ ktap_assert((int)(ls->fs != NULL) || ls->t.token == TK_EOS); ++ if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) { ++ ktap_string *ts = ls->t.seminfo.ts; ++ lex_newstring(ls, getstr(ts), ts->tsv.len); ++ } ++} ++ ++/* semantic error */ ++static void semerror(ktap_lexstate *ls, const char *msg) ++{ ++ ls->t.token = 0; /* remove 'near to' from final message */ ++ lex_syntaxerror(ls, msg); ++} ++ ++static void error_expected(ktap_lexstate *ls, int token) ++{ ++ lex_syntaxerror(ls, ++ ktapc_sprintf("%s expected", lex_token2str(ls, token))); ++} ++ ++static void errorlimit(ktap_funcstate *fs, int limit, const char *what) ++{ ++ const char *msg; ++ int line = fs->f->linedefined; ++ const char *where = (line == 0) ? "main function" ++ : ktapc_sprintf("function at line %d", line); ++ ++ msg = ktapc_sprintf("too many %s (limit is %d) in %s", ++ what, limit, where); ++ lex_syntaxerror(fs->ls, msg); ++} ++ ++static void checklimit(ktap_funcstate *fs, int v, int l, const char *what) ++{ ++ if (v > l) ++ errorlimit(fs, l, what); ++} ++ ++static int testnext(ktap_lexstate *ls, int c) ++{ ++ if (ls->t.token == c) { ++ lex_next(ls); ++ return 1; ++ } ++ else ++ return 0; ++} ++ ++static void check(ktap_lexstate *ls, int c) ++{ ++ if (ls->t.token != c) ++ error_expected(ls, c); ++} ++ ++static void checknext(ktap_lexstate *ls, int c) ++{ ++ check(ls, c); ++ lex_next(ls); ++} ++ ++#define check_condition(ls,c,msg) { if (!(c)) lex_syntaxerror(ls, msg); } ++ ++static void check_match(ktap_lexstate *ls, int what, int who, int where) ++{ ++ if (!testnext(ls, what)) { ++ if (where == ls->linenumber) ++ error_expected(ls, what); ++ else { ++ lex_syntaxerror(ls, ktapc_sprintf( ++ "%s expected (to close %s at line %d)", ++ lex_token2str(ls, what), ++ lex_token2str(ls, who), where)); ++ } ++ } ++} ++ ++static ktap_string *str_checkname(ktap_lexstate *ls) ++{ ++ ktap_string *ts; ++ ++ check(ls, TK_NAME); ++ ts = ls->t.seminfo.ts; ++ lex_next(ls); ++ return ts; ++} ++ ++static void init_exp(ktap_expdesc *e, expkind k, int i) ++{ ++ e->f = e->t = NO_JUMP; ++ e->k = k; ++ e->u.info = i; ++} ++ ++static void codestring(ktap_lexstate *ls, ktap_expdesc *e, ktap_string *s) ++{ ++ init_exp(e, VK, codegen_stringK(ls->fs, s)); ++} ++ ++static void codenumber(ktap_lexstate *ls, ktap_expdesc *e, ktap_number n) ++{ ++ init_exp(e, VK, codegen_numberK(ls->fs, n)); ++} ++ ++static void checkname(ktap_lexstate *ls, ktap_expdesc *e) ++{ ++ codestring(ls, e, str_checkname(ls)); ++} ++ ++static int registerlocalvar(ktap_lexstate *ls, ktap_string *varname) ++{ ++ ktap_funcstate *fs = ls->fs; ++ ktap_proto *f = fs->f; ++ int oldsize = f->sizelocvars; ++ ++ ktapc_growvector(f->locvars, fs->nlocvars, f->sizelocvars, ++ ktap_locvar, SHRT_MAX, "local variables"); ++ ++ while (oldsize < f->sizelocvars) ++ f->locvars[oldsize++].varname = NULL; ++ ++ f->locvars[fs->nlocvars].varname = varname; ++ return fs->nlocvars++; ++} ++ ++static void new_localvar(ktap_lexstate *ls, ktap_string *name) ++{ ++ ktap_funcstate *fs = ls->fs; ++ ktap_dyndata *dyd = ls->dyd; ++ int reg = registerlocalvar(ls, name); ++ ++ checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, ++ MAXVARS, "local variables"); ++ ktapc_growvector(dyd->actvar.arr, dyd->actvar.n + 1, ++ dyd->actvar.size, ktap_vardesc, MAX_INT, "local variables"); ++ dyd->actvar.arr[dyd->actvar.n++].idx = (short)reg; ++} ++ ++static void new_localvarliteral_(ktap_lexstate *ls, const char *name, size_t sz) ++{ ++ new_localvar(ls, lex_newstring(ls, name, sz)); ++} ++ ++#define new_localvarliteral(ls,v) \ ++ new_localvarliteral_(ls, "" v, (sizeof(v)/sizeof(char))-1) ++ ++static ktap_locvar *getlocvar(ktap_funcstate *fs, int i) ++{ ++ int idx = fs->ls->dyd->actvar.arr[fs->firstlocal + i].idx; ++ ++ ktap_assert(idx < fs->nlocvars); ++ return &fs->f->locvars[idx]; ++} ++ ++static void adjustlocalvars(ktap_lexstate *ls, int nvars) ++{ ++ ktap_funcstate *fs = ls->fs; ++ ++ fs->nactvar = (u8)(fs->nactvar + nvars); ++ for (; nvars; nvars--) { ++ getlocvar(fs, fs->nactvar - nvars)->startpc = fs->pc; ++ } ++} ++ ++static void removevars(ktap_funcstate *fs, int tolevel) ++{ ++ fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); ++ ++ while (fs->nactvar > tolevel) ++ getlocvar(fs, --fs->nactvar)->endpc = fs->pc; ++} ++ ++static int searchupvalue(ktap_funcstate *fs, ktap_string *name) ++{ ++ int i; ++ ktap_upvaldesc *up = fs->f->upvalues; ++ ++ for (i = 0; i < fs->nups; i++) { ++ if (ktapc_ts_eqstr(up[i].name, name)) ++ return i; ++ } ++ return -1; /* not found */ ++} ++ ++static int newupvalue(ktap_funcstate *fs, ktap_string *name, ktap_expdesc *v) ++{ ++ ktap_proto *f = fs->f; ++ int oldsize = f->sizeupvalues; ++ ++ checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); ++ ktapc_growvector(f->upvalues, fs->nups, f->sizeupvalues, ++ ktap_upvaldesc, MAXUPVAL, "upvalues"); ++ ++ while (oldsize < f->sizeupvalues) ++ f->upvalues[oldsize++].name = NULL; ++ f->upvalues[(int)fs->nups].instack = (v->k == VLOCAL); ++ f->upvalues[(int)fs->nups].idx = (u8)(v->u.info); ++ f->upvalues[(int)fs->nups].name = name; ++ return fs->nups++; ++} ++ ++static int searchvar(ktap_funcstate *fs, ktap_string *n) ++{ ++ int i; ++ ++ for (i = fs->nactvar-1; i >= 0; i--) { ++ if (ktapc_ts_eqstr(n, getlocvar(fs, i)->varname)) ++ return i; ++ } ++ return -1; /* not found */ ++} ++ ++/* ++ * Mark block where variable at given level was defined ++ * (to emit close instructions later). ++ */ ++static void markupval(ktap_funcstate *fs, int level) ++{ ++ ktap_blockcnt *bl = fs->bl; ++ ++ while (bl->nactvar > level) ++ bl = bl->previous; ++ bl->upval = 1; ++} ++ ++/* ++ * Find variable with given name 'n'. If it is an upvalue, add this ++ * upvalue into all intermediate functions. ++ */ ++static int singlevaraux(ktap_funcstate *fs, ktap_string *n, ktap_expdesc *var, int base) ++{ ++ if (fs == NULL) /* no more levels? */ ++ return VVOID; /* default is global */ ++ else { ++ int v = searchvar(fs, n); /* look up locals at current level */ ++ if (v >= 0) { /* found? */ ++ init_exp(var, VLOCAL, v); /* variable is local */ ++ if (!base) ++ markupval(fs, v); /* local will be used as an upval */ ++ return VLOCAL; ++ } else { /* not found as local at current level; try upvalues */ ++ int idx = searchupvalue(fs, n); /* try existing upvalues */ ++ if (idx < 0) { /* not found? */ ++ if (singlevaraux(fs->prev, n, var, 0) == VVOID) /* try upper levels */ ++ return VVOID; /* not found; is a global */ ++ /* else was LOCAL or UPVAL */ ++ idx = newupvalue(fs, n, var); /* will be a new upvalue */ ++ } ++ init_exp(var, VUPVAL, idx); ++ return VUPVAL; ++ } ++ } ++} ++ ++static void singlevar(ktap_lexstate *ls, ktap_expdesc *var) ++{ ++ ktap_string *varname = str_checkname(ls); ++ ktap_funcstate *fs = ls->fs; ++ ++ if (singlevaraux(fs, varname, var, 1) == VVOID) { /* global name? */ ++ ktap_expdesc key; ++ singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ ++ ktap_assert(var->k == VLOCAL || var->k == VUPVAL); ++ codestring(ls, &key, varname); /* key is variable name */ ++ codegen_indexed(fs, var, &key); /* env[varname] */ ++ } ++} ++ ++static void adjust_assign(ktap_lexstate *ls, int nvars, int nexps, ktap_expdesc *e) ++{ ++ ktap_funcstate *fs = ls->fs; ++ int extra = nvars - nexps; ++ ++ if (hasmultret(e->k)) { ++ extra++; /* includes call itself */ ++ if (extra < 0) ++ extra = 0; ++ codegen_setreturns(fs, e, extra); /* last exp. provides the difference */ ++ if (extra > 1) ++ codegen_reserveregs(fs, extra-1); ++ } else { ++ if (e->k != VVOID) ++ codegen_exp2nextreg(fs, e); /* close last expression */ ++ if (extra > 0) { ++ int reg = fs->freereg; ++ ++ codegen_reserveregs(fs, extra); ++ codegen_nil(fs, reg, extra); ++ } ++ } ++} ++ ++static void enterlevel(ktap_lexstate *ls) ++{ ++ ++ls->nCcalls; ++ checklimit(ls->fs, ls->nCcalls, KTAP_MAXCCALLS, "C levels"); ++} ++ ++static void closegoto(ktap_lexstate *ls, int g, ktap_labeldesc *label) ++{ ++ int i; ++ ktap_funcstate *fs = ls->fs; ++ ktap_labellist *gl = &ls->dyd->gt; ++ ktap_labeldesc *gt = &gl->arr[g]; ++ ++ ktap_assert(ktapc_ts_eqstr(gt->name, label->name)); ++ if (gt->nactvar < label->nactvar) { ++ ktap_string *vname = getlocvar(fs, gt->nactvar)->varname; ++ const char *msg = ktapc_sprintf( ++ " at line %d jumps into the scope of local " KTAP_QS, ++ getstr(gt->name), gt->line, getstr(vname)); ++ semerror(ls, msg); ++ } ++ ++ codegen_patchlist(fs, gt->pc, label->pc); ++ /* remove goto from pending list */ ++ for (i = g; i < gl->n - 1; i++) ++ gl->arr[i] = gl->arr[i + 1]; ++ gl->n--; ++} ++ ++/* ++ * try to close a goto with existing labels; this solves backward jumps ++ */ ++static int findlabel(ktap_lexstate *ls, int g) ++{ ++ int i; ++ ktap_blockcnt *bl = ls->fs->bl; ++ ktap_dyndata *dyd = ls->dyd; ++ ktap_labeldesc *gt = &dyd->gt.arr[g]; ++ ++ /* check labels in current block for a match */ ++ for (i = bl->firstlabel; i < dyd->label.n; i++) { ++ ktap_labeldesc *lb = &dyd->label.arr[i]; ++ if (ktapc_ts_eqstr(lb->name, gt->name)) { /* correct label? */ ++ if (gt->nactvar > lb->nactvar && ++ (bl->upval || dyd->label.n > bl->firstlabel)) ++ codegen_patchclose(ls->fs, gt->pc, lb->nactvar); ++ closegoto(ls, g, lb); /* close it */ ++ return 1; ++ } ++ } ++ return 0; /* label not found; cannot close goto */ ++} ++ ++static int newlabelentry(ktap_lexstate *ls, ktap_labellist *l, ktap_string *name, ++ int line, int pc) ++{ ++ int n = l->n; ++ ++ ktapc_growvector(l->arr, n, l->size, ++ ktap_labeldesc, SHRT_MAX, "labels/gotos"); ++ l->arr[n].name = name; ++ l->arr[n].line = line; ++ l->arr[n].nactvar = ls->fs->nactvar; ++ l->arr[n].pc = pc; ++ l->n++; ++ return n; ++} ++ ++ ++/* ++ * check whether new label 'lb' matches any pending gotos in current ++ * block; solves forward jumps ++ */ ++static void findgotos(ktap_lexstate *ls, ktap_labeldesc *lb) ++{ ++ ktap_labellist *gl = &ls->dyd->gt; ++ int i = ls->fs->bl->firstgoto; ++ ++ while (i < gl->n) { ++ if (ktapc_ts_eqstr(gl->arr[i].name, lb->name)) ++ closegoto(ls, i, lb); ++ else ++ i++; ++ } ++} ++ ++/* ++ * "export" pending gotos to outer level, to check them against ++ * outer labels; if the block being exited has upvalues, and ++ * the goto exits the scope of any variable (which can be the ++ * upvalue), close those variables being exited. ++ */ ++static void movegotosout(ktap_funcstate *fs, ktap_blockcnt *bl) ++{ ++ int i = bl->firstgoto; ++ ktap_labellist *gl = &fs->ls->dyd->gt; ++ ++ /* correct pending gotos to current block and try to close it ++ with visible labels */ ++ while (i < gl->n) { ++ ktap_labeldesc *gt = &gl->arr[i]; ++ ++ if (gt->nactvar > bl->nactvar) { ++ if (bl->upval) ++ codegen_patchclose(fs, gt->pc, bl->nactvar); ++ gt->nactvar = bl->nactvar; ++ } ++ if (!findlabel(fs->ls, i)) ++ i++; /* move to next one */ ++ } ++} ++ ++static void enterblock(ktap_funcstate *fs, ktap_blockcnt *bl, u8 isloop) ++{ ++ bl->isloop = isloop; ++ bl->nactvar = fs->nactvar; ++ bl->firstlabel = fs->ls->dyd->label.n; ++ bl->firstgoto = fs->ls->dyd->gt.n; ++ bl->upval = 0; ++ bl->previous = fs->bl; ++ fs->bl = bl; ++ ktap_assert(fs->freereg == fs->nactvar); ++} ++ ++ ++/* ++ * create a label named "break" to resolve break statements ++ */ ++static void breaklabel(ktap_lexstate *ls) ++{ ++ ktap_string *n = ktapc_ts_new("break"); ++ int l = newlabelentry(ls, &ls->dyd->label, n, 0, ls->fs->pc); ++ ++ findgotos(ls, &ls->dyd->label.arr[l]); ++} ++ ++/* ++ * generates an error for an undefined 'goto'; choose appropriate ++ * message when label name is a reserved word (which can only be 'break') ++ */ ++static void undefgoto(ktap_lexstate *ls, ktap_labeldesc *gt) ++{ ++ const char *msg = isreserved(gt->name) ++ ? "<%s> at line %d not inside a loop" ++ : "no visible label " KTAP_QS " for at line %d"; ++ ++ msg = ktapc_sprintf(msg, getstr(gt->name), gt->line); ++ semerror(ls, msg); ++} ++ ++static void leaveblock(ktap_funcstate *fs) ++{ ++ ktap_blockcnt *bl = fs->bl; ++ ktap_lexstate *ls = fs->ls; ++ if (bl->previous && bl->upval) { ++ /* create a 'jump to here' to close upvalues */ ++ int j = codegen_jump(fs); ++ ++ codegen_patchclose(fs, j, bl->nactvar); ++ codegen_patchtohere(fs, j); ++ } ++ ++ if (bl->isloop) ++ breaklabel(ls); /* close pending breaks */ ++ ++ fs->bl = bl->previous; ++ removevars(fs, bl->nactvar); ++ ktap_assert(bl->nactvar == fs->nactvar); ++ fs->freereg = fs->nactvar; /* free registers */ ++ ls->dyd->label.n = bl->firstlabel; /* remove local labels */ ++ if (bl->previous) /* inner block? */ ++ movegotosout(fs, bl); /* update pending gotos to outer block */ ++ else if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ ++ undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ ++} ++ ++/* ++ * adds a new prototype into list of prototypes ++ */ ++static ktap_proto *addprototype(ktap_lexstate *ls) ++{ ++ ktap_proto *clp; ++ ktap_funcstate *fs = ls->fs; ++ ktap_proto *f = fs->f; /* prototype of current function */ ++ ++ if (fs->np >= f->sizep) { ++ int oldsize = f->sizep; ++ ktapc_growvector(f->p, fs->np, f->sizep, ktap_proto *, MAXARG_Bx, "functions"); ++ while (oldsize < f->sizep) ++ f->p[oldsize++] = NULL; ++ } ++ f->p[fs->np++] = clp = ktapc_newproto(); ++ return clp; ++} ++ ++/* ++ * codes instruction to create new closure in parent function ++ */ ++static void codeclosure(ktap_lexstate *ls, ktap_expdesc *v) ++{ ++ ktap_funcstate *fs = ls->fs->prev; ++ init_exp(v, VRELOCABLE, codegen_codeABx(fs, OP_CLOSURE, 0, fs->np - 1)); ++ codegen_exp2nextreg(fs, v); /* fix it at stack top (for GC) */ ++} ++ ++static void open_func(ktap_lexstate *ls, ktap_funcstate *fs, ktap_blockcnt *bl) ++{ ++ ktap_proto *f; ++ ++ fs->prev = ls->fs; /* linked list of funcstates */ ++ fs->ls = ls; ++ ls->fs = fs; ++ fs->pc = 0; ++ fs->lasttarget = 0; ++ fs->jpc = NO_JUMP; ++ fs->freereg = 0; ++ fs->nk = 0; ++ fs->np = 0; ++ fs->nups = 0; ++ fs->nlocvars = 0; ++ fs->nactvar = 0; ++ fs->firstlocal = ls->dyd->actvar.n; ++ fs->bl = NULL; ++ f = fs->f; ++ f->source = ls->source; ++ f->maxstacksize = 2; /* registers 0/1 are always valid */ ++ fs->h = ktapc_table_new(); ++ //table_resize(NULL, fs->h, 32, 32); ++ enterblock(fs, bl, 0); ++} ++ ++static void close_func(ktap_lexstate *ls) ++{ ++ ktap_funcstate *fs = ls->fs; ++ ktap_proto *f = fs->f; ++ ++ codegen_ret(fs, 0, 0); /* final return */ ++ leaveblock(fs); ++ ktapc_reallocvector(f->code, f->sizecode, fs->pc, ktap_instruction); ++ f->sizecode = fs->pc; ++ ktapc_reallocvector(f->lineinfo, f->sizelineinfo, fs->pc, int); ++ f->sizelineinfo = fs->pc; ++ ktapc_reallocvector(f->k, f->sizek, fs->nk, ktap_value); ++ f->sizek = fs->nk; ++ ktapc_reallocvector(f->p, f->sizep, fs->np, ktap_proto *); ++ f->sizep = fs->np; ++ ktapc_reallocvector(f->locvars, f->sizelocvars, fs->nlocvars, ktap_locvar); ++ f->sizelocvars = fs->nlocvars; ++ ktapc_reallocvector(f->upvalues, f->sizeupvalues, fs->nups, ktap_upvaldesc); ++ f->sizeupvalues = fs->nups; ++ ktap_assert((int)(fs->bl == NULL)); ++ ls->fs = fs->prev; ++ /* last token read was anchored in defunct function; must re-anchor it */ ++ anchor_token(ls); ++} ++ ++ ++/*============================================================*/ ++/* GRAMMAR RULES */ ++/*============================================================*/ ++ ++/* ++ * check whether current token is in the follow set of a block. ++ * 'until' closes syntactical blocks, but do not close scope, ++ * so it handled in separate. ++ */ ++static int block_follow(ktap_lexstate *ls, int withuntil) ++{ ++ switch (ls->t.token) { ++ case TK_ELSE: case TK_ELSEIF: ++ case TK_END: case TK_EOS: ++ return 1; ++ case TK_UNTIL: ++ return withuntil; ++ case '}': ++ return 1; ++ default: ++ return 0; ++ } ++} ++ ++static void statlist(ktap_lexstate *ls) ++{ ++ /* statlist -> { stat [`;'] } */ ++ while (!block_follow(ls, 1)) { ++ if (ls->t.token == TK_RETURN) { ++ statement(ls); ++ return; /* 'return' must be last statement */ ++ } ++ statement(ls); ++ } ++} ++ ++static void fieldsel(ktap_lexstate *ls, ktap_expdesc *v) ++{ ++ /* fieldsel -> ['.' | ':'] NAME */ ++ ktap_funcstate *fs = ls->fs; ++ ktap_expdesc key; ++ ++ codegen_exp2anyregup(fs, v); ++ lex_next(ls); /* skip the dot or colon */ ++ checkname(ls, &key); ++ codegen_indexed(fs, v, &key); ++} ++ ++static void yindex(ktap_lexstate *ls, ktap_expdesc *v) ++{ ++ /* index -> '[' expr ']' */ ++ lex_next(ls); /* skip the '[' */ ++ expr(ls, v); ++ codegen_exp2val(ls->fs, v); ++ checknext(ls, ']'); ++} ++ ++/* ++ * {====================================================================== ++ * Rules for Constructors ++ * ======================================================================= ++ */ ++struct ConsControl { ++ ktap_expdesc v; /* last list item read */ ++ ktap_expdesc *t; /* table descriptor */ ++ int nh; /* total number of `record' elements */ ++ int na; /* total number of array elements */ ++ int tostore; /* number of array elements pending to be stored */ ++}; ++ ++static void recfield(ktap_lexstate *ls, struct ConsControl *cc) ++{ ++ /* recfield -> (NAME | `['exp1`]') = exp1 */ ++ ktap_funcstate *fs = ls->fs; ++ int reg = ls->fs->freereg; ++ ktap_expdesc key, val; ++ int rkkey; ++ ++ if (ls->t.token == TK_NAME) { ++ checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); ++ checkname(ls, &key); ++ } else /* ls->t.token == '[' */ ++ yindex(ls, &key); ++ ++ cc->nh++; ++ checknext(ls, '='); ++ rkkey = codegen_exp2RK(fs, &key); ++ expr(ls, &val); ++ codegen_codeABC(fs, OP_SETTABLE, cc->t->u.info, rkkey, codegen_exp2RK(fs, &val)); ++ fs->freereg = reg; /* free registers */ ++} ++ ++static void closelistfield(ktap_funcstate *fs, struct ConsControl *cc) ++{ ++ if (cc->v.k == VVOID) ++ return; /* there is no list item */ ++ codegen_exp2nextreg(fs, &cc->v); ++ cc->v.k = VVOID; ++ if (cc->tostore == LFIELDS_PER_FLUSH) { ++ codegen_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ ++ cc->tostore = 0; /* no more items pending */ ++ } ++} ++ ++static void lastlistfield(ktap_funcstate *fs, struct ConsControl *cc) ++{ ++ if (cc->tostore == 0) ++ return; ++ ++ if (hasmultret(cc->v.k)) { ++ codegen_setmultret(fs, &cc->v); ++ codegen_setlist(fs, cc->t->u.info, cc->na, KTAP_MULTRET); ++ cc->na--; /* do not count last expression (unknown number of elements) */ ++ } else { ++ if (cc->v.k != VVOID) ++ codegen_exp2nextreg(fs, &cc->v); ++ codegen_setlist(fs, cc->t->u.info, cc->na, cc->tostore); ++ } ++} ++ ++static void listfield(ktap_lexstate *ls, struct ConsControl *cc) ++{ ++ /* listfield -> exp */ ++ expr(ls, &cc->v); ++ checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); ++ cc->na++; ++ cc->tostore++; ++} ++ ++static void field(ktap_lexstate *ls, struct ConsControl *cc) ++{ ++ /* field -> listfield | recfield */ ++ switch(ls->t.token) { ++ case TK_NAME: { /* may be 'listfield' or 'recfield' */ ++ if (lex_lookahead(ls) != '=') /* expression? */ ++ listfield(ls, cc); ++ else ++ recfield(ls, cc); ++ break; ++ } ++ case '[': { ++ recfield(ls, cc); ++ break; ++ } ++ default: ++ listfield(ls, cc); ++ break; ++ } ++} ++ ++static void constructor(ktap_lexstate *ls, ktap_expdesc *t) ++{ ++ /* constructor -> '{' [ field { sep field } [sep] ] '}' ++ sep -> ',' | ';' */ ++ ktap_funcstate *fs = ls->fs; ++ int line = ls->linenumber; ++ int pc = codegen_codeABC(fs, OP_NEWTABLE, 0, 0, 0); ++ struct ConsControl cc; ++ ++ cc.na = cc.nh = cc.tostore = 0; ++ cc.t = t; ++ init_exp(t, VRELOCABLE, pc); ++ init_exp(&cc.v, VVOID, 0); /* no value (yet) */ ++ codegen_exp2nextreg(ls->fs, t); /* fix it at stack top */ ++ checknext(ls, '{'); ++ do { ++ ktap_assert(cc.v.k == VVOID || cc.tostore > 0); ++ if (ls->t.token == '}') ++ break; ++ closelistfield(fs, &cc); ++ field(ls, &cc); ++ } while (testnext(ls, ',') || testnext(ls, ';')); ++ check_match(ls, '}', '{', line); ++ lastlistfield(fs, &cc); ++ SETARG_B(fs->f->code[pc], ktapc_int2fb(cc.na)); /* set initial array size */ ++ SETARG_C(fs->f->code[pc], ktapc_int2fb(cc.nh)); /* set initial table size */ ++} ++ ++/* }====================================================================== */ ++ ++static void parlist(ktap_lexstate *ls) ++{ ++ /* parlist -> [ param { `,' param } ] */ ++ ktap_funcstate *fs = ls->fs; ++ ktap_proto *f = fs->f; ++ int nparams = 0; ++ f->is_vararg = 0; ++ ++ if (ls->t.token != ')') { /* is `parlist' not empty? */ ++ do { ++ switch (ls->t.token) { ++ case TK_NAME: { /* param -> NAME */ ++ new_localvar(ls, str_checkname(ls)); ++ nparams++; ++ break; ++ } ++ case TK_DOTS: { /* param -> `...' */ ++ lex_next(ls); ++ f->is_vararg = 1; ++ break; ++ } ++ default: ++ lex_syntaxerror(ls, " or " KTAP_QL("...") " expected"); ++ } ++ } while (!f->is_vararg && testnext(ls, ',')); ++ } ++ adjustlocalvars(ls, nparams); ++ f->numparams = (u8)(fs->nactvar); ++ codegen_reserveregs(fs, fs->nactvar); /* reserve register for parameters */ ++} ++ ++static void body(ktap_lexstate *ls, ktap_expdesc *e, int ismethod, int line) ++{ ++ /* body -> `(' parlist `)' block END */ ++ ktap_funcstate new_fs; ++ ktap_blockcnt bl; ++ ++ new_fs.f = addprototype(ls); ++ new_fs.f->linedefined = line; ++ open_func(ls, &new_fs, &bl); ++ checknext(ls, '('); ++ if (ismethod) { ++ new_localvarliteral(ls, "self"); /* create 'self' parameter */ ++ adjustlocalvars(ls, 1); ++ } ++ parlist(ls); ++ checknext(ls, ')'); ++ checknext(ls, '{'); ++ statlist(ls); ++ new_fs.f->lastlinedefined = ls->linenumber; ++ checknext(ls, '}'); ++ //check_match(ls, TK_END, TK_FUNCTION, line); ++ codeclosure(ls, e); ++ close_func(ls); ++} ++ ++static void func_body_no_args(ktap_lexstate *ls, ktap_expdesc *e, int line) ++{ ++ /* body -> `(' parlist `)' block END */ ++ ktap_funcstate new_fs; ++ ktap_blockcnt bl; ++ ++ new_fs.f = addprototype(ls); ++ new_fs.f->linedefined = line; ++ open_func(ls, &new_fs, &bl); ++ checknext(ls, '{'); ++ statlist(ls); ++ new_fs.f->lastlinedefined = ls->linenumber; ++ checknext(ls, '}'); ++ //check_match(ls, TK_END, TK_FUNCTION, line); ++ codeclosure(ls, e); ++ close_func(ls); ++} ++ ++static int explist(ktap_lexstate *ls, ktap_expdesc *v) ++{ ++ /* explist -> expr { `,' expr } */ ++ int n = 1; /* at least one expression */ ++ ++ expr(ls, v); ++ while (testnext(ls, ',')) { ++ codegen_exp2nextreg(ls->fs, v); ++ expr(ls, v); ++ n++; ++ } ++ return n; ++} ++ ++static void funcargs(ktap_lexstate *ls, ktap_expdesc *f, int line) ++{ ++ ktap_funcstate *fs = ls->fs; ++ ktap_expdesc args; ++ int base, nparams; ++ ++ switch (ls->t.token) { ++ case '(': { /* funcargs -> `(' [ explist ] `)' */ ++ lex_next(ls); ++ if (ls->t.token == ')') /* arg list is empty? */ ++ args.k = VVOID; ++ else { ++ explist(ls, &args); ++ codegen_setmultret(fs, &args); ++ } ++ check_match(ls, ')', '(', line); ++ break; ++ } ++ case '{': { /* funcargs -> constructor */ ++ constructor(ls, &args); ++ break; ++ } ++ case TK_STRING: { /* funcargs -> STRING */ ++ codestring(ls, &args, ls->t.seminfo.ts); ++ lex_next(ls); /* must use `seminfo' before `next' */ ++ break; ++ } ++ default: { ++ lex_syntaxerror(ls, "function arguments expected"); ++ } ++ } ++ ktap_assert(f->k == VNONRELOC); ++ base = f->u.info; /* base register for call */ ++ if (hasmultret(args.k)) ++ nparams = KTAP_MULTRET; /* open call */ ++ else { ++ if (args.k != VVOID) ++ codegen_exp2nextreg(fs, &args); /* close last argument */ ++ nparams = fs->freereg - (base+1); ++ } ++ init_exp(f, VCALL, codegen_codeABC(fs, OP_CALL, base, nparams+1, 2)); ++ codegen_fixline(fs, line); ++ fs->freereg = base+1; /* call remove function and arguments and leaves ++ (unless changed) one result */ ++} ++ ++/* ++ * {====================================================================== ++ * Expression parsing ++ * ======================================================================= ++ */ ++static void primaryexp(ktap_lexstate *ls, ktap_expdesc *v) ++{ ++ /* primaryexp -> NAME | '(' expr ')' */ ++ switch (ls->t.token) { ++ case '(': { ++ int line = ls->linenumber; ++ ++ lex_next(ls); ++ expr(ls, v); ++ check_match(ls, ')', '(', line); ++ codegen_dischargevars(ls->fs, v); ++ return; ++ } ++ case TK_NAME: ++ singlevar(ls, v); ++ return; ++ default: ++ lex_syntaxerror(ls, "unexpected symbol"); ++ } ++} ++ ++static void suffixedexp(ktap_lexstate *ls, ktap_expdesc *v) ++{ ++ /* suffixedexp -> ++ primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */ ++ ktap_funcstate *fs = ls->fs; ++ int line = ls->linenumber; ++ ++ primaryexp(ls, v); ++ for (;;) { ++ switch (ls->t.token) { ++ case '.': { /* fieldsel */ ++ fieldsel(ls, v); ++ break; ++ } ++ case '[': { /* `[' exp1 `]' */ ++ ktap_expdesc key; ++ codegen_exp2anyregup(fs, v); ++ yindex(ls, &key); ++ codegen_indexed(fs, v, &key); ++ break; ++ } ++ case ':': { /* `:' NAME funcargs */ ++ ktap_expdesc key; ++ lex_next(ls); ++ checkname(ls, &key); ++ codegen_self(fs, v, &key); ++ funcargs(ls, v, line); ++ break; ++ } ++ case '(': case TK_STRING: case '{': { /* funcargs */ ++ codegen_exp2nextreg(fs, v); ++ funcargs(ls, v, line); ++ break; ++ } ++ default: ++ return; ++ } ++ } ++} ++ ++static void simpleexp(ktap_lexstate *ls, ktap_expdesc *v) ++{ ++ /* simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... | ++ constructor | FUNCTION body | suffixedexp */ ++ switch (ls->t.token) { ++ case TK_NUMBER: { ++ init_exp(v, VKNUM, 0); ++ v->u.nval = ls->t.seminfo.r; ++ break; ++ } ++ case TK_STRING: { ++ codestring(ls, v, ls->t.seminfo.ts); ++ break; ++ } ++ case TK_KSYM: { ++ init_exp(v, VKNUM, 0); ++ v->u.nval = ++ (ktap_number)find_kernel_symbol(getstr(ls->t.seminfo.ts)); ++ break; ++ } ++ case TK_NIL: { ++ init_exp(v, VNIL, 0); ++ break; ++ } ++ case TK_TRUE: { ++ init_exp(v, VTRUE, 0); ++ break; ++ } ++ case TK_FALSE: { ++ init_exp(v, VFALSE, 0); ++ break; ++ } ++ case TK_DOTS: { /* vararg */ ++ ktap_funcstate *fs = ls->fs; ++ check_condition(ls, fs->f->is_vararg, ++ "cannot use " KTAP_QL("...") " outside a vararg function"); ++ init_exp(v, VVARARG, codegen_codeABC(fs, OP_VARARG, 0, 1, 0)); ++ break; ++ } ++ case '{': { /* constructor */ ++ constructor(ls, v); ++ return; ++ } ++ case TK_FUNCTION: { ++ lex_next(ls); ++ body(ls, v, 0, ls->linenumber); ++ return; ++ } ++ case TK_ARGEVENT: ++ init_exp(v, VEVENT, 0); ++ break; ++ ++ case TK_ARGNAME: ++ init_exp(v, VEVENTNAME, 0); ++ break; ++ case TK_ARG1: ++ case TK_ARG2: ++ case TK_ARG3: ++ case TK_ARG4: ++ case TK_ARG5: ++ case TK_ARG6: ++ case TK_ARG7: ++ case TK_ARG8: ++ case TK_ARG9: ++ init_exp(v, VEVENTARG, ls->t.token - TK_ARG1 + 1); ++ break; ++ default: { ++ suffixedexp(ls, v); ++ return; ++ } ++ } ++ lex_next(ls); ++} ++ ++static UnOpr getunopr(int op) ++{ ++ switch (op) { ++ case TK_NOT: return OPR_NOT; ++ case '-': return OPR_MINUS; ++ case '#': return OPR_LEN; ++ default: return OPR_NOUNOPR; ++ } ++} ++ ++static BinOpr getbinopr(int op) ++{ ++ switch (op) { ++ case '+': return OPR_ADD; ++ case '-': return OPR_SUB; ++ case '*': return OPR_MUL; ++ case '/': return OPR_DIV; ++ case '%': return OPR_MOD; ++ case '^': return OPR_POW; ++ case TK_CONCAT: return OPR_CONCAT; ++ case TK_NE: return OPR_NE; ++ case TK_EQ: return OPR_EQ; ++ case '<': return OPR_LT; ++ case TK_LE: return OPR_LE; ++ case '>': return OPR_GT; ++ case TK_GE: return OPR_GE; ++ case TK_AND: return OPR_AND; ++ case TK_OR: return OPR_OR; ++ default: return OPR_NOBINOPR; ++ } ++} ++ ++static const struct { ++ u8 left; /* left priority for each binary operator */ ++ u8 right; /* right priority */ ++} priority[] = { /* ORDER OPR */ ++ {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `*' `/' `%' */ ++ {10, 9}, {5, 4}, /* ^, .. (right associative) */ ++ {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ ++ {3, 3}, {3, 3}, {3, 3}, /* !=, >, >= */ ++ {2, 2}, {1, 1} /* and, or */ ++}; ++ ++#define UNARY_PRIORITY 8 /* priority for unary operators */ ++ ++#define leavelevel(ls) (ls->nCcalls--) ++ ++/* ++ * subexpr -> (simpleexp | unop subexpr) { binop subexpr } ++ * where `binop' is any binary operator with a priority higher than `limit' ++ */ ++static BinOpr subexpr(ktap_lexstate *ls, ktap_expdesc *v, int limit) ++{ ++ BinOpr op; ++ UnOpr uop; ++ ++ enterlevel(ls); ++ uop = getunopr(ls->t.token); ++ if (uop != OPR_NOUNOPR) { ++ int line = ls->linenumber; ++ ++ lex_next(ls); ++ subexpr(ls, v, UNARY_PRIORITY); ++ codegen_prefix(ls->fs, uop, v, line); ++ } else ++ simpleexp(ls, v); ++ ++ /* expand while operators have priorities higher than `limit' */ ++ op = getbinopr(ls->t.token); ++ while (op != OPR_NOBINOPR && priority[op].left > limit) { ++ ktap_expdesc v2; ++ BinOpr nextop; ++ int line = ls->linenumber; ++ ++ lex_next(ls); ++ codegen_infix(ls->fs, op, v); ++ /* read sub-expression with higher priority */ ++ nextop = subexpr(ls, &v2, priority[op].right); ++ codegen_posfix(ls->fs, op, v, &v2, line); ++ op = nextop; ++ } ++ leavelevel(ls); ++ return op; /* return first untreated operator */ ++} ++ ++static void expr(ktap_lexstate *ls, ktap_expdesc *v) ++{ ++ subexpr(ls, v, 0); ++} ++ ++/* }==================================================================== */ ++ ++/* ++ * {====================================================================== ++ * Rules for Statements ++ * ======================================================================= ++ */ ++static void block(ktap_lexstate *ls) ++{ ++ /* block -> statlist */ ++ ktap_funcstate *fs = ls->fs; ++ ktap_blockcnt bl; ++ ++ enterblock(fs, &bl, 0); ++ statlist(ls); ++ leaveblock(fs); ++} ++ ++/* ++ * structure to chain all variables in the left-hand side of an ++ * assignment ++ */ ++struct LHS_assign { ++ struct LHS_assign *prev; ++ ktap_expdesc v; /* variable (global, local, upvalue, or indexed) */ ++}; ++ ++/* ++ * check whether, in an assignment to an upvalue/local variable, the ++ * upvalue/local variable is begin used in a previous assignment to a ++ * table. If so, save original upvalue/local value in a safe place and ++ * use this safe copy in the previous assignment. ++ */ ++static void check_conflict(ktap_lexstate *ls, struct LHS_assign *lh, ktap_expdesc *v) ++{ ++ ktap_funcstate *fs = ls->fs; ++ int extra = fs->freereg; /* eventual position to save local variable */ ++ int conflict = 0; ++ ++ for (; lh; lh = lh->prev) { /* check all previous assignments */ ++ if (lh->v.k == VINDEXED) { /* assigning to a table? */ ++ /* table is the upvalue/local being assigned now? */ ++ if (lh->v.u.ind.vt == v->k && lh->v.u.ind.t == v->u.info) { ++ conflict = 1; ++ lh->v.u.ind.vt = VLOCAL; ++ lh->v.u.ind.t = extra; /* previous assignment will use safe copy */ ++ } ++ /* index is the local being assigned? (index cannot be upvalue) */ ++ if (v->k == VLOCAL && lh->v.u.ind.idx == v->u.info) { ++ conflict = 1; ++ lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ ++ } ++ } ++ } ++ if (conflict) { ++ /* copy upvalue/local value to a temporary (in position 'extra') */ ++ OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; ++ codegen_codeABC(fs, op, extra, v->u.info, 0); ++ codegen_reserveregs(fs, 1); ++ } ++} ++ ++static void assignment(ktap_lexstate *ls, struct LHS_assign *lh, int nvars) ++{ ++ ktap_expdesc e; ++ ++ check_condition(ls, vkisvar(lh->v.k), "syntax error"); ++ if (testnext(ls, ',')) { /* assignment -> ',' suffixedexp assignment */ ++ struct LHS_assign nv; ++ ++ nv.prev = lh; ++ suffixedexp(ls, &nv.v); ++ if (nv.v.k != VINDEXED) ++ check_conflict(ls, lh, &nv.v); ++ checklimit(ls->fs, nvars + ls->nCcalls, KTAP_MAXCCALLS, ++ "C levels"); ++ assignment(ls, &nv, nvars+1); ++ } else if (testnext(ls, '=')) { /* assignment -> '=' explist */ ++ int nexps; ++ ++ nexps = explist(ls, &e); ++ if (nexps != nvars) { ++ adjust_assign(ls, nvars, nexps, &e); ++ /* remove extra values */ ++ if (nexps > nvars) ++ ls->fs->freereg -= nexps - nvars; ++ } else { ++ /* close last expression */ ++ codegen_setoneret(ls->fs, &e); ++ codegen_storevar(ls->fs, &lh->v, &e); ++ return; /* avoid default */ ++ } ++ } else if (testnext(ls, TK_INCR)) { /* assignment -> '+=' explist */ ++ int nexps; ++ ++ nexps = explist(ls, &e); ++ if (nexps != nvars) { ++ lex_syntaxerror(ls, "don't allow multi-assign for +="); ++ } else { ++ /* close last expression */ ++ codegen_setoneret(ls->fs, &e); ++ codegen_storeincr(ls->fs, &lh->v, &e); ++ return; /* avoid default */ ++ } ++ } else if (testnext(ls, TK_AGGR_ASSIGN)) { /* assignment -> '<<<' explist */ ++ int nexps; ++ ++ nexps = explist(ls, &e); ++ if (nexps != nvars) { ++ lex_syntaxerror(ls, "don't allow multi-assign for <<<"); ++ } else { ++ /* close last expression */ ++ codegen_setoneret(ls->fs, &e); ++ codegen_store_aggr(ls->fs, &lh->v, &e); ++ return; /* avoid default */ ++ } ++ } ++ ++ init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ ++ codegen_storevar(ls->fs, &lh->v, &e); ++} ++ ++static int cond(ktap_lexstate *ls) ++{ ++ /* cond -> exp */ ++ ktap_expdesc v; ++ expr(ls, &v); /* read condition */ ++ if (v.k == VNIL) ++ v.k = VFALSE; /* `falses' are all equal here */ ++ codegen_goiftrue(ls->fs, &v); ++ return v.f; ++} ++ ++static void gotostat(ktap_lexstate *ls, int pc) ++{ ++ int line = ls->linenumber; ++ ktap_string *label; ++ int g; ++ ++ if (testnext(ls, TK_GOTO)) ++ label = str_checkname(ls); ++ else { ++ lex_next(ls); /* skip break */ ++ label = ktapc_ts_new("break"); ++ } ++ g = newlabelentry(ls, &ls->dyd->gt, label, line, pc); ++ findlabel(ls, g); /* close it if label already defined */ ++} ++ ++/* check for repeated labels on the same block */ ++static void checkrepeated(ktap_funcstate *fs, ktap_labellist *ll, ktap_string *label) ++{ ++ int i; ++ for (i = fs->bl->firstlabel; i < ll->n; i++) { ++ if (ktapc_ts_eqstr(label, ll->arr[i].name)) { ++ const char *msg = ktapc_sprintf( ++ "label " KTAP_QS " already defined on line %d", ++ getstr(label), ll->arr[i].line); ++ semerror(fs->ls, msg); ++ } ++ } ++} ++ ++/* skip no-op statements */ ++static void skipnoopstat(ktap_lexstate *ls) ++{ ++ while (ls->t.token == ';' || ls->t.token == TK_DBCOLON) ++ statement(ls); ++} ++ ++static void labelstat (ktap_lexstate *ls, ktap_string *label, int line) ++{ ++ /* label -> '::' NAME '::' */ ++ ktap_funcstate *fs = ls->fs; ++ ktap_labellist *ll = &ls->dyd->label; ++ int l; /* index of new label being created */ ++ ++ checkrepeated(fs, ll, label); /* check for repeated labels */ ++ checknext(ls, TK_DBCOLON); /* skip double colon */ ++ /* create new entry for this label */ ++ l = newlabelentry(ls, ll, label, line, fs->pc); ++ skipnoopstat(ls); /* skip other no-op statements */ ++ if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */ ++ /* assume that locals are already out of scope */ ++ ll->arr[l].nactvar = fs->bl->nactvar; ++ } ++ findgotos(ls, &ll->arr[l]); ++} ++ ++static void whilestat(ktap_lexstate *ls, int line) ++{ ++ /* whilestat -> WHILE cond DO block END */ ++ ktap_funcstate *fs = ls->fs; ++ int whileinit; ++ int condexit; ++ ktap_blockcnt bl; ++ ++ lex_next(ls); /* skip WHILE */ ++ whileinit = codegen_getlabel(fs); ++ checknext(ls, '('); ++ condexit = cond(ls); ++ checknext(ls, ')'); ++ ++ enterblock(fs, &bl, 1); ++ //checknext(ls, TK_DO); ++ checknext(ls, '{'); ++ block(ls); ++ codegen_jumpto(fs, whileinit); ++ checknext(ls, '}'); ++ //check_match(ls, TK_END, TK_WHILE, line); ++ leaveblock(fs); ++ codegen_patchtohere(fs, condexit); /* false conditions finish the loop */ ++} ++ ++static void repeatstat(ktap_lexstate *ls, int line) ++{ ++ /* repeatstat -> REPEAT block UNTIL cond */ ++ int condexit; ++ ktap_funcstate *fs = ls->fs; ++ int repeat_init = codegen_getlabel(fs); ++ ktap_blockcnt bl1, bl2; ++ ++ enterblock(fs, &bl1, 1); /* loop block */ ++ enterblock(fs, &bl2, 0); /* scope block */ ++ lex_next(ls); /* skip REPEAT */ ++ statlist(ls); ++ check_match(ls, TK_UNTIL, TK_REPEAT, line); ++ condexit = cond(ls); /* read condition (inside scope block) */ ++ if (bl2.upval) /* upvalues? */ ++ codegen_patchclose(fs, condexit, bl2.nactvar); ++ leaveblock(fs); /* finish scope */ ++ codegen_patchlist(fs, condexit, repeat_init); /* close the loop */ ++ leaveblock(fs); /* finish loop */ ++} ++ ++static int exp1(ktap_lexstate *ls) ++{ ++ ktap_expdesc e; ++ int reg; ++ ++ expr(ls, &e); ++ codegen_exp2nextreg(ls->fs, &e); ++ ktap_assert(e.k == VNONRELOC); ++ reg = e.u.info; ++ return reg; ++} ++ ++static void forbody(ktap_lexstate *ls, int base, int line, int nvars, int isnum) ++{ ++ /* forbody -> DO block */ ++ ktap_blockcnt bl; ++ ktap_funcstate *fs = ls->fs; ++ int prep, endfor; ++ ++ checknext(ls, ')'); ++ ++ adjustlocalvars(ls, 3); /* control variables */ ++ //checknext(ls, TK_DO); ++ checknext(ls, '{'); ++ prep = isnum ? codegen_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : codegen_jump(fs); ++ enterblock(fs, &bl, 0); /* scope for declared variables */ ++ adjustlocalvars(ls, nvars); ++ codegen_reserveregs(fs, nvars); ++ block(ls); ++ leaveblock(fs); /* end of scope for declared variables */ ++ codegen_patchtohere(fs, prep); ++ if (isnum) /* numeric for? */ ++ endfor = codegen_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP); ++ else { /* generic for */ ++ codegen_codeABC(fs, OP_TFORCALL, base, 0, nvars); ++ codegen_fixline(fs, line); ++ endfor = codegen_codeAsBx(fs, OP_TFORLOOP, base + 2, NO_JUMP); ++ } ++ codegen_patchlist(fs, endfor, prep + 1); ++ codegen_fixline(fs, line); ++} ++ ++static void fornum(ktap_lexstate *ls, ktap_string *varname, int line) ++{ ++ /* fornum -> NAME = exp1,exp1[,exp1] forbody */ ++ ktap_funcstate *fs = ls->fs; ++ int base = fs->freereg; ++ ++ new_localvarliteral(ls, "(for index)"); ++ new_localvarliteral(ls, "(for limit)"); ++ new_localvarliteral(ls, "(for step)"); ++ new_localvar(ls, varname); ++ checknext(ls, '='); ++ exp1(ls); /* initial value */ ++ checknext(ls, ','); ++ exp1(ls); /* limit */ ++ if (testnext(ls, ',')) ++ exp1(ls); /* optional step */ ++ else { /* default step = 1 */ ++ codegen_codek(fs, fs->freereg, codegen_numberK(fs, 1)); ++ codegen_reserveregs(fs, 1); ++ } ++ forbody(ls, base, line, 1, 1); ++} ++ ++static void forlist(ktap_lexstate *ls, ktap_string *indexname) ++{ ++ /* forlist -> NAME {,NAME} IN explist forbody */ ++ ktap_funcstate *fs = ls->fs; ++ ktap_expdesc e; ++ int nvars = 4; /* gen, state, control, plus at least one declared var */ ++ int line; ++ int base = fs->freereg; ++ ++ /* create control variables */ ++ new_localvarliteral(ls, "(for generator)"); ++ new_localvarliteral(ls, "(for state)"); ++ new_localvarliteral(ls, "(for control)"); ++ /* create declared variables */ ++ new_localvar(ls, indexname); ++ while (testnext(ls, ',')) { ++ new_localvar(ls, str_checkname(ls)); ++ nvars++; ++ } ++ checknext(ls, TK_IN); ++ line = ls->linenumber; ++ adjust_assign(ls, 3, explist(ls, &e), &e); ++ codegen_checkstack(fs, 3); /* extra space to call generator */ ++ forbody(ls, base, line, nvars - 3, 0); ++} ++ ++static void forstat(ktap_lexstate *ls, int line) ++{ ++ /* forstat -> FOR (fornum | forlist) END */ ++ ktap_funcstate *fs = ls->fs; ++ ktap_string *varname; ++ ktap_blockcnt bl; ++ ++ enterblock(fs, &bl, 1); /* scope for loop and control variables */ ++ lex_next(ls); /* skip `for' */ ++ ++ checknext(ls, '('); ++ varname = str_checkname(ls); /* first variable name */ ++ switch (ls->t.token) { ++ case '=': ++ fornum(ls, varname, line); ++ break; ++ case ',': case TK_IN: ++ forlist(ls, varname); ++ break; ++ default: ++ lex_syntaxerror(ls, KTAP_QL("=") " or " KTAP_QL("in") " expected"); ++ } ++ //check_match(ls, TK_END, TK_FOR, line); ++ checknext(ls, '}'); ++ leaveblock(fs); /* loop scope (`break' jumps to this point) */ ++} ++ ++static void test_then_block(ktap_lexstate *ls, int *escapelist) ++{ ++ /* test_then_block -> [IF | ELSEIF] cond THEN block */ ++ ktap_blockcnt bl; ++ ktap_funcstate *fs = ls->fs; ++ ktap_expdesc v; ++ int jf; /* instruction to skip 'then' code (if condition is false) */ ++ ++ lex_next(ls); /* skip IF or ELSEIF */ ++ checknext(ls, '('); ++ expr(ls, &v); /* read condition */ ++ checknext(ls, ')'); ++ //checknext(ls, TK_THEN); ++ checknext(ls, '{'); ++ if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) { ++ codegen_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ ++ enterblock(fs, &bl, 0); /* must enter block before 'goto' */ ++ gotostat(ls, v.t); /* handle goto/break */ ++ skipnoopstat(ls); /* skip other no-op statements */ ++ if (block_follow(ls, 0)) { /* 'goto' is the entire block? */ ++ leaveblock(fs); ++ checknext(ls, '}'); ++ return; /* and that is it */ ++ } else /* must skip over 'then' part if condition is false */ ++ jf = codegen_jump(fs); ++ } else { /* regular case (not goto/break) */ ++ codegen_goiftrue(ls->fs, &v); /* skip over block if condition is false */ ++ enterblock(fs, &bl, 0); ++ jf = v.f; ++ } ++ statlist(ls); /* `then' part */ ++ checknext(ls, '}'); ++ leaveblock(fs); ++ if (ls->t.token == TK_ELSE || ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ ++ codegen_concat(fs, escapelist, codegen_jump(fs)); /* must jump over it */ ++ codegen_patchtohere(fs, jf); ++} ++ ++static void ifstat(ktap_lexstate *ls, int line) ++{ ++ /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ ++ ktap_funcstate *fs = ls->fs; ++ int escapelist = NO_JUMP; /* exit list for finished parts */ ++ ++ test_then_block(ls, &escapelist); /* IF cond THEN block */ ++ while (ls->t.token == TK_ELSEIF) ++ test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */ ++ if (testnext(ls, TK_ELSE)) { ++ checknext(ls, '{'); ++ block(ls); /* `else' part */ ++ checknext(ls, '}'); ++ } ++ //check_match(ls, TK_END, TK_IF, line); ++ codegen_patchtohere(fs, escapelist); /* patch escape list to 'if' end */ ++} ++ ++static void localfunc(ktap_lexstate *ls) ++{ ++ ktap_expdesc b; ++ ktap_funcstate *fs = ls->fs; ++ ++ new_localvar(ls, str_checkname(ls)); /* new local variable */ ++ adjustlocalvars(ls, 1); /* enter its scope */ ++ body(ls, &b, 0, ls->linenumber); /* function created in next register */ ++ /* debug information will only see the variable after this point! */ ++ getlocvar(fs, b.u.info)->startpc = fs->pc; ++} ++ ++static void localstat(ktap_lexstate *ls) ++{ ++ /* stat -> LOCAL NAME {`,' NAME} [`=' explist] */ ++ int nvars = 0; ++ int nexps; ++ ktap_expdesc e; ++ ++ do { ++ new_localvar(ls, str_checkname(ls)); ++ nvars++; ++ } while (testnext(ls, ',')); ++ if (testnext(ls, '=')) ++ nexps = explist(ls, &e); ++ else { ++ e.k = VVOID; ++ nexps = 0; ++ } ++ adjust_assign(ls, nvars, nexps, &e); ++ adjustlocalvars(ls, nvars); ++} ++ ++static int funcname(ktap_lexstate *ls, ktap_expdesc *v) ++{ ++ /* funcname -> NAME {fieldsel} [`:' NAME] */ ++ int ismethod = 0; ++ ++ singlevar(ls, v); ++ while (ls->t.token == '.') ++ fieldsel(ls, v); ++ if (ls->t.token == ':') { ++ ismethod = 1; ++ fieldsel(ls, v); ++ } ++ return ismethod; ++} ++ ++static void funcstat(ktap_lexstate *ls, int line) ++{ ++ /* funcstat -> FUNCTION funcname body */ ++ int ismethod; ++ ktap_expdesc v, b; ++ ++ lex_next(ls); /* skip FUNCTION */ ++ ismethod = funcname(ls, &v); ++ body(ls, &b, ismethod, line); ++ codegen_storevar(ls->fs, &v, &b); ++ codegen_fixline(ls->fs, line); /* definition `happens' in the first line */ ++} ++ ++static void exprstat(ktap_lexstate *ls) ++{ ++ /* stat -> func | assignment */ ++ ktap_funcstate *fs = ls->fs; ++ struct LHS_assign v; ++ ++ suffixedexp(ls, &v.v); ++ /* stat -> assignment ? */ ++ if (ls->t.token == '=' || ls->t.token == ',' || ++ ls->t.token == TK_INCR || ls->t.token == TK_AGGR_ASSIGN) { ++ v.prev = NULL; ++ assignment(ls, &v, 1); ++ } else { /* stat -> func */ ++ check_condition(ls, v.v.k == VCALL, "syntax error"); ++ SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */ ++ } ++} ++ ++static void retstat(ktap_lexstate *ls) ++{ ++ /* stat -> RETURN [explist] [';'] */ ++ ktap_funcstate *fs = ls->fs; ++ ktap_expdesc e; ++ int first, nret; /* registers with returned values */ ++ ++ if (block_follow(ls, 1) || ls->t.token == ';') ++ first = nret = 0; /* return no values */ ++ else { ++ nret = explist(ls, &e); /* optional return values */ ++ if (hasmultret(e.k)) { ++ codegen_setmultret(fs, &e); ++ if (e.k == VCALL && nret == 1) { /* tail call? */ ++ SET_OPCODE(getcode(fs,&e), OP_TAILCALL); ++ ktap_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar); ++ } ++ first = fs->nactvar; ++ nret = KTAP_MULTRET; /* return all values */ ++ } else { ++ if (nret == 1) /* only one single value? */ ++ first = codegen_exp2anyreg(fs, &e); ++ else { ++ codegen_exp2nextreg(fs, &e); /* values must go to the `stack' */ ++ first = fs->nactvar; /* return all `active' values */ ++ ktap_assert(nret == fs->freereg - first); ++ } ++ } ++ } ++ codegen_ret(fs, first, nret); ++ testnext(ls, ';'); /* skip optional semicolon */ ++} ++ ++static void tracestat(ktap_lexstate *ls) ++{ ++ ktap_expdesc v0, key, args; ++ ktap_expdesc *v = &v0; ++ ktap_string *kdebug_str = ktapc_ts_new("kdebug"); ++ ktap_string *probe_str = ktapc_ts_new("probe_by_id"); ++ ktap_string *probe_end_str = ktapc_ts_new("probe_end"); ++ ktap_funcstate *fs = ls->fs; ++ int token = ls->t.token; ++ int line = ls->linenumber; ++ int base, nparams; ++ ++ if (token == TK_TRACE) ++ lex_read_string_until(ls, '{'); ++ else ++ lex_next(ls); /* skip "trace_end" keyword */ ++ ++ /* kdebug */ ++ singlevaraux(fs, ls->envn, v, 1); /* get environment variable */ ++ codestring(ls, &key, kdebug_str); /* key is variable name */ ++ codegen_indexed(fs, v, &key); /* env[varname] */ ++ ++ /* fieldsel: kdebug.probe */ ++ codegen_exp2anyregup(fs, v); ++ if (token == TK_TRACE) ++ codestring(ls, &key, probe_str); ++ else if (token == TK_TRACE_END) ++ codestring(ls, &key, probe_end_str); ++ codegen_indexed(fs, v, &key); ++ ++ /* funcargs*/ ++ codegen_exp2nextreg(fs, v); ++ ++ if (token == TK_TRACE) { ++ ktap_eventdef_info *evdef_info; ++ ++ /* argument: EVENTDEF string */ ++ check(ls, TK_STRING); ++ enterlevel(ls); ++ evdef_info = ktapc_parse_eventdef(getstr(ls->t.seminfo.ts)); ++ check_condition(ls, evdef_info != NULL, "Cannot parse eventdef"); ++ ++ /* pass a userspace pointer to kernel */ ++ codenumber(ls, &args, (ktap_number)evdef_info); ++ lex_next(ls); /* skip EVENTDEF string */ ++ leavelevel(ls); ++ ++ codegen_exp2nextreg(fs, &args); /* for next argument */ ++ } ++ ++ /* argument: callback function */ ++ enterlevel(ls); ++ func_body_no_args(ls, &args, ls->linenumber); ++ leavelevel(ls); ++ ++ codegen_setmultret(fs, &args); ++ ++ base = v->u.info; /* base register for call */ ++ if (hasmultret(args.k)) ++ nparams = KTAP_MULTRET; /* open call */ ++ else { ++ codegen_exp2nextreg(fs, &args); /* close last argument */ ++ nparams = fs->freereg - (base+1); ++ } ++ init_exp(v, VCALL, codegen_codeABC(fs, OP_CALL, base, nparams+1, 2)); ++ codegen_fixline(fs, line); ++ fs->freereg = base+1; ++ ++ check_condition(ls, v->k == VCALL, "syntax error"); ++ SETARG_C(getcode(fs, v), 1); /* call statement uses no results */ ++} ++ ++static void timerstat(ktap_lexstate *ls) ++{ ++ ktap_expdesc v0, key, args; ++ ktap_expdesc *v = &v0; ++ ktap_funcstate *fs = ls->fs; ++ ktap_string *token_str = ls->t.seminfo.ts; ++ ktap_string *interval_str; ++ int line = ls->linenumber; ++ int base, nparams; ++ ++ lex_next(ls); /* skip profile/tick keyword */ ++ check(ls, '-'); ++ ++ lex_read_string_until(ls, '{'); ++ interval_str = ls->t.seminfo.ts; ++ ++ //printf("timerstat str: %s\n", getstr(interval_str)); ++ //exit(0); ++ ++ /* timer */ ++ singlevaraux(fs, ls->envn, v, 1); /* get environment variable */ ++ codestring(ls, &key, ktapc_ts_new("timer")); /* key is variable name */ ++ codegen_indexed(fs, v, &key); /* env[varname] */ ++ ++ /* fieldsel: timer.profile, timer.tick */ ++ codegen_exp2anyregup(fs, v); ++ codestring(ls, &key, token_str); ++ codegen_indexed(fs, v, &key); ++ ++ /* funcargs*/ ++ codegen_exp2nextreg(fs, v); ++ ++ /* argument: interval string */ ++ check(ls, TK_STRING); ++ enterlevel(ls); ++ codestring(ls, &args, interval_str); ++ lex_next(ls); /* skip interval string */ ++ leavelevel(ls); ++ ++ codegen_exp2nextreg(fs, &args); /* for next argument */ ++ ++ /* argument: callback function */ ++ enterlevel(ls); ++ func_body_no_args(ls, &args, ls->linenumber); ++ leavelevel(ls); ++ ++ codegen_setmultret(fs, &args); ++ ++ base = v->u.info; /* base register for call */ ++ if (hasmultret(args.k)) ++ nparams = KTAP_MULTRET; /* open call */ ++ else { ++ codegen_exp2nextreg(fs, &args); /* close last argument */ ++ nparams = fs->freereg - (base+1); ++ } ++ init_exp(v, VCALL, codegen_codeABC(fs, OP_CALL, base, nparams+1, 2)); ++ codegen_fixline(fs, line); ++ fs->freereg = base+1; ++ ++ check_condition(ls, v->k == VCALL, "syntax error"); ++ SETARG_C(getcode(fs, v), 1); /* call statement uses no results */ ++} ++ ++/* we still keep cdef keyword even FFI feature is disabled, it just does ++ * nothing and prints out a warning */ ++#ifdef CONFIG_KTAP_FFI ++static void parsecdef(ktap_lexstate *ls) ++{ ++ /* read long string cdef */ ++ lex_next(ls); ++ ++ check(ls, TK_STRING); ++ ffi_cdef(getstr(ls->t.seminfo.ts)); ++ ++ /* consume newline */ ++ lex_next(ls); ++} ++#else ++static void parsecdef(ktap_lexstate *ls) ++{ ++ printf("Please compile with FFI support to use cdef!\n"); ++ exit(EXIT_SUCCESS); ++} ++#endif ++ ++ ++static void statement(ktap_lexstate *ls) ++{ ++ int line = ls->linenumber; /* may be needed for error messages */ ++ ++ enterlevel(ls); ++ switch (ls->t.token) { ++ case ';': { /* stat -> ';' (empty statement) */ ++ lex_next(ls); /* skip ';' */ ++ break; ++ } ++ case TK_IF: { /* stat -> ifstat */ ++ ifstat(ls, line); ++ break; ++ } ++ case TK_WHILE: { /* stat -> whilestat */ ++ whilestat(ls, line); ++ break; ++ } ++ case TK_DO: { /* stat -> DO block END */ ++ lex_next(ls); /* skip DO */ ++ block(ls); ++ check_match(ls, TK_END, TK_DO, line); ++ break; ++ } ++ case TK_FOR: { /* stat -> forstat */ ++ forstat(ls, line); ++ break; ++ } ++ case TK_REPEAT: { /* stat -> repeatstat */ ++ repeatstat(ls, line); ++ break; ++ } ++ case TK_FUNCTION: { /* stat -> funcstat */ ++ funcstat(ls, line); ++ break; ++ } ++ case TK_LOCAL: { /* stat -> localstat */ ++ lex_next(ls); /* skip LOCAL */ ++ if (testnext(ls, TK_FUNCTION)) /* local function? */ ++ localfunc(ls); ++ else ++ localstat(ls); ++ break; ++ } ++ case TK_DBCOLON: { /* stat -> label */ ++ lex_next(ls); /* skip double colon */ ++ labelstat(ls, str_checkname(ls), line); ++ break; ++ } ++ case TK_RETURN: { /* stat -> retstat */ ++ lex_next(ls); /* skip RETURN */ ++ retstat(ls); ++ break; ++ } ++ case TK_BREAK: /* stat -> breakstat */ ++ case TK_GOTO: { /* stat -> 'goto' NAME */ ++ gotostat(ls, codegen_jump(ls->fs)); ++ break; ++ } ++ ++ case TK_TRACE: ++ case TK_TRACE_END: ++ tracestat(ls); ++ break; ++ case TK_PROFILE: ++ case TK_TICK: ++ timerstat(ls); ++ break; ++ case TK_FFI_CDEF: ++ parsecdef(ls); ++ break; ++ default: { /* stat -> func | assignment */ ++ exprstat(ls); ++ break; ++ } ++ } ++ //ktap_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && ++ // ls->fs->freereg >= ls->fs->nactvar); ++ ls->fs->freereg = ls->fs->nactvar; /* free registers */ ++ leavelevel(ls); ++} ++/* }====================================================================== */ ++ ++/* ++ * compiles the main function, which is a regular vararg function with an upvalue ++ */ ++static void mainfunc(ktap_lexstate *ls, ktap_funcstate *fs) ++{ ++ ktap_blockcnt bl; ++ ktap_expdesc v; ++ ++ open_func(ls, fs, &bl); ++ fs->f->is_vararg = 1; /* main function is always vararg */ ++ init_exp(&v, VLOCAL, 0); /* create and... */ ++ newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ ++ lex_next(ls); /* read first token */ ++ statlist(ls); /* parse main body */ ++ check(ls, TK_EOS); ++ close_func(ls); ++} ++ ++ktap_closure *ktapc_parser(char *ptr, const char *name) ++{ ++ ktap_lexstate lexstate; ++ ktap_funcstate funcstate; ++ ktap_dyndata dyd; ++ ktap_mbuffer buff; ++ int firstchar = *ptr++; ++ ktap_closure *cl = ktapc_newclosure(1); /* create main closure */ ++ ++ memset(&lexstate, 0, sizeof(ktap_lexstate)); ++ memset(&funcstate, 0, sizeof(ktap_funcstate)); ++ funcstate.f = cl->p = ktapc_newproto(); ++ funcstate.f->source = ktapc_ts_new(name); /* create and anchor ktap_string */ ++ ++ lex_init(); ++ ++ mbuff_init(&buff); ++ memset(&dyd, 0, sizeof(ktap_dyndata)); ++ lexstate.buff = &buff; ++ lexstate.dyd = &dyd; ++ lex_setinput(&lexstate, ptr, funcstate.f->source, firstchar); ++ ++ mainfunc(&lexstate, &funcstate); ++ ++ ktap_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); ++ ++ /* all scopes should be correctly finished */ ++ ktap_assert(dyd.actvar.n == 0 && dyd.gt.n == 0 && dyd.label.n == 0); ++ return cl; ++} +diff --git a/drivers/staging/ktap/userspace/symbol.c b/drivers/staging/ktap/userspace/symbol.c +new file mode 100644 +index 0000000..74129e2 +--- /dev/null ++++ b/drivers/staging/ktap/userspace/symbol.c +@@ -0,0 +1,291 @@ ++/* ++ * symbol.c ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2013 Azat Khuzhin . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "symbol.h" ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, ++ GElf_Shdr *shp, const char *name) ++{ ++ Elf_Scn *scn = NULL; ++ ++ /* Elf is corrupted/truncated, avoid calling elf_strptr. */ ++ if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) ++ return NULL; ++ ++ while ((scn = elf_nextscn(elf, scn)) != NULL) { ++ char *str; ++ ++ gelf_getshdr(scn, shp); ++ str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); ++ if (!strcmp(name, str)) ++ break; ++ } ++ ++ return scn; ++} ++ ++/** ++ * @return v_addr of "LOAD" program header, that have zero offset. ++ */ ++static int find_load_address(Elf *elf, vaddr_t *load_address) ++{ ++ GElf_Phdr phdr; ++ size_t i, phdrnum; ++ ++ if (elf_getphdrnum(elf, &phdrnum)) ++ return -1; ++ ++ for (i = 0; i < phdrnum; i++) { ++ if (gelf_getphdr(elf, i, &phdr) == NULL) ++ return -1; ++ ++ if (phdr.p_type != PT_LOAD || phdr.p_offset != 0) ++ continue; ++ ++ *load_address = phdr.p_vaddr; ++ return 0; ++ } ++ ++ /* cannot found load address */ ++ return -1; ++} ++ ++static size_t elf_symbols(GElf_Shdr shdr) ++{ ++ return shdr.sh_size / shdr.sh_entsize; ++} ++ ++static int dso_symbols(Elf *elf, symbol_actor actor, void *arg) ++{ ++ Elf_Data *elf_data = NULL; ++ Elf_Scn *scn = NULL; ++ GElf_Sym sym; ++ GElf_Shdr shdr; ++ int symbols_count = 0; ++ vaddr_t load_address; ++ ++ if (find_load_address(elf, &load_address)) ++ return -1; ++ ++ while ((scn = elf_nextscn(elf, scn))) { ++ int i; ++ ++ gelf_getshdr(scn, &shdr); ++ ++ if (shdr.sh_type != SHT_SYMTAB) ++ continue; ++ ++ elf_data = elf_getdata(scn, elf_data); ++ ++ for (i = 0; i < elf_symbols(shdr); i++) { ++ char *name; ++ vaddr_t addr; ++ int ret; ++ ++ gelf_getsym(elf_data, i, &sym); ++ ++ if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) ++ continue; ++ ++ name = elf_strptr(elf, shdr.sh_link, sym.st_name); ++ addr = sym.st_value - load_address; ++ ++ ret = actor(name, addr, arg); ++ if (ret) ++ return ret; ++ ++ ++symbols_count; ++ } ++ } ++ ++ return symbols_count; ++} ++ ++#define SDT_NOTE_TYPE 3 ++#define SDT_NOTE_COUNT 3 ++#define SDT_NOTE_SCN ".note.stapsdt" ++#define SDT_NOTE_NAME "stapsdt" ++ ++static vaddr_t sdt_note_addr(Elf *elf, const char *data, size_t len, int type) ++{ ++ vaddr_t vaddr; ++ ++ /* ++ * Three addresses need to be obtained : ++ * Marker location, address of base section and semaphore location ++ */ ++ union { ++ Elf64_Addr a64[3]; ++ Elf32_Addr a32[3]; ++ } buf; ++ ++ /* ++ * dst and src are required for translation from file to memory ++ * representation ++ */ ++ Elf_Data dst = { ++ .d_buf = &buf, .d_type = ELF_T_ADDR, .d_version = EV_CURRENT, ++ .d_size = gelf_fsize(elf, ELF_T_ADDR, SDT_NOTE_COUNT, EV_CURRENT), ++ .d_off = 0, .d_align = 0 ++ }; ++ ++ Elf_Data src = { ++ .d_buf = (void *) data, .d_type = ELF_T_ADDR, ++ .d_version = EV_CURRENT, .d_size = dst.d_size, .d_off = 0, ++ .d_align = 0 ++ }; ++ ++ /* Check the type of each of the notes */ ++ if (type != SDT_NOTE_TYPE) ++ return 0; ++ ++ if (len < dst.d_size + SDT_NOTE_COUNT) ++ return 0; ++ ++ /* Translation from file representation to memory representation */ ++ if (gelf_xlatetom(elf, &dst, &src, ++ elf_getident(elf, NULL)[EI_DATA]) == NULL) ++ return 0; /* TODO */ ++ ++ memcpy(&vaddr, &buf, sizeof(vaddr)); ++ ++ return vaddr; ++} ++ ++static const char *sdt_note_name(Elf *elf, GElf_Nhdr *nhdr, const char *data) ++{ ++ const char *provider = data + gelf_fsize(elf, ++ ELF_T_ADDR, SDT_NOTE_COUNT, EV_CURRENT); ++ const char *name = (const char *)memchr(provider, '\0', ++ data + nhdr->n_descsz - provider); ++ ++ if (name++ == NULL) ++ return NULL; ++ ++ return name; ++} ++ ++static const char *sdt_note_data(const Elf_Data *data, size_t off) ++{ ++ return ((data->d_buf) + off); ++} ++ ++static int dso_sdt_notes(Elf *elf, symbol_actor actor, void *arg) ++{ ++ GElf_Ehdr ehdr; ++ Elf_Scn *scn = NULL; ++ Elf_Data *data; ++ GElf_Shdr shdr; ++ size_t shstrndx; ++ size_t next; ++ GElf_Nhdr nhdr; ++ size_t name_off, desc_off, offset; ++ vaddr_t vaddr = 0; ++ int symbols_count = 0; ++ ++ if (gelf_getehdr(elf, &ehdr) == NULL) ++ return 0; ++ if (elf_getshdrstrndx(elf, &shstrndx) != 0) ++ return 0; ++ ++ /* ++ * Look for section type = SHT_NOTE, flags = no SHF_ALLOC ++ * and name = .note.stapsdt ++ */ ++ scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN); ++ if (!scn) ++ return 0; ++ if (!(shdr.sh_type == SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) ++ return 0; ++ ++ data = elf_getdata(scn, NULL); ++ ++ for (offset = 0; ++ (next = gelf_getnote(data, offset, &nhdr, &name_off, &desc_off)) > 0; ++ offset = next) { ++ const char *name; ++ int ret; ++ ++ if (nhdr.n_namesz != sizeof(SDT_NOTE_NAME) || ++ memcmp(data->d_buf + name_off, SDT_NOTE_NAME, ++ sizeof(SDT_NOTE_NAME))) ++ continue; ++ ++ name = sdt_note_name(elf, &nhdr, sdt_note_data(data, desc_off)); ++ if (!name) ++ continue; ++ ++ vaddr = sdt_note_addr(elf, sdt_note_data(data, desc_off), ++ nhdr.n_descsz, nhdr.n_type); ++ if (!vaddr) ++ continue; ++ ++ ret = actor(name, vaddr, arg); ++ if (ret) ++ return ret; ++ ++ ++symbols_count; ++ } ++ ++ return symbols_count; ++} ++ ++int parse_dso_symbols(const char *exec, int type, symbol_actor actor, void *arg) ++{ ++ int symbols_count = 0; ++ Elf *elf; ++ int fd; ++ ++ if (elf_version(EV_CURRENT) == EV_NONE) ++ return -1; ++ ++ fd = open(exec, O_RDONLY); ++ if (fd < 0) ++ return -1; ++ ++ elf = elf_begin(fd, ELF_C_READ, NULL); ++ if (elf) { ++ switch (type) { ++ case FIND_SYMBOL: ++ symbols_count = dso_symbols(elf, actor, arg); ++ break; ++ case FIND_STAPSDT_NOTE: ++ symbols_count = dso_sdt_notes(elf, actor, arg); ++ break; ++ } ++ ++ elf_end(elf); ++ } ++ ++ close(fd); ++ return symbols_count; ++} +diff --git a/drivers/staging/ktap/userspace/symbol.h b/drivers/staging/ktap/userspace/symbol.h +new file mode 100644 +index 0000000..650e785 +--- /dev/null ++++ b/drivers/staging/ktap/userspace/symbol.h +@@ -0,0 +1,50 @@ ++/* ++ * symbol.h - extract symbols from DSO. ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2013 Azat Khuzhin . ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#define FIND_SYMBOL 1 ++#define FIND_STAPSDT_NOTE 2 ++ ++#ifndef NO_LIBELF ++ ++#include ++#include ++ ++typedef GElf_Addr vaddr_t; ++typedef int (*symbol_actor)(const char *name, vaddr_t addr, void *arg); ++ ++/** ++ * Parse all DSO symbols/sdt notes and all for every of them ++ * an actor. ++ * ++ * @exec - path to DSO ++ * @type - see FIND_* ++ * @symbol_actor - actor to call (callback) ++ * @arg - argument for @actor ++ * ++ * @return ++ * If there have errors, return negative value; ++ * No symbols found, return 0; ++ * Otherwise return number of dso symbols found ++ */ ++int ++parse_dso_symbols(const char *exec, int type, symbol_actor actor, void *arg); ++#endif +diff --git a/drivers/staging/ktap/userspace/util.c b/drivers/staging/ktap/userspace/util.c +new file mode 100644 +index 0000000..c0e6545 +--- /dev/null ++++ b/drivers/staging/ktap/userspace/util.c +@@ -0,0 +1,381 @@ ++/* ++ * util.c ++ * ++ * This file is part of ktap by Jovi Zhangwei. ++ * ++ * Copyright (C) 2012-2013 Jovi Zhangwei . ++ * ++ * Copyright (C) 1994-2013 Lua.org, PUC-Rio. ++ * - The part of code in this file is copied from lua initially. ++ * - lua's MIT license is compatible with GPL. ++ * ++ * ktap is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * ktap is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "../include/ktap_types.h" ++#include "../include/ktap_opcodes.h" ++#include "ktapc.h" ++ ++/* ++ * converts an integer to a "floating point byte", represented as ++ * (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if ++ * eeeee != 0 and (xxx) otherwise. ++ */ ++int ktapc_int2fb(unsigned int x) ++{ ++ int e = 0; /* exponent */ ++ ++ if (x < 8) ++ return x; ++ while (x >= 0x10) { ++ x = (x+1) >> 1; ++ e++; ++ } ++ return ((e+1) << 3) | ((int)x - 8); ++} ++ ++/* converts back */ ++int ktapc_fb2int(int x) ++{ ++ int e = (x >> 3) & 0x1f; ++ ++ if (e == 0) ++ return x; ++ else ++ return ((x & 7) + 8) << (e - 1); ++} ++ ++int ktapc_ceillog2(unsigned int x) ++{ ++ static const u8 log_2[256] = { ++ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, ++ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, ++ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, ++ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, ++ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, ++ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, ++ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, ++ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 ++ }; ++ int l = 0; ++ ++ x--; ++ while (x >= 256) { ++ l += 8; ++ x >>= 8; ++ } ++ return l + log_2[x]; ++} ++ ++ktap_number ktapc_arith(int op, ktap_number v1, ktap_number v2) ++{ ++ switch (op) { ++ case KTAP_OPADD: return NUMADD(v1, v2); ++ case KTAP_OPSUB: return NUMSUB(v1, v2); ++ case KTAP_OPMUL: return NUMMUL(v1, v2); ++ case KTAP_OPDIV: return NUMDIV(v1, v2); ++ case KTAP_OPMOD: return NUMMOD(v1, v2); ++ //case KTAP_OPPOW: return NUMPOW(v1, v2); ++ case KTAP_OPUNM: return NUMUNM(v1); ++ default: ktap_assert(0); return 0; ++ } ++} ++ ++int ktapc_hexavalue(int c) ++{ ++ if (isdigit(c)) ++ return c - '0'; ++ else ++ return tolower(c) - 'a' + 10; ++} ++ ++int ktapc_str2d(const char *s, size_t len, ktap_number *result) ++{ ++ char *endptr; ++ ++ if (strpbrk(s, "nN")) /* reject 'inf' and 'nan' */ ++ return 0; ++ else ++ *result = (long)strtoul(s, &endptr, 0); ++ ++ if (endptr == s) ++ return 0; /* nothing recognized */ ++ while (isspace((unsigned char)(*endptr))) ++ endptr++; ++ return (endptr == s + len); /* OK if no trailing characters */ ++} ++ ++/* number of chars of a literal string without the ending \0 */ ++#define LL(x) (sizeof(x)/sizeof(char) - 1) ++ ++#define RETS "..." ++#define PRE "[string \"" ++#define POS "\"]" ++ ++#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) ) ++ ++void ktapc_chunkid(char *out, const char *source, size_t bufflen) ++{ ++ size_t l = strlen(source); ++ ++ if (*source == '=') { /* 'literal' source */ ++ if (l <= bufflen) /* small enough? */ ++ memcpy(out, source + 1, l * sizeof(char)); ++ else { /* truncate it */ ++ addstr(out, source + 1, bufflen - 1); ++ *out = '\0'; ++ } ++ } else if (*source == '@') { /* file name */ ++ if (l <= bufflen) /* small enough? */ ++ memcpy(out, source + 1, l * sizeof(char)); ++ else { /* add '...' before rest of name */ ++ addstr(out, RETS, LL(RETS)); ++ bufflen -= LL(RETS); ++ memcpy(out, source + 1 + l - bufflen, bufflen * sizeof(char)); ++ } ++ } else { /* string; format as [string "source"] */ ++ const char *nl = strchr(source, '\n'); /* find first new line (if any) */ ++ addstr(out, PRE, LL(PRE)); /* add prefix */ ++ bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */ ++ if (l < bufflen && nl == NULL) { /* small one-line source? */ ++ addstr(out, source, l); /* keep it */ ++ } else { ++ if (nl != NULL) ++ l = nl - source; /* stop at first newline */ ++ if (l > bufflen) ++ l = bufflen; ++ addstr(out, source, l); ++ addstr(out, RETS, LL(RETS)); ++ } ++ memcpy(out, POS, (LL(POS) + 1) * sizeof(char)); ++ } ++} ++ ++ ++/* ++ * strglobmatch is copyed from perf(linux/tools/perf/util/string.c) ++ */ ++ ++/* Character class matching */ ++static bool __match_charclass(const char *pat, char c, const char **npat) ++{ ++ bool complement = false, ret = true; ++ ++ if (*pat == '!') { ++ complement = true; ++ pat++; ++ } ++ if (*pat++ == c) /* First character is special */ ++ goto end; ++ ++ while (*pat && *pat != ']') { /* Matching */ ++ if (*pat == '-' && *(pat + 1) != ']') { /* Range */ ++ if (*(pat - 1) <= c && c <= *(pat + 1)) ++ goto end; ++ if (*(pat - 1) > *(pat + 1)) ++ goto error; ++ pat += 2; ++ } else if (*pat++ == c) ++ goto end; ++ } ++ if (!*pat) ++ goto error; ++ ret = false; ++ ++end: ++ while (*pat && *pat != ']') /* Searching closing */ ++ pat++; ++ if (!*pat) ++ goto error; ++ *npat = pat + 1; ++ return complement ? !ret : ret; ++ ++error: ++ return false; ++} ++ ++/* Glob/lazy pattern matching */ ++static bool __match_glob(const char *str, const char *pat, bool ignore_space) ++{ ++ while (*str && *pat && *pat != '*') { ++ if (ignore_space) { ++ /* Ignore spaces for lazy matching */ ++ if (isspace(*str)) { ++ str++; ++ continue; ++ } ++ if (isspace(*pat)) { ++ pat++; ++ continue; ++ } ++ } ++ if (*pat == '?') { /* Matches any single character */ ++ str++; ++ pat++; ++ continue; ++ } else if (*pat == '[') /* Character classes/Ranges */ ++ if (__match_charclass(pat + 1, *str, &pat)) { ++ str++; ++ continue; ++ } else ++ return false; ++ else if (*pat == '\\') /* Escaped char match as normal char */ ++ pat++; ++ if (*str++ != *pat++) ++ return false; ++ } ++ /* Check wild card */ ++ if (*pat == '*') { ++ while (*pat == '*') ++ pat++; ++ if (!*pat) /* Tail wild card matches all */ ++ return true; ++ while (*str) ++ if (__match_glob(str++, pat, ignore_space)) ++ return true; ++ } ++ return !*str && !*pat; ++} ++ ++/** ++ * strglobmatch - glob expression pattern matching ++ * @str: the target string to match ++ * @pat: the pattern string to match ++ * ++ * This returns true if the @str matches @pat. @pat can includes wildcards ++ * ('*','?') and character classes ([CHARS], complementation and ranges are ++ * also supported). Also, this supports escape character ('\') to use special ++ * characters as normal character. ++ * ++ * Note: if @pat syntax is broken, this always returns false. ++ */ ++bool strglobmatch(const char *str, const char *pat) ++{ ++ return __match_glob(str, pat, false); ++} ++ ++#define handle_error(str) do { perror(str); exit(-1); } while(0) ++ ++#define KALLSYMS_PATH "/proc/kallsyms" ++/* ++ * read kernel symbol from /proc/kallsyms ++ */ ++int kallsyms_parse(void *arg, ++ int(*process_symbol)(void *arg, const char *name, ++ char type, unsigned long start)) ++{ ++ int ret = 0; ++ FILE *file; ++ char *line = NULL; ++ ++ file = fopen(KALLSYMS_PATH, "r"); ++ if (file == NULL) ++ handle_error("open " KALLSYMS_PATH " failed"); ++ ++ while (!feof(file)) { ++ char *symbol_addr, *symbol_name; ++ char symbol_type; ++ unsigned long start; ++ int line_len; ++ size_t n; ++ ++ line_len = getline(&line, &n, file); ++ if (line_len < 0 || !line) ++ break; ++ ++ line[--line_len] = '\0'; /* \n */ ++ ++ symbol_addr = strtok(line, " \t"); ++ start = strtoul(symbol_addr, NULL, 16); ++ ++ symbol_type = *strtok(NULL, " \t"); ++ symbol_name = strtok(NULL, " \t"); ++ ++ ret = process_symbol(arg, symbol_name, symbol_type, start); ++ if (ret) ++ break; ++ } ++ ++ free(line); ++ fclose(file); ++ ++ return ret; ++} ++ ++struct ksym_addr_t { ++ const char *name; ++ unsigned long addr; ++}; ++ ++static int symbol_cmp(void *arg, const char *name, char type, ++ unsigned long start) ++{ ++ struct ksym_addr_t *base = arg; ++ ++ if (strcmp(base->name, name) == 0) { ++ base->addr = start; ++ return 1; ++ } ++ ++ return 0; ++} ++ ++unsigned long find_kernel_symbol(const char *symbol) ++{ ++ int ret; ++ struct ksym_addr_t arg = { ++ .name = symbol, ++ .addr = 0 ++ }; ++ ++ ret = kallsyms_parse(&arg, symbol_cmp); ++ if (ret < 0 || arg.addr == 0) { ++ fprintf(stderr, "cannot read kernel symbol \"%s\" in %s\n", ++ symbol, KALLSYMS_PATH); ++ exit(EXIT_FAILURE); ++ } ++ ++ return arg.addr; ++} ++ ++ ++#define AVAILABLE_EVENTS_PATH "/sys/kernel/debug/tracing/available_events" ++ ++void list_available_events(const char *match) ++{ ++ FILE *file; ++ char *line = NULL; ++ ++ file = fopen(AVAILABLE_EVENTS_PATH, "r"); ++ if (file == NULL) ++ handle_error("open " AVAILABLE_EVENTS_PATH " failed"); ++ ++ while (!feof(file)) { ++ size_t n; ++ ++ getline(&line, &n, file); ++ ++ if (!match || strglobmatch(line, match)) ++ printf("%s", line); ++ } ++ ++ free(line); ++ fclose(file); ++} ++ +-- +1.7.9.5 + diff --git a/series b/series index 8c25217..cd1d4f6 100644 --- a/series +++ b/series @@ -28,6 +28,14 @@ patches.ltsi/ltsi-makefile-addition.patch # patches.lttng/lttng-2.3.4.patch +############################################################################# +# ktap +# +# 0.4 version of ktap, taken from the upstream: +# https://github.com/ktap/ktap.git +# repo. +# +patches.ktap/ktap-0.4.patch ############################################################################# # Renesas patches