mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-01 08:04:22 +08:00
staging: ktap: remove code from tree
ktap should be merged through the "proper" place in the kernel tree, in the perf tool, not as a stand-alone kernel module in staging. So remove it from here for now so that it can be merged correctly later. Reported-by: Ingo Molnar <mingo@kernel.org> Cc: Jovi Zhangwei <jovi.zhangwei@gmail.com> Cc: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
9908b4f32f
commit
365aa51e11
@ -150,6 +150,4 @@ source "drivers/staging/dgnc/Kconfig"
|
||||
|
||||
source "drivers/staging/dgap/Kconfig"
|
||||
|
||||
source "drivers/staging/ktap/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
@ -67,4 +67,3 @@ obj-$(CONFIG_XILLYBUS) += xillybus/
|
||||
obj-$(CONFIG_DGNC) += dgnc/
|
||||
obj-$(CONFIG_DGAP) += dgap/
|
||||
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
|
||||
obj-$(CONFIG_KTAP) += ktap/
|
||||
|
@ -1,21 +0,0 @@
|
||||
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
|
||||
|
@ -1,101 +0,0 @@
|
||||
|
||||
# Do not instrument the tracer itself:
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
ORIG_CFLAGS := $(KBUILD_CFLAGS)
|
||||
KBUILD_CFLAGS = $(subst -pg,,$(ORIG_CFLAGS))
|
||||
endif
|
||||
|
||||
all: mod ktap
|
||||
|
||||
INTP = interpreter
|
||||
|
||||
LIBDIR = $(INTP)/library
|
||||
|
||||
LIB_OBJS += $(LIBDIR)/baselib.o $(LIBDIR)/kdebug.o $(LIBDIR)/timer.o \
|
||||
$(LIBDIR)/ansilib.o
|
||||
|
||||
INTP_OBJS += $(INTP)/ktap.o $(INTP)/loader.o $(INTP)/object.o \
|
||||
$(INTP)/tstring.o $(INTP)/table.o $(INTP)/vm.o \
|
||||
$(INTP)/opcode.o $(INTP)/strfmt.o $(INTP)/transport.o \
|
||||
$(LIB_OBJS)
|
||||
|
||||
obj-m += ktapvm.o
|
||||
ktapvm-y := $(INTP_OBJS)
|
||||
|
||||
KVERSION ?= $(shell uname -r)
|
||||
KERNEL_SRC ?= /lib/modules/$(KVERSION)/build
|
||||
mod:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules
|
||||
|
||||
modules_install:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules_install
|
||||
|
||||
KTAPC_CFLAGS = -Wall -O2
|
||||
|
||||
UDIR = userspace
|
||||
|
||||
$(UDIR)/lex.o: $(UDIR)/lex.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/parser.o: $(UDIR)/parser.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/code.o: $(UDIR)/code.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/dump.o: $(UDIR)/dump.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/main.o: $(UDIR)/main.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/util.o: $(UDIR)/util.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/ktapio.o: $(UDIR)/ktapio.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/eventdef.o: $(UDIR)/eventdef.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/opcode.o: $(INTP)/opcode.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/table.o: $(INTP)/table.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/tstring.o: $(INTP)/tstring.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/object.o: $(INTP)/object.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
|
||||
KTAPOBJS =
|
||||
KTAPOBJS += $(UDIR)/lex.o
|
||||
KTAPOBJS += $(UDIR)/parser.o
|
||||
KTAPOBJS += $(UDIR)/code.o
|
||||
KTAPOBJS += $(UDIR)/dump.o
|
||||
KTAPOBJS += $(UDIR)/main.o
|
||||
KTAPOBJS += $(UDIR)/util.o
|
||||
KTAPOBJS += $(UDIR)/ktapio.o
|
||||
KTAPOBJS += $(UDIR)/eventdef.o
|
||||
KTAPOBJS += $(UDIR)/opcode.o
|
||||
KTAPOBJS += $(UDIR)/table.o
|
||||
KTAPOBJS += $(UDIR)/tstring.o
|
||||
KTAPOBJS += $(UDIR)/object.o
|
||||
|
||||
ktap: $(KTAPOBJS)
|
||||
$(QUIET_LINK)$(CC) $(KTAPC_CFLAGS) -o $@ $(KTAPOBJS) -lpthread
|
||||
|
||||
KMISC := /lib/modules/$(KVERSION)/ktapvm/
|
||||
|
||||
install: mod ktap
|
||||
install -d $(KMISC)
|
||||
install -m 644 -c *.ko /lib/modules/$(KVERSION)/ktapvm/
|
||||
/sbin/depmod -a
|
||||
|
||||
load:
|
||||
insmod ktapvm.ko
|
||||
|
||||
unload:
|
||||
rmmod ktapvm
|
||||
|
||||
test: FORCE
|
||||
cd test; sh ./run_test.sh; cd -
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
|
||||
$(RM) ktap
|
||||
|
||||
PHONY += FORCE
|
||||
FORCE:
|
||||
|
@ -1,144 +0,0 @@
|
||||
# ktap
|
||||
|
||||
A New Scripting Dynamic Tracing Tool For Linux
|
||||
[www.ktap.org][homepage]
|
||||
|
||||
ktap is a new scripting dynamic tracing tool for Linux,
|
||||
it uses a scripting language and lets users trace the Linux kernel dynamically.
|
||||
ktap is designed to give operational insights with interoperability
|
||||
that allows users to tune, troubleshoot and extend kernel and application.
|
||||
It's similar with Linux Systemtap and Solaris Dtrace.
|
||||
|
||||
ktap have different design principles from Linux mainstream dynamic tracing
|
||||
language in that it's based on bytecode, so it doesn't depend upon GCC,
|
||||
doesn't require compiling kernel module for each script, safe to use in
|
||||
production environment, fulfilling the embedded ecosystem's tracing needs.
|
||||
|
||||
More information can be found at [ktap homepage][homepage].
|
||||
|
||||
[homepage]: http://www.ktap.org
|
||||
|
||||
## Highlights
|
||||
|
||||
* simple but powerful scripting language
|
||||
* register based interpreter (heavily optimized) in Linux kernel
|
||||
* small and lightweight (6KLOC of interpreter)
|
||||
* not depend on gcc for each script running
|
||||
* easy to use in embedded environment without debugging info
|
||||
* support for tracepoint, kprobe, uprobe, function trace, timer, and more
|
||||
* supported in x86, arm, ppc, mips
|
||||
* safety in sandbox
|
||||
|
||||
## Building & Running
|
||||
|
||||
1. Clone ktap from github
|
||||
|
||||
$ git clone http://github.com/ktap/ktap.git
|
||||
|
||||
2. Compiling ktap
|
||||
|
||||
$ cd ktap
|
||||
$ make #generate ktapvm kernel module and ktap binary
|
||||
|
||||
3. Load ktapvm kernel module(make sure debugfs mounted)
|
||||
|
||||
$ make load #need to be root or have sudo access
|
||||
|
||||
4. Running ktap
|
||||
|
||||
$ ./ktap scripts/helloworld.kp
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
1. simplest one-liner command to enable all tracepoints
|
||||
|
||||
ktap -e "trace *:* { print(argevent) }"
|
||||
|
||||
2. syscall tracing on target process
|
||||
|
||||
ktap -e "trace syscalls:* { print(argevent) }" -- ls
|
||||
|
||||
3. function tracing
|
||||
|
||||
ktap -e "trace ftrace:function { print(argevent) }"
|
||||
|
||||
ktap -e "trace ftrace:function /ip==mutex*/ { print(argevent) }"
|
||||
|
||||
4. simple syscall tracing
|
||||
|
||||
trace syscalls:* {
|
||||
print(cpu(), pid(), execname(), argevent)
|
||||
}
|
||||
|
||||
5. syscall tracing in histogram style
|
||||
|
||||
s = {}
|
||||
|
||||
trace syscalls:sys_enter_* {
|
||||
s[argname] += 1
|
||||
}
|
||||
|
||||
trace_end {
|
||||
histogram(s)
|
||||
}
|
||||
|
||||
6. kprobe tracing
|
||||
|
||||
trace probe:do_sys_open dfd=%di fname=%dx flags=%cx mode=+4($stack) {
|
||||
print("entry:", execname(), argevent)
|
||||
}
|
||||
|
||||
trace probe:do_sys_open%return fd=$retval {
|
||||
print("exit:", execname(), argevent)
|
||||
}
|
||||
|
||||
7. uprobe tracing
|
||||
|
||||
trace probe:/lib/libc.so.6:0x000773c0 {
|
||||
print("entry:", execname(), argevent)
|
||||
}
|
||||
|
||||
trace probe:/lib/libc.so.6:0x000773c0%return {
|
||||
print("exit:", execname(), argevent)
|
||||
}
|
||||
|
||||
8. timer
|
||||
|
||||
tick-1ms {
|
||||
printf("time fired on one cpu\n");
|
||||
}
|
||||
|
||||
profile-2s {
|
||||
printf("time fired on every cpu\n");
|
||||
}
|
||||
|
||||
More sample scripts can be found at scripts/ directory.
|
||||
|
||||
## Mailing list
|
||||
|
||||
ktap@freelists.org
|
||||
You can subscribe to ktap mailing list at link (subscribe before posting):
|
||||
http://www.freelists.org/list/ktap
|
||||
|
||||
|
||||
## Copyright and License
|
||||
|
||||
ktap is licensed under GPL v2
|
||||
|
||||
Copyright (C) 2012-2013, Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
All rights reserved.
|
||||
|
||||
|
||||
## Contribution
|
||||
|
||||
ktap is still under active development, so contributions are welcome.
|
||||
You are encouraged to report bugs, provide feedback, send feature request,
|
||||
or hack on it.
|
||||
|
||||
|
||||
## See More
|
||||
|
||||
More info can be found at [documentation][tutorial]
|
||||
[tutorial]: http://www.ktap.org/doc/tutorial.html
|
||||
|
@ -1,552 +0,0 @@
|
||||
% The ktap Tutorial
|
||||
|
||||
# Introduction
|
||||
|
||||
ktap is a new scripting dynamic tracing tool for linux
|
||||
|
||||
ktap is a new scripting dynamic tracing tool for Linux,
|
||||
it uses a scripting language and lets users trace the Linux kernel dynamically.
|
||||
ktap is designed to give operational insights with interoperability
|
||||
that allows users to tune, troubleshoot and extend kernel and application.
|
||||
It's similar with Linux Systemtap and Solaris Dtrace.
|
||||
|
||||
ktap have different design principles from Linux mainstream dynamic tracing
|
||||
language in that it's based on bytecode, so it doesn't depend upon GCC,
|
||||
doesn't require compiling kernel module for each script, safe to use in
|
||||
production environment, fulfilling the embedded ecosystem's tracing needs.
|
||||
|
||||
Highlights features:
|
||||
|
||||
* simple but powerful scripting language
|
||||
* register based interpreter (heavily optimized) in Linux kernel
|
||||
* small and lightweight (6KLOC of interpreter)
|
||||
* not depend on gcc for each script running
|
||||
* easy to use in embedded environment without debugging info
|
||||
* support for tracepoint, kprobe, uprobe, function trace, timer, and more
|
||||
* supported in x86, arm, ppc, mips
|
||||
* safety in sandbox
|
||||
|
||||
|
||||
# Getting started
|
||||
|
||||
Requirements
|
||||
|
||||
* Linux 3.1 or later(Need some kernel patches for kernel earlier than 3.1)
|
||||
* CONFIG_EVENT_TRACING enabled
|
||||
* CONFIG_PERF_EVENTS enabled
|
||||
* CONFIG_DEBUG_FS enabled
|
||||
(make sure debugfs mounted before insmod ktapvm
|
||||
mount debugfs: mount -t debugfs none /sys/kernel/debug/)
|
||||
|
||||
Note that those configuration is always enabled in Linux distribution,
|
||||
like REHL, Fedora, Ubuntu, etc.
|
||||
|
||||
1. Clone ktap from github
|
||||
|
||||
$ git clone http://github.com/ktap/ktap.git
|
||||
|
||||
2. Compiling ktap
|
||||
|
||||
$ cd ktap
|
||||
$ make #generate ktapvm kernel module and ktap binary
|
||||
|
||||
3. Load ktapvm kernel module(make sure debugfs mounted)
|
||||
|
||||
$ make load #need to be root or have sudo access
|
||||
|
||||
4. Running ktap
|
||||
|
||||
$ ./ktap scripts/helloworld.kp
|
||||
|
||||
|
||||
# Language basics
|
||||
|
||||
## Syntax basics
|
||||
|
||||
ktap's syntax is design on the mind of C language syntax friendly,
|
||||
to make it easy scripting by kernel developer.
|
||||
|
||||
1. Variable declaration
|
||||
The biggest syntax differences with C is that ktap is a dynamic typed
|
||||
language, so you won't need add any variable type declaration, just
|
||||
use the variable.
|
||||
|
||||
2. function
|
||||
All functions in ktap should use keyword "function" declaration
|
||||
|
||||
3. comments
|
||||
The comments of ktap is starting from '#', long comments doesn't support now.
|
||||
|
||||
4. others
|
||||
Don't need place any ';' at the ending of statement in ktap.
|
||||
ktap use free syntax style, so you can choose to use the ';' or not.
|
||||
|
||||
ktap use nil as NULL, the result of any number operate on nil is nil.
|
||||
|
||||
ktap don't have array structure, also don't have any pointer operation.
|
||||
|
||||
## Control structures
|
||||
|
||||
ktap if/else is same as C language.
|
||||
|
||||
There have two method of for-loop in ktap:
|
||||
|
||||
for (i = init, limit, step) { body }
|
||||
|
||||
this is same as below in C:
|
||||
|
||||
for (i = init; i < limit; i += step) { body }
|
||||
|
||||
The next for-loop method is:
|
||||
|
||||
for (k, v in pairs(t)) { body } # looping all elements of table
|
||||
|
||||
Note that ktap don't have "continue" keyword, but C does.
|
||||
|
||||
## Date structures
|
||||
|
||||
Associative array is heavily used in ktap, it's also called by table.
|
||||
|
||||
table declaration:
|
||||
|
||||
t = {}
|
||||
|
||||
how to use table:
|
||||
|
||||
t[1] = 1
|
||||
t[1] = "xxx"
|
||||
t["key"] = 10
|
||||
t["key"] = "value"
|
||||
|
||||
for (k, v in pairs(t)) { body } # looping all elements of table
|
||||
|
||||
|
||||
# Built in functions and librarys
|
||||
|
||||
## Built in functions
|
||||
|
||||
**print (...)**
|
||||
Receives any number of arguments, and prints their values,
|
||||
print is not intended for formatted output, but only as a
|
||||
quick way to show a value, typically for debugging.
|
||||
For formatted output, use printf.
|
||||
|
||||
**printf (fmt, ...)**
|
||||
Similar with C printf, use for format string output.
|
||||
|
||||
**pairs (t)**
|
||||
Returns three values: the next function, the table t, and nil,
|
||||
so that the construction
|
||||
for (k,v in pairs(t)) { body }
|
||||
will iterate over all key-value pairs of table t.
|
||||
|
||||
**len (t) /len (s)**
|
||||
If the argument is string, return length of string,
|
||||
if the argument is table, return counts of table pairs.
|
||||
|
||||
**in_interrupt ()**
|
||||
checking is context is interrupt context
|
||||
|
||||
**exit ()**
|
||||
quit ktap executing, similar with exit syscall
|
||||
|
||||
**pid ()**
|
||||
return current process pid
|
||||
|
||||
**execname ()**
|
||||
return current process exec name string
|
||||
|
||||
**cpu ()**
|
||||
return current cpu id
|
||||
|
||||
**arch ()**
|
||||
return machine architecture, like x86, arm, etc.
|
||||
|
||||
**kernel_v ()**
|
||||
return Linux kernel version string, like 3.9, etc.
|
||||
|
||||
**user_string (addr)**
|
||||
Receive userspace address, read string from userspace, return string.
|
||||
|
||||
**histogram (t)**
|
||||
Receive table, output table histogram to user.
|
||||
|
||||
**curr_task_info (offset, fetch_bytes)**
|
||||
fetch value in field offset of task_struct structure, argument fetch_bytes
|
||||
could be 4 or 8, if fetch_bytes is not given, default is 4.
|
||||
|
||||
user may need to get field offset by gdb, for example:
|
||||
gdb vmlinux
|
||||
(gdb)p &(((struct task_struct *)0).prio)
|
||||
|
||||
**print_backtrace ()**
|
||||
print current task stack info
|
||||
|
||||
|
||||
## Librarys
|
||||
|
||||
### Kdebug Library
|
||||
|
||||
**kdebug.probe_by_id (event_ids, eventfun)**
|
||||
|
||||
This function is underly representation of high level tracing primitive.
|
||||
event_ids is the id of all events, it's read from
|
||||
/sys/kernel/debug/tracing/events/$SYS/$EVENT/id
|
||||
|
||||
for multi-events tracing, the event_ids is concatenation of all id, for example:
|
||||
"2 3 4", seperated by blank space.
|
||||
|
||||
The second argument in above examples is a function:
|
||||
function eventfun () { action }
|
||||
|
||||
|
||||
**kdebug.probe_end (endfunc)**
|
||||
|
||||
This function is used for invoking a function when tracing end, it will wait
|
||||
until user press CTRL+C to stop tracing, then ktap will call endfunc function,
|
||||
user could show tracing results in that function, or do other things.
|
||||
|
||||
|
||||
### Timer Library
|
||||
|
||||
|
||||
|
||||
# Linux tracing basics
|
||||
|
||||
tracepoints, probe, timer
|
||||
filters
|
||||
above explaintion
|
||||
Ring buffer
|
||||
|
||||
# Tracing semantics in ktap
|
||||
|
||||
## Tracing block
|
||||
|
||||
**trace EVENTDEF /FILTER/ { ACTION }**
|
||||
|
||||
This is the basic tracing block for ktap, you need to use a specific EVENTDEF
|
||||
string, and own event function.
|
||||
|
||||
EVENTDEF is compatible with perf(see perf-list), with glob match, for example:
|
||||
|
||||
syscalls:* trace all syscalls events
|
||||
syscalls:sys_enter_* trace all syscalls entry events
|
||||
kmem:* trace all kmem related events
|
||||
sched:* trace all sched related events
|
||||
*:* trace all tracepoints in system.
|
||||
|
||||
All events are based on: /sys/kernel/debug/tracing/events/$SYS/$EVENT
|
||||
|
||||
**trace_end { ACTION }**
|
||||
|
||||
This is based on kdebug.probe_end function.
|
||||
|
||||
## Tracing built-in variable
|
||||
|
||||
**argevent**
|
||||
event object, you can print it by: print(argevent), it will print events
|
||||
into human readable string, the result is mostly same as each entry of
|
||||
/sys/kernel/debug/tracing/trace
|
||||
|
||||
**argname**
|
||||
event name, each event have a name associated with it.
|
||||
|
||||
**arg1..9**
|
||||
get argument 1..9 of event object.
|
||||
|
||||
|
||||
## Timer syntax
|
||||
|
||||
**tick-Ns { ACTION }**
|
||||
**tick-Nsec { ACTION }**
|
||||
**tick-Nms { ACTION }**
|
||||
**tick-Nmsec { ACTION }**
|
||||
**tick-Nus { ACTION }**
|
||||
**tick-Nusec { ACTION }**
|
||||
|
||||
**profile-Ns { ACTION }**
|
||||
**profile-Nsec { ACTION }**
|
||||
**profile-Nms { ACTION }**
|
||||
**profile-Nmsec { ACTION }**
|
||||
**profile-Nus { ACTION }**
|
||||
**profile-Nusec { ACTION }**
|
||||
|
||||
architecture overview picture reference(pnp format)
|
||||
one-liners
|
||||
simple event tracing
|
||||
|
||||
# Advanced tracing pattern
|
||||
|
||||
Aggregation/Histogram
|
||||
thread local
|
||||
flame graph
|
||||
|
||||
# Overhead/Performance
|
||||
|
||||
ktap have more fast boot time thant Systemtap(try the helloword script)
|
||||
ktap have little memory usage than Systemtap
|
||||
and some scripts show that ktap have a little overhead then Systemtap
|
||||
(we choosed two scripts to compare, function profile, stack profile.
|
||||
this is not means all scripts in Systemtap have big overhead than ktap)
|
||||
|
||||
|
||||
# FAQ
|
||||
|
||||
**Q: Why use bytecode design?**
|
||||
A: Using bytecode would be a clean and lightweight solution,
|
||||
you don't need gcc toolchain to compile every scripts, all you
|
||||
need is a ktapvm kernel modules and userspace tool called ktap.
|
||||
Since its language virtual machine design, it have great portability,
|
||||
suppose you are working at a multi-arch cluster, if you want to run
|
||||
a tracing script on each board, you won't need cross-compile tracing
|
||||
script onto all board, what you really need to do is use ktap tool
|
||||
to run script just in time.
|
||||
|
||||
Bytecode based design also will make executing more safer, than native code
|
||||
generation.
|
||||
|
||||
Reality already showing that SystemTap is not widely used in embedded Linux,
|
||||
caused by problem of SystemTap's architecture design choice, it's a natural
|
||||
design for Redhat and IBM, because Redhat/IBM is focusing on server area,
|
||||
not embedded area.
|
||||
|
||||
**Q: What's the differences with SystemTap and Dtrace?**
|
||||
A: For SystemTap, the answer is already mentioned at above question,
|
||||
SystemTap use translator design, for trade-off on performance with usability,
|
||||
based on GCC, that's what ktap want to solve.
|
||||
|
||||
For Dtrace, one common design with Dtrace is also use bytecode, so basically
|
||||
Dtrace and ktap is on the same road. There have some projects aim to porting
|
||||
Dtrace from Solaris to Linux, but the process is still on the road, Dtrace
|
||||
is rooted in Solaris, and there have many huge differences between Solaris
|
||||
tracing infrastructure with Linux's.
|
||||
|
||||
Dtrace is based on D language, a language subset of C, it's a restricted
|
||||
language, like without for-looping, for safty use in production system.
|
||||
It seems that Dtrace for Linux only support x86 architecture, not work on
|
||||
powerpc and arm/mips, obviously it's not suit for embedded Linux currently.
|
||||
|
||||
Dtrace use ctf as input for debuginfo handing, compare with vmlinux for
|
||||
SystemTap.
|
||||
|
||||
On the license part, Dtrace is released as CDDL, which is incompatible with
|
||||
GPL(this is why it's impossible to upstream Dtrace into mainline).
|
||||
|
||||
**Q: Why use dynamically typed language? but not statically typed language?**
|
||||
A: It's hard to say which one is more better than other, dynamically typed
|
||||
language bring efficiency and fast prototype production, but loosing type
|
||||
check at compiling phase, and easy to make mistake in runtime, also it's
|
||||
need many runtime checking, In contrast, statically typed language win on
|
||||
programing safety, and performance. Statically language would suit for
|
||||
interoperate with kernel, as kernel is wrote mainly in C, Need to note that
|
||||
SystemTap and Dtrace both is statically language.
|
||||
|
||||
ktap choose dynamically typed language as initial implementation.
|
||||
|
||||
**Q: Why we need ktap for event tracing? There already have a built-in ftrace**
|
||||
A: This also is a common question for all dynamic tracing tool, not only ktap.
|
||||
ktap provide more flexibility than built-in tracing infrastructure. Suppose
|
||||
you need print a global variable when tracepoint hit, or you want print
|
||||
backtrace, even more, you want to store some info into associative array, and
|
||||
display it in histogram style when tracing end, in these case, some of them
|
||||
ftrace can take it, some of them ftrace can not.
|
||||
Overall, ktap provide you with great flexibility to scripting your own trace
|
||||
need.
|
||||
|
||||
**Q: How about the performance? Is ktap slow?**
|
||||
A: ktap is not slow, the bytecode is very high-level, based on lua, the language
|
||||
virtual machine is register-based(compare with stack-based), with little
|
||||
instruction, the table data structure is heavily optimized in ktapvm.
|
||||
ktap use per-cpu allocation in many place, without global locking scheme,
|
||||
it's very fast when executing tracepoint callback.
|
||||
Performance benchmark showing that the overhead of ktap running is nearly
|
||||
10%(store event name into associative array), compare with full speed
|
||||
running without any tracepoint enabled.
|
||||
|
||||
ktap will optimize overhead all the time, hopefully the overhead will
|
||||
decrease to little than 5%, even more.
|
||||
|
||||
**Q: Why not porting a high level language implementation into kernel directly?
|
||||
Like python/JVM?**
|
||||
A: I take serious on the size of vm and memory footprint. Python vm is large,
|
||||
it's not suit to embed into kernel, and python have some functionality
|
||||
which we don't need.
|
||||
|
||||
The bytecode of other high level language is also big, ktap only have 32
|
||||
bytecodes, python/java/erlang have nearly two hundred bytecodes.
|
||||
There also have some problems when porting those language into kernel,
|
||||
userspace programming have many differences with kernel programming,
|
||||
like float numbers, handle sleeping code carefully in kernel, deadloop is
|
||||
not allowed in kernel, multi-thread management, etc.., so it's impossible
|
||||
to porting language implementation into kernel with little adaption work.
|
||||
|
||||
**Q: What's the status of ktap now?**
|
||||
A: Basically it works on x86-32, x86-64, powerpc, arm, it also could work for
|
||||
other hardware architecture, but not proven yet(I don't have enough hardware
|
||||
to test)
|
||||
If you found some bug, fix it on you own programming skill, or report to me.
|
||||
|
||||
**Q: How to hack ktap? I want to write some extensions onto ktap.**
|
||||
A: welcome hacking.
|
||||
You can write your own library to fulfill your specific need,
|
||||
you can write any script as you want.
|
||||
|
||||
**Q: What's the plan of ktap? any roadmap?**
|
||||
A: the current plan is deliver stable ktapvm kernel modules, more ktap script,
|
||||
and bugfix.
|
||||
|
||||
|
||||
# References
|
||||
|
||||
* [Linux Performance Analysis and Tools][LPAT]
|
||||
* [Dtrace Blog][dtraceblog]
|
||||
* [Dtrace User Guide][dug]
|
||||
* [LWN: ktap -- yet another kernel tracer][lwn]
|
||||
* [ktap introduction in LinuxCon Japan 2013][lcj]
|
||||
|
||||
[LPAT]: http://www.brendangregg.com/Slides/SCaLE_Linux_Performance2013.pdf
|
||||
[dtraceblog]: http://dtrace.org/blogs/
|
||||
[dug]: http://docs.huihoo.com/opensolaris/dtrace-user-guide/html/index.html
|
||||
[lwn]: http://lwn.net/Articles/551314/
|
||||
[lcj]: http://events.linuxfoundation.org/sites/events/files/lcjpcojp13_zhangwei.pdf
|
||||
|
||||
|
||||
# History
|
||||
|
||||
* ktap was invented at 2002
|
||||
* First RFC sent to LKML at 2012.12.31
|
||||
* The code was released in github at 2013.01.18
|
||||
* ktap released v0.1 at 2013.05.21
|
||||
* ktap released v0.2 at 2013.07.31
|
||||
|
||||
For more release info, please look at RELEASES.txt in project root directory.
|
||||
|
||||
# Sample scripts
|
||||
|
||||
1. simplest one-liner command to enable all tracepoints
|
||||
|
||||
ktap -e "trace *:* { print(argevent) }"
|
||||
|
||||
2. syscall tracing on target process
|
||||
|
||||
ktap -e "trace syscalls:* { print(argevent) }" -- ls
|
||||
|
||||
3. function tracing
|
||||
|
||||
ktap -e "trace ftrace:function { print(argevent) }"
|
||||
|
||||
ktap -e "trace ftrace:function /ip==mutex*/ { print(argevent) }"
|
||||
|
||||
4. simple syscall tracing
|
||||
|
||||
trace syscalls:* {
|
||||
print(cpu(), pid(), execname(), argevent)
|
||||
}
|
||||
|
||||
5. syscall tracing in histogram style
|
||||
|
||||
s = {}
|
||||
|
||||
trace syscalls:sys_enter_* {
|
||||
s[argname] += 1
|
||||
}
|
||||
|
||||
trace_end {
|
||||
histogram(s)
|
||||
}
|
||||
|
||||
6. kprobe tracing
|
||||
|
||||
trace probe:do_sys_open dfd=%di fname=%dx flags=%cx mode=+4($stack) {
|
||||
print("entry:", execname(), argevent)
|
||||
}
|
||||
|
||||
trace probe:do_sys_open%return fd=$retval {
|
||||
print("exit:", execname(), argevent)
|
||||
}
|
||||
|
||||
7. uprobe tracing
|
||||
|
||||
trace probe:/lib/libc.so.6:0x000773c0 {
|
||||
print("entry:", execname(), argevent)
|
||||
}
|
||||
|
||||
trace probe:/lib/libc.so.6:0x000773c0%return {
|
||||
print("exit:", execname(), argevent)
|
||||
}
|
||||
|
||||
8. timer
|
||||
|
||||
tick-1ms {
|
||||
printf("time fired on one cpu\n");
|
||||
}
|
||||
|
||||
profile-2s {
|
||||
printf("time fired on every cpu\n");
|
||||
}
|
||||
|
||||
More sample scripts can be found at scripts/ directory.
|
||||
|
||||
|
||||
# Appendix
|
||||
|
||||
Here is the complete syntax of ktap in extended BNF.
|
||||
(based on lua syntax: http://www.lua.org/manual/5.1/manual.html#5.1)
|
||||
|
||||
chunk ::= {stat [';']} [laststat [';']
|
||||
|
||||
block ::= chunk
|
||||
|
||||
stat ::= varlist '=' explist |
|
||||
functioncall |
|
||||
{ block } |
|
||||
while exp { block } |
|
||||
repeat block until exp |
|
||||
if exp { block {elseif exp { block }} [else block] } |
|
||||
for Name '=' exp ',' exp [',' exp] { block } |
|
||||
for namelist in explist { block } |
|
||||
function funcname funcbody |
|
||||
local function Name funcbody |
|
||||
local namelist ['=' explist]
|
||||
|
||||
laststat ::= return [explist] | break
|
||||
|
||||
funcname ::= Name {'.' Name} [':' Name]
|
||||
|
||||
varlist ::= var {',' var}
|
||||
|
||||
var ::= Name | prefixexp '[' exp ']'| prefixexp '.' Name
|
||||
|
||||
namelist ::= Name {',' Name}
|
||||
|
||||
explist ::= {exp ',' exp
|
||||
|
||||
exp ::= nil | false | true | Number | String | '...' | function |
|
||||
prefixexp | tableconstructor | exp binop exp | unop exp
|
||||
|
||||
prefixexp ::= var | functioncall | '(' exp ')'
|
||||
|
||||
functioncall ::= prefixexp args | prefixexp ':' Name args
|
||||
|
||||
args ::= '(' [explist] ')' | tableconstructor | String
|
||||
|
||||
function ::= function funcbody
|
||||
|
||||
funcbody ::= '(' [parlist] ')' { block }
|
||||
|
||||
parlist ::= namelist [',' '...'] | '...'
|
||||
|
||||
tableconstructor ::= '{' [fieldlist] '}'
|
||||
|
||||
fieldlist ::= field {fieldsep field} [fieldsep]
|
||||
|
||||
field ::= '[' exp ']' '=' exp | Name '=' exp | exp
|
||||
|
||||
fieldsep ::= ',' | ';'
|
||||
|
||||
binop ::= '+' | '-' | '*' | '/' | '^' | '%' | '..' |
|
||||
'<' | '<=' | '>' | '>=' | '==' | '!=' |
|
||||
and | or
|
||||
|
||||
unop ::= '-'
|
||||
|
@ -1,169 +0,0 @@
|
||||
#ifndef __KTAP_H__
|
||||
#define __KTAP_H__
|
||||
|
||||
#include "ktap_types.h"
|
||||
#include "ktap_opcodes.h"
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/trace_seq.h>
|
||||
|
||||
typedef struct ktap_Reg {
|
||||
const char *name;
|
||||
ktap_cfunction func;
|
||||
} ktap_Reg;
|
||||
|
||||
struct ktap_probe_event {
|
||||
struct list_head list;
|
||||
struct perf_event *perf;
|
||||
ktap_state *ks;
|
||||
ktap_closure *cl;
|
||||
};
|
||||
|
||||
/* this structure allocate on stack */
|
||||
struct ktap_event {
|
||||
struct ktap_probe_event *pevent;
|
||||
struct ftrace_event_call *call;
|
||||
struct trace_entry *entry;
|
||||
int entry_size;
|
||||
struct pt_regs *regs;
|
||||
};
|
||||
|
||||
enum {
|
||||
KTAP_PERCPU_DATA_STATE,
|
||||
KTAP_PERCPU_DATA_STACK,
|
||||
KTAP_PERCPU_DATA_BUFFER,
|
||||
KTAP_PERCPU_DATA_BUFFER2,
|
||||
KTAP_PERCPU_DATA_BTRACE,
|
||||
|
||||
KTAP_PERCPU_DATA_MAX
|
||||
};
|
||||
|
||||
#define KTAP_PERCPU_BUFFER_SIZE (3 * PAGE_SIZE)
|
||||
|
||||
int gettimeofday_us(void);
|
||||
ktap_state *kp_newstate(struct ktap_parm *parm, struct dentry *dir);
|
||||
void kp_exit(ktap_state *ks);
|
||||
void kp_final_exit(ktap_state *ks);
|
||||
ktap_state *kp_newthread(ktap_state *mainthread);
|
||||
void kp_exitthread(ktap_state *ks);
|
||||
ktap_closure *kp_load(ktap_state *ks, unsigned char *buff);
|
||||
void kp_call(ktap_state *ks, StkId func, int nresults);
|
||||
void kp_optimize_code(ktap_state *ks, int level, ktap_proto *f);
|
||||
void kp_register_lib(ktap_state *ks, const char *libname, const ktap_Reg *funcs);
|
||||
void *kp_percpu_data(int type);
|
||||
|
||||
void kp_init_baselib(ktap_state *ks);
|
||||
void kp_init_oslib(ktap_state *ks);
|
||||
void kp_init_kdebuglib(ktap_state *ks);
|
||||
void kp_init_timerlib(ktap_state *ks);
|
||||
void kp_init_ansilib(ktap_state *ks);
|
||||
|
||||
int kp_probe_init(ktap_state *ks);
|
||||
void kp_probe_exit(ktap_state *ks);
|
||||
|
||||
void kp_perf_event_register(ktap_state *ks, struct perf_event_attr *attr,
|
||||
struct task_struct *task, char *filter,
|
||||
ktap_closure *cl);
|
||||
|
||||
void kp_event_getarg(ktap_state *ks, ktap_value *ra, int n);
|
||||
void kp_event_tostring(ktap_state *ks, struct trace_seq *seq);
|
||||
|
||||
int kp_strfmt(ktap_state *ks, struct trace_seq *seq);
|
||||
|
||||
void kp_transport_write(ktap_state *ks, const void *data, size_t length);
|
||||
void kp_transport_event_write(ktap_state *ks, struct ktap_event *e);
|
||||
void kp_transport_print_backtrace(ktap_state *ks);
|
||||
void *kp_transport_reserve(ktap_state *ks, size_t length);
|
||||
void kp_transport_exit(ktap_state *ks);
|
||||
int kp_transport_init(ktap_state *ks, struct dentry *dir);
|
||||
|
||||
void kp_exit_timers(ktap_state *ks);
|
||||
|
||||
extern int kp_max_exec_count;
|
||||
|
||||
/* get from kernel/trace/trace.h */
|
||||
static __always_inline int trace_get_context_bit(void)
|
||||
{
|
||||
int bit;
|
||||
|
||||
if (in_interrupt()) {
|
||||
if (in_nmi())
|
||||
bit = 0;
|
||||
else if (in_irq())
|
||||
bit = 1;
|
||||
else
|
||||
bit = 2;
|
||||
} else
|
||||
bit = 3;
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
/* use a special timer context kp_state instead use this recursion approach? */
|
||||
DECLARE_PER_CPU(int, kp_recursion_context[PERF_NR_CONTEXTS]);
|
||||
|
||||
static __always_inline int get_recursion_context(void)
|
||||
{
|
||||
int rctx = trace_get_context_bit();
|
||||
|
||||
if (__this_cpu_read(kp_recursion_context[rctx]))
|
||||
return -1;
|
||||
|
||||
__this_cpu_write(kp_recursion_context[rctx], true);
|
||||
barrier();
|
||||
|
||||
return rctx;
|
||||
}
|
||||
|
||||
static inline void put_recursion_context(int rctx)
|
||||
{
|
||||
barrier();
|
||||
__this_cpu_write(kp_recursion_context[rctx], false);
|
||||
}
|
||||
|
||||
|
||||
extern unsigned int kp_stub_exit_instr;
|
||||
|
||||
static inline void set_next_as_exit(ktap_state *ks)
|
||||
{
|
||||
ktap_callinfo *ci;
|
||||
|
||||
ci = ks->ci;
|
||||
if (!ci)
|
||||
return;
|
||||
|
||||
ci->u.l.savedpc = &kp_stub_exit_instr;
|
||||
|
||||
/* See precall, ci changed to ci->prev after invoke C function */
|
||||
if (ci->prev) {
|
||||
ci = ci->prev;
|
||||
ci->u.l.savedpc = &kp_stub_exit_instr;
|
||||
}
|
||||
}
|
||||
|
||||
#define kp_verbose_printf(ks, ...) \
|
||||
if (G(ks)->parm->verbose) \
|
||||
kp_printf(ks, "[verbose] "__VA_ARGS__);
|
||||
|
||||
/* get argument operation macro */
|
||||
#define kp_arg(ks, n) ((ks)->ci->func + (n))
|
||||
#define kp_arg_nr(ks) ((int)(ks->top - (ks->ci->func + 1)))
|
||||
|
||||
#define kp_arg_check(ks, narg, type) \
|
||||
do { \
|
||||
if (unlikely(ttypenv(kp_arg(ks, narg)) != type)) { \
|
||||
kp_error(ks, "wrong type of argument %d\n", narg);\
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 5, 0)
|
||||
#define SPRINT_SYMBOL sprint_symbol_no_offset
|
||||
#else
|
||||
#define SPRINT_SYMBOL sprint_symbol
|
||||
#endif
|
||||
|
||||
#endif /* __KTAP_H__ */
|
@ -1,240 +0,0 @@
|
||||
#ifndef __KTAP_BYTECODE_H__
|
||||
#define __KTAP_BYTECODE_H__
|
||||
|
||||
|
||||
/* opcode is copied from lua initially */
|
||||
|
||||
typedef enum {
|
||||
/*----------------------------------------------------------------------
|
||||
* name args description
|
||||
* ------------------------------------------------------------------------*/
|
||||
OP_MOVE,/* A B R(A) := R(B) */
|
||||
OP_LOADK,/* A Bx R(A) := Kst(Bx) */
|
||||
OP_LOADKX,/* A R(A) := Kst(extra arg) */
|
||||
OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
|
||||
OP_LOADNIL,/* A B R(A), R(A+1), ..., R(A+B) := nil */
|
||||
OP_GETUPVAL,/* A B R(A) := UpValue[B] */
|
||||
|
||||
OP_GETTABUP,/* A B C R(A) := UpValue[B][RK(C)] */
|
||||
OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
|
||||
|
||||
OP_SETTABUP,/* A B C UpValue[A][RK(B)] := RK(C) */
|
||||
OP_SETTABUP_INCR,/* A B C UpValue[A][RK(B)] += RK(C) */
|
||||
OP_SETUPVAL,/* A B UpValue[B] := R(A) */
|
||||
OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
|
||||
OP_SETTABLE_INCR,/* A B C R(A)[RK(B)] += RK(C) */
|
||||
|
||||
OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
|
||||
|
||||
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
|
||||
|
||||
OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
|
||||
OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
|
||||
OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
|
||||
OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
|
||||
OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
|
||||
OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
|
||||
OP_UNM,/* A B R(A) := -R(B) */
|
||||
OP_NOT,/* A B R(A) := not R(B) */
|
||||
OP_LEN,/* A B R(A) := length of R(B) */
|
||||
|
||||
OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
|
||||
|
||||
OP_JMP,/* A sBx pc+=sBx; if (A) close all upvalues >= R(A) + 1 */
|
||||
OP_EQ,/* A B C if ((RK(B) == RK(C)) != A) then pc++ */
|
||||
OP_LT,/* A B C if ((RK(B) < RK(C)) != A) then pc++ */
|
||||
OP_LE,/* A B C if ((RK(B) <= RK(C)) != A) then pc++ */
|
||||
|
||||
OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
|
||||
OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
|
||||
|
||||
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
|
||||
|
||||
OP_FORLOOP,/* A sBx R(A)+=R(A+2);
|
||||
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
|
||||
OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
|
||||
|
||||
OP_TFORCALL,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
|
||||
OP_TFORLOOP,/* A sBx if R(A+1) != nil then { R(A)=R(A+1); pc += sBx }*/
|
||||
|
||||
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
||||
|
||||
OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx]) */
|
||||
|
||||
OP_VARARG,/* A B R(A), R(A+1), ..., R(A+B-2) = vararg */
|
||||
|
||||
OP_EXTRAARG,/* Ax extra (larger) argument for previous opcode */
|
||||
|
||||
OP_EVENT,/* A B C R(A) := R(B)[C] */
|
||||
|
||||
OP_EVENTNAME, /* A R(A) = event_name() */
|
||||
|
||||
OP_EVENTARG,/* A B R(A) := event_arg(B)*/
|
||||
|
||||
OP_LOAD_GLOBAL,/* A B C R(A) := R(B)[C] */
|
||||
|
||||
OP_EXIT,
|
||||
|
||||
} OpCode;
|
||||
|
||||
|
||||
#define NUM_OPCODES ((int)OP_LOAD_GLOBAL + 1)
|
||||
|
||||
|
||||
enum OpMode {iABC, iABx, iAsBx, iAx}; /* basic instruction format */
|
||||
|
||||
|
||||
/*
|
||||
* ** size and position of opcode arguments.
|
||||
* */
|
||||
#define SIZE_C 9
|
||||
#define SIZE_B 9
|
||||
#define SIZE_Bx (SIZE_C + SIZE_B)
|
||||
#define SIZE_A 8
|
||||
#define SIZE_Ax (SIZE_C + SIZE_B + SIZE_A)
|
||||
|
||||
#define SIZE_OP 6
|
||||
|
||||
#define POS_OP 0
|
||||
#define POS_A (POS_OP + SIZE_OP)
|
||||
#define POS_C (POS_A + SIZE_A)
|
||||
#define POS_B (POS_C + SIZE_C)
|
||||
#define POS_Bx POS_C
|
||||
#define POS_Ax POS_A
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ** limits for opcode arguments.
|
||||
* ** we use (signed) int to manipulate most arguments,
|
||||
* ** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
|
||||
* */
|
||||
#define MAXARG_Bx ((1<<SIZE_Bx)-1)
|
||||
#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */
|
||||
|
||||
#define MAXARG_Ax ((1<<SIZE_Ax)-1)
|
||||
|
||||
#define MAXARG_A ((1<<SIZE_A)-1)
|
||||
#define MAXARG_B ((1<<SIZE_B)-1)
|
||||
#define MAXARG_C ((1<<SIZE_C)-1)
|
||||
|
||||
|
||||
/* creates a mask with `n' 1 bits at position `p' */
|
||||
#define MASK1(n,p) ((~((~(ktap_instruction)0)<<(n)))<<(p))
|
||||
|
||||
/* creates a mask with `n' 0 bits at position `p' */
|
||||
#define MASK0(n,p) (~MASK1(n,p))
|
||||
|
||||
/*
|
||||
* ** the following macros help to manipulate instructions
|
||||
* */
|
||||
|
||||
#define GET_OPCODE(i) ((OpCode)((i)>>POS_OP) & MASK1(SIZE_OP,0))
|
||||
#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
|
||||
((((ktap_instruction)o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
|
||||
|
||||
#define getarg(i,pos,size) ((int)((i)>>pos) & MASK1(size,0))
|
||||
#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \
|
||||
((((ktap_instruction)v)<<pos)&MASK1(size,pos))))
|
||||
|
||||
#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
|
||||
#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
|
||||
|
||||
#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
|
||||
#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
|
||||
|
||||
#define GETARG_B(i) getarg(i, POS_B, SIZE_B)
|
||||
#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B)
|
||||
|
||||
#define GETARG_C(i) getarg(i, POS_C, SIZE_C)
|
||||
#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C)
|
||||
|
||||
#define GETARG_Bx(i) getarg(i, POS_Bx, SIZE_Bx)
|
||||
#define SETARG_Bx(i,v) setarg(i, v, POS_Bx, SIZE_Bx)
|
||||
|
||||
#define GETARG_Ax(i) getarg(i, POS_Ax, SIZE_Ax)
|
||||
#define SETARG_Ax(i,v) setarg(i, v, POS_Ax, SIZE_Ax)
|
||||
|
||||
#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx)
|
||||
#define SETARG_sBx(i,b) SETARG_Bx((i), (unsigned int)(b)+MAXARG_sBx)
|
||||
|
||||
#define CREATE_ABC(o,a,b,c) (((ktap_instruction)(o))<<POS_OP) \
|
||||
| (((ktap_instruction)(a))<<POS_A) \
|
||||
| (((ktap_instruction)(b))<<POS_B) \
|
||||
| (((ktap_instruction)(c))<<POS_C)
|
||||
|
||||
#define CREATE_ABx(o,a,bc) (((ktap_instruction)(o))<<POS_OP) \
|
||||
| (((ktap_instruction)(a))<<POS_A) \
|
||||
| (((ktap_instruction)(bc))<<POS_Bx)
|
||||
|
||||
#define CREATE_Ax(o,a) (((ktap_instruction)(o))<<POS_OP) \
|
||||
| (((ktap_instruction)(a))<<POS_Ax)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ** Macros to operate RK indices
|
||||
* */
|
||||
|
||||
/* this bit 1 means constant (0 means register) */
|
||||
#define BITRK (1 << (SIZE_B - 1))
|
||||
|
||||
/* test whether value is a constant */
|
||||
#define ISK(x) ((x) & BITRK)
|
||||
|
||||
/* gets the index of the constant */
|
||||
#define INDEXK(r) ((int)(r) & ~BITRK)
|
||||
|
||||
#define MAXINDEXRK (BITRK - 1)
|
||||
|
||||
/* code a constant index as a RK value */
|
||||
#define RKASK(x) ((x) | BITRK)
|
||||
|
||||
|
||||
/*
|
||||
* ** invalid register that fits in 8 bits
|
||||
* */
|
||||
#define NO_REG MAXARG_A
|
||||
|
||||
|
||||
/*
|
||||
* ** R(x) - register
|
||||
* ** Kst(x) - constant (in constant table)
|
||||
* ** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
|
||||
* */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ** masks for instruction properties. The format is:
|
||||
* ** bits 0-1: op mode
|
||||
* ** bits 2-3: C arg mode
|
||||
* ** bits 4-5: B arg mode
|
||||
* ** bit 6: instruction set register A
|
||||
* ** bit 7: operator is a test (next instruction must be a jump)
|
||||
* */
|
||||
|
||||
enum OpArgMask {
|
||||
OpArgN, /* argument is not used */
|
||||
OpArgU, /* argument is used */
|
||||
OpArgR, /* argument is a register or a jump offset */
|
||||
OpArgK /* argument is a constant or register/constant */
|
||||
};
|
||||
|
||||
extern const u8 ktap_opmodes[NUM_OPCODES];
|
||||
|
||||
#define getOpMode(m) ((enum OpMode)ktap_opmodes[m] & 3)
|
||||
#define getBMode(m) ((enum OpArgMask)(ktap_opmodes[m] >> 4) & 3)
|
||||
#define getCMode(m) ((enum OpArgMask)(ktap_opmodes[m] >> 2) & 3)
|
||||
#define testAMode(m) (ktap_opmodes[m] & (1 << 6))
|
||||
#define testTMode(m) (ktap_opmodes[m] & (1 << 7))
|
||||
|
||||
|
||||
/* number of list items to accumulate before a SETLIST instruction */
|
||||
#define LFIELDS_PER_FLUSH 50
|
||||
|
||||
extern const char *const ktap_opnames[NUM_OPCODES + 1];
|
||||
|
||||
#endif /* __KTAP_BYTECODE_H__ */
|
@ -1,674 +0,0 @@
|
||||
#ifndef __KTAP_TYPES_H__
|
||||
#define __KTAP_TYPES_H__
|
||||
|
||||
/* opcode is copied from lua initially */
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/wait.h>
|
||||
#else
|
||||
typedef char u8;
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
typedef struct ktap_parm {
|
||||
char *trunk; /* __user */
|
||||
int trunk_len;
|
||||
int argc;
|
||||
char **argv; /* __user */
|
||||
int verbose;
|
||||
int trace_pid;
|
||||
int workload;
|
||||
int trace_cpu;
|
||||
int print_timestamp;
|
||||
} ktap_parm;
|
||||
|
||||
/*
|
||||
* Ioctls that can be done on a ktap fd:
|
||||
* todo: use _IO macro in include/uapi/asm-generic/ioctl.h
|
||||
*/
|
||||
#define KTAP_CMD_IOC_VERSION ('$' + 0)
|
||||
#define KTAP_CMD_IOC_RUN ('$' + 1)
|
||||
#define KTAP_CMD_IOC_EXIT ('$' + 3)
|
||||
|
||||
#define KTAP_ENV "_ENV"
|
||||
|
||||
#define KTAP_VERSION_MAJOR "0"
|
||||
#define KTAP_VERSION_MINOR "2"
|
||||
|
||||
#define KTAP_VERSION "ktap " KTAP_VERSION_MAJOR "." KTAP_VERSION_MINOR
|
||||
#define KTAP_AUTHOR "Jovi Zhangwei <jovi.zhangwei@gmail.com>"
|
||||
#define KTAP_COPYRIGHT KTAP_VERSION " Copyright (C) 2012-2013, " KTAP_AUTHOR
|
||||
|
||||
#define MYINT(s) (s[0] - '0')
|
||||
#define VERSION (MYINT(KTAP_VERSION_MAJOR) * 16 + MYINT(KTAP_VERSION_MINOR))
|
||||
#define FORMAT 0 /* this is the official format */
|
||||
|
||||
#define KTAP_SIGNATURE "\033ktap"
|
||||
|
||||
/* data to catch conversion errors */
|
||||
#define KTAPC_TAIL "\x19\x93\r\n\x1a\n"
|
||||
|
||||
/* size in bytes of header of binary files */
|
||||
#define KTAPC_HEADERSIZE (sizeof(KTAP_SIGNATURE) - sizeof(char) + 2 + \
|
||||
6 + sizeof(KTAPC_TAIL) - sizeof(char))
|
||||
|
||||
typedef int ktap_instruction;
|
||||
|
||||
typedef union ktap_gcobject ktap_gcobject;
|
||||
|
||||
#define CommonHeader ktap_gcobject *next; u8 tt;
|
||||
|
||||
struct ktap_state;
|
||||
typedef int (*ktap_cfunction) (struct ktap_state *ks);
|
||||
|
||||
typedef union ktap_string {
|
||||
int dummy; /* ensures maximum alignment for strings */
|
||||
struct {
|
||||
CommonHeader;
|
||||
u8 extra; /* reserved words for short strings; "has hash" for longs */
|
||||
unsigned int hash;
|
||||
size_t len; /* number of characters in string */
|
||||
} tsv;
|
||||
} ktap_string;
|
||||
|
||||
#define getstr(ts) (const char *)((ts) + 1)
|
||||
#define eqshrstr(a,b) ((a) == (b))
|
||||
|
||||
#define svalue(o) getstr(rawtsvalue(o))
|
||||
|
||||
|
||||
union _ktap_value {
|
||||
ktap_gcobject *gc; /* collectable objects */
|
||||
void *p; /* light userdata */
|
||||
int b; /* booleans */
|
||||
ktap_cfunction f; /* light C functions */
|
||||
long n; /* numbers */
|
||||
};
|
||||
|
||||
|
||||
typedef struct ktap_value {
|
||||
union _ktap_value val;
|
||||
int type;
|
||||
} ktap_value;
|
||||
|
||||
typedef ktap_value * StkId;
|
||||
|
||||
|
||||
|
||||
typedef union ktap_udata {
|
||||
struct {
|
||||
CommonHeader;
|
||||
size_t len; /* number of bytes */
|
||||
} uv;
|
||||
} ktap_udata;
|
||||
|
||||
/*
|
||||
* Description of an upvalue for function prototypes
|
||||
*/
|
||||
typedef struct ktap_upvaldesc {
|
||||
ktap_string *name; /* upvalue name (for debug information) */
|
||||
u8 instack; /* whether it is in stack */
|
||||
u8 idx; /* index of upvalue (in stack or in outer function's list) */
|
||||
} ktap_upvaldesc;
|
||||
|
||||
/*
|
||||
* Description of a local variable for function prototypes
|
||||
* (used for debug information)
|
||||
*/
|
||||
typedef struct ktap_locvar {
|
||||
ktap_string *varname;
|
||||
int startpc; /* first point where variable is active */
|
||||
int endpc; /* first point where variable is dead */
|
||||
} ktap_locvar;
|
||||
|
||||
|
||||
typedef struct ktap_upval {
|
||||
CommonHeader;
|
||||
ktap_value *v; /* points to stack or to its own value */
|
||||
union {
|
||||
ktap_value value; /* the value (when closed) */
|
||||
struct { /* double linked list (when open) */
|
||||
struct ktap_upval *prev;
|
||||
struct ktap_upval *next;
|
||||
} l;
|
||||
} u;
|
||||
} ktap_upval;
|
||||
|
||||
|
||||
#define KTAP_STACK_MAX_ENTRIES 10
|
||||
|
||||
typedef struct ktap_btrace {
|
||||
CommonHeader;
|
||||
unsigned int nr_entries;
|
||||
unsigned long entries[KTAP_STACK_MAX_ENTRIES];
|
||||
} ktap_btrace;
|
||||
|
||||
#define ktap_closure_header \
|
||||
CommonHeader; u8 nupvalues; ktap_gcobject *gclist
|
||||
|
||||
typedef struct ktap_cclosure {
|
||||
ktap_closure_header;
|
||||
ktap_cfunction f;
|
||||
ktap_value upvalue[1]; /* list of upvalues */
|
||||
} ktap_cclosure;
|
||||
|
||||
|
||||
typedef struct ktap_lclosure {
|
||||
ktap_closure_header;
|
||||
struct ktap_proto *p;
|
||||
struct ktap_upval *upvals[1]; /* list of upvalues */
|
||||
} ktap_lclosure;
|
||||
|
||||
|
||||
typedef struct ktap_closure {
|
||||
struct ktap_cclosure c;
|
||||
struct ktap_lclosure l;
|
||||
} ktap_closure;
|
||||
|
||||
|
||||
typedef struct ktap_proto {
|
||||
CommonHeader;
|
||||
ktap_value *k; /* constants used by the function */
|
||||
ktap_instruction *code;
|
||||
struct ktap_proto **p; /* functions defined inside the function */
|
||||
int *lineinfo; /* map from opcodes to source lines (debug information) */
|
||||
struct ktap_locvar *locvars; /* information about local variables (debug information) */
|
||||
struct ktap_upvaldesc *upvalues; /* upvalue information */
|
||||
ktap_closure *cache; /* last created closure with this prototype */
|
||||
ktap_string *source; /* used for debug information */
|
||||
int sizeupvalues; /* size of 'upvalues' */
|
||||
int sizek; /* size of `k' */
|
||||
int sizecode;
|
||||
int sizelineinfo;
|
||||
int sizep; /* size of `p' */
|
||||
int sizelocvars;
|
||||
int linedefined;
|
||||
int lastlinedefined;
|
||||
u8 numparams; /* number of fixed parameters */
|
||||
u8 is_vararg;
|
||||
u8 maxstacksize; /* maximum stack used by this function */
|
||||
} ktap_proto;
|
||||
|
||||
|
||||
/*
|
||||
* information about a call
|
||||
*/
|
||||
typedef struct ktap_callinfo {
|
||||
StkId func; /* function index in the stack */
|
||||
StkId top; /* top for this function */
|
||||
struct ktap_callinfo *prev, *next; /* dynamic call link */
|
||||
short nresults; /* expected number of results from this function */
|
||||
u8 callstatus;
|
||||
int extra;
|
||||
union {
|
||||
struct { /* only for Lua functions */
|
||||
StkId base; /* base for this function */
|
||||
const unsigned int *savedpc;
|
||||
} l;
|
||||
struct { /* only for C functions */
|
||||
int ctx; /* context info. in case of yields */
|
||||
u8 status;
|
||||
} c;
|
||||
} u;
|
||||
} ktap_callinfo;
|
||||
|
||||
|
||||
/*
|
||||
* ktap_tables
|
||||
*/
|
||||
typedef union ktap_tkey {
|
||||
struct {
|
||||
union _ktap_value value_;
|
||||
int tt_;
|
||||
struct ktap_tnode *next; /* for chaining */
|
||||
} nk;
|
||||
ktap_value tvk;
|
||||
} ktap_tkey;
|
||||
|
||||
|
||||
typedef struct ktap_tnode {
|
||||
ktap_value i_val;
|
||||
ktap_tkey i_key;
|
||||
} ktap_tnode;
|
||||
|
||||
|
||||
typedef struct ktap_table {
|
||||
CommonHeader;
|
||||
#ifdef __KERNEL__
|
||||
arch_spinlock_t lock;
|
||||
#endif
|
||||
u8 flags; /* 1<<p means tagmethod(p) is not present */
|
||||
u8 lsizenode; /* log2 of size of `node' array */
|
||||
int sizearray; /* size of `array' array */
|
||||
ktap_value *array; /* array part */
|
||||
ktap_tnode *node;
|
||||
ktap_tnode *lastfree; /* any free position is before this position */
|
||||
ktap_gcobject *gclist;
|
||||
} ktap_table;
|
||||
|
||||
#define lmod(s,size) ((int)((s) & ((size)-1)))
|
||||
|
||||
enum AGGREGATION_TYPE {
|
||||
AGGREGATION_TYPE_COUNT,
|
||||
AGGREGATION_TYPE_MAX,
|
||||
AGGREGATION_TYPE_MIN,
|
||||
AGGREGATION_TYPE_SUM,
|
||||
AGGREGATION_TYPE_AVG
|
||||
};
|
||||
|
||||
typedef struct ktap_aggrtable {
|
||||
CommonHeader;
|
||||
ktap_table **pcpu_tbl;
|
||||
ktap_gcobject *gclist;
|
||||
} ktap_aggrtable;
|
||||
|
||||
typedef struct ktap_aggraccval {
|
||||
CommonHeader;
|
||||
int type;
|
||||
int val;
|
||||
int more;
|
||||
} ktap_aggraccval;
|
||||
|
||||
typedef struct ktap_stringtable {
|
||||
ktap_gcobject **hash;
|
||||
int nuse;
|
||||
int size;
|
||||
} ktap_stringtable;
|
||||
|
||||
typedef struct ktap_global_state {
|
||||
ktap_stringtable strt; /* hash table for strings */
|
||||
ktap_value registry;
|
||||
unsigned int seed; /* randonized seed for hashes */
|
||||
u8 gcstate; /* state of garbage collector */
|
||||
u8 gckind; /* kind of GC running */
|
||||
u8 gcrunning; /* true if GC is running */
|
||||
|
||||
ktap_gcobject *allgc; /* list of all collectable objects */
|
||||
|
||||
ktap_upval uvhead; /* head of double-linked list of all open upvalues */
|
||||
|
||||
struct ktap_state *mainthread;
|
||||
#ifdef __KERNEL__
|
||||
ktap_parm *parm;
|
||||
pid_t trace_pid;
|
||||
struct task_struct *trace_task;
|
||||
cpumask_var_t cpumask;
|
||||
struct ring_buffer *buffer;
|
||||
struct dentry *trace_pipe_dentry;
|
||||
int nr_builtin_cfunction;
|
||||
ktap_value *cfunction_tbl;
|
||||
struct task_struct *task;
|
||||
int trace_enabled;
|
||||
struct list_head timers;
|
||||
struct list_head probe_events_head;
|
||||
int exit;
|
||||
int wait_user;
|
||||
ktap_closure *trace_end_closure;
|
||||
#endif
|
||||
int error;
|
||||
} ktap_global_state;
|
||||
|
||||
typedef struct ktap_state {
|
||||
CommonHeader;
|
||||
u8 status;
|
||||
ktap_global_state *g;
|
||||
int stop;
|
||||
StkId top;
|
||||
ktap_callinfo *ci;
|
||||
const unsigned long *oldpc;
|
||||
StkId stack_last;
|
||||
StkId stack;
|
||||
int stacksize;
|
||||
ktap_gcobject *openupval;
|
||||
ktap_callinfo baseci;
|
||||
|
||||
int debug;
|
||||
int version;
|
||||
int gcrunning;
|
||||
|
||||
/* list of temp collectable objects, free when thread exit */
|
||||
ktap_gcobject *gclist;
|
||||
|
||||
#ifdef __KERNEL__
|
||||
struct ktap_event *current_event;
|
||||
int aggr_accval; /* for temp value storage */
|
||||
#endif
|
||||
} ktap_state;
|
||||
|
||||
|
||||
typedef struct gcheader {
|
||||
CommonHeader;
|
||||
} gcheader;
|
||||
|
||||
/*
|
||||
* Union of all collectable objects
|
||||
*/
|
||||
union ktap_gcobject {
|
||||
gcheader gch; /* common header */
|
||||
union ktap_string ts;
|
||||
union ktap_udata u;
|
||||
struct ktap_closure cl;
|
||||
struct ktap_table h;
|
||||
struct ktap_aggrtable ah;
|
||||
struct ktap_aggraccval acc;
|
||||
struct ktap_proto p;
|
||||
struct ktap_upval uv;
|
||||
struct ktap_state th; /* thread */
|
||||
struct ktap_btrace bt; /* thread */
|
||||
};
|
||||
|
||||
#define gch(o) (&(o)->gch)
|
||||
/* macros to convert a GCObject into a specific value */
|
||||
#define rawgco2ts(o) (&((o)->ts))
|
||||
#define gco2ts(o) (&rawgco2ts(o)->tsv)
|
||||
|
||||
#define gco2uv(o) (&((o)->uv))
|
||||
|
||||
#define obj2gco(v) ((ktap_gcobject *)(v))
|
||||
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#define ktap_assert(s)
|
||||
#else
|
||||
#define ktap_assert(s)
|
||||
#if 0
|
||||
#define ktap_assert(s) \
|
||||
do { \
|
||||
if (!s) { \
|
||||
printf("assert failed %s, %d\n", __func__, __LINE__);\
|
||||
exit(0); \
|
||||
} \
|
||||
} while(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define check_exp(c,e) (e)
|
||||
|
||||
|
||||
typedef int ktap_number;
|
||||
|
||||
|
||||
#define ktap_number2int(i,n) ((i)=(int)(n))
|
||||
|
||||
|
||||
/* predefined values in the registry */
|
||||
#define KTAP_RIDX_MAINTHREAD 1
|
||||
#define KTAP_RIDX_GLOBALS 2
|
||||
#define KTAP_RIDX_LAST KTAP_RIDX_GLOBALS
|
||||
|
||||
|
||||
#define KTAP_TNONE (-1)
|
||||
|
||||
#define KTAP_TNIL 0
|
||||
#define KTAP_TBOOLEAN 1
|
||||
#define KTAP_TLIGHTUSERDATA 2
|
||||
#define KTAP_TNUMBER 3
|
||||
#define KTAP_TSTRING 4
|
||||
#define KTAP_TSHRSTR (KTAP_TSTRING | (0 << 4)) /* short strings */
|
||||
#define KTAP_TLNGSTR (KTAP_TSTRING | (1 << 4)) /* long strings */
|
||||
#define KTAP_TTABLE 5
|
||||
#define KTAP_TFUNCTION 6
|
||||
#define KTAP_TLCL (KTAP_TFUNCTION | (0 << 4)) /* closure */
|
||||
#define KTAP_TLCF (KTAP_TFUNCTION | (1 << 4)) /* light C function */
|
||||
#define KTAP_TCCL (KTAP_TFUNCTION | (2 << 4)) /* C closure */
|
||||
#define KTAP_TUSERDATA 7
|
||||
#define KTAP_TTHREAD 8
|
||||
|
||||
#define KTAP_NUMTAGS 9
|
||||
|
||||
#define KTAP_TPROTO 11
|
||||
#define KTAP_TUPVAL 12
|
||||
|
||||
#define KTAP_TEVENT 13
|
||||
|
||||
#define KTAP_TBTRACE 14
|
||||
|
||||
#define KTAP_TAGGRTABLE 15
|
||||
#define KTAP_TAGGRACCVAL 16
|
||||
#define KTAP_TAGGRVAL 17
|
||||
|
||||
#define ttype(o) ((o->type) & 0x3F)
|
||||
#define settype(obj, t) ((obj)->type = (t))
|
||||
|
||||
|
||||
|
||||
/* raw type tag of a TValue */
|
||||
#define rttype(o) ((o)->type)
|
||||
|
||||
/* tag with no variants (bits 0-3) */
|
||||
#define novariant(x) ((x) & 0x0F)
|
||||
|
||||
/* type tag of a TValue with no variants (bits 0-3) */
|
||||
#define ttypenv(o) (novariant(rttype(o)))
|
||||
|
||||
#define val_(o) ((o)->val)
|
||||
|
||||
#define bvalue(o) (val_(o).b)
|
||||
#define nvalue(o) (val_(o).n)
|
||||
#define hvalue(o) (&val_(o).gc->h)
|
||||
#define ahvalue(o) (&val_(o).gc->ah)
|
||||
#define aggraccvalue(o) (&val_(o).gc->acc)
|
||||
#define CLVALUE(o) (&val_(o).gc->cl.l)
|
||||
#define clcvalue(o) (&val_(o).gc->cl.c)
|
||||
#define clvalue(o) (&val_(o).gc->cl)
|
||||
#define rawtsvalue(o) (&val_(o).gc->ts)
|
||||
#define pvalue(o) (&val_(o).p)
|
||||
#define fvalue(o) (val_(o).f)
|
||||
#define rawuvalue(o) (&val_(o).gc->u)
|
||||
#define uvalue(o) (&rawuvalue(o)->uv)
|
||||
#define evalue(o) (val_(o).p)
|
||||
#define btvalue(o) (&val_(o).gc->bt)
|
||||
|
||||
#define gcvalue(o) (val_(o).gc)
|
||||
|
||||
#define isnil(o) (o->type == KTAP_TNIL)
|
||||
#define isboolean(o) (o->type == KTAP_TBOOLEAN)
|
||||
#define isfalse(o) (isnil(o) || (isboolean(o) && bvalue(o) == 0))
|
||||
|
||||
#define ttisshrstring(o) ((o)->type == KTAP_TSHRSTR)
|
||||
#define ttisstring(o) (((o)->type & 0x0F) == KTAP_TSTRING)
|
||||
#define ttisnumber(o) ((o)->type == KTAP_TNUMBER)
|
||||
#define ttisfunc(o) ((o)->type == KTAP_TFUNCTION)
|
||||
#define ttistable(o) ((o)->type == KTAP_TTABLE)
|
||||
#define ttisaggrtable(o) ((o)->type == KTAP_TAGGRTABLE)
|
||||
#define ttisaggrval(o) ((o)->type == KTAP_TAGGRVAL)
|
||||
#define ttisaggracc(o) ((o)->type == KTAP_TAGGRACCVAL)
|
||||
#define ttisnil(o) ((o)->type == KTAP_TNIL)
|
||||
#define ttisboolean(o) ((o)->type == KTAP_TBOOLEAN)
|
||||
#define ttisequal(o1,o2) ((o1)->type == (o2)->type)
|
||||
#define ttisevent(o) ((o)->type == KTAP_TEVENT)
|
||||
#define ttisbtrace(o) ((o)->type == KTAP_TBTRACE)
|
||||
|
||||
#define ttisclone(o) ttisbtrace(o)
|
||||
|
||||
|
||||
#define setnilvalue(obj) \
|
||||
{ ktap_value *io = (obj); io->val.n = 0; settype(io, KTAP_TNIL); }
|
||||
|
||||
#define setbvalue(obj, x) \
|
||||
{ ktap_value *io = (obj); io->val.b = (x); settype(io, KTAP_TBOOLEAN); }
|
||||
|
||||
#define setnvalue(obj, x) \
|
||||
{ ktap_value *io = (obj); io->val.n = (x); settype(io, KTAP_TNUMBER); }
|
||||
|
||||
#define setaggrvalue(obj, x) \
|
||||
{ ktap_value *io = (obj); io->val.n = (x); settype(io, KTAP_TAGGRVAL); }
|
||||
|
||||
#define setaggraccvalue(obj,x) \
|
||||
{ ktap_value *io=(obj); \
|
||||
val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TAGGRACCVAL); }
|
||||
|
||||
#define setsvalue(obj, x) \
|
||||
{ ktap_value *io = (obj); \
|
||||
ktap_string *x_ = (x); \
|
||||
io->val.gc = (ktap_gcobject *)x_; settype(io, x_->tsv.tt); }
|
||||
|
||||
#define setcllvalue(obj, x) \
|
||||
{ ktap_value *io = (obj); \
|
||||
io->val.gc = (ktap_gcobject *)x; settype(io, KTAP_TLCL); }
|
||||
|
||||
#define sethvalue(obj,x) \
|
||||
{ ktap_value *io=(obj); \
|
||||
val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TTABLE); }
|
||||
|
||||
#define setahvalue(obj,x) \
|
||||
{ ktap_value *io=(obj); \
|
||||
val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TAGGRTABLE); }
|
||||
|
||||
#define setfvalue(obj,x) \
|
||||
{ ktap_value *io=(obj); val_(io).f=(x); settype(io, KTAP_TLCF); }
|
||||
|
||||
#define setthvalue(L,obj,x) \
|
||||
{ ktap_value *io=(obj); \
|
||||
val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TTHREAD); }
|
||||
|
||||
#define setevalue(obj, x) \
|
||||
{ ktap_value *io=(obj); val_(io).p = (x); settype(io, KTAP_TEVENT); }
|
||||
|
||||
#define setbtvalue(obj,x) \
|
||||
{ ktap_value *io=(obj); \
|
||||
val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TBTRACE); }
|
||||
|
||||
#define setobj(obj1,obj2) \
|
||||
{ const ktap_value *io2=(obj2); ktap_value *io1=(obj1); \
|
||||
io1->val = io2->val; io1->type = io2->type; }
|
||||
|
||||
#define rawequalobj(t1, t2) \
|
||||
(ttisequal(t1, t2) && kp_equalobjv(NULL, t1, t2))
|
||||
|
||||
#define equalobj(ks, t1, t2) rawequalobj(t1, t2)
|
||||
|
||||
#define incr_top(ks) {ks->top++;}
|
||||
|
||||
#define NUMADD(a, b) ((a) + (b))
|
||||
#define NUMSUB(a, b) ((a) - (b))
|
||||
#define NUMMUL(a, b) ((a) * (b))
|
||||
#define NUMDIV(a, b) ((a) / (b))
|
||||
#define NUMUNM(a) (-(a))
|
||||
#define NUMEQ(a, b) ((a) == (b))
|
||||
#define NUMLT(a, b) ((a) < (b))
|
||||
#define NUMLE(a, b) ((a) <= (b))
|
||||
#define NUMISNAN(a) (!NUMEQ((a), (a)))
|
||||
|
||||
/* todo: floor and pow in kernel */
|
||||
#define NUMMOD(a, b) ((a) % (b))
|
||||
#define NUMPOW(a, b) (pow(a, b))
|
||||
|
||||
|
||||
ktap_string *kp_tstring_newlstr(ktap_state *ks, const char *str, size_t l);
|
||||
ktap_string *kp_tstring_newlstr_local(ktap_state *ks, const char *str, size_t l);
|
||||
ktap_string *kp_tstring_new(ktap_state *ks, const char *str);
|
||||
ktap_string *kp_tstring_new_local(ktap_state *ks, const char *str);
|
||||
int kp_tstring_eqstr(ktap_string *a, ktap_string *b);
|
||||
unsigned int kp_string_hash(const char *str, size_t l, unsigned int seed);
|
||||
int kp_tstring_eqlngstr(ktap_string *a, ktap_string *b);
|
||||
int kp_tstring_cmp(const ktap_string *ls, const ktap_string *rs);
|
||||
void kp_tstring_resize(ktap_state *ks, int newsize);
|
||||
void kp_tstring_freeall(ktap_state *ks);
|
||||
|
||||
ktap_value *kp_table_set(ktap_state *ks, ktap_table *t, const ktap_value *key);
|
||||
ktap_table *kp_table_new(ktap_state *ks);
|
||||
const ktap_value *kp_table_getint(ktap_table *t, int key);
|
||||
void kp_table_setint(ktap_state *ks, ktap_table *t, int key, ktap_value *v);
|
||||
const ktap_value *kp_table_get(ktap_table *t, const ktap_value *key);
|
||||
void kp_table_setvalue(ktap_state *ks, ktap_table *t, const ktap_value *key, ktap_value *val);
|
||||
void kp_table_resize(ktap_state *ks, ktap_table *t, int nasize, int nhsize);
|
||||
void kp_table_resizearray(ktap_state *ks, ktap_table *t, int nasize);
|
||||
void kp_table_free(ktap_state *ks, ktap_table *t);
|
||||
int kp_table_length(ktap_state *ks, ktap_table *t);
|
||||
void kp_table_dump(ktap_state *ks, ktap_table *t);
|
||||
void kp_table_clear(ktap_state *ks, ktap_table *t);
|
||||
void kp_table_histogram(ktap_state *ks, ktap_table *t);
|
||||
int kp_table_next(ktap_state *ks, ktap_table *t, StkId key);
|
||||
void kp_table_atomic_inc(ktap_state *ks, ktap_table *t, ktap_value *key, int n);
|
||||
void kp_aggraccval_dump(ktap_state *ks, ktap_aggraccval *acc);
|
||||
ktap_aggrtable *kp_aggrtable_new(ktap_state *ks);
|
||||
ktap_table *kp_aggrtable_synthesis(ktap_state *ks, ktap_aggrtable *ah);
|
||||
void kp_aggrtable_dump(ktap_state *ks, ktap_aggrtable *ah);
|
||||
void kp_aggrtable_free(ktap_state *ks, ktap_aggrtable *ah);
|
||||
void kp_aggrtable_set(ktap_state *ks, ktap_aggrtable *ah,
|
||||
ktap_value *key, ktap_value *val);
|
||||
void kp_aggrtable_get(ktap_state *ks, ktap_aggrtable *ah,
|
||||
ktap_value *key, ktap_value *val);
|
||||
void kp_aggrtable_histogram(ktap_state *ks, ktap_aggrtable *ah);
|
||||
void kp_obj_dump(ktap_state *ks, const ktap_value *v);
|
||||
void kp_showobj(ktap_state *ks, const ktap_value *v);
|
||||
int kp_objlen(ktap_state *ks, const ktap_value *rb);
|
||||
void kp_objclone(ktap_state *ks, const ktap_value *o, ktap_value *newo,
|
||||
ktap_gcobject **list);
|
||||
ktap_gcobject *kp_newobject(ktap_state *ks, int type, size_t size, ktap_gcobject **list);
|
||||
int kp_equalobjv(ktap_state *ks, const ktap_value *t1, const ktap_value *t2);
|
||||
ktap_closure *kp_newlclosure(ktap_state *ks, int n);
|
||||
ktap_proto *kp_newproto(ktap_state *ks);
|
||||
ktap_upval *kp_newupval(ktap_state *ks);
|
||||
void kp_free_gclist(ktap_state *ks, ktap_gcobject *o);
|
||||
void kp_free_all_gcobject(ktap_state *ks);
|
||||
void kp_header(u8 *h);
|
||||
|
||||
int kp_str2d(const char *s, size_t len, ktap_number *result);
|
||||
|
||||
#define kp_realloc(ks, v, osize, nsize, t) \
|
||||
((v) = (t *)kp_reallocv(ks, v, osize * sizeof(t), nsize * sizeof(t)))
|
||||
|
||||
#define kp_error(ks, args...) \
|
||||
do { \
|
||||
kp_printf(ks, "error: "args); \
|
||||
G(ks)->error = 1; \
|
||||
kp_exit(ks); \
|
||||
} while(0)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#define G(ks) (ks->g)
|
||||
|
||||
void *kp_malloc(ktap_state *ks, int size);
|
||||
void kp_free(ktap_state *ks, void *addr);
|
||||
void *kp_reallocv(ktap_state *ks, void *addr, int oldsize, int newsize);
|
||||
void *kp_zalloc(ktap_state *ks, int size);
|
||||
|
||||
void kp_printf(ktap_state *ks, const char *fmt, ...);
|
||||
extern void __kp_puts(ktap_state *ks, const char *str);
|
||||
extern void __kp_bputs(ktap_state *ks, const char *str);
|
||||
|
||||
#define kp_puts(ks, str) ({ \
|
||||
static const char *trace_printk_fmt \
|
||||
__attribute__((section("__trace_printk_fmt"))) = \
|
||||
__builtin_constant_p(str) ? str : NULL; \
|
||||
\
|
||||
if (__builtin_constant_p(str)) \
|
||||
__kp_bputs(ks, trace_printk_fmt); \
|
||||
else \
|
||||
__kp_puts(ks, str); \
|
||||
})
|
||||
|
||||
#else
|
||||
/*
|
||||
* this is used for ktapc tstring operation, tstring need G(ks)->strt
|
||||
* and G(ks)->seed, so ktapc need to init those field
|
||||
*/
|
||||
#define G(ks) (&dummy_global_state)
|
||||
extern ktap_global_state dummy_global_state;
|
||||
|
||||
#define kp_malloc(ks, size) malloc(size)
|
||||
#define kp_free(ks, block) free(block)
|
||||
#define kp_reallocv(ks, block, osize, nsize) realloc(block, nsize)
|
||||
#define kp_printf(ks, args...) printf(args)
|
||||
#define kp_puts(ks, str) printf("%s", str)
|
||||
#define kp_exit(ks) exit(EXIT_FAILURE)
|
||||
#endif
|
||||
|
||||
#define __maybe_unused __attribute__((unused))
|
||||
|
||||
/*
|
||||
* KTAP_QL describes how error messages quote program elements.
|
||||
* CHANGE it if you want a different appearance.
|
||||
*/
|
||||
#define KTAP_QL(x) "'" x "'"
|
||||
#define KTAP_QS KTAP_QL("%s")
|
||||
|
||||
#endif /* __KTAP_TYPES_H__ */
|
||||
|
@ -1,235 +0,0 @@
|
||||
/*
|
||||
* ktap.c - ktapvm kernel module main entry
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* this file is the first file to be compile, add CONFIG_ checking in here.
|
||||
* See Requirements in doc/introduction.txt
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
|
||||
#error "Currently ktap don't support kernel older than 3.1"
|
||||
#endif
|
||||
|
||||
#if !CONFIG_EVENT_TRACING
|
||||
#error "Please enable CONFIG_EVENT_TRACING before compile ktap"
|
||||
#endif
|
||||
|
||||
#if !CONFIG_PERF_EVENTS
|
||||
#error "Please enable CONFIG_PERF_EVENTS before compile ktap"
|
||||
#endif
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include "../include/ktap.h"
|
||||
|
||||
static int load_trunk(struct ktap_parm *parm, unsigned long **buff)
|
||||
{
|
||||
int ret;
|
||||
unsigned long *vmstart;
|
||||
|
||||
vmstart = vmalloc(parm->trunk_len);
|
||||
if (!vmstart)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = copy_from_user(vmstart, (void __user *)parm->trunk,
|
||||
parm->trunk_len);
|
||||
if (ret < 0) {
|
||||
vfree(vmstart);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*buff = vmstart;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gettimeofday_us(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
do_gettimeofday(&tv);
|
||||
return tv.tv_sec * USEC_PER_SEC + tv.tv_usec;
|
||||
}
|
||||
|
||||
struct dentry *kp_dir_dentry;
|
||||
static atomic_t kp_is_running = ATOMIC_INIT(0);
|
||||
|
||||
/* Ktap Main Entry */
|
||||
static int ktap_main(struct file *file, ktap_parm *parm)
|
||||
{
|
||||
unsigned long *buff = NULL;
|
||||
ktap_state *ks;
|
||||
ktap_closure *cl;
|
||||
int start_time, delta_time;
|
||||
int ret;
|
||||
|
||||
if (atomic_inc_return(&kp_is_running) != 1) {
|
||||
atomic_dec(&kp_is_running);
|
||||
pr_info("only one ktap thread allow to run\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
start_time = gettimeofday_us();
|
||||
|
||||
ks = kp_newstate(parm, kp_dir_dentry);
|
||||
if (unlikely(!ks)) {
|
||||
ret = -ENOEXEC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
file->private_data = ks;
|
||||
|
||||
ret = load_trunk(parm, &buff);
|
||||
if (ret) {
|
||||
pr_err("cannot load file\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
cl = kp_load(ks, (unsigned char *)buff);
|
||||
|
||||
vfree(buff);
|
||||
|
||||
if (cl) {
|
||||
/* optimize bytecode before excuting */
|
||||
kp_optimize_code(ks, 0, cl->l.p);
|
||||
|
||||
delta_time = gettimeofday_us() - start_time;
|
||||
kp_verbose_printf(ks, "booting time: %d (us)\n", delta_time);
|
||||
kp_call(ks, ks->top - 1, 0);
|
||||
}
|
||||
|
||||
kp_final_exit(ks);
|
||||
|
||||
out:
|
||||
atomic_dec(&kp_is_running);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void print_version(void)
|
||||
{
|
||||
}
|
||||
|
||||
static long ktap_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
ktap_parm parm;
|
||||
int ret;
|
||||
|
||||
switch (cmd) {
|
||||
case KTAP_CMD_IOC_VERSION:
|
||||
print_version();
|
||||
return 0;
|
||||
case KTAP_CMD_IOC_RUN:
|
||||
ret = copy_from_user(&parm, (void __user *)arg,
|
||||
sizeof(ktap_parm));
|
||||
if (ret < 0)
|
||||
return -EFAULT;
|
||||
|
||||
return ktap_main(file, &parm);
|
||||
default:
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations ktap_fops = {
|
||||
.llseek = no_llseek,
|
||||
.unlocked_ioctl = ktap_ioctl,
|
||||
};
|
||||
|
||||
static long ktapvm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int new_fd, err;
|
||||
struct file *new_file;
|
||||
|
||||
new_fd = get_unused_fd();
|
||||
if (new_fd < 0)
|
||||
return new_fd;
|
||||
|
||||
new_file = anon_inode_getfile("[ktap]", &ktap_fops, NULL, O_RDWR);
|
||||
if (IS_ERR(new_file)) {
|
||||
err = PTR_ERR(new_file);
|
||||
put_unused_fd(new_fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
file->private_data = NULL;
|
||||
fd_install(new_fd, new_file);
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
static const struct file_operations ktapvm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = ktapvm_ioctl,
|
||||
};
|
||||
|
||||
unsigned int kp_stub_exit_instr;
|
||||
|
||||
static int __init init_ktap(void)
|
||||
{
|
||||
struct dentry *ktapvm_dentry;
|
||||
|
||||
kp_dir_dentry = debugfs_create_dir("ktap", NULL);
|
||||
if (!kp_dir_dentry) {
|
||||
pr_err("ktap: debugfs_create_dir failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ktapvm_dentry = debugfs_create_file("ktapvm", 0444, kp_dir_dentry, NULL,
|
||||
&ktapvm_fops);
|
||||
|
||||
if (!ktapvm_dentry) {
|
||||
pr_err("ktapvm: cannot create ktapvm file\n");
|
||||
debugfs_remove_recursive(kp_dir_dentry);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SET_OPCODE(kp_stub_exit_instr, OP_EXIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit exit_ktap(void)
|
||||
{
|
||||
debugfs_remove_recursive(kp_dir_dentry);
|
||||
}
|
||||
|
||||
module_init(init_ktap);
|
||||
module_exit(exit_ktap);
|
||||
|
||||
MODULE_AUTHOR("Jovi Zhangwei <jovi.zhangwei@gmail.com>");
|
||||
MODULE_DESCRIPTION("ktap");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
int kp_max_exec_count = 10000;
|
||||
module_param_named(max_exec_count, kp_max_exec_count, int, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(max_exec_count, "non-mainthread max instruction execution count");
|
||||
|
@ -1,153 +0,0 @@
|
||||
/*
|
||||
* ansilib.c - ANSI escape sequences library
|
||||
*
|
||||
* http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "../../include/ktap.h"
|
||||
|
||||
/**
|
||||
* function ansi.clear_screen - Move cursor to top left and clear screen.
|
||||
*
|
||||
* Description: Sends ansi code for moving cursor to top left and then the
|
||||
* ansi code for clearing the screen from the cursor position to the end.
|
||||
*/
|
||||
|
||||
static int ktap_lib_clear_screen(ktap_state *ks)
|
||||
{
|
||||
kp_printf(ks, "\033[1;1H\033[J");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* function ansi.set_color - Set the ansi Select Graphic Rendition mode.
|
||||
* @fg: Foreground color to set.
|
||||
*
|
||||
* Description: Sends ansi code for Select Graphic Rendition mode for the
|
||||
* given forground color. Black (30), Blue (34), Green (32), Cyan (36),
|
||||
* Red (31), Purple (35), Brown (33), Light Gray (37).
|
||||
*/
|
||||
|
||||
static int ktap_lib_set_color(ktap_state *ks)
|
||||
{
|
||||
int fg;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
fg = nvalue(kp_arg(ks, 1));
|
||||
kp_printf(ks, "\033[%dm", fg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* function ansi.set_color2 - Set the ansi Select Graphic Rendition mode.
|
||||
* @fg: Foreground color to set.
|
||||
* @bg: Background color to set.
|
||||
*
|
||||
* Description: Sends ansi code for Select Graphic Rendition mode for the
|
||||
* given forground color, Black (30), Blue (34), Green (32), Cyan (36),
|
||||
* Red (31), Purple (35), Brown (33), Light Gray (37) and the given
|
||||
* background color, Black (40), Red (41), Green (42), Yellow (43),
|
||||
* Blue (44), Magenta (45), Cyan (46), White (47).
|
||||
*/
|
||||
static int ktap_lib_set_color2(ktap_state *ks)
|
||||
{
|
||||
int fg, bg;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
kp_arg_check(ks, 2, KTAP_TNUMBER);
|
||||
|
||||
fg = nvalue(kp_arg(ks, 1));
|
||||
bg = nvalue(kp_arg(ks, 2));
|
||||
kp_printf(ks, "\033[%d;%dm", fg, bg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* function ansi.set_color3 - Set the ansi Select Graphic Rendition mode.
|
||||
* @fg: Foreground color to set.
|
||||
* @bg: Background color to set.
|
||||
* @attr: Color attribute to set.
|
||||
*
|
||||
* Description: Sends ansi code for Select Graphic Rendition mode for the
|
||||
* given forground color, Black (30), Blue (34), Green (32), Cyan (36),
|
||||
* Red (31), Purple (35), Brown (33), Light Gray (37), the given
|
||||
* background color, Black (40), Red (41), Green (42), Yellow (43),
|
||||
* Blue (44), Magenta (45), Cyan (46), White (47) and the color attribute
|
||||
* All attributes off (0), Intensity Bold (1), Underline Single (4),
|
||||
* Blink Slow (5), Blink Rapid (6), Image Negative (7).
|
||||
*/
|
||||
static int ktap_lib_set_color3(ktap_state *ks)
|
||||
{
|
||||
int fg, bg, attr;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
kp_arg_check(ks, 2, KTAP_TNUMBER);
|
||||
kp_arg_check(ks, 3, KTAP_TNUMBER);
|
||||
|
||||
fg = nvalue(kp_arg(ks, 1));
|
||||
bg = nvalue(kp_arg(ks, 2));
|
||||
attr = nvalue(kp_arg(ks, 3));
|
||||
|
||||
if (attr)
|
||||
kp_printf(ks, "\033[%d;%d;%dm", fg, bg, attr);
|
||||
else
|
||||
kp_printf(ks, "\033[%d;%dm", fg, bg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* function ansi.reset_color - Resets Select Graphic Rendition mode.
|
||||
*
|
||||
* Description: Sends ansi code to reset foreground, background and color
|
||||
* attribute to default values.
|
||||
*/
|
||||
static int ktap_lib_reset_color(ktap_state *ks)
|
||||
{
|
||||
kp_printf(ks, "\033[0;0m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* function ansi.new_line - Move cursor to new line.
|
||||
*
|
||||
* Description: Sends ansi code new line.
|
||||
*/
|
||||
static int ktap_lib_new_line (ktap_state *ks)
|
||||
{
|
||||
kp_printf(ks, "\12");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const ktap_Reg ansi_funcs[] = {
|
||||
{"clear_screen", ktap_lib_clear_screen},
|
||||
{"set_color", ktap_lib_set_color},
|
||||
{"set_color2", ktap_lib_set_color2},
|
||||
{"set_color3", ktap_lib_set_color3},
|
||||
{"reset_color", ktap_lib_reset_color},
|
||||
{"new_line", ktap_lib_new_line},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
void kp_init_ansilib(ktap_state *ks)
|
||||
{
|
||||
kp_register_lib(ks, "ansi", ansi_funcs);
|
||||
}
|
@ -1,455 +0,0 @@
|
||||
/*
|
||||
* baselib.c - ktapvm kernel module base library
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/ring_buffer.h>
|
||||
#include <linux/stacktrace.h>
|
||||
#include "../../include/ktap.h"
|
||||
|
||||
static int ktap_lib_next(ktap_state *ks)
|
||||
{
|
||||
ktap_table *t = hvalue(ks->top - 2);
|
||||
|
||||
if (kp_table_next(ks, t, ks->top-1)) {
|
||||
ks->top += 1;
|
||||
return 2;
|
||||
} else {
|
||||
ks->top -= 1;
|
||||
setnilvalue(ks->top++);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int ktap_lib_pairs(ktap_state *ks)
|
||||
{
|
||||
ktap_value *v = kp_arg(ks, 1);
|
||||
ktap_table *t;
|
||||
|
||||
if (G(ks)->mainthread != ks) {
|
||||
kp_error(ks, "only mainthread can call table pairs\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ttistable(v)) {
|
||||
t = hvalue(v);
|
||||
} else if (ttisaggrtable(v)) {
|
||||
t = kp_aggrtable_synthesis(ks, ahvalue(v));
|
||||
} else if (isnil(v)) {
|
||||
kp_error(ks, "table is nil in pairs\n");
|
||||
return 0;
|
||||
} else {
|
||||
kp_error(ks, "wrong argument for pairs\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
setfvalue(ks->top++, ktap_lib_next);
|
||||
sethvalue(ks->top++, t);
|
||||
setnilvalue(ks->top++);
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int ktap_lib_len(ktap_state *ks)
|
||||
{
|
||||
int len = kp_objlen(ks, kp_arg(ks, 1));
|
||||
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
setnvalue(ks->top, len);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_print(ktap_state *ks)
|
||||
{
|
||||
int i;
|
||||
int n = kp_arg_nr(ks);
|
||||
|
||||
for (i = 1; i <= n; i++) {
|
||||
ktap_value *arg = kp_arg(ks, i);
|
||||
if (i > 1)
|
||||
kp_puts(ks, "\t");
|
||||
kp_showobj(ks, arg);
|
||||
}
|
||||
|
||||
kp_puts(ks, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* don't engage with tstring when printf, use buffer directly */
|
||||
static int ktap_lib_printf(ktap_state *ks)
|
||||
{
|
||||
struct trace_seq *seq;
|
||||
|
||||
preempt_disable_notrace();
|
||||
|
||||
seq = kp_percpu_data(KTAP_PERCPU_DATA_BUFFER);
|
||||
trace_seq_init(seq);
|
||||
|
||||
if (kp_strfmt(ks, seq))
|
||||
return 0;
|
||||
|
||||
seq->buffer[seq->len] = '\0';
|
||||
kp_transport_write(ks, seq->buffer, seq->len + 1);
|
||||
|
||||
preempt_enable_notrace();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STACKTRACE
|
||||
static int ktap_lib_print_backtrace(ktap_state *ks)
|
||||
{
|
||||
kp_transport_print_backtrace(ks);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int ktap_lib_print_backtrace(ktap_state *ks)
|
||||
{
|
||||
kp_error(ks, "Please enable CONFIG_STACKTRACE before use "
|
||||
"ktap print_backtrace\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ktap_lib_backtrace(ktap_state *ks)
|
||||
{
|
||||
struct stack_trace trace;
|
||||
ktap_btrace *bt;
|
||||
|
||||
bt = kp_percpu_data(KTAP_PERCPU_DATA_BTRACE);
|
||||
|
||||
trace.nr_entries = 0;
|
||||
trace.skip = 10;
|
||||
trace.max_entries = KTAP_STACK_MAX_ENTRIES;
|
||||
trace.entries = &bt->entries[0];
|
||||
save_stack_trace(&trace);
|
||||
|
||||
bt->nr_entries = trace.nr_entries;
|
||||
setbtvalue(ks->top, bt);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern unsigned long long ns2usecs(cycle_t nsec);
|
||||
static int ktap_lib_print_trace_clock(ktap_state *ks)
|
||||
{
|
||||
unsigned long long t;
|
||||
unsigned long secs, usec_rem;
|
||||
u64 timestamp;
|
||||
|
||||
/* use ring buffer's timestamp */
|
||||
timestamp = ring_buffer_time_stamp(G(ks)->buffer, smp_processor_id());
|
||||
|
||||
t = ns2usecs(timestamp);
|
||||
usec_rem = do_div(t, USEC_PER_SEC);
|
||||
secs = (unsigned long)t;
|
||||
|
||||
kp_printf(ks, "%5lu.%06lu\n", secs, usec_rem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ktap_lib_exit(ktap_state *ks)
|
||||
{
|
||||
kp_exit(ks);
|
||||
|
||||
/* do not execute bytecode any more in this thread */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ktap_lib_pid(ktap_state *ks)
|
||||
{
|
||||
pid_t pid = task_tgid_vnr(current);
|
||||
|
||||
setnvalue(ks->top, (int)pid);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_tid(ktap_state *ks)
|
||||
{
|
||||
pid_t pid = task_pid_vnr(current);
|
||||
|
||||
setnvalue(ks->top, (int)pid);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_execname(ktap_state *ks)
|
||||
{
|
||||
ktap_string *ts = kp_tstring_new(ks, current->comm);
|
||||
setsvalue(ks->top, ts);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_cpu(ktap_state *ks)
|
||||
{
|
||||
setnvalue(ks->top, smp_processor_id());
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_num_cpus(ktap_state *ks)
|
||||
{
|
||||
setnvalue(ks->top, num_online_cpus());
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_in_interrupt(ktap_state *ks)
|
||||
{
|
||||
int ret = in_interrupt();
|
||||
|
||||
setnvalue(ks->top, ret);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_arch(ktap_state *ks)
|
||||
{
|
||||
setsvalue(ks->top, kp_tstring_new(ks, utsname()->machine));
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_kernel_v(ktap_state *ks)
|
||||
{
|
||||
setsvalue(ks->top, kp_tstring_new(ks, utsname()->release));
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_user_string(ktap_state *ks)
|
||||
{
|
||||
unsigned long addr;
|
||||
char str[256] = {0};
|
||||
int ret;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
addr = nvalue(kp_arg(ks, 1));
|
||||
|
||||
pagefault_disable();
|
||||
ret = __copy_from_user_inatomic((void *)str, (const void *)addr, 256);
|
||||
(void) &ret; /* Silence compiler warning. */
|
||||
pagefault_enable();
|
||||
str[255] = '\0';
|
||||
setsvalue(ks->top, kp_tstring_new(ks, str));
|
||||
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_histogram(ktap_state *ks)
|
||||
{
|
||||
ktap_value *v = kp_arg(ks, 1);
|
||||
|
||||
if (G(ks)->mainthread != ks) {
|
||||
kp_error(ks, "only mainthread can call table historgram\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ttistable(v))
|
||||
kp_table_histogram(ks, hvalue(v));
|
||||
else if (ttisaggrtable(v))
|
||||
kp_aggrtable_histogram(ks, ahvalue(v));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_table(ktap_state *ks)
|
||||
{
|
||||
ktap_aggrtable *ah;
|
||||
|
||||
ah = kp_aggrtable_new(ks);
|
||||
setahvalue(ks->top, ah);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_count(ktap_state *ks)
|
||||
{
|
||||
setaggrvalue(ks->top, AGGREGATION_TYPE_COUNT);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_max(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
ks->aggr_accval = nvalue(kp_arg(ks, 1));
|
||||
setaggrvalue(ks->top, AGGREGATION_TYPE_MAX);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_min(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
ks->aggr_accval = nvalue(kp_arg(ks, 1));
|
||||
setaggrvalue(ks->top, AGGREGATION_TYPE_MIN);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_sum(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
ks->aggr_accval = nvalue(kp_arg(ks, 1));
|
||||
setaggrvalue(ks->top, AGGREGATION_TYPE_SUM);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_avg(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
ks->aggr_accval = nvalue(kp_arg(ks, 1));
|
||||
setaggrvalue(ks->top, AGGREGATION_TYPE_AVG);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_delete(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TTABLE);
|
||||
|
||||
kp_table_clear(ks, hvalue(kp_arg(ks, 1)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ktap_lib_gettimeofday_us(ktap_state *ks)
|
||||
{
|
||||
setnvalue(ks->top, gettimeofday_us());
|
||||
incr_top(ks);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* use gdb to get field offset of struct task_struct, for example:
|
||||
*
|
||||
* gdb vmlinux
|
||||
* (gdb)p &(((struct task_struct *)0).prio)
|
||||
*/
|
||||
static int ktap_lib_curr_task_info(ktap_state *ks)
|
||||
{
|
||||
int offset;
|
||||
int fetch_bytes;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
offset = nvalue(kp_arg(ks, 1));
|
||||
|
||||
if (kp_arg_nr(ks) == 1)
|
||||
fetch_bytes = 4; /* default fetch 4 bytes*/
|
||||
else {
|
||||
kp_arg_check(ks, 2, KTAP_TNUMBER);
|
||||
fetch_bytes = nvalue(kp_arg(ks, 2));
|
||||
}
|
||||
|
||||
if (offset >= sizeof(struct task_struct)) {
|
||||
setnilvalue(ks->top++);
|
||||
kp_error(ks, "access out of bound value of task_struct\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define RET_VALUE ((unsigned long)current + offset)
|
||||
|
||||
switch (fetch_bytes) {
|
||||
case 4:
|
||||
setnvalue(ks->top, *(unsigned int *)RET_VALUE);
|
||||
break;
|
||||
case 8:
|
||||
setnvalue(ks->top, *(unsigned long *)RET_VALUE);
|
||||
break;
|
||||
default:
|
||||
kp_error(ks, "unsupported fetch bytes in curr_task_info\n");
|
||||
setnilvalue(ks->top);
|
||||
break;
|
||||
}
|
||||
|
||||
#undef RET_VALUE
|
||||
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This built-in function mainly purpose scripts/schedule/schedtimes.kp
|
||||
*/
|
||||
static int ktap_lib_in_iowait(ktap_state *ks)
|
||||
{
|
||||
setnvalue(ks->top, current->in_iowait);
|
||||
incr_top(ks);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const ktap_Reg base_funcs[] = {
|
||||
{"pairs", ktap_lib_pairs},
|
||||
{"len", ktap_lib_len},
|
||||
{"print", ktap_lib_print},
|
||||
{"printf", ktap_lib_printf},
|
||||
{"print_backtrace", ktap_lib_print_backtrace},
|
||||
{"backtrace", ktap_lib_backtrace},
|
||||
{"print_trace_clock", ktap_lib_print_trace_clock},
|
||||
{"in_interrupt", ktap_lib_in_interrupt},
|
||||
{"exit", ktap_lib_exit},
|
||||
{"pid", ktap_lib_pid},
|
||||
{"tid", ktap_lib_tid},
|
||||
{"execname", ktap_lib_execname},
|
||||
{"cpu", ktap_lib_cpu},
|
||||
{"num_cpus", ktap_lib_num_cpus},
|
||||
{"arch", ktap_lib_arch},
|
||||
{"kernel_v", ktap_lib_kernel_v},
|
||||
{"user_string", ktap_lib_user_string},
|
||||
{"histogram", ktap_lib_histogram},
|
||||
{"aggr_table", ktap_lib_aggr_table},
|
||||
{"count", ktap_lib_aggr_count},
|
||||
{"max", ktap_lib_aggr_max},
|
||||
{"min", ktap_lib_aggr_min},
|
||||
{"sum", ktap_lib_aggr_sum},
|
||||
{"avg", ktap_lib_aggr_avg},
|
||||
|
||||
{"delete", ktap_lib_delete},
|
||||
{"gettimeofday_us", ktap_lib_gettimeofday_us},
|
||||
{"curr_taskinfo", ktap_lib_curr_task_info},
|
||||
{"in_iowait", ktap_lib_in_iowait},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
void kp_init_baselib(ktap_state *ks)
|
||||
{
|
||||
kp_register_lib(ks, NULL, base_funcs);
|
||||
}
|
@ -1,449 +0,0 @@
|
||||
/*
|
||||
* kdebug.c - ktap probing core implementation
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/ftrace_event.h>
|
||||
#include "../../include/ktap.h"
|
||||
|
||||
static void ktap_call_probe_closure(ktap_state *mainthread, ktap_closure *cl,
|
||||
struct ktap_event *e)
|
||||
{
|
||||
ktap_state *ks;
|
||||
ktap_value *func;
|
||||
|
||||
ks = kp_newthread(mainthread);
|
||||
setcllvalue(ks->top, cl);
|
||||
func = ks->top;
|
||||
incr_top(ks);
|
||||
|
||||
ks->current_event = e;
|
||||
|
||||
kp_call(ks, func, 0);
|
||||
|
||||
ks->current_event = NULL;
|
||||
kp_exitthread(ks);
|
||||
}
|
||||
|
||||
void kp_event_tostring(ktap_state *ks, struct trace_seq *seq)
|
||||
{
|
||||
struct ktap_event *e = ks->current_event;
|
||||
struct trace_iterator *iter;
|
||||
struct trace_event *ev;
|
||||
enum print_line_t ret = TRACE_TYPE_NO_CONSUME;
|
||||
|
||||
/* Simulate the iterator */
|
||||
|
||||
/*
|
||||
* use temp percpu buffer as trace_iterator
|
||||
* we cannot use same temp buffer as printf.
|
||||
*/
|
||||
iter = kp_percpu_data(KTAP_PERCPU_DATA_BUFFER2);
|
||||
|
||||
trace_seq_init(&iter->seq);
|
||||
iter->ent = e->entry;
|
||||
|
||||
ev = &(e->call->event);
|
||||
if (ev)
|
||||
ret = ev->funcs->trace(iter, 0, ev);
|
||||
|
||||
if (ret != TRACE_TYPE_NO_CONSUME) {
|
||||
struct trace_seq *s = &iter->seq;
|
||||
int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len;
|
||||
|
||||
s->buffer[len] = '\0';
|
||||
trace_seq_puts(seq, s->buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* check pt_regs defintion in linux/arch/x86/include/asm/ptrace.h */
|
||||
/* support other architecture pt_regs showing */
|
||||
static void event_regstr(ktap_state *ks, struct ktap_event *e, StkId ra)
|
||||
{
|
||||
struct pt_regs *regs = e->regs;
|
||||
char str[256] = {0};
|
||||
|
||||
#if defined(CONFIG_X86_32)
|
||||
snprintf(str, sizeof(str),
|
||||
"{ax: 0x%lx, orig_ax: 0x%lx, bx: 0x%lx, cx: 0x%lx, dx: 0x%lx, "
|
||||
"si: 0x%lx, di: 0x%lx, bp: 0x%lx, ds: 0x%lx, es: 0x%lx, fs: 0x%lx, "
|
||||
"gs: 0x%lx, ip: 0x%lx, cs: 0x%lx, flags: 0x%lx, sp: 0x%lx, ss: 0x%lx}\n",
|
||||
regs->ax, regs->orig_ax, regs->bx, regs->cx, regs->dx,
|
||||
regs->si, regs->di, regs->bp, regs->ds, regs->es, regs->fs,
|
||||
regs->gs, regs->ip, regs->cs, regs->flags, regs->sp, regs->ss);
|
||||
#elif defined(CONFIG_X86_64)
|
||||
/* x86_64 pt_regs doesn't have ds, es, fs or gs. */
|
||||
snprintf(str, sizeof(str),
|
||||
"{ax: 0x%lx, orig_ax: 0x%lx, bx: 0x%lx, cx: 0x%lx, dx: 0x%lx, "
|
||||
"si: 0x%lx, di: 0x%lx, r8: 0x%lx, r9: 0x%lx, r10: 0x%lx, r11: 0x%lx, "
|
||||
"r12: 0x%lx, r13: 0x%lx, r14: 0x%lx, r15: 0x%lx, bp: 0x%lx, ip: 0x%lx, "
|
||||
"cs: 0x%lx, flags: 0x%lx, sp: 0x%lx, ss: 0x%lx}\n",
|
||||
regs->ax, regs->orig_ax, regs->bx, regs->cx, regs->dx,
|
||||
regs->si, regs->di, regs->r8, regs->r9, regs->r10, regs->r11,
|
||||
regs->r12, regs->r13, regs->r14, regs->r15, regs->bp, regs->ip,
|
||||
regs->cs, regs->flags, regs->sp, regs->ss);
|
||||
#endif
|
||||
setsvalue(ra, kp_tstring_new_local(ks, str));
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************/
|
||||
/* This definition should keep update with kernel/trace/trace.h */
|
||||
struct ftrace_event_field {
|
||||
struct list_head link;
|
||||
const char *name;
|
||||
const char *type;
|
||||
int filter_type;
|
||||
int offset;
|
||||
int size;
|
||||
int is_signed;
|
||||
};
|
||||
|
||||
static struct list_head *ktap_get_fields(struct ftrace_event_call *event_call)
|
||||
{
|
||||
if (!event_call->class->get_fields)
|
||||
return &event_call->class->fields;
|
||||
return event_call->class->get_fields(event_call);
|
||||
}
|
||||
|
||||
static void get_field_value(ktap_state *ks, struct ktap_event *e,
|
||||
struct ftrace_event_field *field, ktap_value *ra)
|
||||
{
|
||||
void *value = (unsigned char *)e->entry + field->offset;
|
||||
|
||||
if (field->size == 4) {
|
||||
int n = *(int *)value;
|
||||
setnvalue(ra, n);
|
||||
return;
|
||||
} else if (field->size == 8) {
|
||||
long n = *(long *)value;
|
||||
setnvalue(ra, n);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp(field->type, "char", 4)) {
|
||||
setsvalue(ra, kp_tstring_new(ks, (char *)value));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void kp_event_getarg(ktap_state *ks, ktap_value *ra, int n)
|
||||
{
|
||||
struct ktap_event *e = ks->current_event;
|
||||
int index = n;
|
||||
struct ftrace_event_field *field;
|
||||
struct list_head *head;
|
||||
|
||||
/* this is very slow and not safe, fix it in future */
|
||||
head = ktap_get_fields(e->call);
|
||||
list_for_each_entry_reverse(field, head, link) {
|
||||
if (--index == 0) {
|
||||
get_field_value(ks, e, field, ra);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setnilvalue(ra);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Callback function for perf event subsystem
|
||||
* make ktap reentrant, don't disable irq in callback function,
|
||||
* same as perf and ftrace. to make reentrant, we need some
|
||||
* percpu data to be context isolation(irq/sirq/nmi/process)
|
||||
*
|
||||
* perf callback already consider on the recursion issue,
|
||||
* so ktap don't need to check again in here.
|
||||
*
|
||||
* Note tracepoint handler is calling with rcu_read_lock.
|
||||
*/
|
||||
static void ktap_overflow_callback(struct perf_event *event,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct ktap_probe_event *ktap_pevent;
|
||||
struct ktap_event e;
|
||||
ktap_state *ks;
|
||||
int rctx;
|
||||
|
||||
ktap_pevent = event->overflow_handler_context;
|
||||
ks = ktap_pevent->ks;
|
||||
|
||||
if (unlikely(ks->stop))
|
||||
return;
|
||||
|
||||
rctx = get_recursion_context();
|
||||
if (rctx < 0)
|
||||
return;
|
||||
|
||||
/* profile perf event don't have valid associated tp_event */
|
||||
if (event->tp_event) {
|
||||
e.call = event->tp_event;
|
||||
e.entry = data->raw->data;
|
||||
e.entry_size = data->raw->size;
|
||||
}
|
||||
e.pevent = ktap_pevent;
|
||||
e.regs = regs;
|
||||
|
||||
ktap_call_probe_closure(ks, ktap_pevent->cl, &e);
|
||||
|
||||
put_recursion_context(rctx);
|
||||
}
|
||||
|
||||
static void perf_destructor(struct ktap_probe_event *ktap_pevent)
|
||||
{
|
||||
perf_event_release_kernel(ktap_pevent->perf);
|
||||
}
|
||||
|
||||
static int (*kp_ftrace_profile_set_filter)(struct perf_event *event,
|
||||
int event_id, char *filter_str);
|
||||
|
||||
/*
|
||||
* Generic perf event register function
|
||||
* used by tracepoints/kprobe/uprobe/profile-timer/hw_breakpoint.
|
||||
*/
|
||||
void kp_perf_event_register(ktap_state *ks, struct perf_event_attr *attr,
|
||||
struct task_struct *task, char *filter,
|
||||
ktap_closure *cl)
|
||||
{
|
||||
struct ktap_probe_event *ktap_pevent;
|
||||
struct perf_event *event;
|
||||
int cpu, ret;
|
||||
|
||||
kp_verbose_printf(ks, "enable perf event id: %d, filter: %s "
|
||||
"pid: %d\n", attr->config, filter,
|
||||
task ? task_tgid_vnr(task) : -1);
|
||||
|
||||
/*
|
||||
* don't tracing until ktap_wait, the reason is:
|
||||
* 1). some event may hit before apply filter
|
||||
* 2). more simple to manage tracing thread
|
||||
* 3). avoid race with mainthread.
|
||||
*
|
||||
* Another way to do this is make attr.disabled as 1, then use
|
||||
* perf_event_enable after filter apply, however, perf_event_enable
|
||||
* was not exported in kernel older than 3.3, so we drop this method.
|
||||
*/
|
||||
ks->stop = 1;
|
||||
|
||||
for_each_cpu(cpu, G(ks)->cpumask) {
|
||||
ktap_pevent = kp_zalloc(ks, sizeof(*ktap_pevent));
|
||||
ktap_pevent->ks = ks;
|
||||
ktap_pevent->cl = cl;
|
||||
event = perf_event_create_kernel_counter(attr, cpu, task,
|
||||
ktap_overflow_callback,
|
||||
ktap_pevent);
|
||||
if (IS_ERR(event)) {
|
||||
int err = PTR_ERR(event);
|
||||
kp_error(ks, "unable register perf event %d on cpu %d, "
|
||||
"err: %d\n", attr->config, cpu, err);
|
||||
kp_free(ks, ktap_pevent);
|
||||
return;
|
||||
}
|
||||
|
||||
ktap_pevent->perf = event;
|
||||
INIT_LIST_HEAD(&ktap_pevent->list);
|
||||
list_add_tail(&ktap_pevent->list, &G(ks)->probe_events_head);
|
||||
|
||||
if (!filter)
|
||||
continue;
|
||||
|
||||
ret = kp_ftrace_profile_set_filter(event, attr->config, filter);
|
||||
if (ret) {
|
||||
kp_error(ks, "unable set filter %s for event id %d, "
|
||||
"ret: %d\n", filter, attr->config, ret);
|
||||
perf_destructor(ktap_pevent);
|
||||
list_del(&ktap_pevent->list);
|
||||
kp_free(ks, ktap_pevent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void end_probes(struct ktap_state *ks)
|
||||
{
|
||||
struct ktap_probe_event *ktap_pevent;
|
||||
struct list_head *tmp, *pos;
|
||||
struct list_head *head = &G(ks)->probe_events_head;
|
||||
|
||||
list_for_each(pos, head) {
|
||||
ktap_pevent = container_of(pos, struct ktap_probe_event,
|
||||
list);
|
||||
perf_destructor(ktap_pevent);
|
||||
}
|
||||
/*
|
||||
* Ensure our callback won't be called anymore. The buffers
|
||||
* will be freed after that.
|
||||
*/
|
||||
tracepoint_synchronize_unregister();
|
||||
|
||||
list_for_each_safe(pos, tmp, head) {
|
||||
ktap_pevent = container_of(pos, struct ktap_probe_event,
|
||||
list);
|
||||
list_del(&ktap_pevent->list);
|
||||
kp_free(ks, ktap_pevent);
|
||||
}
|
||||
}
|
||||
|
||||
static int ktap_lib_probe_by_id(ktap_state *ks)
|
||||
{
|
||||
const char *ids_str;
|
||||
char *start;
|
||||
ktap_closure *cl;
|
||||
struct task_struct *task = G(ks)->trace_task;
|
||||
char filter_str[128] = {0};
|
||||
char *filter, *ptr1, *sep, *ptr;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TSTRING);
|
||||
kp_arg_check(ks, 2, KTAP_TFUNCTION);
|
||||
|
||||
ids_str = svalue(kp_arg(ks, 1));
|
||||
cl = clvalue(kp_arg(ks, 2));
|
||||
|
||||
start = (char *)ids_str;
|
||||
|
||||
again:
|
||||
filter = NULL;
|
||||
|
||||
sep = strchr(start, ',');
|
||||
if (!sep)
|
||||
ptr1 = strchr(start, '/');
|
||||
else
|
||||
ptr1 = strnchr(start, sep - start, '/');
|
||||
|
||||
if (ptr1) {
|
||||
char *ptr2 = strrchr(ptr1 + 1, '/');
|
||||
|
||||
if (ptr2) {
|
||||
memset(filter_str, 0, sizeof(filter_str));
|
||||
strncpy(filter_str, ptr1 + 1, ptr2 - ptr1 - 1);
|
||||
filter = &filter_str[0];
|
||||
} else {
|
||||
kp_printf(ks, "cannot parse ids_str: %s\n", ids_str);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (ptr = start; *ptr != ',' && *ptr != '\0' && *ptr != '/'; ptr++) {
|
||||
char token[32] = {0};
|
||||
int id;
|
||||
int i = 0;
|
||||
|
||||
if (*ptr == ' ')
|
||||
continue;
|
||||
|
||||
while (isdigit(*ptr)) {
|
||||
token[i++] = *ptr++;
|
||||
}
|
||||
|
||||
if (!kstrtoint(token, 10, &id)) {
|
||||
struct perf_event_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.type = PERF_TYPE_TRACEPOINT;
|
||||
attr.config = id;
|
||||
attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
|
||||
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD;
|
||||
attr.sample_period = 1;
|
||||
attr.size = sizeof(attr);
|
||||
attr.disabled = 0;
|
||||
|
||||
kp_perf_event_register(ks, &attr, task, filter, cl);
|
||||
}
|
||||
}
|
||||
|
||||
if (sep && (*(sep + 1) != '\0')) {
|
||||
start = sep + 1;
|
||||
goto again;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ktap_lib_probe_end(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TFUNCTION);
|
||||
|
||||
G(ks)->trace_end_closure = clvalue(kp_arg(ks, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ktap_lib_traceoff(ktap_state *ks)
|
||||
{
|
||||
end_probes(ks);
|
||||
|
||||
/* call trace_end_closure after probed end */
|
||||
if (G(ks)->trace_end_closure) {
|
||||
setcllvalue(ks->top, G(ks)->trace_end_closure);
|
||||
incr_top(ks);
|
||||
kp_call(ks, ks->top - 1, 0);
|
||||
G(ks)->trace_end_closure = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kp_probe_exit(ktap_state *ks)
|
||||
{
|
||||
if (!G(ks)->trace_enabled)
|
||||
return;
|
||||
|
||||
end_probes(ks);
|
||||
|
||||
/* call trace_end_closure after probed end */
|
||||
if (!G(ks)->error && G(ks)->trace_end_closure) {
|
||||
setcllvalue(ks->top, G(ks)->trace_end_closure);
|
||||
incr_top(ks);
|
||||
kp_call(ks, ks->top - 1, 0);
|
||||
G(ks)->trace_end_closure = NULL;
|
||||
}
|
||||
|
||||
G(ks)->trace_enabled = 0;
|
||||
}
|
||||
|
||||
int kp_probe_init(ktap_state *ks)
|
||||
{
|
||||
G(ks)->trace_enabled = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const ktap_Reg kdebuglib_funcs[] = {
|
||||
{"probe_by_id", ktap_lib_probe_by_id},
|
||||
{"probe_end", ktap_lib_probe_end},
|
||||
{"traceoff", ktap_lib_traceoff},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
void kp_init_kdebuglib(ktap_state *ks)
|
||||
{
|
||||
kp_ftrace_profile_set_filter =
|
||||
(void *)kallsyms_lookup_name("ftrace_profile_set_filter");
|
||||
if (!kp_ftrace_profile_set_filter) {
|
||||
printk("ktap: cannot lookup ftrace_profile_set_filter "
|
||||
"in kallsyms\n");
|
||||
return;
|
||||
}
|
||||
|
||||
kp_register_lib(ks, "kdebug", kdebuglib_funcs);
|
||||
}
|
||||
|
@ -1,192 +0,0 @@
|
||||
/*
|
||||
* timer.c - timer library support for ktap
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include "../../include/ktap.h"
|
||||
|
||||
struct hrtimer_ktap {
|
||||
struct hrtimer timer;
|
||||
ktap_state *ks;
|
||||
ktap_closure *cl;
|
||||
u64 ns;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/*
|
||||
* Currently ktap disallow tracing event in timer callback closure,
|
||||
* that will corrupt ktap_state and ktap stack, because timer closure
|
||||
* and event closure use same irq percpu ktap_state and stack.
|
||||
* We can use a different percpu ktap_state and stack for timer purpuse,
|
||||
* but that's don't bring any big value with cost on memory consuming.
|
||||
*
|
||||
* So just simply disable tracing in timer closure,
|
||||
* get_recursion_context()/put_recursion_context() is used for this purpose.
|
||||
*
|
||||
* option: export perf_swevent_put_recursion_context to slove this issue.
|
||||
*/
|
||||
static enum hrtimer_restart hrtimer_ktap_fn(struct hrtimer *timer)
|
||||
{
|
||||
struct hrtimer_ktap *t;
|
||||
ktap_state *ks;
|
||||
int rctx;
|
||||
|
||||
rcu_read_lock_sched_notrace();
|
||||
rctx = get_recursion_context();
|
||||
|
||||
t = container_of(timer, struct hrtimer_ktap, timer);
|
||||
|
||||
ks = kp_newthread(t->ks);
|
||||
setcllvalue(ks->top, t->cl);
|
||||
incr_top(ks);
|
||||
kp_call(ks, ks->top - 1, 0);
|
||||
kp_exitthread(ks);
|
||||
|
||||
hrtimer_add_expires_ns(timer, t->ns);
|
||||
|
||||
put_recursion_context(rctx);
|
||||
rcu_read_unlock_sched_notrace();
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
static void set_tick_timer(ktap_state *ks, u64 period, ktap_closure *cl)
|
||||
{
|
||||
struct hrtimer_ktap *t;
|
||||
|
||||
t = kp_malloc(ks, sizeof(*t));
|
||||
t->ks = ks;
|
||||
t->cl = cl;
|
||||
t->ns = period;
|
||||
|
||||
INIT_LIST_HEAD(&t->list);
|
||||
list_add(&t->list, &(G(ks)->timers));
|
||||
|
||||
hrtimer_init(&t->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
t->timer.function = hrtimer_ktap_fn;
|
||||
hrtimer_start(&t->timer, ns_to_ktime(period), HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
static void set_profile_timer(ktap_state *ks, u64 period, ktap_closure *cl)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.type = PERF_TYPE_SOFTWARE;
|
||||
attr.config = PERF_COUNT_SW_CPU_CLOCK;
|
||||
attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
|
||||
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD;
|
||||
attr.sample_period = period;
|
||||
attr.size = sizeof(attr);
|
||||
attr.disabled = 0;
|
||||
|
||||
kp_perf_event_register(ks, &attr, NULL, NULL, cl);
|
||||
}
|
||||
|
||||
static int do_tick_profile(ktap_state *ks, int is_tick)
|
||||
{
|
||||
const char *str, *tmp;
|
||||
char interval_str[32] = {0};
|
||||
char suffix[10] = {0};
|
||||
int n, i = 0;
|
||||
int factor;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TSTRING);
|
||||
kp_arg_check(ks, 2, KTAP_TFUNCTION);
|
||||
|
||||
str = svalue(kp_arg(ks, 1));
|
||||
tmp = str;
|
||||
while (isdigit(*tmp))
|
||||
tmp++;
|
||||
|
||||
strncpy(interval_str, str, tmp - str);
|
||||
if (kstrtoint(interval_str, 10, &n))
|
||||
goto error;
|
||||
|
||||
strncpy(suffix, tmp, 9);
|
||||
while (suffix[i] != ' ' && suffix[i] != '\0')
|
||||
i++;
|
||||
|
||||
suffix[i] = '\0';
|
||||
|
||||
if (!strcmp(suffix, "s") || !strcmp(suffix, "sec"))
|
||||
factor = NSEC_PER_SEC;
|
||||
else if (!strcmp(suffix, "ms") || !strcmp(suffix, "msec"))
|
||||
factor = NSEC_PER_MSEC;
|
||||
else if (!strcmp(suffix, "us") || !strcmp(suffix, "usec"))
|
||||
factor = NSEC_PER_USEC;
|
||||
else
|
||||
goto error;
|
||||
|
||||
if (is_tick)
|
||||
set_tick_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2)));
|
||||
else
|
||||
set_profile_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2)));
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kp_error(ks, "cannot parse timer interval: %s\n", str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* tick-n probes fire on only one CPU per interval.
|
||||
* valid time suffixes: sec/s, msec/ms, usec/us
|
||||
*/
|
||||
static int ktap_lib_tick(ktap_state *ks)
|
||||
{
|
||||
return do_tick_profile(ks, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* A profile-n probe fires every fixed interval on every CPU
|
||||
* valid time suffixes: sec/s, msec/ms, usec/us
|
||||
*/
|
||||
static int ktap_lib_profile(ktap_state *ks)
|
||||
{
|
||||
return do_tick_profile(ks, 0);
|
||||
}
|
||||
|
||||
void kp_exit_timers(ktap_state *ks)
|
||||
{
|
||||
struct hrtimer_ktap *t, *tmp;
|
||||
struct list_head *timers_list = &(G(ks)->timers);
|
||||
|
||||
list_for_each_entry_safe(t, tmp, timers_list, list) {
|
||||
hrtimer_cancel(&t->timer);
|
||||
kp_free(ks, t);
|
||||
}
|
||||
}
|
||||
|
||||
static const ktap_Reg timerlib_funcs[] = {
|
||||
{"profile", ktap_lib_profile},
|
||||
{"tick", ktap_lib_tick},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
void kp_init_timerlib(ktap_state *ks)
|
||||
{
|
||||
kp_register_lib(ks, "timer", timerlib_funcs);
|
||||
}
|
||||
|
@ -1,310 +0,0 @@
|
||||
/*
|
||||
* loader.c - loader for ktap bytecode chunk file
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include "../include/ktap.h"
|
||||
|
||||
#define KTAPC_TAIL "\x19\x93\r\n\x1a\n"
|
||||
|
||||
struct load_state {
|
||||
unsigned char *buff;
|
||||
int pos;
|
||||
ktap_state *ks;
|
||||
};
|
||||
|
||||
#define READ_CHAR(S) (S->buff[S->pos++])
|
||||
#define READ_BYTE(S) READ_CHAR(S)
|
||||
#define READ_INT(S) load_int(S)
|
||||
#define READ_NUMBER(S) load_number(S)
|
||||
#define READ_STRING(S) load_string(S)
|
||||
#define READ_VECTOR(S, dst, size) \
|
||||
do { \
|
||||
memcpy(dst, &S->buff[S->pos], size); \
|
||||
S->pos += size; \
|
||||
} while(0)
|
||||
|
||||
#define NEW_VECTOR(S, size) kp_malloc(S->ks, size)
|
||||
#define GET_CURRENT(S) &S->buff[S->pos]
|
||||
#define ADD_POS(S, size) S->pos += size
|
||||
|
||||
|
||||
static int load_function(struct load_state *S, ktap_proto *f);
|
||||
|
||||
|
||||
static int load_int(struct load_state *S)
|
||||
{
|
||||
int x;
|
||||
|
||||
READ_VECTOR(S, &x, sizeof(int));
|
||||
return x;
|
||||
}
|
||||
|
||||
static int load_number(struct load_state *S)
|
||||
{
|
||||
int x;
|
||||
|
||||
READ_VECTOR(S, &x, sizeof(ktap_number));
|
||||
return x;
|
||||
}
|
||||
|
||||
static ktap_string *load_string(struct load_state *S)
|
||||
{
|
||||
ktap_string *ts;
|
||||
size_t size;
|
||||
|
||||
size = READ_INT(S);
|
||||
|
||||
if (!size)
|
||||
return NULL;
|
||||
else {
|
||||
char *s = GET_CURRENT(S);
|
||||
ADD_POS(S, size);
|
||||
/* remove trailing '\0' */
|
||||
ts = kp_tstring_newlstr(S->ks, s, size - 1);
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int load_code(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
int n = READ_INT(S);
|
||||
|
||||
f->sizecode = n;
|
||||
f->code = NEW_VECTOR(S, n * sizeof(ktap_instruction));
|
||||
READ_VECTOR(S, f->code, n * sizeof(ktap_instruction));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_constants(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
int i,n;
|
||||
|
||||
n = READ_INT(S);
|
||||
|
||||
f->sizek = n;
|
||||
f->k = NEW_VECTOR(S, n * sizeof(ktap_value));
|
||||
for (i = 0; i < n; i++)
|
||||
setnilvalue(&f->k[i]);
|
||||
|
||||
for (i=0; i < n; i++) {
|
||||
ktap_value *o = &f->k[i];
|
||||
|
||||
int t = READ_CHAR(S);
|
||||
switch (t) {
|
||||
case KTAP_TNIL:
|
||||
setnilvalue(o);
|
||||
break;
|
||||
case KTAP_TBOOLEAN:
|
||||
setbvalue(o, READ_CHAR(S));
|
||||
break;
|
||||
case KTAP_TNUMBER:
|
||||
/*
|
||||
* todo: kernel not support fp, check double when
|
||||
* loading
|
||||
*/
|
||||
setnvalue(o, READ_NUMBER(S));
|
||||
break;
|
||||
case KTAP_TSTRING:
|
||||
setsvalue(o, READ_STRING(S));
|
||||
break;
|
||||
default:
|
||||
kp_error(S->ks, "ktap: load_constants: "
|
||||
"unknow ktap_value\n");
|
||||
return -1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
n = READ_INT(S);
|
||||
f->p = NEW_VECTOR(S, n * sizeof(ktap_proto));
|
||||
f->sizep = n;
|
||||
for (i = 0; i < n; i++)
|
||||
f->p[i] = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->p[i] = kp_newproto(S->ks);
|
||||
if (load_function(S, f->p[i]))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int load_upvalues(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
int i,n;
|
||||
|
||||
n = READ_INT(S);
|
||||
f->upvalues = NEW_VECTOR(S, n * sizeof(ktap_upvaldesc));
|
||||
f->sizeupvalues = n;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
f->upvalues[i].name = NULL;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
f->upvalues[i].instack = READ_BYTE(S);
|
||||
f->upvalues[i].idx = READ_BYTE(S);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_debuginfo(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
int i,n;
|
||||
|
||||
f->source = READ_STRING(S);
|
||||
n = READ_INT(S);
|
||||
f->sizelineinfo = n;
|
||||
f->lineinfo = NEW_VECTOR(S, n * sizeof(int));
|
||||
READ_VECTOR(S, f->lineinfo, n * sizeof(int));
|
||||
n = READ_INT(S);
|
||||
f->locvars = NEW_VECTOR(S, n * sizeof(struct ktap_locvar));
|
||||
f->sizelocvars = n;
|
||||
for (i = 0; i < n; i++)
|
||||
f->locvars[i].varname = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->locvars[i].varname = READ_STRING(S);
|
||||
f->locvars[i].startpc = READ_INT(S);
|
||||
f->locvars[i].endpc = READ_INT(S);
|
||||
}
|
||||
n = READ_INT(S);
|
||||
for (i = 0; i < n; i++)
|
||||
f->upvalues[i].name = READ_STRING(S);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_function(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
f->linedefined = READ_INT(S);
|
||||
f->lastlinedefined = READ_INT(S);
|
||||
f->numparams = READ_BYTE(S);
|
||||
f->is_vararg = READ_BYTE(S);
|
||||
f->maxstacksize = READ_BYTE(S);
|
||||
if (load_code(S, f))
|
||||
return -1;
|
||||
if (load_constants(S, f))
|
||||
return -1;
|
||||
if (load_upvalues(S, f))
|
||||
return -1;
|
||||
if (load_debuginfo(S, f))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define error(S, why) \
|
||||
kp_error(S->ks, "load failed: %s precompiled chunk\n", why)
|
||||
|
||||
#define N0 KTAPC_HEADERSIZE
|
||||
#define N1 (sizeof(KTAP_SIGNATURE) - sizeof(char))
|
||||
#define N2 N1 + 2
|
||||
#define N3 N2 + 6
|
||||
|
||||
static int load_header(struct load_state *S)
|
||||
{
|
||||
u8 h[KTAPC_HEADERSIZE];
|
||||
u8 s[KTAPC_HEADERSIZE];
|
||||
|
||||
kp_header(h);
|
||||
READ_VECTOR(S, s, KTAPC_HEADERSIZE);
|
||||
|
||||
if (memcmp(h, s, N0) == 0)
|
||||
return 0;
|
||||
if (memcmp(h, s, N1) != 0)
|
||||
error(S, "not a");
|
||||
else if (memcmp(h, s, N2) != 0)
|
||||
error(S, "version mismatch in");
|
||||
else if (memcmp(h, s, N3) != 0)
|
||||
error(S, "incompatible");
|
||||
else
|
||||
error(S,"corrupted");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int verify_code(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
/* not support now */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
ktap_closure *kp_load(ktap_state *ks, unsigned char *buff)
|
||||
{
|
||||
struct load_state S;
|
||||
ktap_closure *cl;
|
||||
ktap_lclosure *f;
|
||||
int ret, i;
|
||||
|
||||
S.ks = ks;
|
||||
S.buff = buff;
|
||||
S.pos = 0;
|
||||
|
||||
ret = load_header(&S);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
cl = kp_newlclosure(ks, 1);
|
||||
if (!cl)
|
||||
return cl;
|
||||
|
||||
/* put closure on the top, prepare to run with this closure */
|
||||
setcllvalue(ks->top, cl);
|
||||
incr_top(ks);
|
||||
|
||||
cl->l.p = kp_newproto(ks);
|
||||
if (load_function(&S, cl->l.p))
|
||||
return NULL;
|
||||
|
||||
if (cl->l.p->sizeupvalues != 1) {
|
||||
ktap_proto *p = cl->l.p;
|
||||
cl = kp_newlclosure(ks, cl->l.p->sizeupvalues);
|
||||
cl->l.p = p;
|
||||
setcllvalue(ks->top - 1, cl);
|
||||
}
|
||||
|
||||
f = &cl->l;
|
||||
for (i = 0; i < f->nupvalues; i++) { /* initialize upvalues */
|
||||
ktap_upval *up = kp_newupval(ks);
|
||||
f->upvals[i] = up;
|
||||
}
|
||||
|
||||
/* set global table as 1st upvalue of 'f' */
|
||||
if (f->nupvalues == 1) {
|
||||
ktap_table *reg = hvalue(&G(ks)->registry);
|
||||
const ktap_value *gt = kp_table_getint(reg, KTAP_RIDX_GLOBALS);
|
||||
setobj(f->upvals[0]->v, gt);
|
||||
}
|
||||
|
||||
verify_code(&S, cl->l.p);
|
||||
return cl;
|
||||
}
|
||||
|
@ -1,483 +0,0 @@
|
||||
/*
|
||||
* object.c - ktap object generic operation
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include "../include/ktap.h"
|
||||
#else
|
||||
#include "../include/ktap_types.h"
|
||||
#endif
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#define KTAP_ALLOC_FLAGS ((GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN) \
|
||||
& ~__GFP_WAIT)
|
||||
|
||||
void *kp_malloc(ktap_state *ks, int size)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
/*
|
||||
* Normally we don't want to trace under memory pressure,
|
||||
* so we use a simple rule to handle memory allocation failure:
|
||||
*
|
||||
* retry until allocation success, this will make caller don't need
|
||||
* to handle the unlikely failure case, then ktap exit.
|
||||
*
|
||||
* In this approach, if user find there have memory allocation failure,
|
||||
* user should re-run the ktap script, or fix the memory pressure
|
||||
* issue, or figure out why the script need so many memory.
|
||||
*
|
||||
* Perhaps return pre-allocated stub memory trunk when allocate failed
|
||||
* is a better approch?
|
||||
*/
|
||||
addr = kmalloc(size, KTAP_ALLOC_FLAGS);
|
||||
if (unlikely(!addr)) {
|
||||
kp_error(ks, "kmalloc size %d failed, retry again\n", size);
|
||||
printk("ktap kmalloc size %d failed, retry again\n", size);
|
||||
dump_stack();
|
||||
while (1) {
|
||||
addr = kmalloc(size, KTAP_ALLOC_FLAGS);
|
||||
if (addr)
|
||||
break;
|
||||
}
|
||||
kp_printf(ks, "kmalloc retry success after failed, exit\n");
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
void kp_free(ktap_state *ks, void *addr)
|
||||
{
|
||||
kfree(addr);
|
||||
}
|
||||
|
||||
void *kp_reallocv(ktap_state *ks, void *addr, int oldsize, int newsize)
|
||||
{
|
||||
void *new_addr;
|
||||
|
||||
new_addr = krealloc(addr, newsize, KTAP_ALLOC_FLAGS);
|
||||
if (unlikely(!new_addr)) {
|
||||
kp_error(ks, "krealloc size %d failed, retry again\n", newsize);
|
||||
printk("ktap krealloc size %d failed, retry again\n", newsize);
|
||||
dump_stack();
|
||||
while (1) {
|
||||
new_addr = krealloc(addr, newsize, KTAP_ALLOC_FLAGS);
|
||||
if (new_addr)
|
||||
break;
|
||||
}
|
||||
kp_printf(ks, "krealloc retry success after failed, exit\n");
|
||||
}
|
||||
|
||||
return new_addr;
|
||||
}
|
||||
|
||||
void *kp_zalloc(ktap_state *ks, int size)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
addr = kzalloc(size, KTAP_ALLOC_FLAGS);
|
||||
if (unlikely(!addr)) {
|
||||
kp_error(ks, "kzalloc size %d failed, retry again\n", size);
|
||||
printk("ktap kzalloc size %d failed, retry again\n", size);
|
||||
dump_stack();
|
||||
while (1) {
|
||||
addr = kzalloc(size, KTAP_ALLOC_FLAGS);
|
||||
if (addr)
|
||||
break;
|
||||
}
|
||||
kp_printf(ks, "kzalloc retry success after failed, exit\n");
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
void kp_obj_dump(ktap_state *ks, const ktap_value *v)
|
||||
{
|
||||
switch (ttype(v)) {
|
||||
case KTAP_TNIL:
|
||||
kp_puts(ks, "NIL");
|
||||
break;
|
||||
case KTAP_TNUMBER:
|
||||
kp_printf(ks, "NUMBER %ld", nvalue(v));
|
||||
break;
|
||||
case KTAP_TBOOLEAN:
|
||||
kp_printf(ks, "BOOLEAN %d", bvalue(v));
|
||||
break;
|
||||
case KTAP_TLIGHTUSERDATA:
|
||||
kp_printf(ks, "LIGHTUSERDATA 0x%lx", (unsigned long)pvalue(v));
|
||||
break;
|
||||
case KTAP_TLCF:
|
||||
kp_printf(ks, "LIGHTCFCUNTION 0x%lx", (unsigned long)fvalue(v));
|
||||
break;
|
||||
case KTAP_TSHRSTR:
|
||||
case KTAP_TLNGSTR:
|
||||
kp_printf(ks, "SHRSTR #%s", svalue(v));
|
||||
break;
|
||||
case KTAP_TUSERDATA:
|
||||
kp_printf(ks, "USERDATA 0x%lx", (unsigned long)uvalue(v));
|
||||
break;
|
||||
case KTAP_TTABLE:
|
||||
kp_printf(ks, "TABLE 0x%lx", (unsigned long)hvalue(v));
|
||||
break;
|
||||
default:
|
||||
kp_printf(ks, "GCVALUE 0x%lx", (unsigned long)gcvalue(v));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/stacktrace.h>
|
||||
#include <linux/kallsyms.h>
|
||||
|
||||
static void kp_btrace_dump(ktap_state *ks, ktap_btrace *bt)
|
||||
{
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bt->nr_entries; i++) {
|
||||
unsigned long p = bt->entries[i];
|
||||
|
||||
if (p == ULONG_MAX)
|
||||
break;
|
||||
|
||||
SPRINT_SYMBOL(str, p);
|
||||
kp_printf(ks, "%s\n", str);
|
||||
}
|
||||
}
|
||||
|
||||
static int kp_btrace_equal(ktap_btrace *bt1, ktap_btrace *bt2)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (bt1->nr_entries != bt2->nr_entries)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < bt1->nr_entries; i++) {
|
||||
if (bt1->entries[i] != bt2->entries[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void kp_showobj(ktap_state *ks, const ktap_value *v)
|
||||
{
|
||||
switch (ttype(v)) {
|
||||
case KTAP_TNIL:
|
||||
kp_puts(ks, "nil");
|
||||
break;
|
||||
case KTAP_TNUMBER:
|
||||
kp_printf(ks, "%ld", nvalue(v));
|
||||
break;
|
||||
case KTAP_TBOOLEAN:
|
||||
kp_puts(ks, (bvalue(v) == 1) ? "true" : "false");
|
||||
break;
|
||||
case KTAP_TLIGHTUSERDATA:
|
||||
kp_printf(ks, "0x%lx", (unsigned long)pvalue(v));
|
||||
break;
|
||||
case KTAP_TLCF:
|
||||
kp_printf(ks, "0x%lx", (unsigned long)fvalue(v));
|
||||
break;
|
||||
case KTAP_TSHRSTR:
|
||||
case KTAP_TLNGSTR:
|
||||
kp_puts(ks, svalue(v));
|
||||
break;
|
||||
case KTAP_TUSERDATA:
|
||||
kp_printf(ks, "0x%lx", (unsigned long)uvalue(v));
|
||||
break;
|
||||
case KTAP_TTABLE:
|
||||
kp_table_dump(ks, hvalue(v));
|
||||
break;
|
||||
#ifdef __KERNEL__
|
||||
case KTAP_TEVENT:
|
||||
kp_transport_event_write(ks, evalue(v));
|
||||
break;
|
||||
case KTAP_TBTRACE:
|
||||
kp_btrace_dump(ks, btvalue(v));
|
||||
break;
|
||||
case KTAP_TAGGRTABLE:
|
||||
kp_aggrtable_dump(ks, ahvalue(v));
|
||||
break;
|
||||
case KTAP_TAGGRACCVAL:
|
||||
kp_aggraccval_dump(ks, aggraccvalue(v));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
kp_error(ks, "print unknown value type: %d\n", ttype(v));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* equality of ktap values. ks == NULL means raw equality
|
||||
*/
|
||||
int kp_equalobjv(ktap_state *ks, const ktap_value *t1, const ktap_value *t2)
|
||||
{
|
||||
switch (ttype(t1)) {
|
||||
case KTAP_TNIL:
|
||||
return 1;
|
||||
case KTAP_TNUMBER:
|
||||
return nvalue(t1) == nvalue(t2);
|
||||
case KTAP_TBOOLEAN:
|
||||
return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
|
||||
case KTAP_TLIGHTUSERDATA:
|
||||
return pvalue(t1) == pvalue(t2);
|
||||
case KTAP_TLCF:
|
||||
return fvalue(t1) == fvalue(t2);
|
||||
case KTAP_TSHRSTR:
|
||||
return eqshrstr(rawtsvalue(t1), rawtsvalue(t2));
|
||||
case KTAP_TLNGSTR:
|
||||
return kp_tstring_eqlngstr(rawtsvalue(t1), rawtsvalue(t2));
|
||||
case KTAP_TUSERDATA:
|
||||
if (uvalue(t1) == uvalue(t2))
|
||||
return 1;
|
||||
else if (ks == NULL)
|
||||
return 0;
|
||||
case KTAP_TTABLE:
|
||||
if (hvalue(t1) == hvalue(t2))
|
||||
return 1;
|
||||
else if (ks == NULL)
|
||||
return 0;
|
||||
#ifdef __KERNEL__
|
||||
case KTAP_TBTRACE:
|
||||
return kp_btrace_equal(btvalue(t1), btvalue(t2));
|
||||
#endif
|
||||
default:
|
||||
return gcvalue(t1) == gcvalue(t2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ktap will not use lua's length operator on table meaning,
|
||||
* also # is not for length operator any more in ktap.
|
||||
*
|
||||
* Quote from lua mannal:
|
||||
* 2.5.5 - The Length Operator
|
||||
*
|
||||
* The length operator is denoted by the unary operator #.
|
||||
* The length of a string is its number of bytes(that is,
|
||||
* the usual meaning of string length when each character is one byte).
|
||||
*
|
||||
* The length of a table t is defined to be any integer index n
|
||||
* such that t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil,
|
||||
* n can be zero. For a regular array, with non-nil values from 1 to a given n,
|
||||
* its length is exactly that n, the index of its last value. If the array has
|
||||
* "holes" (that is, nil values between other non-nil values), then #t can be
|
||||
* any of the indices that directly precedes a nil value
|
||||
* (that is, it may consider any such nil value as the end of the array).
|
||||
*/
|
||||
int kp_objlen(ktap_state *ks, const ktap_value *v)
|
||||
{
|
||||
switch(v->type) {
|
||||
case KTAP_TTABLE:
|
||||
return kp_table_length(ks, hvalue(v));
|
||||
case KTAP_TSTRING:
|
||||
return rawtsvalue(v)->tsv.len;
|
||||
default:
|
||||
kp_printf(ks, "cannot get length of type %d\n", v->type);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* need to protect allgc field? */
|
||||
ktap_gcobject *kp_newobject(ktap_state *ks, int type, size_t size,
|
||||
ktap_gcobject **list)
|
||||
{
|
||||
ktap_gcobject *o;
|
||||
|
||||
o = kp_malloc(ks, size);
|
||||
if (list == NULL)
|
||||
list = &G(ks)->allgc;
|
||||
|
||||
gch(o)->tt = type;
|
||||
gch(o)->next = *list;
|
||||
*list = o;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
ktap_upval *kp_newupval(ktap_state *ks)
|
||||
{
|
||||
ktap_upval *uv;
|
||||
|
||||
uv = &kp_newobject(ks, KTAP_TUPVAL, sizeof(ktap_upval), NULL)->uv;
|
||||
uv->v = &uv->u.value;
|
||||
setnilvalue(uv->v);
|
||||
return uv;
|
||||
}
|
||||
|
||||
static ktap_btrace *kp_newbacktrace(ktap_state *ks, ktap_gcobject **list)
|
||||
{
|
||||
ktap_btrace *bt;
|
||||
|
||||
bt = &kp_newobject(ks, KTAP_TBTRACE, sizeof(ktap_btrace), list)->bt;
|
||||
return bt;
|
||||
}
|
||||
|
||||
void kp_objclone(ktap_state *ks, const ktap_value *o, ktap_value *newo,
|
||||
ktap_gcobject **list)
|
||||
{
|
||||
if (ttisbtrace(o)) {
|
||||
ktap_btrace *bt;
|
||||
bt = kp_newbacktrace(ks, list);
|
||||
bt->nr_entries = btvalue(o)->nr_entries;
|
||||
memcpy(&bt->entries[0], &btvalue(o)->entries[0],
|
||||
sizeof(bt->entries));
|
||||
setbtvalue(newo, bt);
|
||||
} else {
|
||||
kp_error(ks, "cannot clone ktap value type %d\n", ttype(o));
|
||||
setnilvalue(newo);
|
||||
}
|
||||
}
|
||||
|
||||
ktap_closure *kp_newlclosure(ktap_state *ks, int n)
|
||||
{
|
||||
ktap_closure *cl;
|
||||
|
||||
cl = (ktap_closure *)kp_newobject(ks, KTAP_TLCL, sizeof(*cl), NULL);
|
||||
cl->l.p = NULL;
|
||||
cl->l.nupvalues = n;
|
||||
while (n--)
|
||||
cl->l.upvals[n] = NULL;
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
static void free_proto(ktap_state *ks, ktap_proto *f)
|
||||
{
|
||||
kp_free(ks, f->code);
|
||||
kp_free(ks, f->p);
|
||||
kp_free(ks, f->k);
|
||||
kp_free(ks, f->lineinfo);
|
||||
kp_free(ks, f->locvars);
|
||||
kp_free(ks, f->upvalues);
|
||||
kp_free(ks, f);
|
||||
}
|
||||
|
||||
ktap_proto *kp_newproto(ktap_state *ks)
|
||||
{
|
||||
ktap_proto *f;
|
||||
f = (ktap_proto *)kp_newobject(ks, KTAP_TPROTO, sizeof(*f), NULL);
|
||||
f->k = NULL;
|
||||
f->sizek = 0;
|
||||
f->p = NULL;
|
||||
f->sizep = 0;
|
||||
f->code = NULL;
|
||||
f->cache = NULL;
|
||||
f->sizecode = 0;
|
||||
f->lineinfo = NULL;
|
||||
f->sizelineinfo = 0;
|
||||
f->upvalues = NULL;
|
||||
f->sizeupvalues = 0;
|
||||
f->numparams = 0;
|
||||
f->is_vararg = 0;
|
||||
f->maxstacksize = 0;
|
||||
f->locvars = NULL;
|
||||
f->sizelocvars = 0;
|
||||
f->linedefined = 0;
|
||||
f->lastlinedefined = 0;
|
||||
f->source = NULL;
|
||||
return f;
|
||||
}
|
||||
|
||||
static ktap_udata *newudata(ktap_state *ks, size_t s)
|
||||
{
|
||||
ktap_udata *u;
|
||||
|
||||
u = &kp_newobject(ks, KTAP_TUSERDATA, sizeof(ktap_udata) + s, NULL)->u;
|
||||
u->uv.len = s;
|
||||
return u;
|
||||
}
|
||||
|
||||
void *kp_newuserdata(ktap_state *ks, size_t size)
|
||||
{
|
||||
ktap_udata *u;
|
||||
|
||||
u = newudata(ks, size);
|
||||
return u + 1;
|
||||
}
|
||||
|
||||
void kp_free_gclist(ktap_state *ks, ktap_gcobject *o)
|
||||
{
|
||||
while (o) {
|
||||
ktap_gcobject *next;
|
||||
|
||||
next = gch(o)->next;
|
||||
switch (gch(o)->tt) {
|
||||
case KTAP_TTABLE:
|
||||
kp_table_free(ks, (ktap_table *)o);
|
||||
break;
|
||||
case KTAP_TPROTO:
|
||||
free_proto(ks, (ktap_proto *)o);
|
||||
break;
|
||||
#ifdef __KERNEL__
|
||||
case KTAP_TAGGRTABLE:
|
||||
kp_aggrtable_free(ks, (ktap_aggrtable *)o);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
kp_free(ks, o);
|
||||
}
|
||||
o = next;
|
||||
}
|
||||
}
|
||||
|
||||
void kp_free_all_gcobject(ktap_state *ks)
|
||||
{
|
||||
kp_free_gclist(ks, G(ks)->allgc);
|
||||
G(ks)->allgc = NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/*
|
||||
* make header for precompiled chunks
|
||||
* if you change the code below be sure to update load_header and FORMAT above
|
||||
* and KTAPC_HEADERSIZE in ktap_types.h
|
||||
*/
|
||||
void kp_header(u8 *h)
|
||||
{
|
||||
int x = 1;
|
||||
|
||||
memcpy(h, KTAP_SIGNATURE, sizeof(KTAP_SIGNATURE) - sizeof(char));
|
||||
h += sizeof(KTAP_SIGNATURE) - sizeof(char);
|
||||
*h++ = (u8)VERSION;
|
||||
*h++ = (u8)FORMAT;
|
||||
*h++ = (u8)(*(char*)&x); /* endianness */
|
||||
*h++ = (u8)(sizeof(int));
|
||||
*h++ = (u8)(sizeof(size_t));
|
||||
*h++ = (u8)(sizeof(ktap_instruction));
|
||||
*h++ = (u8)(sizeof(ktap_number));
|
||||
*h++ = (u8)(((ktap_number)0.5) == 0); /* is ktap_number integral? */
|
||||
memcpy(h, KTAPC_TAIL, sizeof(KTAPC_TAIL) - sizeof(char));
|
||||
}
|
||||
|
||||
|
@ -1,127 +0,0 @@
|
||||
/*
|
||||
* opcode.c
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "../include/ktap_types.h"
|
||||
#include "../include/ktap_opcodes.h"
|
||||
|
||||
const char *const ktap_opnames[NUM_OPCODES + 1] = {
|
||||
"MOVE",
|
||||
"LOADK",
|
||||
"LOADKX",
|
||||
"LOADBOOL",
|
||||
"LOADNIL",
|
||||
"GETUPVAL",
|
||||
"GETTABUP",
|
||||
"GETTABLE",
|
||||
"SETTABUP",
|
||||
"SETTABUP_INCR",
|
||||
"SETUPVAL",
|
||||
"SETTABLE",
|
||||
"SETTABLE_INCR",
|
||||
"NEWTABLE",
|
||||
"SELF",
|
||||
"ADD",
|
||||
"SUB",
|
||||
"MUL",
|
||||
"DIV",
|
||||
"MOD",
|
||||
"POW",
|
||||
"UNM",
|
||||
"NOT",
|
||||
"LEN",
|
||||
"CONCAT",
|
||||
"JMP",
|
||||
"EQ",
|
||||
"LT",
|
||||
"LE",
|
||||
"TEST",
|
||||
"TESTSET",
|
||||
"CALL",
|
||||
"TAILCALL",
|
||||
"RETURN",
|
||||
"FORLOOP",
|
||||
"FORPREP",
|
||||
"TFORCALL",
|
||||
"TFORLOOP",
|
||||
"SETLIST",
|
||||
"CLOSURE",
|
||||
"VARARG",
|
||||
"EXTRAARG",
|
||||
|
||||
"EVENT",
|
||||
"EVENT_NAME",
|
||||
"EVENT_ARG", /* arg1, arg2 .. arg9 */
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))
|
||||
|
||||
const u8 ktap_opmodes[NUM_OPCODES] = {
|
||||
/* T A B C mode opcode */
|
||||
opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */
|
||||
,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */
|
||||
,opmode(0, 1, OpArgN, OpArgN, iABx) /* OP_LOADKX */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */
|
||||
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_LOADNIL */
|
||||
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */
|
||||
,opmode(0, 1, OpArgU, OpArgK, iABC) /* OP_GETTABUP */
|
||||
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */
|
||||
,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABUP */
|
||||
,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */
|
||||
,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */
|
||||
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */
|
||||
,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */
|
||||
,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */
|
||||
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */
|
||||
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */
|
||||
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */
|
||||
,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TEST */
|
||||
,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */
|
||||
,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */
|
||||
,opmode(0, 0, OpArgN, OpArgU, iABC) /* OP_TFORCALL */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_TFORLOOP */
|
||||
,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */
|
||||
,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */
|
||||
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */
|
||||
,opmode(0, 0, OpArgU, OpArgU, iAx) /* OP_EXTRAARG */
|
||||
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_EVENT */
|
||||
};
|
||||
|
||||
|
@ -1,196 +0,0 @@
|
||||
/*
|
||||
* strfmt.c - printf implementation
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include "../include/ktap.h"
|
||||
|
||||
/* macro to `unsign' a character */
|
||||
#define uchar(c) ((unsigned char)(c))
|
||||
|
||||
#define L_ESC '%'
|
||||
|
||||
/* valid flags in a format specification */
|
||||
#define FLAGS "-+ #0"
|
||||
|
||||
#define INTFRMLEN "ll"
|
||||
#define INTFRM_T long long
|
||||
|
||||
/*
|
||||
* maximum size of each format specification (such as '%-099.99d')
|
||||
* (+10 accounts for %99.99x plus margin of error)
|
||||
*/
|
||||
#define MAX_FORMAT (sizeof(FLAGS) + sizeof(INTFRMLEN) + 10)
|
||||
|
||||
static const char *scanformat(ktap_state *ks, const char *strfrmt, char *form)
|
||||
{
|
||||
const char *p = strfrmt;
|
||||
while (*p != '\0' && strchr(FLAGS, *p) != NULL)
|
||||
p++; /* skip flags */
|
||||
|
||||
if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char)) {
|
||||
kp_error(ks, "invalid format (repeated flags)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (isdigit(uchar(*p)))
|
||||
p++; /* skip width */
|
||||
|
||||
if (isdigit(uchar(*p)))
|
||||
p++; /* (2 digits at most) */
|
||||
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
if (isdigit(uchar(*p)))
|
||||
p++; /* skip precision */
|
||||
if (isdigit(uchar(*p)))
|
||||
p++; /* (2 digits at most) */
|
||||
}
|
||||
|
||||
if (isdigit(uchar(*p))) {
|
||||
kp_error(ks, "invalid format (width or precision too long)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*(form++) = '%';
|
||||
memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char));
|
||||
form += p - strfrmt + 1;
|
||||
*form = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* add length modifier into formats
|
||||
*/
|
||||
static void addlenmod(char *form, const char *lenmod)
|
||||
{
|
||||
size_t l = strlen(form);
|
||||
size_t lm = strlen(lenmod);
|
||||
char spec = form[l - 1];
|
||||
|
||||
strcpy(form + l - 1, lenmod);
|
||||
form[l + lm - 1] = spec;
|
||||
form[l + lm] = '\0';
|
||||
}
|
||||
|
||||
|
||||
static void ktap_argerror(ktap_state *ks, int narg, const char *extramsg)
|
||||
{
|
||||
kp_error(ks, "bad argument #%d: (%s)\n", narg, extramsg);
|
||||
}
|
||||
|
||||
int kp_strfmt(ktap_state *ks, struct trace_seq *seq)
|
||||
{
|
||||
int arg = 1;
|
||||
size_t sfl;
|
||||
ktap_value *arg_fmt = kp_arg(ks, 1);
|
||||
int argnum = kp_arg_nr(ks);
|
||||
const char *strfrmt, *strfrmt_end;
|
||||
|
||||
strfrmt = svalue(arg_fmt);
|
||||
sfl = rawtsvalue(arg_fmt)->tsv.len;
|
||||
strfrmt_end = strfrmt + sfl;
|
||||
|
||||
while (strfrmt < strfrmt_end) {
|
||||
if (*strfrmt != L_ESC)
|
||||
trace_seq_putc(seq, *strfrmt++);
|
||||
else if (*++strfrmt == L_ESC)
|
||||
trace_seq_putc(seq, *strfrmt++);
|
||||
else { /* format item */
|
||||
char form[MAX_FORMAT];
|
||||
|
||||
if (++arg > argnum) {
|
||||
ktap_argerror(ks, arg, "no value");
|
||||
return -1;
|
||||
}
|
||||
|
||||
strfrmt = scanformat(ks, strfrmt, form);
|
||||
switch (*strfrmt++) {
|
||||
case 'c':
|
||||
trace_seq_printf(seq, form,
|
||||
nvalue(kp_arg(ks, arg)));
|
||||
break;
|
||||
case 'd': case 'i': {
|
||||
ktap_number n = nvalue(kp_arg(ks, arg));
|
||||
INTFRM_T ni = (INTFRM_T)n;
|
||||
addlenmod(form, INTFRMLEN);
|
||||
trace_seq_printf(seq, form, ni);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
SPRINT_SYMBOL(str, nvalue(kp_arg(ks, arg)));
|
||||
trace_seq_puts(seq, str);
|
||||
break;
|
||||
}
|
||||
case 'o': case 'u': case 'x': case 'X': {
|
||||
ktap_number n = nvalue(kp_arg(ks, arg));
|
||||
unsigned INTFRM_T ni = (unsigned INTFRM_T)n;
|
||||
addlenmod(form, INTFRMLEN);
|
||||
trace_seq_printf(seq, form, ni);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
ktap_value *v = kp_arg(ks, arg);
|
||||
const char *s;
|
||||
size_t l;
|
||||
|
||||
if (isnil(v)) {
|
||||
trace_seq_puts(seq, "nil");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ttisevent(v)) {
|
||||
kp_event_tostring(ks, seq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s = svalue(v);
|
||||
l = rawtsvalue(v)->tsv.len;
|
||||
if (!strchr(form, '.') && l >= 100) {
|
||||
/*
|
||||
* no precision and string is too long
|
||||
* to be formatted;
|
||||
* keep original string
|
||||
*/
|
||||
trace_seq_puts(seq, s);
|
||||
break;
|
||||
} else {
|
||||
trace_seq_printf(seq, form, s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
default: /* also treat cases `pnLlh' */
|
||||
kp_error(ks, "invalid option " KTAP_QL("%%%c")
|
||||
" to " KTAP_QL("format"),
|
||||
*(strfrmt - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,636 +0,0 @@
|
||||
/*
|
||||
* transport.c - ktap transport functionality
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/ftrace_event.h>
|
||||
#include <linux/stacktrace.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "../include/ktap.h"
|
||||
|
||||
struct ktap_trace_iterator {
|
||||
struct ring_buffer *buffer;
|
||||
int print_timestamp;
|
||||
void *private;
|
||||
|
||||
struct trace_iterator iter;
|
||||
};
|
||||
|
||||
enum ktap_trace_type {
|
||||
__TRACE_FIRST_TYPE = 0,
|
||||
|
||||
TRACE_FN = 1, /* must be same as ftrace definition in kernel */
|
||||
TRACE_PRINT,
|
||||
TRACE_BPUTS,
|
||||
TRACE_STACK,
|
||||
TRACE_USER_STACK,
|
||||
|
||||
__TRACE_LAST_TYPE,
|
||||
};
|
||||
|
||||
#define KTAP_TRACE_ITER(iter) \
|
||||
container_of(iter, struct ktap_trace_iterator, iter)
|
||||
|
||||
ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt)
|
||||
{
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
if (!cnt)
|
||||
return 0;
|
||||
|
||||
if (s->len <= s->readpos)
|
||||
return -EBUSY;
|
||||
|
||||
len = s->len - s->readpos;
|
||||
if (cnt > len)
|
||||
cnt = len;
|
||||
ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt);
|
||||
if (ret == cnt)
|
||||
return -EFAULT;
|
||||
|
||||
cnt -= ret;
|
||||
|
||||
s->readpos += cnt;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int trace_seq_puts(struct trace_seq *s, const char *str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
|
||||
if (s->full)
|
||||
return 0;
|
||||
|
||||
if (len > ((PAGE_SIZE - 1) - s->len)) {
|
||||
s->full = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(s->buffer + s->len, str, len);
|
||||
s->len += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int trace_empty(struct trace_iterator *iter)
|
||||
{
|
||||
struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
|
||||
int cpu;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
if (!ring_buffer_empty_cpu(ktap_iter->buffer, cpu))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void trace_consume(struct trace_iterator *iter)
|
||||
{
|
||||
struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
|
||||
|
||||
ring_buffer_consume(ktap_iter->buffer, iter->cpu, &iter->ts,
|
||||
&iter->lost_events);
|
||||
}
|
||||
|
||||
unsigned long long ns2usecs(cycle_t nsec)
|
||||
{
|
||||
nsec += 500;
|
||||
do_div(nsec, 1000);
|
||||
return nsec;
|
||||
}
|
||||
|
||||
static int trace_print_timestamp(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
unsigned long long t;
|
||||
unsigned long secs, usec_rem;
|
||||
|
||||
t = ns2usecs(iter->ts);
|
||||
usec_rem = do_div(t, USEC_PER_SEC);
|
||||
secs = (unsigned long)t;
|
||||
|
||||
return trace_seq_printf(s, "%5lu.%06lu: ", secs, usec_rem);
|
||||
}
|
||||
|
||||
/* todo: export kernel function ftrace_find_event in future, and make faster */
|
||||
static struct trace_event *(*ftrace_find_event)(int type);
|
||||
|
||||
static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
|
||||
{
|
||||
struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
|
||||
struct trace_entry *entry = iter->ent;
|
||||
struct trace_event *ev;
|
||||
|
||||
ev = ftrace_find_event(entry->type);
|
||||
|
||||
if (ktap_iter->print_timestamp && !trace_print_timestamp(iter))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
if (ev) {
|
||||
int ret = ev->funcs->trace(iter, 0, ev);
|
||||
|
||||
/* overwrite '\n' at the ending */
|
||||
iter->seq.buffer[iter->seq.len - 1] = '\0';
|
||||
iter->seq.len--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
static enum print_line_t print_trace_stack(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_entry *entry = iter->ent;
|
||||
struct stack_trace trace;
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
int i;
|
||||
|
||||
trace.entries = (unsigned long *)(entry + 1);
|
||||
trace.nr_entries = (iter->ent_size - sizeof(*entry)) /
|
||||
sizeof(unsigned long);
|
||||
|
||||
if (!trace_seq_puts(&iter->seq, "<stack trace>\n"))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
for (i = 0; i < trace.nr_entries; i++) {
|
||||
unsigned long p = trace.entries[i];
|
||||
|
||||
if (p == ULONG_MAX)
|
||||
break;
|
||||
|
||||
sprint_symbol(str, p);
|
||||
if (!trace_seq_printf(&iter->seq, " => %s\n", str))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
struct ktap_ftrace_entry {
|
||||
struct trace_entry entry;
|
||||
unsigned long ip;
|
||||
unsigned long parent_ip;
|
||||
};
|
||||
|
||||
static enum print_line_t print_trace_fn(struct trace_iterator *iter)
|
||||
{
|
||||
struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
|
||||
struct ktap_ftrace_entry *field = (struct ktap_ftrace_entry *)iter->ent;
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
|
||||
if (ktap_iter->print_timestamp && !trace_print_timestamp(iter))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
sprint_symbol(str, field->ip);
|
||||
if (!trace_seq_puts(&iter->seq, str))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
if (!trace_seq_puts(&iter->seq, " <- "))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
sprint_symbol(str, field->parent_ip);
|
||||
if (!trace_seq_puts(&iter->seq, str))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t print_trace_bputs(struct trace_iterator *iter)
|
||||
{
|
||||
if (!trace_seq_puts(&iter->seq,
|
||||
(const char *)(*(unsigned long *)(iter->ent + 1))))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t print_trace_line(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_entry *entry = iter->ent;
|
||||
char *str = (char *)(entry + 1);
|
||||
|
||||
if (entry->type == TRACE_PRINT) {
|
||||
if (!trace_seq_printf(&iter->seq, "%s", str))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
if (entry->type == TRACE_BPUTS)
|
||||
return print_trace_bputs(iter);
|
||||
|
||||
if (entry->type == TRACE_STACK)
|
||||
return print_trace_stack(iter);
|
||||
|
||||
if (entry->type == TRACE_FN)
|
||||
return print_trace_fn(iter);
|
||||
|
||||
return print_trace_fmt(iter);
|
||||
}
|
||||
|
||||
static struct trace_entry *
|
||||
peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts,
|
||||
unsigned long *lost_events)
|
||||
{
|
||||
struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
|
||||
struct ring_buffer_event *event;
|
||||
|
||||
event = ring_buffer_peek(ktap_iter->buffer, cpu, ts, lost_events);
|
||||
if (event) {
|
||||
iter->ent_size = ring_buffer_event_length(event);
|
||||
return ring_buffer_event_data(event);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct trace_entry *
|
||||
__find_next_entry(struct trace_iterator *iter, int *ent_cpu,
|
||||
unsigned long *missing_events, u64 *ent_ts)
|
||||
{
|
||||
struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
|
||||
struct ring_buffer *buffer = ktap_iter->buffer;
|
||||
struct trace_entry *ent, *next = NULL;
|
||||
unsigned long lost_events = 0, next_lost = 0;
|
||||
u64 next_ts = 0, ts;
|
||||
int next_cpu = -1;
|
||||
int next_size = 0;
|
||||
int cpu;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
if (ring_buffer_empty_cpu(buffer, cpu))
|
||||
continue;
|
||||
|
||||
ent = peek_next_entry(iter, cpu, &ts, &lost_events);
|
||||
/*
|
||||
* Pick the entry with the smallest timestamp:
|
||||
*/
|
||||
if (ent && (!next || ts < next_ts)) {
|
||||
next = ent;
|
||||
next_cpu = cpu;
|
||||
next_ts = ts;
|
||||
next_lost = lost_events;
|
||||
next_size = iter->ent_size;
|
||||
}
|
||||
}
|
||||
|
||||
iter->ent_size = next_size;
|
||||
|
||||
if (ent_cpu)
|
||||
*ent_cpu = next_cpu;
|
||||
|
||||
if (ent_ts)
|
||||
*ent_ts = next_ts;
|
||||
|
||||
if (missing_events)
|
||||
*missing_events = next_lost;
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
/* Find the next real entry, and increment the iterator to the next entry */
|
||||
static void *trace_find_next_entry_inc(struct trace_iterator *iter)
|
||||
{
|
||||
iter->ent = __find_next_entry(iter, &iter->cpu,
|
||||
&iter->lost_events, &iter->ts);
|
||||
if (iter->ent)
|
||||
iter->idx++;
|
||||
|
||||
return iter->ent ? iter : NULL;
|
||||
}
|
||||
|
||||
static void poll_wait_pipe(void)
|
||||
{
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
/* sleep for 100 msecs, and try again. */
|
||||
schedule_timeout(HZ / 10);
|
||||
}
|
||||
|
||||
static int tracing_wait_pipe(struct file *filp)
|
||||
{
|
||||
struct trace_iterator *iter = filp->private_data;
|
||||
struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
|
||||
ktap_state *ks = ktap_iter->private;
|
||||
|
||||
while (trace_empty(iter)) {
|
||||
|
||||
if ((filp->f_flags & O_NONBLOCK)) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
mutex_unlock(&iter->mutex);
|
||||
|
||||
poll_wait_pipe();
|
||||
|
||||
mutex_lock(&iter->mutex);
|
||||
|
||||
if (G(ks)->wait_user && trace_empty(iter))
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
tracing_read_pipe(struct file *filp, char __user *ubuf, size_t cnt,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct trace_iterator *iter = filp->private_data;
|
||||
ssize_t sret;
|
||||
|
||||
/* return any leftover data */
|
||||
sret = trace_seq_to_user(&iter->seq, ubuf, cnt);
|
||||
if (sret != -EBUSY)
|
||||
return sret;
|
||||
/*
|
||||
* Avoid more than one consumer on a single file descriptor
|
||||
* This is just a matter of traces coherency, the ring buffer itself
|
||||
* is protected.
|
||||
*/
|
||||
mutex_lock(&iter->mutex);
|
||||
|
||||
waitagain:
|
||||
sret = tracing_wait_pipe(filp);
|
||||
if (sret <= 0)
|
||||
goto out;
|
||||
|
||||
/* stop when tracing is finished */
|
||||
if (trace_empty(iter)) {
|
||||
sret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cnt >= PAGE_SIZE)
|
||||
cnt = PAGE_SIZE - 1;
|
||||
|
||||
/* reset all but tr, trace, and overruns */
|
||||
memset(&iter->seq, 0,
|
||||
sizeof(struct trace_iterator) -
|
||||
offsetof(struct trace_iterator, seq));
|
||||
iter->pos = -1;
|
||||
|
||||
while (trace_find_next_entry_inc(iter) != NULL) {
|
||||
enum print_line_t ret;
|
||||
int len = iter->seq.len;
|
||||
|
||||
ret = print_trace_line(iter);
|
||||
if (ret == TRACE_TYPE_PARTIAL_LINE) {
|
||||
/* don't print partial lines */
|
||||
iter->seq.len = len;
|
||||
break;
|
||||
}
|
||||
if (ret != TRACE_TYPE_NO_CONSUME)
|
||||
trace_consume(iter);
|
||||
|
||||
if (iter->seq.len >= cnt)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Setting the full flag means we reached the trace_seq buffer
|
||||
* size and we should leave by partial output condition above.
|
||||
* One of the trace_seq_* functions is not used properly.
|
||||
*/
|
||||
WARN_ONCE(iter->seq.full, "full flag set for trace type %d",
|
||||
iter->ent->type);
|
||||
}
|
||||
|
||||
/* Now copy what we have to the user */
|
||||
sret = trace_seq_to_user(&iter->seq, ubuf, cnt);
|
||||
if (iter->seq.readpos >= iter->seq.len)
|
||||
trace_seq_init(&iter->seq);
|
||||
|
||||
/*
|
||||
* If there was nothing to send to user, in spite of consuming trace
|
||||
* entries, go back to wait for more entries.
|
||||
*/
|
||||
if (sret == -EBUSY)
|
||||
goto waitagain;
|
||||
|
||||
out:
|
||||
mutex_unlock(&iter->mutex);
|
||||
|
||||
return sret;
|
||||
}
|
||||
|
||||
static int tracing_open_pipe(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct ktap_trace_iterator *ktap_iter;
|
||||
ktap_state *ks = inode->i_private;
|
||||
|
||||
/* create a buffer to store the information to pass to userspace */
|
||||
ktap_iter = kzalloc(sizeof(*ktap_iter), GFP_KERNEL);
|
||||
if (!ktap_iter)
|
||||
return -ENOMEM;
|
||||
|
||||
ktap_iter->private = ks;
|
||||
ktap_iter->buffer = G(ks)->buffer;
|
||||
ktap_iter->print_timestamp = G(ks)->parm->print_timestamp;
|
||||
mutex_init(&ktap_iter->iter.mutex);
|
||||
filp->private_data = &ktap_iter->iter;
|
||||
|
||||
nonseekable_open(inode, filp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tracing_release_pipe(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct trace_iterator *iter = file->private_data;
|
||||
struct ktap_trace_iterator *ktap_iter = KTAP_TRACE_ITER(iter);
|
||||
|
||||
mutex_destroy(&iter->mutex);
|
||||
kfree(ktap_iter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations tracing_pipe_fops = {
|
||||
.open = tracing_open_pipe,
|
||||
.read = tracing_read_pipe,
|
||||
.splice_read = NULL,
|
||||
.release = tracing_release_pipe,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
/*
|
||||
* print_backtrace maybe called from ktap mainthread, so be
|
||||
* care on race with event closure thread.
|
||||
*
|
||||
* preempt disabled in ring_buffer_lock_reserve
|
||||
*
|
||||
* The implementation is similar with funtion __ftrace_trace_stack.
|
||||
*/
|
||||
void kp_transport_print_backtrace(ktap_state *ks)
|
||||
{
|
||||
struct ring_buffer *buffer = G(ks)->buffer;
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_entry *entry;
|
||||
int size;
|
||||
|
||||
size = KTAP_STACK_MAX_ENTRIES * sizeof(unsigned long);
|
||||
event = ring_buffer_lock_reserve(buffer, sizeof(*entry) + size);
|
||||
if (!event) {
|
||||
return;
|
||||
} else {
|
||||
struct stack_trace trace;
|
||||
|
||||
entry = ring_buffer_event_data(event);
|
||||
tracing_generic_entry_update(entry, 0, 0);
|
||||
entry->type = TRACE_STACK;
|
||||
|
||||
trace.nr_entries = 0;
|
||||
trace.skip = 10;
|
||||
trace.max_entries = KTAP_STACK_MAX_ENTRIES;
|
||||
trace.entries = (unsigned long *)(entry + 1);
|
||||
save_stack_trace(&trace);
|
||||
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void kp_transport_event_write(ktap_state *ks, struct ktap_event *e)
|
||||
{
|
||||
struct ring_buffer *buffer = G(ks)->buffer;
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_entry *entry;
|
||||
|
||||
event = ring_buffer_lock_reserve(buffer, e->entry_size +
|
||||
sizeof(struct ftrace_event_call *));
|
||||
if (!event) {
|
||||
return;
|
||||
} else {
|
||||
entry = ring_buffer_event_data(event);
|
||||
|
||||
memcpy(entry, e->entry, e->entry_size);
|
||||
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
}
|
||||
}
|
||||
|
||||
void kp_transport_write(ktap_state *ks, const void *data, size_t length)
|
||||
{
|
||||
struct ring_buffer *buffer = G(ks)->buffer;
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_entry *entry;
|
||||
int size;
|
||||
|
||||
size = sizeof(struct trace_entry) + length;
|
||||
|
||||
event = ring_buffer_lock_reserve(buffer, size);
|
||||
if (!event) {
|
||||
return;
|
||||
} else {
|
||||
entry = ring_buffer_event_data(event);
|
||||
|
||||
tracing_generic_entry_update(entry, 0, 0);
|
||||
entry->type = TRACE_PRINT;
|
||||
memcpy(entry + 1, data, length);
|
||||
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
}
|
||||
}
|
||||
|
||||
/* general print function */
|
||||
void kp_printf(ktap_state *ks, const char *fmt, ...)
|
||||
{
|
||||
char buff[1024];
|
||||
va_list args;
|
||||
int len;
|
||||
|
||||
va_start(args, fmt);
|
||||
len = vscnprintf(buff, 1024, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
buff[len] = '\0';
|
||||
kp_transport_write(ks, buff, len + 1);
|
||||
}
|
||||
|
||||
void __kp_puts(ktap_state *ks, const char *str)
|
||||
{
|
||||
kp_transport_write(ks, str, strlen(str) + 1);
|
||||
}
|
||||
|
||||
void __kp_bputs(ktap_state *ks, const char *str)
|
||||
{
|
||||
struct ring_buffer *buffer = G(ks)->buffer;
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_entry *entry;
|
||||
int size;
|
||||
|
||||
size = sizeof(struct trace_entry) + sizeof(unsigned long *);
|
||||
|
||||
event = ring_buffer_lock_reserve(buffer, size);
|
||||
if (!event) {
|
||||
return;
|
||||
} else {
|
||||
entry = ring_buffer_event_data(event);
|
||||
|
||||
tracing_generic_entry_update(entry, 0, 0);
|
||||
entry->type = TRACE_BPUTS;
|
||||
*(unsigned long *)(entry + 1) = (unsigned long)str;
|
||||
|
||||
ring_buffer_unlock_commit(buffer, event);
|
||||
}
|
||||
}
|
||||
|
||||
void kp_transport_exit(ktap_state *ks)
|
||||
{
|
||||
ring_buffer_free(G(ks)->buffer);
|
||||
debugfs_remove(G(ks)->trace_pipe_dentry);
|
||||
}
|
||||
|
||||
#define TRACE_BUF_SIZE_DEFAULT 1441792UL /* 16384 * 88 (sizeof(entry)) */
|
||||
|
||||
int kp_transport_init(ktap_state *ks, struct dentry *dir)
|
||||
{
|
||||
struct ring_buffer *buffer;
|
||||
struct dentry *dentry;
|
||||
char filename[32] = {0};
|
||||
|
||||
ftrace_find_event = (void *)kallsyms_lookup_name("ftrace_find_event");
|
||||
if (!ftrace_find_event) {
|
||||
printk("ktap: cannot lookup ftrace_find_event in kallsyms\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buffer = ring_buffer_alloc(TRACE_BUF_SIZE_DEFAULT, RB_FL_OVERWRITE);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
sprintf(filename, "trace_pipe_%d", (int)task_tgid_vnr(current));
|
||||
|
||||
dentry = debugfs_create_file(filename, 0444, dir,
|
||||
ks, &tracing_pipe_fops);
|
||||
if (!dentry) {
|
||||
pr_err("ktapvm: cannot create trace_pipe file in debugfs\n");
|
||||
ring_buffer_free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
G(ks)->buffer = buffer;
|
||||
G(ks)->trace_pipe_dentry = dentry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,287 +0,0 @@
|
||||
/*
|
||||
* tstring.c - ktap tstring data struction manipulation function
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include "../include/ktap.h"
|
||||
#else
|
||||
#include "../include/ktap_types.h"
|
||||
#endif
|
||||
|
||||
#define STRING_MAXSHORTLEN 40
|
||||
|
||||
int kp_tstring_cmp(const ktap_string *ls, const ktap_string *rs)
|
||||
{
|
||||
const char *l = getstr(ls);
|
||||
size_t ll = ls->tsv.len;
|
||||
const char *r = getstr(rs);
|
||||
size_t lr = rs->tsv.len;
|
||||
|
||||
for (;;) {
|
||||
int temp = strcmp(l, r);
|
||||
if (temp != 0)
|
||||
return temp;
|
||||
else {
|
||||
/* strings are equal up to a `\0' */
|
||||
|
||||
/* index of first `\0' in both strings */
|
||||
size_t len = strlen(l);
|
||||
|
||||
/* r is finished? */
|
||||
if (len == lr)
|
||||
return (len == ll) ? 0 : 1;
|
||||
else if (len == ll) /* l is finished? */
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* both strings longer than `len';
|
||||
* go on comparing (after the `\0')
|
||||
*/
|
||||
len++;
|
||||
l += len; ll -= len; r += len; lr -= len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* equality for long strings
|
||||
*/
|
||||
int kp_tstring_eqlngstr(ktap_string *a, ktap_string *b)
|
||||
{
|
||||
size_t len = a->tsv.len;
|
||||
|
||||
return (a == b) || ((len == b->tsv.len) &&
|
||||
(memcmp(getstr(a), getstr(b), len) == 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* equality for strings
|
||||
*/
|
||||
int kp_tstring_eqstr(ktap_string *a, ktap_string *b)
|
||||
{
|
||||
return (a->tsv.tt == b->tsv.tt) &&
|
||||
(a->tsv.tt == KTAP_TSHRSTR ? eqshrstr(a, b) :
|
||||
kp_tstring_eqlngstr(a, b));
|
||||
}
|
||||
|
||||
#define STRING_HASHLIMIT 5
|
||||
unsigned int kp_string_hash(const char *str, size_t l, unsigned int seed)
|
||||
{
|
||||
unsigned int h = seed ^ l;
|
||||
size_t l1;
|
||||
size_t step = (l >> STRING_HASHLIMIT) + 1;
|
||||
|
||||
for (l1 = l; l1 >= step; l1 -= step)
|
||||
h = h ^ ((h<<5) + (h>>2) + (u8)(str[l1 - 1]));
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* resizes the string table
|
||||
*/
|
||||
void kp_tstring_resize(ktap_state *ks, int newsize)
|
||||
{
|
||||
int i;
|
||||
ktap_stringtable *tb = &G(ks)->strt;
|
||||
|
||||
if (newsize > tb->size) {
|
||||
kp_realloc(ks, tb->hash, tb->size, newsize, ktap_gcobject *);
|
||||
|
||||
for (i = tb->size; i < newsize; i++)
|
||||
tb->hash[i] = NULL;
|
||||
}
|
||||
|
||||
/* rehash */
|
||||
for (i = 0; i < tb->size; i++) {
|
||||
ktap_gcobject *p = tb->hash[i];
|
||||
tb->hash[i] = NULL;
|
||||
|
||||
while (p) {
|
||||
ktap_gcobject *next = gch(p)->next;
|
||||
unsigned int h = lmod(gco2ts(p)->hash, newsize);
|
||||
|
||||
gch(p)->next = tb->hash[h];
|
||||
tb->hash[h] = p;
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
|
||||
if (newsize < tb->size) {
|
||||
/* shrinking slice must be empty */
|
||||
kp_realloc(ks, tb->hash, tb->size, newsize, ktap_gcobject *);
|
||||
}
|
||||
|
||||
tb->size = newsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* creates a new string object
|
||||
*/
|
||||
static ktap_string *createstrobj(ktap_state *ks, const char *str, size_t l,
|
||||
int tag, unsigned int h, ktap_gcobject **list)
|
||||
{
|
||||
ktap_string *ts;
|
||||
size_t totalsize; /* total size of TString object */
|
||||
|
||||
totalsize = sizeof(ktap_string) + ((l + 1) * sizeof(char));
|
||||
ts = &kp_newobject(ks, tag, totalsize, list)->ts;
|
||||
ts->tsv.len = l;
|
||||
ts->tsv.hash = h;
|
||||
ts->tsv.extra = 0;
|
||||
memcpy(ts + 1, str, l * sizeof(char));
|
||||
((char *)(ts + 1))[l] = '\0'; /* ending 0 */
|
||||
return ts;
|
||||
}
|
||||
|
||||
/*
|
||||
* creates a new short string, inserting it into string table
|
||||
*/
|
||||
static ktap_string *newshrstr(ktap_state *ks, const char *str, size_t l,
|
||||
unsigned int h)
|
||||
{
|
||||
ktap_gcobject **list;
|
||||
ktap_stringtable *tb = &G(ks)->strt;
|
||||
ktap_string *s;
|
||||
|
||||
if (tb->nuse >= (int)tb->size)
|
||||
kp_tstring_resize(ks, tb->size * 2); /* too crowded */
|
||||
|
||||
list = &tb->hash[lmod(h, tb->size)];
|
||||
s = createstrobj(ks, str, l, KTAP_TSHRSTR, h, list);
|
||||
tb->nuse++;
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifdef __KERNEL__
|
||||
static arch_spinlock_t tstring_lock =
|
||||
(arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* checks whether short string exists and reuses it or creates a new one
|
||||
*/
|
||||
static ktap_string *internshrstr(ktap_state *ks, const char *str, size_t l)
|
||||
{
|
||||
ktap_gcobject *o;
|
||||
ktap_global_state *g = G(ks);
|
||||
ktap_string *ts;
|
||||
unsigned int h = kp_string_hash(str, l, g->seed);
|
||||
unsigned long __maybe_unused flags;
|
||||
|
||||
#ifdef __KERNEL__
|
||||
local_irq_save(flags);
|
||||
arch_spin_lock(&tstring_lock);
|
||||
#endif
|
||||
|
||||
for (o = g->strt.hash[lmod(h, g->strt.size)]; o != NULL;
|
||||
o = gch(o)->next) {
|
||||
ts = rawgco2ts(o);
|
||||
|
||||
if (h == ts->tsv.hash && ts->tsv.len == l &&
|
||||
(memcmp(str, getstr(ts), l * sizeof(char)) == 0))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ts = newshrstr(ks, str, l, h); /* not found; create a new string */
|
||||
|
||||
out:
|
||||
#ifdef __KERNEL__
|
||||
arch_spin_unlock(&tstring_lock);
|
||||
local_irq_restore(flags);
|
||||
#endif
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* new string (with explicit length)
|
||||
*/
|
||||
ktap_string *kp_tstring_newlstr(ktap_state *ks, const char *str, size_t l)
|
||||
{
|
||||
/* short string? */
|
||||
if (l <= STRING_MAXSHORTLEN)
|
||||
return internshrstr(ks, str, l);
|
||||
else
|
||||
return createstrobj(ks, str, l, KTAP_TLNGSTR, G(ks)->seed,
|
||||
NULL);
|
||||
}
|
||||
|
||||
ktap_string *kp_tstring_newlstr_local(ktap_state *ks, const char *str, size_t l)
|
||||
{
|
||||
return createstrobj(ks, str, l, KTAP_TLNGSTR, G(ks)->seed,
|
||||
&ks->gclist);
|
||||
}
|
||||
|
||||
/*
|
||||
* new zero-terminated string
|
||||
*/
|
||||
ktap_string *kp_tstring_new(ktap_state *ks, const char *str)
|
||||
{
|
||||
return kp_tstring_newlstr(ks, str, strlen(str));
|
||||
}
|
||||
|
||||
ktap_string *kp_tstring_new_local(ktap_state *ks, const char *str)
|
||||
{
|
||||
return createstrobj(ks, str, strlen(str), KTAP_TLNGSTR, G(ks)->seed,
|
||||
&ks->gclist);
|
||||
}
|
||||
|
||||
void kp_tstring_freeall(ktap_state *ks)
|
||||
{
|
||||
ktap_global_state *g = G(ks);
|
||||
int h;
|
||||
|
||||
for (h = 0; h < g->strt.size; h++) {
|
||||
ktap_gcobject *o, *next;
|
||||
o = g->strt.hash[h];
|
||||
while (o) {
|
||||
next = gch(o)->next;
|
||||
kp_free(ks, o);
|
||||
o = next;
|
||||
}
|
||||
g->strt.hash[h] = NULL;
|
||||
}
|
||||
|
||||
kp_free(ks, g->strt.hash);
|
||||
}
|
||||
|
||||
/* todo: dump long string, strt table only contain short string */
|
||||
void kp_tstring_dump(ktap_state *ks)
|
||||
{
|
||||
ktap_gcobject *o;
|
||||
ktap_global_state *g = G(ks);
|
||||
int h;
|
||||
|
||||
kp_printf(ks, "tstring dump: strt size: %d, nuse: %d\n", g->strt.size,
|
||||
g->strt.nuse);
|
||||
for (h = 0; h < g->strt.size; h++) {
|
||||
for (o = g->strt.hash[h]; o != NULL; o = gch(o)->next) {
|
||||
ktap_string *ts = rawgco2ts(o);
|
||||
kp_printf(ks, "%s [%d]\n", getstr(ts), (int)ts->tsv.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
trace sched:sched_switch {
|
||||
print_backtrace()
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
#!/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()
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
#!/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()
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
trace ftrace:function /ip==mutex*/ {
|
||||
print(cpu(), pid(), execname(), argevent)
|
||||
}
|
||||
|
@ -1,57 +0,0 @@
|
||||
#!/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)
|
||||
}
|
||||
|
||||
|
@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
trace probe:vfs_read%return fd=$retval {
|
||||
print(execname(), argevent);
|
||||
}
|
||||
|
@ -1,328 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
#
|
||||
# Tetris KTAP Script
|
||||
#
|
||||
# Copyright (C) 2013/OCT/05 Tadaki SAKAI
|
||||
#
|
||||
# based on stapgames (Systemtap Game Collection)
|
||||
# https://github.com/mhiramat/stapgames/blob/master/games/tetris.stp
|
||||
#
|
||||
# - Requirements
|
||||
# Kernel Configuration: CONFIG_KPROBE_EVENT=y
|
||||
# CONFIG_EVENT_TRACING=y
|
||||
# CONFIG_PERF_EVENTS=y
|
||||
# CONFIG_DEBUG_FS=y
|
||||
# CPU Architecture : x86_64
|
||||
#
|
||||
# - Setup
|
||||
# $ sudo mount -t debugfs none /sys/kernel/debug/
|
||||
#
|
||||
# $ git clone https://github.com/ktap/ktap
|
||||
# $ cd ktap
|
||||
# $ make 2>&1 | tee ../make.log
|
||||
# $ sudo make load
|
||||
# $ sudo sh -c 'echo 50000 > /sys/module/ktapvm/parameters/max_exec_count'
|
||||
#
|
||||
# - Run Tetris
|
||||
# $ sudo ./ktap scripts/game/tetris.kp
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# utils
|
||||
#
|
||||
|
||||
function rand(max) {
|
||||
r = gettimeofday_us()
|
||||
if (r < 0) {
|
||||
r = r * -1
|
||||
}
|
||||
return r % max
|
||||
}
|
||||
|
||||
color_table = {}
|
||||
color_table["Black"] = 40
|
||||
color_table["Red"] = 41
|
||||
color_table["Green"] = 42
|
||||
color_table["Yellow"] = 43
|
||||
color_table["Blue"] = 44
|
||||
color_table["Purple"] = 45
|
||||
color_table["Cyan"] = 46
|
||||
color_table["White"] = 47
|
||||
|
||||
function get_color_number(txt) {
|
||||
return color_table[txt]
|
||||
}
|
||||
|
||||
color_table_text = {}
|
||||
color_table_text[40] = "Black"
|
||||
color_table_text[41] = "Red"
|
||||
color_table_text[42] = "Green"
|
||||
color_table_text[43] = "Yellow"
|
||||
color_table_text[44] = "Blue"
|
||||
color_table_text[45] = "Purple"
|
||||
color_table_text[46] = "Cyan"
|
||||
color_table_text[47] = "White"
|
||||
|
||||
function get_color_text(num) {
|
||||
return color_table_text[num]
|
||||
}
|
||||
|
||||
function update_display() {
|
||||
for (i = 0, 239, 1) {
|
||||
if ((i % 12 - 11) != 0) {
|
||||
tmp = ""
|
||||
} else {
|
||||
tmp = "\n"
|
||||
}
|
||||
|
||||
if (display_buffer[240 + i] == back_text) {
|
||||
printf("%s%s", back_text, tmp)
|
||||
} else {
|
||||
ctext = display_buffer[240 + i]
|
||||
ansi.set_color2(get_color_number(ctext),
|
||||
get_color_number(ctext))
|
||||
printf(" %s", tmp)
|
||||
ansi.reset_color()
|
||||
}
|
||||
|
||||
# clear the display buffer
|
||||
display_buffer[240 + i] = display_buffer[i]
|
||||
}
|
||||
|
||||
printf("%d\n",point)
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# global value
|
||||
#
|
||||
|
||||
key_code = 0
|
||||
point = 0
|
||||
block_number = 0
|
||||
height = 0
|
||||
height_update = 0
|
||||
|
||||
destination_position = {}
|
||||
back_text = {}
|
||||
block_color = {}
|
||||
display_buffer = {}
|
||||
|
||||
block_data0 = {}
|
||||
block_data1 = {}
|
||||
block_data2 = {}
|
||||
block_data3 = {}
|
||||
block_data4 = {}
|
||||
block_data5 = {}
|
||||
block_data6 = {}
|
||||
block_table = {}
|
||||
|
||||
#
|
||||
# Initialize
|
||||
#
|
||||
|
||||
# Create blocks
|
||||
# block is represented by the position from the center.
|
||||
# Every block has "L" part in the center except for a bar.
|
||||
block_data0[0] = -11 # non-"L" part for each block
|
||||
block_data1[0] = -24
|
||||
block_data2[0] = 2
|
||||
block_data3[0] = 13
|
||||
block_data4[0] = -13
|
||||
block_data5[0] = -1
|
||||
block_data6[0] = 2
|
||||
|
||||
block_table[0] = block_data0
|
||||
block_table[1] = block_data1
|
||||
block_table[2] = block_data2
|
||||
block_table[3] = block_data3
|
||||
block_table[4] = block_data4
|
||||
block_table[5] = block_data5
|
||||
block_table[6] = block_data6
|
||||
|
||||
for (i = 0, len(block_table) - 1, 1) {
|
||||
# common "L" part
|
||||
block_table[i][1] = 0
|
||||
block_table[i][2] = 1
|
||||
block_table[i][3] = -12
|
||||
}
|
||||
|
||||
block_table[6][3] = -1 # bar is not common
|
||||
# Position: 1 row has 12 columns,
|
||||
# and (x, y) is represented by h = x + y * 12.p
|
||||
height = 17 # First block position (center)
|
||||
|
||||
for (i = 0, 240, 1) {
|
||||
# Wall and Floor (sentinel)
|
||||
if (((i % 12) < 2) || (i > 228)) {
|
||||
block_color = "White"
|
||||
tmp = block_color
|
||||
} else {
|
||||
back_text = " "
|
||||
tmp = back_text
|
||||
}
|
||||
display_buffer[i - 1] = tmp
|
||||
display_buffer[240 + i - 1] = tmp
|
||||
}
|
||||
|
||||
block_number = rand(len(color_table) - 1)
|
||||
block_color = get_color_text(block_number + 40)
|
||||
|
||||
ansi.clear_screen()
|
||||
|
||||
|
||||
#
|
||||
# Key Input
|
||||
#
|
||||
|
||||
trace probe:kbd_event handle=%di event_type=%si event_code=%dx value=%cx {
|
||||
# Only can run it in x86_64
|
||||
#
|
||||
# Register follow x86_64 call conversion:
|
||||
#
|
||||
# x86_64:
|
||||
# %rcx 4 argument
|
||||
# %rdx 3 argument
|
||||
# %rsi 2 argument
|
||||
# %rdi 1 argument
|
||||
|
||||
local event_code = arg4
|
||||
local value = arg5
|
||||
|
||||
if (value != 0) {
|
||||
if ((event_code - 4) != 0) {
|
||||
key_code = event_code
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# timer
|
||||
#
|
||||
|
||||
tick-200ms {
|
||||
ansi.clear_screen()
|
||||
|
||||
f = 0 # move/rotate flag
|
||||
|
||||
if (key_code != 0) { # if key is pressed
|
||||
if(key_code != 103) { #move left or right
|
||||
# d: movement direction
|
||||
if ((key_code - 105) != 0) {
|
||||
if ((key_code - 106) != 0) {
|
||||
d = 0
|
||||
} else {
|
||||
d = 1
|
||||
}
|
||||
} else {
|
||||
d = -1
|
||||
}
|
||||
|
||||
for (i = 0, 3, 1) { # check if the block can be moved
|
||||
# destination is free
|
||||
if (display_buffer[height +
|
||||
block_table[block_number][i] + d]
|
||||
!= back_text) {
|
||||
f = 1
|
||||
}
|
||||
}
|
||||
# move if destinations of every block are free
|
||||
if (f == 0) {
|
||||
height = height + d
|
||||
}
|
||||
} else { # rotate
|
||||
for (i = 0, 3, 1) { # check if block can be rotated
|
||||
# each block position
|
||||
p = block_table[block_number][i]
|
||||
|
||||
# destination x pos(p/12 rounded)
|
||||
v = (p * 2 + 252) / 24 - 10
|
||||
w = p - v * 12 # destination y pos
|
||||
|
||||
# destination position
|
||||
destination_position[i] = w * 12 - v
|
||||
|
||||
# check if desetination is free
|
||||
if (display_buffer[height +
|
||||
destination_position[i]] != back_text) {
|
||||
f = 1
|
||||
}
|
||||
}
|
||||
|
||||
if (f == 0) {
|
||||
# rotate if destinations of every block
|
||||
# are free
|
||||
for (i = 0, 3, 1) {
|
||||
block_table[block_number][i] =
|
||||
destination_position[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
key_code = 0 # clear the input key
|
||||
|
||||
f = 0
|
||||
for (i = 0, 3, 1) { # drop 1 row
|
||||
# check if destination is free
|
||||
p = height + block_table[block_number][i]
|
||||
if (display_buffer[12 + p] != back_text) {
|
||||
f = 1
|
||||
}
|
||||
|
||||
# copy the moving block to display buffer
|
||||
display_buffer[240 + p] = block_color
|
||||
}
|
||||
|
||||
if ((f == 1) && (height == 17)) {
|
||||
update_display()
|
||||
exit() # exit if there are block at initial position
|
||||
}
|
||||
|
||||
height_update = !height_update
|
||||
if (height_update != 0) {
|
||||
if(f != 0) { # the block can't drop anymore
|
||||
for (i = 0, 3, 1) {
|
||||
# fix the block
|
||||
display_buffer[height +
|
||||
block_table[block_number][i]] = block_color
|
||||
}
|
||||
# determin the next block
|
||||
block_number = rand(len(color_table) - 1)
|
||||
|
||||
block_color = get_color_text(block_number + 40)
|
||||
|
||||
height = 17 # make the block to initial position
|
||||
} else {
|
||||
height = height + 12 # drop the block 1 row
|
||||
}
|
||||
}
|
||||
|
||||
k = 1
|
||||
for (i = 18, 0, -1) { #check if line is filled
|
||||
# search for filled line
|
||||
j = 10
|
||||
while ((j > 0) &&
|
||||
(display_buffer[i * 12 + j] != back_text)) {
|
||||
j = j - 1
|
||||
}
|
||||
|
||||
if (j == 0) { # filled!
|
||||
# add a point: 1 line - 1 point, ..., tetris - 10points
|
||||
point = point + k
|
||||
k = k + 1
|
||||
|
||||
# drop every upper block
|
||||
j = (i + 1) * 12
|
||||
i = i + 1
|
||||
while (j > 2 * 12) {
|
||||
j = j - 1
|
||||
display_buffer[j] = display_buffer[j - 12]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_display()
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
print("Hello World! I am ktap")
|
@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
#this script output each average consumimg time of each hardirq
|
||||
s = aggr_table()
|
||||
map = {}
|
||||
|
||||
trace irq:irq_handler_entry {
|
||||
map[cpu()] = gettimeofday_us()
|
||||
}
|
||||
|
||||
trace irq:irq_handler_exit {
|
||||
local entry_time = map[cpu()]
|
||||
if (entry_time == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
s[arg1] = avg(gettimeofday_us() - entry_time)
|
||||
map[cpu()] = nil
|
||||
}
|
||||
|
||||
trace_end {
|
||||
print("hardirq average executing time (us)")
|
||||
histogram(s)
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
#this script output each average consumimg time of each softirq line
|
||||
s = aggr_table()
|
||||
map = {}
|
||||
|
||||
trace irq:softirq_entry {
|
||||
map[cpu()] = gettimeofday_us()
|
||||
}
|
||||
|
||||
trace irq:softirq_exit {
|
||||
local entry_time = map[cpu()]
|
||||
if (entry_time == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
s[arg1] = avg(gettimeofday_us() - entry_time)
|
||||
map[cpu()] = nil
|
||||
}
|
||||
|
||||
trace_end {
|
||||
print("softirq average executing time (us)")
|
||||
histogram(s)
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
#!/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)
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
#! /usr/bin/env ktap
|
||||
|
||||
# Based on systemtap traceio.stp
|
||||
|
||||
#this script is broken, fix it soon.
|
||||
|
||||
reads = aggr_table()
|
||||
writes = aggr_table()
|
||||
total_io = aggr_table()
|
||||
|
||||
trace syscalls:sys_exit_read {
|
||||
reads[execname()] = sum(arg2)
|
||||
total_io[execname()] = sum(arg2)
|
||||
}
|
||||
|
||||
trace syscalls:sys_exit_write {
|
||||
writes[execname()] = sum(arg2)
|
||||
total_io[execname()] = sum(arg2)
|
||||
}
|
||||
|
||||
function humanread_digit(bytes) {
|
||||
if (bytes > 1024*1024*1024) {
|
||||
return bytes/1024/1024/1024
|
||||
} elseif (bytes > 1024*1024) {
|
||||
return bytes/1024/1024
|
||||
} elseif (bytes > 1024) {
|
||||
return bytes/1024
|
||||
} else {
|
||||
return bytes
|
||||
}
|
||||
}
|
||||
|
||||
function humanread_x(bytes) {
|
||||
if (bytes > 1024*1024*1024) {
|
||||
return " GiB"
|
||||
} elseif (bytes > 1024*1024) {
|
||||
return " MiB"
|
||||
} elseif (bytes > 1024) {
|
||||
return " KiB"
|
||||
} else {
|
||||
return " B"
|
||||
}
|
||||
}
|
||||
|
||||
tick-1s {
|
||||
ansi.clear_screen()
|
||||
for (exec, count in pairs(total_io)) {
|
||||
local readnum = reads[exec]
|
||||
local writenum = writes[exec]
|
||||
printf("%15s r: %12d%s w: %12d%s\n", exec,
|
||||
humanread_digit(readnum), humanread_x(readnum),
|
||||
humanread_digit(writenum), humanread_x(writenum))
|
||||
}
|
||||
printf("\n")
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
#!/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()
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
#!/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")
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
#kernel function profile
|
||||
#You can use this script to know what function is called frequently,
|
||||
#without enable CONFIG_FUNCTION_PROFILER in kernel.
|
||||
|
||||
s = aggr_table()
|
||||
|
||||
trace ftrace:function {
|
||||
s[arg1] = count()
|
||||
}
|
||||
|
||||
trace_end {
|
||||
histogram(s)
|
||||
}
|
||||
|
||||
#sample output
|
||||
#^C
|
||||
# value ------------- Distribution ------------- count
|
||||
# sub_preempt_count | @@@@@ 34904
|
||||
# add_preempt_count | @@@@@ 33435
|
||||
# nsecs_to_jiffies64 | @@@ 19919
|
||||
# irqtime_account_process_tick... | @ 9970
|
||||
# account_idle_time | @ 9880
|
||||
# _raw_spin_lock | 5100
|
||||
# _raw_spin_unlock | 5021
|
||||
# _raw_spin_unlock_irqrestore | 4235
|
||||
# _raw_spin_lock_irqsave | 4232
|
||||
# __rcu_read_lock | 3373
|
||||
# __rcu_read_unlock | 3373
|
||||
# lookup_address | 2392
|
||||
# pfn_range_is_mapped | 2384
|
||||
# update_cfs_rq_blocked_load | 1983
|
||||
# idle_cpu | 1808
|
||||
# ktime_get | 1394
|
||||
# _raw_spin_unlock_irq | 1270
|
||||
# _raw_spin_lock_irq | 1091
|
||||
# update_curr | 950
|
||||
# irqtime_account_irq | 950
|
||||
# ... |
|
||||
#
|
@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
# This ktap script samples stacktrace of system per 10us,
|
||||
# you can use generated output to make a flame graph.
|
||||
#
|
||||
# Flame Graphs:
|
||||
# http://dtrace.org/blogs/brendan/2012/03/17/linux-kernel-performance-flame-graphs/
|
||||
|
||||
s = aggr_table()
|
||||
|
||||
profile-10us {
|
||||
s[backtrace()] = count()
|
||||
}
|
||||
|
||||
tick-60s {
|
||||
exit()
|
||||
}
|
||||
|
||||
trace_end {
|
||||
for (k, v in pairs(s)) {
|
||||
print(k)
|
||||
print(v)
|
||||
print()
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
trace sched:sched_switch {
|
||||
printf("%s ... ", arg1)
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
#!/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]);
|
||||
}
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
#!/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])
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#! /usr/bin/env ktap
|
||||
|
||||
#this script is broken, fix it soon.
|
||||
s = {}
|
||||
|
||||
trace syscalls:sys_enter_* {
|
||||
s[argname] += 1
|
||||
}
|
||||
|
||||
tick-5s {
|
||||
ansi.clear_screen()
|
||||
histogram(s)
|
||||
delete(s)
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
trace syscalls:* {
|
||||
print(cpu(), pid(), execname(), argevent)
|
||||
}
|
||||
|
@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
s = aggr_table()
|
||||
|
||||
trace syscalls:sys_enter_* {
|
||||
s[argname] = count()
|
||||
}
|
||||
|
||||
trace_end {
|
||||
histogram(s)
|
||||
}
|
||||
|
||||
print("Press Control-C to stop.")
|
||||
|
||||
#Result:
|
||||
#
|
||||
#[root@jovi ktap]# ./ktap scripts/syscalls_histogram.kp
|
||||
#^C
|
||||
# value ------------- Distribution ------------- count
|
||||
# sys_enter_rt_sigprocmask |@@@@@@ 326
|
||||
# sys_enter_read |@@@@@ 287
|
||||
# sys_enter_close |@@@@ 236
|
||||
# sys_enter_open |@@@@ 222
|
||||
# sys_enter_stat64 |@@ 132
|
||||
# sys_enter_select |@@ 123
|
||||
# sys_enter_rt_sigaction |@@ 107
|
||||
# sys_enter_poll |@ 72
|
||||
# sys_enter_write |@ 70
|
||||
# sys_enter_mmap_pgoff |@ 58
|
||||
# sys_enter_fstat64 | 41
|
||||
# sys_enter_nanosleep | 23
|
||||
# sys_enter_access | 20
|
||||
# sys_enter_mprotect | 18
|
||||
# sys_enter_geteuid | 17
|
||||
# sys_enter_getegid | 16
|
||||
# sys_enter_getuid | 16
|
||||
# sys_enter_getgid | 16
|
||||
# sys_enter_brk | 15
|
||||
# sys_enter_waitpid | 11
|
||||
# sys_enter_time | 10
|
||||
# sys_enter_ioctl | 9
|
||||
# sys_enter_munmap | 9
|
||||
# sys_enter_fcntl64 | 7
|
||||
# sys_enter_dup2 | 7
|
||||
# sys_enter_clone | 6
|
||||
# sys_enter_exit_group | 6
|
||||
# sys_enter_execve | 4
|
||||
# sys_enter_pipe | 3
|
||||
# sys_enter_gettimeofday | 3
|
||||
# sys_enter_getdents | 2
|
||||
# sys_enter_getgroups | 2
|
||||
# sys_enter_statfs64 | 2
|
||||
# sys_enter_lseek | 2
|
||||
# sys_enter_openat | 1
|
||||
# sys_enter_newuname | 1
|
||||
|
@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
s = aggr_table()
|
||||
|
||||
trace syscalls:sys_enter_* {
|
||||
s[execname()] = count()
|
||||
}
|
||||
|
||||
trace_end {
|
||||
histogram(s)
|
||||
}
|
||||
|
||||
print("Press Control-C to stop.")
|
||||
|
||||
#Result:
|
||||
#
|
||||
#[root@jovi ktap]# ./ktap scripts/syscalls_histogram2.kp
|
||||
#^C
|
||||
# value ------------- Distribution ------------- count
|
||||
# sshd |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 196
|
||||
# iscsid |@@@@ 24
|
||||
# sendmail |@ 9
|
||||
|
||||
|
@ -1,212 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
# showing all tracepoints in histogram style
|
||||
|
||||
s = {}
|
||||
|
||||
trace *:* {
|
||||
s[argname] += 1
|
||||
}
|
||||
|
||||
trace_end {
|
||||
histogram(s)
|
||||
}
|
||||
|
||||
print("Press Control-C to stop.")
|
||||
|
||||
#Results:
|
||||
#^C
|
||||
#
|
||||
# value ------------- Distribution ------------- count
|
||||
# rcu_utilization |@@@@@ 225289
|
||||
# cpu_idle |@@@ 120168
|
||||
# sched_wakeup |@@ 91950
|
||||
# timer_cancel |@@ 91232
|
||||
# timer_start |@@ 91201
|
||||
# sched_stat_sleep |@@ 90981
|
||||
# timer_expire_exit |@@ 90634
|
||||
# timer_expire_entry |@@ 90625
|
||||
# hrtimer_cancel |@ 75411
|
||||
# hrtimer_start |@ 74946
|
||||
# softirq_raise |@ 63117
|
||||
# softirq_exit |@ 63109
|
||||
# softirq_entry |@ 63094
|
||||
# sched_switch |@ 62331
|
||||
# sched_stat_wait |@ 60491
|
||||
# hrtimer_expire_exit |@ 47538
|
||||
# hrtimer_expire_entry |@ 47530
|
||||
# sched_stat_runtime | 2780
|
||||
# kmem_cache_free | 2684
|
||||
# kmem_cache_alloc | 2415
|
||||
# kfree | 2288
|
||||
# sys_exit | 2145
|
||||
# sys_enter | 2145
|
||||
# sys_exit_rt_sigprocmask | 1000
|
||||
# sys_enter_rt_sigprocmask | 1000
|
||||
# timer_init | 912
|
||||
# sched_stat_blocked | 685
|
||||
# kmalloc | 667
|
||||
# workqueue_execute_end | 621
|
||||
# workqueue_execute_start | 621
|
||||
# sys_enter_select | 566
|
||||
# sys_exit_select | 566
|
||||
# sys_enter_read | 526
|
||||
# sys_exit_read | 526
|
||||
# mm_page_free | 478
|
||||
# mm_page_alloc | 427
|
||||
# mm_page_free_batched | 382
|
||||
# net_dev_queue | 296
|
||||
# net_dev_xmit | 296
|
||||
# consume_skb | 296
|
||||
# sys_exit_write | 290
|
||||
# sys_enter_write | 290
|
||||
# kfree_skb | 289
|
||||
# kmem_cache_alloc_node | 269
|
||||
# kmalloc_node | 263
|
||||
# sys_enter_close | 249
|
||||
# sys_exit_close | 249
|
||||
# hrtimer_init | 248
|
||||
# netif_receive_skb | 242
|
||||
# sys_enter_open | 237
|
||||
# sys_exit_open | 237
|
||||
# napi_poll | 226
|
||||
# sched_migrate_task | 207
|
||||
# sys_exit_poll | 173
|
||||
# sys_enter_poll | 173
|
||||
# workqueue_queue_work | 152
|
||||
# workqueue_activate_work | 152
|
||||
# sys_enter_stat64 | 133
|
||||
# sys_exit_stat64 | 133
|
||||
# sys_exit_rt_sigaction | 133
|
||||
# sys_enter_rt_sigaction | 133
|
||||
# irq_handler_entry | 125
|
||||
# irq_handler_exit | 125
|
||||
# mm_page_alloc_zone_locked | 99
|
||||
# sys_exit_mmap_pgoff | 66
|
||||
# sys_enter_mmap_pgoff | 66
|
||||
# sys_exit_fstat64 | 54
|
||||
# sys_enter_fstat64 | 54
|
||||
# sys_enter_nanosleep | 51
|
||||
# sys_exit_nanosleep | 51
|
||||
# block_bio_queue | 46
|
||||
# block_bio_remap | 46
|
||||
# block_bio_complete | 46
|
||||
# mix_pool_bytes | 44
|
||||
# mm_page_pcpu_drain | 31
|
||||
# sys_exit_time | 23
|
||||
# sys_enter_time | 23
|
||||
# sys_exit_access | 20
|
||||
# sys_enter_access | 20
|
||||
# mix_pool_bytes_nolock | 18
|
||||
# sys_enter_mprotect | 18
|
||||
# sys_exit_mprotect | 18
|
||||
# sys_enter_geteuid | 17
|
||||
# sys_exit_geteuid | 17
|
||||
# sys_enter_munmap | 17
|
||||
# sys_exit_munmap | 17
|
||||
# block_getrq | 16
|
||||
# sys_enter_getuid | 16
|
||||
# sys_enter_getgid | 16
|
||||
# sys_exit_getgid | 16
|
||||
# sys_exit_getuid | 16
|
||||
# block_rq_issue | 16
|
||||
# scsi_dispatch_cmd_start | 16
|
||||
# block_rq_complete | 16
|
||||
# scsi_dispatch_cmd_done | 16
|
||||
# sys_enter_getegid | 16
|
||||
# sys_exit_getegid | 16
|
||||
# block_rq_insert | 16
|
||||
# skb_copy_datagram_iovec | 15
|
||||
# sys_enter_brk | 15
|
||||
# sys_exit_brk | 15
|
||||
# credit_entropy_bits | 14
|
||||
# wbc_writepage | 14
|
||||
# sys_exit_clone | 12
|
||||
# block_touch_buffer | 12
|
||||
# sched_process_wait | 11
|
||||
# sys_enter_waitpid | 11
|
||||
# sys_exit_waitpid | 11
|
||||
# writeback_written | 10
|
||||
# writeback_start | 10
|
||||
# writeback_queue_io | 10
|
||||
# ext4_es_lookup_extent_enter | 9
|
||||
# sys_enter_ioctl | 9
|
||||
# sys_exit_ioctl | 9
|
||||
# ext4_ext_map_blocks_enter | 9
|
||||
# ext4_ext_map_blocks_exit | 9
|
||||
# ext4_es_lookup_extent_exit | 9
|
||||
# ext4_es_insert_extent | 9
|
||||
# ext4_ext_show_extent | 8
|
||||
# extract_entropy | 8
|
||||
#ext4_es_find_delayed_extent_exit | 8
|
||||
# ext4_es_find_delayed_extent_... | 8
|
||||
# writeback_pages_written | 7
|
||||
# sys_exit_dup2 | 7
|
||||
# sys_enter_dup2 | 7
|
||||
# signal_generate | 7
|
||||
# sys_enter_fcntl64 | 7
|
||||
# sys_exit_fcntl64 | 7
|
||||
# global_dirty_state | 7
|
||||
# writeback_dirty_inode_start | 7
|
||||
# block_bio_backmerge | 7
|
||||
# writeback_dirty_inode | 7
|
||||
# sched_wakeup_new | 6
|
||||
# sched_process_free | 6
|
||||
# sys_enter_exit_group | 6
|
||||
# task_newtask | 6
|
||||
# sys_enter_clone | 6
|
||||
# sched_process_fork | 6
|
||||
# sched_process_exit | 6
|
||||
# sys_exit_gettimeofday | 5
|
||||
# signal_deliver | 5
|
||||
# sys_enter_gettimeofday | 5
|
||||
# writeback_single_inode | 4
|
||||
# sys_enter_execve | 4
|
||||
# task_rename | 4
|
||||
# sched_process_exec | 4
|
||||
# block_dirty_buffer | 4
|
||||
# sys_exit_execve | 4
|
||||
# block_unplug | 4
|
||||
# sched_stat_iowait | 4
|
||||
# writeback_single_inode_start | 4
|
||||
# block_plug | 4
|
||||
# writeback_write_inode | 3
|
||||
# sys_enter_pipe | 3
|
||||
# writeback_dirty_page | 3
|
||||
# writeback_write_inode_start | 3
|
||||
# ext4_mark_inode_dirty | 3
|
||||
# ext4_journal_start | 3
|
||||
# sys_exit_pipe | 3
|
||||
# jbd2_drop_transaction | 2
|
||||
# jbd2_commit_locking | 2
|
||||
# jbd2_commit_flushing | 2
|
||||
# jbd2_handle_start | 2
|
||||
# jbd2_run_stats | 2
|
||||
# sys_exit_getdents | 2
|
||||
# jbd2_checkpoint_stats | 2
|
||||
# sys_enter_getgroups | 2
|
||||
# jbd2_start_commit | 2
|
||||
# jbd2_end_commit | 2
|
||||
# ext4_da_writepages | 2
|
||||
# jbd2_handle_stats | 2
|
||||
# sys_enter_statfs64 | 2
|
||||
# sys_exit_statfs64 | 2
|
||||
# sys_exit_getgroups | 2
|
||||
# sys_exit_lseek | 2
|
||||
# sys_enter_lseek | 2
|
||||
# sys_enter_getdents | 2
|
||||
# ext4_da_write_pages | 2
|
||||
# jbd2_commit_logging | 2
|
||||
# ext4_request_blocks | 1
|
||||
# sys_exit_openat | 1
|
||||
# ext4_discard_preallocations | 1
|
||||
# ext4_mballoc_alloc | 1
|
||||
# sys_enter_openat | 1
|
||||
# ext4_da_writepages_result | 1
|
||||
# ext4_allocate_blocks | 1
|
||||
# sys_enter_newuname | 1
|
||||
# ext4_da_update_reserve_space | 1
|
||||
# ext4_get_reserved_cluster_alloc | 1
|
||||
# sys_exit_newuname | 1
|
||||
# writeback_wake_thread | 1
|
||||
|
@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
# showing all tracepoints in histogram style
|
||||
|
||||
s = aggr_table()
|
||||
|
||||
trace *:* {
|
||||
s[execname()] = count()
|
||||
}
|
||||
|
||||
trace_end {
|
||||
histogram(s)
|
||||
}
|
||||
|
||||
print("Press Control-C to stop.")
|
||||
|
||||
#Results:
|
||||
#^C
|
||||
# value ------------- Distribution ------------- count
|
||||
# swapper/0 |@@@@@@@@@@@@ 354378
|
||||
# swapper/1 |@@@@@@@@@@ 284984
|
||||
# ps |@@@@ 115697
|
||||
# ksmtuned |@@@ 95857
|
||||
# iscsid |@@ 80008
|
||||
# awk |@ 30354
|
||||
# irqbalance | 16530
|
||||
# rcu_sched | 15892
|
||||
# sendmail | 14463
|
||||
# kworker/0:1 | 10540
|
||||
# kworker/u4:2 | 9250
|
||||
# kworker/1:2 | 7943
|
||||
# sleep | 7555
|
||||
# crond | 3911
|
||||
# ksoftirqd/0 | 3817
|
||||
# sshd | 2849
|
||||
# systemd-journal | 2209
|
||||
# migration/1 | 1601
|
||||
# migration/0 | 1350
|
||||
# dhclient | 1343
|
||||
# nm-dhcp-client. | 1208
|
||||
# ksoftirqd/1 | 1064
|
||||
# watchdog/1 | 966
|
||||
# watchdog/0 | 964
|
||||
# khugepaged | 776
|
||||
# dbus-daemon | 611
|
||||
# rpcbind | 607
|
||||
# gdbus | 529
|
||||
# NetworkManager | 399
|
||||
# jbd2/dm-1-8 | 378
|
||||
# modem-manager | 184
|
||||
# abrt-watch-log | 157
|
||||
# polkitd | 156
|
||||
# rs:main Q:Reg | 153
|
||||
# avahi-daemon | 151
|
||||
# rsyslogd | 102
|
||||
# systemd | 96
|
||||
# kworker/0:1H | 45
|
||||
# smartd | 30
|
||||
|
@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
trace *:* {
|
||||
print(cpu(), pid(), execname(), argevent)
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
trace probe:/lib/libc.so.6:0x000773c0 {
|
||||
print("entry:", execname(), argevent)
|
||||
}
|
||||
|
||||
trace probe:/lib/libc.so.6:0x000773c0%return {
|
||||
print("exit:", execname(), argevent)
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
function failed() {
|
||||
printf("failed\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#---------------------------------#
|
||||
|
||||
s = aggr_table()
|
||||
|
||||
s["count"] = count()
|
||||
s["count"] = count()
|
||||
s["count"] = count()
|
||||
|
||||
s["max"] = max(1)
|
||||
s["max"] = max(0)
|
||||
s["max"] = max(100)
|
||||
|
||||
s["min"] = min(50)
|
||||
s["min"] = min(2)
|
||||
s["min"] = min(100)
|
||||
|
||||
s["sum"] = sum(10)
|
||||
s["sum"] = sum(20)
|
||||
s["sum"] = sum(30)
|
||||
|
||||
s["avg"] = avg(10)
|
||||
s["avg"] = avg(20)
|
||||
s["avg"] = avg(30)
|
||||
|
||||
if (s["count"] != 3) {
|
||||
failed()
|
||||
}
|
||||
|
||||
if (s["max"] != 100) {
|
||||
failed()
|
||||
}
|
||||
|
||||
if (s["min"] != 2) {
|
||||
failed()
|
||||
}
|
||||
|
||||
if (s["sum"] != 60) {
|
||||
failed()
|
||||
}
|
||||
|
||||
if (s["avg"] != 20) {
|
||||
failed()
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
ansi.clear_screen()
|
||||
|
||||
ansi.set_color(32)
|
||||
printf("this line should be Green color\n")
|
||||
|
||||
ansi.set_color(31)
|
||||
printf("this line should be Red color\n")
|
||||
|
||||
ansi.set_color2(34, 43)
|
||||
printf("this line should be Blue color, with Yellow background\n")
|
||||
|
||||
ansi.reset_color()
|
||||
ansi.set_color3(34, 46, 4)
|
||||
printf("this line should be Blue color, with Cyan background, underline single attribute\n")
|
||||
|
||||
ansi.reset_color()
|
||||
ansi.new_line()
|
||||
|
@ -1,24 +0,0 @@
|
||||
#!/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()
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
function failed() {
|
||||
printf("failed\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#-----------------------------------------#
|
||||
|
||||
a = 4
|
||||
b = 5
|
||||
|
||||
if ((a + b) != 9) {
|
||||
failed()
|
||||
}
|
||||
|
||||
if ((a - b) != -1) {
|
||||
failed()
|
||||
}
|
||||
|
||||
if ((a % b) != 4) {
|
||||
failed()
|
||||
}
|
||||
|
||||
if ((a / b) != 0) {
|
||||
failed()
|
||||
}
|
@ -1,556 +0,0 @@
|
||||
/*
|
||||
* copyright Oracle 2007. Licensed under GPLv2
|
||||
* To compile: gcc -Wall -o sembench sembench.c -lpthread
|
||||
*
|
||||
* usage: sembench -t thread count -w wakenum -r runtime -o op
|
||||
* op can be: 0 (ipc sem) 1 (nanosleep) 2 (futexes)
|
||||
*
|
||||
* example:
|
||||
* sembench -t 1024 -w 512 -r 60 -o 2
|
||||
* runs 1024 threads, waking up 512 at a time, running for 60 seconds using
|
||||
* futex locking.
|
||||
*
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#define _POSIX_C_SOURCE 199309
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/sem.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define VERSION "0.2"
|
||||
|
||||
/* futexes have been around since 2.5.something, but it still seems I
|
||||
* need to make my own syscall. Sigh.
|
||||
*/
|
||||
#define FUTEX_WAIT 0
|
||||
#define FUTEX_WAKE 1
|
||||
#define FUTEX_FD 2
|
||||
#define FUTEX_REQUEUE 3
|
||||
#define FUTEX_CMP_REQUEUE 4
|
||||
#define FUTEX_WAKE_OP 5
|
||||
static inline int futex (int *uaddr, int op, int val,
|
||||
const struct timespec *timeout,
|
||||
int *uaddr2, int val3)
|
||||
{
|
||||
return syscall(__NR_futex, uaddr, op, val, timeout, uaddr2, val3);
|
||||
}
|
||||
|
||||
static void smp_mb(void)
|
||||
{
|
||||
__sync_synchronize();
|
||||
}
|
||||
|
||||
static int all_done = 0;
|
||||
static int timeout_test = 0;
|
||||
|
||||
#define SEMS_PERID 250
|
||||
|
||||
struct sem_operations;
|
||||
|
||||
struct lockinfo {
|
||||
unsigned long id;
|
||||
unsigned long index;
|
||||
int data;
|
||||
pthread_t tid;
|
||||
struct lockinfo *next;
|
||||
struct sem_operations *ops;
|
||||
unsigned long ready;
|
||||
};
|
||||
|
||||
struct sem_wakeup_info {
|
||||
int wakeup_count;
|
||||
struct sembuf sb[SEMS_PERID];
|
||||
};
|
||||
|
||||
struct sem_operations {
|
||||
void (*wait)(struct lockinfo *l);
|
||||
int (*wake)(struct sem_wakeup_info *wi, int num_semids, int num);
|
||||
void (*setup)(struct sem_wakeup_info **wi, int num_semids);
|
||||
void (*cleanup)(int num_semids);
|
||||
char *name;
|
||||
};
|
||||
|
||||
int *semid_lookup = NULL;
|
||||
|
||||
pthread_mutex_t worklist_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static unsigned long total_burns = 0;
|
||||
static unsigned long min_burns = ~0UL;
|
||||
static unsigned long max_burns = 0;
|
||||
|
||||
/* currently running threads */
|
||||
static int thread_count = 0;
|
||||
|
||||
struct lockinfo *worklist = NULL;
|
||||
static int workers_started = 0;
|
||||
|
||||
/* total threads started */
|
||||
static int num_threads = 2048;
|
||||
|
||||
static void worklist_add(struct lockinfo *l)
|
||||
{
|
||||
smp_mb();
|
||||
l->ready = 1;
|
||||
}
|
||||
|
||||
static struct lockinfo *worklist_rm(void)
|
||||
{
|
||||
static int last_index = 0;
|
||||
int i;
|
||||
struct lockinfo *l;
|
||||
|
||||
for (i = 0; i < num_threads; i++) {
|
||||
int test = (last_index + i) % num_threads;
|
||||
|
||||
l = worklist + test;
|
||||
smp_mb();
|
||||
if (l->ready) {
|
||||
l->ready = 0;
|
||||
last_index = test;
|
||||
return l;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ipc semaphore post& wait */
|
||||
void wait_ipc_sem(struct lockinfo *l)
|
||||
{
|
||||
struct sembuf sb;
|
||||
int ret;
|
||||
struct timespec *tvp = NULL;
|
||||
struct timespec tv = { 0, 1 };
|
||||
|
||||
sb.sem_num = l->index;
|
||||
sb.sem_flg = 0;
|
||||
|
||||
sb.sem_op = -1;
|
||||
l->data = 1;
|
||||
|
||||
if (timeout_test && (l->id % 5) == 0)
|
||||
tvp = &tv;
|
||||
|
||||
worklist_add(l);
|
||||
ret = semtimedop(semid_lookup[l->id], &sb, 1, tvp);
|
||||
|
||||
while(l->data != 0 && tvp) {
|
||||
struct timespec tv2 = { 0, 500 };
|
||||
nanosleep(&tv2, NULL);
|
||||
}
|
||||
|
||||
if (l->data != 0) {
|
||||
if (tvp)
|
||||
return;
|
||||
fprintf(stderr, "wakeup without data update\n");
|
||||
exit(1);
|
||||
}
|
||||
if (ret) {
|
||||
if (errno == EAGAIN && tvp)
|
||||
return;
|
||||
perror("semtimed op");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int ipc_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
struct lockinfo *l;
|
||||
int found = 0;
|
||||
|
||||
for (i = 0; i < num_semids; i++) {
|
||||
wi[i].wakeup_count = 0;
|
||||
}
|
||||
while(num > 0) {
|
||||
struct sembuf *sb;
|
||||
l = worklist_rm();
|
||||
if (!l)
|
||||
break;
|
||||
if (l->data != 1)
|
||||
fprintf(stderr, "warning, lockinfo data was %d\n",
|
||||
l->data);
|
||||
l->data = 0;
|
||||
sb = wi[l->id].sb + wi[l->id].wakeup_count;
|
||||
sb->sem_num = l->index;
|
||||
sb->sem_op = 1;
|
||||
sb->sem_flg = IPC_NOWAIT;
|
||||
wi[l->id].wakeup_count++;
|
||||
found++;
|
||||
num--;
|
||||
}
|
||||
if (!found)
|
||||
return 0;
|
||||
for (i = 0; i < num_semids; i++) {
|
||||
int wakeup_total;
|
||||
int cur;
|
||||
int offset = 0;
|
||||
if (!wi[i].wakeup_count)
|
||||
continue;
|
||||
wakeup_total = wi[i].wakeup_count;
|
||||
while(wakeup_total > 0) {
|
||||
cur = wakeup_total > 64 ? 64 : wakeup_total;
|
||||
ret = semtimedop(semid_lookup[i], wi[i].sb + offset,
|
||||
cur, NULL);
|
||||
if (ret) {
|
||||
perror("semtimedop");
|
||||
exit(1);
|
||||
}
|
||||
offset += cur;
|
||||
wakeup_total -= cur;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void setup_ipc_sems(struct sem_wakeup_info **wi, int num_semids)
|
||||
{
|
||||
int i;
|
||||
*wi = malloc(sizeof(**wi) * num_semids);
|
||||
semid_lookup = malloc(num_semids * sizeof(int));
|
||||
for(i = 0; i < num_semids; i++) {
|
||||
semid_lookup[i] = semget(IPC_PRIVATE, SEMS_PERID,
|
||||
IPC_CREAT | 0777);
|
||||
if (semid_lookup[i] < 0) {
|
||||
perror("semget");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
void cleanup_ipc_sems(int num)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num; i++) {
|
||||
semctl(semid_lookup[i], 0, IPC_RMID);
|
||||
}
|
||||
}
|
||||
|
||||
struct sem_operations ipc_sem_ops = {
|
||||
.wait = wait_ipc_sem,
|
||||
.wake = ipc_wake_some,
|
||||
.setup = setup_ipc_sems,
|
||||
.cleanup = cleanup_ipc_sems,
|
||||
.name = "ipc sem operations",
|
||||
};
|
||||
|
||||
/* futex post & wait */
|
||||
void wait_futex_sem(struct lockinfo *l)
|
||||
{
|
||||
int ret;
|
||||
l->data = 1;
|
||||
worklist_add(l);
|
||||
while(l->data == 1) {
|
||||
ret = futex(&l->data, FUTEX_WAIT, 1, NULL, NULL, 0);
|
||||
/*
|
||||
if (ret && ret != EWOULDBLOCK) {
|
||||
perror("futex wait");
|
||||
exit(1);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
int futex_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
struct lockinfo *l;
|
||||
int found = 0;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
l = worklist_rm();
|
||||
if (!l)
|
||||
break;
|
||||
if (l->data != 1)
|
||||
fprintf(stderr, "warning, lockinfo data was %d\n",
|
||||
l->data);
|
||||
l->data = 0;
|
||||
ret = futex(&l->data, FUTEX_WAKE, 1, NULL, NULL, 0);
|
||||
if (ret < 0) {
|
||||
perror("futex wake");
|
||||
exit(1);
|
||||
}
|
||||
found++;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void setup_futex_sems(struct sem_wakeup_info **wi, int num_semids)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_futex_sems(int num)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
struct sem_operations futex_sem_ops = {
|
||||
.wait = wait_futex_sem,
|
||||
.wake = futex_wake_some,
|
||||
.setup = setup_futex_sems,
|
||||
.cleanup = cleanup_futex_sems,
|
||||
.name = "futex sem operations",
|
||||
};
|
||||
|
||||
/* nanosleep sems here */
|
||||
void wait_nanosleep_sem(struct lockinfo *l)
|
||||
{
|
||||
int ret;
|
||||
struct timespec tv = { 0, 1000000 };
|
||||
int count = 0;
|
||||
|
||||
l->data = 1;
|
||||
worklist_add(l);
|
||||
while(l->data) {
|
||||
ret = nanosleep(&tv, NULL);
|
||||
if (ret) {
|
||||
perror("nanosleep");
|
||||
exit(1);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
int nanosleep_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
|
||||
{
|
||||
int i;
|
||||
struct lockinfo *l;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
l = worklist_rm();
|
||||
if (!l)
|
||||
break;
|
||||
if (l->data != 1)
|
||||
fprintf(stderr, "warning, lockinfo data was %d\n",
|
||||
l->data);
|
||||
l->data = 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void setup_nanosleep_sems(struct sem_wakeup_info **wi, int num_semids)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_nanosleep_sems(int num)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
struct sem_operations nanosleep_sem_ops = {
|
||||
.wait = wait_nanosleep_sem,
|
||||
.wake = nanosleep_wake_some,
|
||||
.setup = setup_nanosleep_sems,
|
||||
.cleanup = cleanup_nanosleep_sems,
|
||||
.name = "nano sleep sem operations",
|
||||
};
|
||||
|
||||
void *worker(void *arg)
|
||||
{
|
||||
struct lockinfo *l = (struct lockinfo *)arg;
|
||||
int burn_count = 0;
|
||||
pthread_t tid = pthread_self();
|
||||
size_t pagesize = getpagesize();
|
||||
char *buf = malloc(pagesize);
|
||||
|
||||
if (!buf) {
|
||||
perror("malloc");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
l->tid = tid;
|
||||
workers_started = 1;
|
||||
smp_mb();
|
||||
|
||||
while(!all_done) {
|
||||
l->ops->wait(l);
|
||||
if (all_done)
|
||||
break;
|
||||
burn_count++;
|
||||
}
|
||||
pthread_mutex_lock(&worklist_mutex);
|
||||
total_burns += burn_count;
|
||||
if (burn_count < min_burns)
|
||||
min_burns = burn_count;
|
||||
if (burn_count > max_burns)
|
||||
max_burns = burn_count;
|
||||
thread_count--;
|
||||
pthread_mutex_unlock(&worklist_mutex);
|
||||
return (void *)0;
|
||||
}
|
||||
|
||||
void print_usage(void)
|
||||
{
|
||||
printf("usage: sembench [-t threads] [-w wake incr] [-r runtime]");
|
||||
printf(" [-o num] (0=ipc, 1=nanosleep, 2=futex)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define NUM_OPERATIONS 3
|
||||
struct sem_operations *allops[NUM_OPERATIONS] = { &ipc_sem_ops,
|
||||
&nanosleep_sem_ops,
|
||||
&futex_sem_ops};
|
||||
|
||||
int main(int ac, char **av) {
|
||||
int ret;
|
||||
int i;
|
||||
int semid = 0;
|
||||
int sem_num = 0;
|
||||
int burn_count = 0;
|
||||
struct sem_wakeup_info *wi = NULL;
|
||||
struct timeval start;
|
||||
struct timeval now;
|
||||
int num_semids = 0;
|
||||
int wake_num = 256;
|
||||
int run_secs = 30;
|
||||
int pagesize = getpagesize();
|
||||
char *buf = malloc(pagesize);
|
||||
struct sem_operations *ops = allops[0];
|
||||
cpu_set_t cpu_mask;
|
||||
cpu_set_t target_mask;
|
||||
int target_cpu = 0;
|
||||
int max_cpu = -1;
|
||||
|
||||
if (!buf) {
|
||||
perror("malloc");
|
||||
exit(1);
|
||||
}
|
||||
for (i = 1; i < ac; i++) {
|
||||
if (strcmp(av[i], "-t") == 0) {
|
||||
if (i == ac -1)
|
||||
print_usage();
|
||||
num_threads = atoi(av[i+1]);
|
||||
i++;
|
||||
} else if (strcmp(av[i], "-w") == 0) {
|
||||
if (i == ac -1)
|
||||
print_usage();
|
||||
wake_num = atoi(av[i+1]);
|
||||
i++;
|
||||
} else if (strcmp(av[i], "-r") == 0) {
|
||||
if (i == ac -1)
|
||||
print_usage();
|
||||
run_secs = atoi(av[i+1]);
|
||||
i++;
|
||||
} else if (strcmp(av[i], "-o") == 0) {
|
||||
int index;
|
||||
if (i == ac -1)
|
||||
print_usage();
|
||||
index = atoi(av[i+1]);
|
||||
if (index >= NUM_OPERATIONS) {
|
||||
fprintf(stderr, "invalid operations %d\n",
|
||||
index);
|
||||
exit(1);
|
||||
}
|
||||
ops = allops[index];
|
||||
i++;
|
||||
} else if (strcmp(av[i], "-T") == 0) {
|
||||
timeout_test = 1;
|
||||
} else if (strcmp(av[i], "-h") == 0) {
|
||||
print_usage();
|
||||
}
|
||||
}
|
||||
num_semids = (num_threads + SEMS_PERID - 1) / SEMS_PERID;
|
||||
ops->setup(&wi, num_semids);
|
||||
|
||||
ret = sched_getaffinity(0, sizeof(cpu_set_t), &cpu_mask);
|
||||
if (ret) {
|
||||
perror("sched_getaffinity");
|
||||
exit(1);
|
||||
}
|
||||
for (i = 0; i < CPU_SETSIZE; i++)
|
||||
if (CPU_ISSET(i, &cpu_mask))
|
||||
max_cpu = i;
|
||||
if (max_cpu == -1) {
|
||||
fprintf(stderr, "sched_getaffinity returned empty mask\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
CPU_ZERO(&target_mask);
|
||||
|
||||
worklist = malloc(sizeof(*worklist) * num_threads);
|
||||
memset(worklist, 0, sizeof(*worklist) * num_threads);
|
||||
|
||||
for (i = 0; i < num_threads; i++) {
|
||||
struct lockinfo *l;
|
||||
pthread_t tid;
|
||||
thread_count++;
|
||||
l = worklist + i;
|
||||
if (!l) {
|
||||
perror("malloc");
|
||||
exit(1);
|
||||
}
|
||||
l->id = semid;
|
||||
l->index = sem_num++;
|
||||
l->ops = ops;
|
||||
if (sem_num >= SEMS_PERID) {
|
||||
semid++;
|
||||
sem_num = 0;
|
||||
}
|
||||
ret = pthread_create(&tid, NULL, worker, (void *)l);
|
||||
if (ret) {
|
||||
perror("pthread_create");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (!CPU_ISSET(target_cpu, &cpu_mask)) {
|
||||
target_cpu++;
|
||||
if (target_cpu > max_cpu)
|
||||
target_cpu = 0;
|
||||
}
|
||||
CPU_SET(target_cpu, &target_mask);
|
||||
ret = pthread_setaffinity_np(tid, sizeof(cpu_set_t),
|
||||
&target_mask);
|
||||
CPU_CLR(target_cpu, &target_mask);
|
||||
target_cpu++;
|
||||
|
||||
ret = pthread_detach(tid);
|
||||
if (ret) {
|
||||
perror("pthread_detach");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
while(!workers_started) {
|
||||
smp_mb();
|
||||
usleep(200);
|
||||
}
|
||||
gettimeofday(&start, NULL);
|
||||
fprintf(stderr, "main loop going\n");
|
||||
while(1) {
|
||||
ops->wake(wi, num_semids, wake_num);
|
||||
burn_count++;
|
||||
gettimeofday(&now, NULL);
|
||||
if (now.tv_sec - start.tv_sec >= run_secs)
|
||||
break;
|
||||
}
|
||||
fprintf(stderr, "all done\n");
|
||||
all_done = 1;
|
||||
while(thread_count > 0) {
|
||||
ops->wake(wi, num_semids, wake_num);
|
||||
usleep(200);
|
||||
}
|
||||
printf("%d threads, waking %d at a time\n", num_threads, wake_num);
|
||||
printf("using %s\n", ops->name);
|
||||
printf("main thread burns: %d\n", burn_count);
|
||||
printf("worker burn count total %lu min %lu max %lu avg %lu\n",
|
||||
total_burns, min_burns, max_burns, total_burns / num_threads);
|
||||
printf("run time %d seconds %lu worker burns per second\n",
|
||||
(int)(now.tv_sec - start.tv_sec),
|
||||
total_burns / (now.tv_sec - start.tv_sec));
|
||||
ops->cleanup(num_semids);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
gcc -o sembench sembench.c -O2 -lpthread
|
||||
|
||||
COMMAND="./sembench -t 200 -w 20 -r 30 -o 2"
|
||||
|
||||
echo -e "\n\t\tPass 1 without tracing"
|
||||
$COMMAND
|
||||
echo -e "\n\t\tPass 2 without tracing"
|
||||
$COMMAND
|
||||
echo -e "\n\t\tPass 3 without tracing"
|
||||
$COMMAND
|
||||
|
||||
echo ""
|
||||
|
||||
KTAP_ONE_LINER="trace syscalls:sys_*_futex {}"
|
||||
|
||||
echo -e "\n\t\tPass 1 with tracing"
|
||||
../../ktap -e "$KTAP_ONE_LINER" -- $COMMAND
|
||||
echo -e "\n\t\tPass 2 with tracing"
|
||||
../../ktap -e "$KTAP_ONE_LINER" -- $COMMAND
|
||||
echo -e "\n\t\tPass 3 with tracing"
|
||||
../../ktap -e "$KTAP_ONE_LINER" -- $COMMAND
|
||||
|
||||
rm -rf ./sembench
|
@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
function failed() {
|
||||
printf("failed\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#----------------------------------------#
|
||||
|
||||
a = "123"
|
||||
b = "456"
|
||||
|
||||
if (a..b != "123456") {
|
||||
failed()
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#!/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()
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
#!/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()
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
#!/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();
|
||||
}
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
#!/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()
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
n = 0
|
||||
trace probe:schedule {
|
||||
n = n + 1
|
||||
}
|
||||
|
||||
tick-1s {
|
||||
if (n == 0) {
|
||||
printf("failed\n");
|
||||
}
|
||||
exit()
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
n = 0
|
||||
trace probe:__schedule%return {
|
||||
n = n + 1
|
||||
}
|
||||
|
||||
tick-1s {
|
||||
if (n == 0) {
|
||||
printf("failed\n");
|
||||
}
|
||||
exit()
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
#!/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()
|
||||
}
|
||||
|
||||
|
@ -1,40 +0,0 @@
|
||||
#!/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()
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
#!/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()
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
rmmod ktapvm > /dev/null 2>&1
|
||||
insmod ../ktapvm.ko
|
||||
if test $? -ne 0; then
|
||||
echo "Cannot insmod ../ktapvm.ko"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
KTAP=../ktap
|
||||
function ktaprun {
|
||||
echo "$KTAP $@"
|
||||
$KTAP $@
|
||||
}
|
||||
|
||||
|
||||
|
||||
#######################################################
|
||||
# Use $ktap directly if the arguments contains strings
|
||||
$KTAP arg.kp 1 testing "2 3 4"
|
||||
$KTAP -e 'print("one-liner testing")'
|
||||
$KTAP -e 'exit()'
|
||||
$KTAP -o /dev/null -e 'trace syscalls:* { print(argevent) }' \
|
||||
-- ls > /devnull
|
||||
|
||||
$KTAP -o /dev/null -e 'trace syscalls:* { print(argevent) }' \
|
||||
-- $KTAP -e 'while (1) {}'
|
||||
|
||||
ktaprun arith.kp
|
||||
ktaprun concat.kp
|
||||
ktaprun count.kp
|
||||
ktaprun fibonacci.kp
|
||||
ktaprun function.kp
|
||||
ktaprun if.kp
|
||||
ktaprun kprobe.kp
|
||||
ktaprun kretprobe.kp
|
||||
ktaprun len.kp
|
||||
ktaprun looping.kp
|
||||
ktaprun pairs.kp
|
||||
ktaprun table.kp
|
||||
ktaprun aggr_table.kp
|
||||
ktaprun timer.kp
|
||||
ktaprun tracepoint.kp
|
||||
ktaprun -o /dev/null zerodivide.kp
|
||||
ktaprun ansi.kp
|
||||
|
||||
echo "testing kill deadloop ktap script"
|
||||
$KTAP -e 'while (1) {}' &
|
||||
pkill ktap
|
||||
sleep 1
|
||||
|
||||
#####################################################
|
||||
rmmod ktapvm
|
||||
if test $? -ne 0; then
|
||||
echo "Error in rmmod ../ktapvm.ko, leak module refcount?"
|
||||
exit -1
|
||||
fi
|
||||
|
@ -1,71 +0,0 @@
|
||||
#!/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()
|
||||
}
|
||||
|
||||
|
@ -1,28 +0,0 @@
|
||||
#!/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()
|
||||
}
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
#!/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()
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env ktap
|
||||
|
||||
a = 1/0
|
||||
#should not go here
|
||||
printf("Failed\n")
|
@ -1,968 +0,0 @@
|
||||
/*
|
||||
* code.c - Code generator for ktap
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../include/ktap_types.h"
|
||||
#include "../include/ktap_opcodes.h"
|
||||
#include "ktapc.h"
|
||||
|
||||
|
||||
#define hasjumps(e) ((e)->t != (e)->f)
|
||||
|
||||
void codegen_patchtohere (ktap_funcstate *fs, int list);
|
||||
|
||||
static int isnumeral(ktap_expdesc *e)
|
||||
{
|
||||
return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP);
|
||||
}
|
||||
|
||||
void codegen_nil(ktap_funcstate *fs, int from, int n)
|
||||
{
|
||||
ktap_instruction *previous;
|
||||
int l = from + n - 1; /* last register to set nil */
|
||||
|
||||
if (fs->pc > fs->lasttarget) { /* no jumps to current position? */
|
||||
previous = &fs->f->code[fs->pc-1];
|
||||
if (GET_OPCODE(*previous) == OP_LOADNIL) {
|
||||
int pfrom = GETARG_A(*previous);
|
||||
int pl = pfrom + GETARG_B(*previous);
|
||||
|
||||
if ((pfrom <= from && from <= pl + 1) ||
|
||||
(from <= pfrom && pfrom <= l + 1)) { /* can connect both? */
|
||||
if (pfrom < from)
|
||||
from = pfrom; /* from = min(from, pfrom) */
|
||||
if (pl > l)
|
||||
l = pl; /* l = max(l, pl) */
|
||||
SETARG_A(*previous, from);
|
||||
SETARG_B(*previous, l - from);
|
||||
return;
|
||||
}
|
||||
} /* else go through */
|
||||
}
|
||||
codegen_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */
|
||||
}
|
||||
|
||||
int codegen_jump(ktap_funcstate *fs)
|
||||
{
|
||||
int jpc = fs->jpc; /* save list of jumps to here */
|
||||
int j;
|
||||
|
||||
fs->jpc = NO_JUMP;
|
||||
j = codegen_codeAsBx(fs, OP_JMP, 0, NO_JUMP);
|
||||
codegen_concat(fs, &j, jpc); /* keep them on hold */
|
||||
return j;
|
||||
}
|
||||
|
||||
void codegen_ret(ktap_funcstate *fs, int first, int nret)
|
||||
{
|
||||
codegen_codeABC(fs, OP_RETURN, first, nret+1, 0);
|
||||
}
|
||||
|
||||
static int condjump(ktap_funcstate *fs, OpCode op, int A, int B, int C)
|
||||
{
|
||||
codegen_codeABC(fs, op, A, B, C);
|
||||
return codegen_jump(fs);
|
||||
}
|
||||
|
||||
static void fixjump(ktap_funcstate *fs, int pc, int dest)
|
||||
{
|
||||
ktap_instruction *jmp = &fs->f->code[pc];
|
||||
int offset = dest-(pc+1);
|
||||
|
||||
ktap_assert(dest != NO_JUMP);
|
||||
if (abs(offset) > MAXARG_sBx)
|
||||
lex_syntaxerror(fs->ls, "control structure too long");
|
||||
SETARG_sBx(*jmp, offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* returns current `pc' and marks it as a jump target (to avoid wrong
|
||||
* optimizations with consecutive instructions not in the same basic block).
|
||||
*/
|
||||
int codegen_getlabel(ktap_funcstate *fs)
|
||||
{
|
||||
fs->lasttarget = fs->pc;
|
||||
return fs->pc;
|
||||
}
|
||||
|
||||
static int getjump(ktap_funcstate *fs, int pc)
|
||||
{
|
||||
int offset = GETARG_sBx(fs->f->code[pc]);
|
||||
|
||||
if (offset == NO_JUMP) /* point to itself represents end of list */
|
||||
return NO_JUMP; /* end of list */
|
||||
else
|
||||
return (pc+1)+offset; /* turn offset into absolute position */
|
||||
}
|
||||
|
||||
static ktap_instruction *getjumpcontrol(ktap_funcstate *fs, int pc)
|
||||
{
|
||||
ktap_instruction *pi = &fs->f->code[pc];
|
||||
if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))
|
||||
return pi-1;
|
||||
else
|
||||
return pi;
|
||||
}
|
||||
|
||||
/*
|
||||
* check whether list has any jump that do not produce a value
|
||||
* (or produce an inverted value)
|
||||
*/
|
||||
static int need_value(ktap_funcstate *fs, int list)
|
||||
{
|
||||
for (; list != NO_JUMP; list = getjump(fs, list)) {
|
||||
ktap_instruction i = *getjumpcontrol(fs, list);
|
||||
if (GET_OPCODE(i) != OP_TESTSET)
|
||||
return 1;
|
||||
}
|
||||
return 0; /* not found */
|
||||
}
|
||||
|
||||
static int patchtestreg(ktap_funcstate *fs, int node, int reg)
|
||||
{
|
||||
ktap_instruction *i = getjumpcontrol(fs, node);
|
||||
if (GET_OPCODE(*i) != OP_TESTSET)
|
||||
return 0; /* cannot patch other instructions */
|
||||
if (reg != NO_REG && reg != GETARG_B(*i))
|
||||
SETARG_A(*i, reg);
|
||||
else /* no register to put value or register already has the value */
|
||||
*i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void removevalues(ktap_funcstate *fs, int list)
|
||||
{
|
||||
for (; list != NO_JUMP; list = getjump(fs, list))
|
||||
patchtestreg(fs, list, NO_REG);
|
||||
}
|
||||
|
||||
static void patchlistaux(ktap_funcstate *fs, int list, int vtarget, int reg,
|
||||
int dtarget)
|
||||
{
|
||||
while (list != NO_JUMP) {
|
||||
int next = getjump(fs, list);
|
||||
if (patchtestreg(fs, list, reg))
|
||||
fixjump(fs, list, vtarget);
|
||||
else
|
||||
fixjump(fs, list, dtarget); /* jump to default target */
|
||||
list = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void dischargejpc(ktap_funcstate *fs)
|
||||
{
|
||||
patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc);
|
||||
fs->jpc = NO_JUMP;
|
||||
}
|
||||
|
||||
void codegen_patchlist(ktap_funcstate *fs, int list, int target)
|
||||
{
|
||||
if (target == fs->pc)
|
||||
codegen_patchtohere(fs, list);
|
||||
else {
|
||||
ktap_assert(target < fs->pc);
|
||||
patchlistaux(fs, list, target, NO_REG, target);
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_patchclose(ktap_funcstate *fs, int list, int level)
|
||||
{
|
||||
level++; /* argument is +1 to reserve 0 as non-op */
|
||||
while (list != NO_JUMP) {
|
||||
int next = getjump(fs, list);
|
||||
ktap_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP &&
|
||||
(GETARG_A(fs->f->code[list]) == 0 ||
|
||||
GETARG_A(fs->f->code[list]) >= level));
|
||||
SETARG_A(fs->f->code[list], level);
|
||||
list = next;
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_patchtohere(ktap_funcstate *fs, int list)
|
||||
{
|
||||
codegen_getlabel(fs);
|
||||
codegen_concat(fs, &fs->jpc, list);
|
||||
}
|
||||
|
||||
void codegen_concat(ktap_funcstate *fs, int *l1, int l2)
|
||||
{
|
||||
if (l2 == NO_JUMP)
|
||||
return;
|
||||
else if (*l1 == NO_JUMP)
|
||||
*l1 = l2;
|
||||
else {
|
||||
int list = *l1;
|
||||
int next;
|
||||
while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */
|
||||
list = next;
|
||||
fixjump(fs, list, l2);
|
||||
}
|
||||
}
|
||||
|
||||
static int codegen_code(ktap_funcstate *fs, ktap_instruction i)
|
||||
{
|
||||
ktap_proto *f = fs->f;
|
||||
|
||||
dischargejpc(fs); /* `pc' will change */
|
||||
|
||||
/* put new instruction in code array */
|
||||
ktapc_growvector(f->code, fs->pc, f->sizecode, ktap_instruction,
|
||||
MAX_INT, "opcodes");
|
||||
f->code[fs->pc] = i;
|
||||
|
||||
/* save corresponding line information */
|
||||
ktapc_growvector(f->lineinfo, fs->pc, f->sizelineinfo, int,
|
||||
MAX_INT, "opcodes");
|
||||
f->lineinfo[fs->pc] = fs->ls->lastline;
|
||||
return fs->pc++;
|
||||
}
|
||||
|
||||
int codegen_codeABC(ktap_funcstate *fs, OpCode o, int a, int b, int c)
|
||||
{
|
||||
ktap_assert(getOpMode(o) == iABC);
|
||||
//ktap_assert(getBMode(o) != OpArgN || b == 0);
|
||||
//ktap_assert(getCMode(o) != OpArgN || c == 0);
|
||||
//ktap_assert(a <= MAXARG_A && b <= MAXARG_B && c <= MAXARG_C);
|
||||
return codegen_code(fs, CREATE_ABC(o, a, b, c));
|
||||
}
|
||||
|
||||
int codegen_codeABx(ktap_funcstate *fs, OpCode o, int a, unsigned int bc)
|
||||
{
|
||||
ktap_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
|
||||
ktap_assert(getCMode(o) == OpArgN);
|
||||
ktap_assert(a <= MAXARG_A && bc <= MAXARG_Bx);
|
||||
return codegen_code(fs, CREATE_ABx(o, a, bc));
|
||||
}
|
||||
|
||||
static int codeextraarg(ktap_funcstate *fs, int a)
|
||||
{
|
||||
ktap_assert(a <= MAXARG_Ax);
|
||||
return codegen_code(fs, CREATE_Ax(OP_EXTRAARG, a));
|
||||
}
|
||||
|
||||
int codegen_codek(ktap_funcstate *fs, int reg, int k)
|
||||
{
|
||||
if (k <= MAXARG_Bx)
|
||||
return codegen_codeABx(fs, OP_LOADK, reg, k);
|
||||
else {
|
||||
int p = codegen_codeABx(fs, OP_LOADKX, reg, 0);
|
||||
codeextraarg(fs, k);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_checkstack(ktap_funcstate *fs, int n)
|
||||
{
|
||||
int newstack = fs->freereg + n;
|
||||
|
||||
if (newstack > fs->f->maxstacksize) {
|
||||
if (newstack >= MAXSTACK)
|
||||
lex_syntaxerror(fs->ls, "function or expression too complex");
|
||||
fs->f->maxstacksize = (u8)(newstack);
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_reserveregs(ktap_funcstate *fs, int n)
|
||||
{
|
||||
codegen_checkstack(fs, n);
|
||||
fs->freereg += n;
|
||||
}
|
||||
|
||||
static void freereg(ktap_funcstate *fs, int reg)
|
||||
{
|
||||
if (!ISK(reg) && reg >= fs->nactvar) {
|
||||
fs->freereg--;
|
||||
ktap_assert(reg == fs->freereg);
|
||||
}
|
||||
}
|
||||
|
||||
static void freeexp(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
if (e->k == VNONRELOC)
|
||||
freereg(fs, e->u.info);
|
||||
}
|
||||
|
||||
static int addk(ktap_funcstate *fs, ktap_value *key, ktap_value *v)
|
||||
{
|
||||
const ktap_value *idx = ktapc_table_get(fs->h, key);
|
||||
ktap_proto *f = fs->f;
|
||||
ktap_value kn;
|
||||
int k, oldsize;
|
||||
|
||||
if (ttisnumber(idx)) {
|
||||
ktap_number n = nvalue(idx);
|
||||
ktap_number2int(k, n);
|
||||
if (ktapc_equalobj(&f->k[k], v))
|
||||
return k;
|
||||
/* else may be a collision (e.g., between 0.0 and "\0\0\0\0\0\0\0\0");
|
||||
go through and create a new entry for this value */
|
||||
}
|
||||
/* constant not found; create a new entry */
|
||||
oldsize = f->sizek;
|
||||
k = fs->nk;
|
||||
|
||||
/* numerical value does not need GC barrier;
|
||||
table has no metatable, so it does not need to invalidate cache */
|
||||
setnvalue(&kn, (ktap_number)k);
|
||||
ktapc_table_setvalue(fs->h, key, &kn);
|
||||
ktapc_growvector(f->k, k, f->sizek, ktap_value, MAXARG_Ax, "constants");
|
||||
while (oldsize < f->sizek)
|
||||
setnilvalue(&f->k[oldsize++]);
|
||||
setobj(&f->k[k], v);
|
||||
fs->nk++;
|
||||
return k;
|
||||
}
|
||||
|
||||
int codegen_stringK(ktap_funcstate *fs, ktap_string *s)
|
||||
{
|
||||
ktap_value o;
|
||||
|
||||
setsvalue(&o, s);
|
||||
return addk(fs, &o, &o);
|
||||
}
|
||||
|
||||
int codegen_numberK(ktap_funcstate *fs, ktap_number r)
|
||||
{
|
||||
int n;
|
||||
ktap_value o, s;
|
||||
|
||||
setnvalue(&o, r);
|
||||
if (r == 0 || ktap_numisnan(NULL, r)) { /* handle -0 and NaN */
|
||||
/* use raw representation as key to avoid numeric problems */
|
||||
setsvalue(&s, ktapc_ts_newlstr((char *)&r, sizeof(r)));
|
||||
// incr_top(L);
|
||||
n = addk(fs, &s, &o);
|
||||
// L->top--;
|
||||
} else
|
||||
n = addk(fs, &o, &o); /* regular case */
|
||||
return n;
|
||||
}
|
||||
|
||||
static int boolK(ktap_funcstate *fs, int b)
|
||||
{
|
||||
ktap_value o;
|
||||
setbvalue(&o, b);
|
||||
return addk(fs, &o, &o);
|
||||
}
|
||||
|
||||
static int nilK(ktap_funcstate *fs)
|
||||
{
|
||||
ktap_value k, v;
|
||||
setnilvalue(&v);
|
||||
/* cannot use nil as key; instead use table itself to represent nil */
|
||||
sethvalue(&k, fs->h);
|
||||
return addk(fs, &k, &v);
|
||||
}
|
||||
|
||||
void codegen_setreturns(ktap_funcstate *fs, ktap_expdesc *e, int nresults)
|
||||
{
|
||||
if (e->k == VCALL) { /* expression is an open function call? */
|
||||
SETARG_C(getcode(fs, e), nresults+1);
|
||||
}
|
||||
else if (e->k == VVARARG) {
|
||||
SETARG_B(getcode(fs, e), nresults+1);
|
||||
SETARG_A(getcode(fs, e), fs->freereg);
|
||||
codegen_reserveregs(fs, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_setoneret(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
if (e->k == VCALL) { /* expression is an open function call? */
|
||||
e->k = VNONRELOC;
|
||||
e->u.info = GETARG_A(getcode(fs, e));
|
||||
} else if (e->k == VVARARG) {
|
||||
SETARG_B(getcode(fs, e), 2);
|
||||
e->k = VRELOCABLE; /* can relocate its simple result */
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_dischargevars(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
switch (e->k) {
|
||||
case VLOCAL: {
|
||||
e->k = VNONRELOC;
|
||||
break;
|
||||
}
|
||||
case VUPVAL: {
|
||||
e->u.info = codegen_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0);
|
||||
e->k = VRELOCABLE;
|
||||
break;
|
||||
}
|
||||
case VINDEXED: {
|
||||
OpCode op = OP_GETTABUP; /* assume 't' is in an upvalue */
|
||||
freereg(fs, e->u.ind.idx);
|
||||
if (e->u.ind.vt == VLOCAL) { /* 't' is in a register? */
|
||||
freereg(fs, e->u.ind.t);
|
||||
op = OP_GETTABLE;
|
||||
}
|
||||
e->u.info = codegen_codeABC(fs, op, 0, e->u.ind.t, e->u.ind.idx);
|
||||
e->k = VRELOCABLE;
|
||||
break;
|
||||
}
|
||||
case VVARARG:
|
||||
case VCALL: {
|
||||
codegen_setoneret(fs, e);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break; /* there is one value available (somewhere) */
|
||||
}
|
||||
}
|
||||
|
||||
static int code_label(ktap_funcstate *fs, int A, int b, int jump)
|
||||
{
|
||||
codegen_getlabel(fs); /* those instructions may be jump targets */
|
||||
return codegen_codeABC(fs, OP_LOADBOOL, A, b, jump);
|
||||
}
|
||||
|
||||
static void discharge2reg(ktap_funcstate *fs, ktap_expdesc *e, int reg)
|
||||
{
|
||||
codegen_dischargevars(fs, e);
|
||||
switch (e->k) {
|
||||
case VNIL: {
|
||||
codegen_nil(fs, reg, 1);
|
||||
break;
|
||||
}
|
||||
case VFALSE: case VTRUE: {
|
||||
codegen_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
|
||||
break;
|
||||
}
|
||||
case VEVENT:
|
||||
codegen_codeABC(fs, OP_EVENT, reg, 0, 0);
|
||||
break;
|
||||
case VEVENTNAME:
|
||||
codegen_codeABC(fs, OP_EVENTNAME, reg, 0, 0);
|
||||
break;
|
||||
case VEVENTARG:
|
||||
codegen_codeABC(fs, OP_EVENTARG, reg, e->u.info, 0);
|
||||
break;
|
||||
case VK: {
|
||||
codegen_codek(fs, reg, e->u.info);
|
||||
break;
|
||||
}
|
||||
case VKNUM: {
|
||||
codegen_codek(fs, reg, codegen_numberK(fs, e->u.nval));
|
||||
break;
|
||||
}
|
||||
case VRELOCABLE: {
|
||||
ktap_instruction *pc = &getcode(fs, e);
|
||||
SETARG_A(*pc, reg);
|
||||
break;
|
||||
}
|
||||
case VNONRELOC: {
|
||||
if (reg != e->u.info)
|
||||
codegen_codeABC(fs, OP_MOVE, reg, e->u.info, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ktap_assert(e->k == VVOID || e->k == VJMP);
|
||||
return; /* nothing to do... */
|
||||
}
|
||||
|
||||
e->u.info = reg;
|
||||
e->k = VNONRELOC;
|
||||
}
|
||||
|
||||
static void discharge2anyreg(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
if (e->k != VNONRELOC) {
|
||||
codegen_reserveregs(fs, 1);
|
||||
discharge2reg(fs, e, fs->freereg-1);
|
||||
}
|
||||
}
|
||||
|
||||
static void exp2reg(ktap_funcstate *fs, ktap_expdesc *e, int reg)
|
||||
{
|
||||
discharge2reg(fs, e, reg);
|
||||
if (e->k == VJMP)
|
||||
codegen_concat(fs, &e->t, e->u.info); /* put this jump in `t' list */
|
||||
if (hasjumps(e)) {
|
||||
int final; /* position after whole expression */
|
||||
int p_f = NO_JUMP; /* position of an eventual LOAD false */
|
||||
int p_t = NO_JUMP; /* position of an eventual LOAD true */
|
||||
|
||||
if (need_value(fs, e->t) || need_value(fs, e->f)) {
|
||||
int fj = (e->k == VJMP) ? NO_JUMP : codegen_jump(fs);
|
||||
|
||||
p_f = code_label(fs, reg, 0, 1);
|
||||
p_t = code_label(fs, reg, 1, 0);
|
||||
codegen_patchtohere(fs, fj);
|
||||
}
|
||||
final = codegen_getlabel(fs);
|
||||
patchlistaux(fs, e->f, final, reg, p_f);
|
||||
patchlistaux(fs, e->t, final, reg, p_t);
|
||||
}
|
||||
e->f = e->t = NO_JUMP;
|
||||
e->u.info = reg;
|
||||
e->k = VNONRELOC;
|
||||
}
|
||||
|
||||
void codegen_exp2nextreg(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
codegen_dischargevars(fs, e);
|
||||
freeexp(fs, e);
|
||||
codegen_reserveregs(fs, 1);
|
||||
exp2reg(fs, e, fs->freereg - 1);
|
||||
}
|
||||
|
||||
int codegen_exp2anyreg(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
codegen_dischargevars(fs, e);
|
||||
if (e->k == VNONRELOC) {
|
||||
if (!hasjumps(e))
|
||||
return e->u.info; /* exp is already in a register */
|
||||
if (e->u.info >= fs->nactvar) { /* reg. is not a local? */
|
||||
exp2reg(fs, e, e->u.info); /* put value on it */
|
||||
return e->u.info;
|
||||
}
|
||||
}
|
||||
codegen_exp2nextreg(fs, e); /* default */
|
||||
return e->u.info;
|
||||
}
|
||||
|
||||
void codegen_exp2anyregup(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
if (e->k != VUPVAL || hasjumps(e))
|
||||
codegen_exp2anyreg(fs, e);
|
||||
}
|
||||
|
||||
void codegen_exp2val(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
if (hasjumps(e))
|
||||
codegen_exp2anyreg(fs, e);
|
||||
else
|
||||
codegen_dischargevars(fs, e);
|
||||
}
|
||||
|
||||
int codegen_exp2RK(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
codegen_exp2val(fs, e);
|
||||
switch (e->k) {
|
||||
case VTRUE:
|
||||
case VFALSE:
|
||||
case VNIL: {
|
||||
if (fs->nk <= MAXINDEXRK) { /* constant fits in RK operand? */
|
||||
e->u.info = (e->k == VNIL) ? nilK(fs) :
|
||||
boolK(fs, (e->k == VTRUE));
|
||||
e->k = VK;
|
||||
return RKASK(e->u.info);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
case VKNUM: {
|
||||
e->u.info = codegen_numberK(fs, e->u.nval);
|
||||
e->k = VK;
|
||||
/* go through */
|
||||
}
|
||||
case VK: {
|
||||
if (e->u.info <= MAXINDEXRK) /* constant fits in argC? */
|
||||
return RKASK(e->u.info);
|
||||
else
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* not a constant in the right range: put it in a register */
|
||||
return codegen_exp2anyreg(fs, e);
|
||||
}
|
||||
|
||||
void codegen_storevar(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex)
|
||||
{
|
||||
switch (var->k) {
|
||||
case VLOCAL: {
|
||||
freeexp(fs, ex);
|
||||
exp2reg(fs, ex, var->u.info);
|
||||
return;
|
||||
}
|
||||
case VUPVAL: {
|
||||
int e = codegen_exp2anyreg(fs, ex);
|
||||
codegen_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
|
||||
break;
|
||||
}
|
||||
case VINDEXED: {
|
||||
OpCode op = (var->u.ind.vt == VLOCAL) ? OP_SETTABLE : OP_SETTABUP;
|
||||
int e = codegen_exp2RK(fs, ex);
|
||||
codegen_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ktap_assert(0); /* invalid var kind to store */
|
||||
break;
|
||||
}
|
||||
|
||||
freeexp(fs, ex);
|
||||
}
|
||||
|
||||
void codegen_storeincr(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex)
|
||||
{
|
||||
switch (var->k) {
|
||||
#if 0 /*current not supported */
|
||||
case VLOCAL: {
|
||||
freeexp(fs, ex);
|
||||
exp2reg(fs, ex, var->u.info);
|
||||
return;
|
||||
}
|
||||
case VUPVAL: {
|
||||
int e = codegen_exp2anyreg(fs, ex);
|
||||
codegen_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case VINDEXED: {
|
||||
OpCode op = (var->u.ind.vt == VLOCAL) ? OP_SETTABLE_INCR :
|
||||
OP_SETTABUP_INCR;
|
||||
int e = codegen_exp2RK(fs, ex);
|
||||
codegen_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ktap_assert(0); /* invalid var kind to store */
|
||||
break;
|
||||
}
|
||||
|
||||
freeexp(fs, ex);
|
||||
}
|
||||
|
||||
|
||||
void codegen_self(ktap_funcstate *fs, ktap_expdesc *e, ktap_expdesc *key)
|
||||
{
|
||||
int ereg;
|
||||
|
||||
codegen_exp2anyreg(fs, e);
|
||||
ereg = e->u.info; /* register where 'e' was placed */
|
||||
freeexp(fs, e);
|
||||
e->u.info = fs->freereg; /* base register for op_self */
|
||||
e->k = VNONRELOC;
|
||||
codegen_reserveregs(fs, 2); /* function and 'self' produced by op_self */
|
||||
codegen_codeABC(fs, OP_SELF, e->u.info, ereg, codegen_exp2RK(fs, key));
|
||||
freeexp(fs, key);
|
||||
}
|
||||
|
||||
static void invertjump(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
ktap_instruction *pc = getjumpcontrol(fs, e->u.info);
|
||||
ktap_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
|
||||
GET_OPCODE(*pc) != OP_TEST);
|
||||
SETARG_A(*pc, !(GETARG_A(*pc)));
|
||||
}
|
||||
|
||||
static int jumponcond(ktap_funcstate *fs, ktap_expdesc *e, int cond)
|
||||
{
|
||||
if (e->k == VRELOCABLE) {
|
||||
ktap_instruction ie = getcode(fs, e);
|
||||
if (GET_OPCODE(ie) == OP_NOT) {
|
||||
fs->pc--; /* remove previous OP_NOT */
|
||||
return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
|
||||
}
|
||||
/* else go through */
|
||||
}
|
||||
discharge2anyreg(fs, e);
|
||||
freeexp(fs, e);
|
||||
return condjump(fs, OP_TESTSET, NO_REG, e->u.info, cond);
|
||||
}
|
||||
|
||||
void codegen_goiftrue(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
int pc; /* pc of last jump */
|
||||
|
||||
codegen_dischargevars(fs, e);
|
||||
switch (e->k) {
|
||||
case VJMP: {
|
||||
invertjump(fs, e);
|
||||
pc = e->u.info;
|
||||
break;
|
||||
}
|
||||
case VK: case VKNUM: case VTRUE: {
|
||||
pc = NO_JUMP; /* always true; do nothing */
|
||||
break;
|
||||
}
|
||||
default:
|
||||
pc = jumponcond(fs, e, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
codegen_concat(fs, &e->f, pc); /* insert last jump in `f' list */
|
||||
codegen_patchtohere(fs, e->t);
|
||||
e->t = NO_JUMP;
|
||||
}
|
||||
|
||||
void codegen_goiffalse(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
int pc; /* pc of last jump */
|
||||
codegen_dischargevars(fs, e);
|
||||
|
||||
switch (e->k) {
|
||||
case VJMP: {
|
||||
pc = e->u.info;
|
||||
break;
|
||||
}
|
||||
case VNIL: case VFALSE: {
|
||||
pc = NO_JUMP; /* always false; do nothing */
|
||||
break;
|
||||
}
|
||||
default:
|
||||
pc = jumponcond(fs, e, 1);
|
||||
break;
|
||||
}
|
||||
codegen_concat(fs, &e->t, pc); /* insert last jump in `t' list */
|
||||
codegen_patchtohere(fs, e->f);
|
||||
e->f = NO_JUMP;
|
||||
}
|
||||
|
||||
static void codenot(ktap_funcstate *fs, ktap_expdesc *e)
|
||||
{
|
||||
codegen_dischargevars(fs, e);
|
||||
switch (e->k) {
|
||||
case VNIL: case VFALSE: {
|
||||
e->k = VTRUE;
|
||||
break;
|
||||
}
|
||||
case VK: case VKNUM: case VTRUE: {
|
||||
e->k = VFALSE;
|
||||
break;
|
||||
}
|
||||
case VJMP: {
|
||||
invertjump(fs, e);
|
||||
break;
|
||||
}
|
||||
case VRELOCABLE:
|
||||
case VNONRELOC: {
|
||||
discharge2anyreg(fs, e);
|
||||
freeexp(fs, e);
|
||||
e->u.info = codegen_codeABC(fs, OP_NOT, 0, e->u.info, 0);
|
||||
e->k = VRELOCABLE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ktap_assert(0); /* cannot happen */
|
||||
break;
|
||||
}
|
||||
|
||||
/* interchange true and false lists */
|
||||
{ int temp = e->f; e->f = e->t; e->t = temp; }
|
||||
removevalues(fs, e->f);
|
||||
removevalues(fs, e->t);
|
||||
}
|
||||
|
||||
void codegen_indexed(ktap_funcstate *fs, ktap_expdesc *t, ktap_expdesc *k)
|
||||
{
|
||||
ktap_assert(!hasjumps(t));
|
||||
t->u.ind.t = t->u.info;
|
||||
t->u.ind.idx = codegen_exp2RK(fs, k);
|
||||
t->u.ind.vt = (t->k == VUPVAL) ? VUPVAL
|
||||
: check_exp(vkisinreg(t->k), VLOCAL);
|
||||
t->k = VINDEXED;
|
||||
}
|
||||
|
||||
static int constfolding(OpCode op, ktap_expdesc *e1, ktap_expdesc *e2)
|
||||
{
|
||||
ktap_number r;
|
||||
|
||||
if (!isnumeral(e1) || !isnumeral(e2))
|
||||
return 0;
|
||||
|
||||
if ((op == OP_DIV || op == OP_MOD) && e2->u.nval == 0)
|
||||
return 0; /* do not attempt to divide by 0 */
|
||||
|
||||
if (op == OP_POW)
|
||||
return 0; /* ktap current do not suppor pow arith */
|
||||
|
||||
r = ktapc_arith(op - OP_ADD + KTAP_OPADD, e1->u.nval, e2->u.nval);
|
||||
e1->u.nval = r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void codearith(ktap_funcstate *fs, OpCode op,
|
||||
ktap_expdesc *e1, ktap_expdesc *e2, int line)
|
||||
{
|
||||
if (constfolding(op, e1, e2))
|
||||
return;
|
||||
else {
|
||||
int o2 = (op != OP_UNM && op != OP_LEN) ? codegen_exp2RK(fs, e2) : 0;
|
||||
int o1 = codegen_exp2RK(fs, e1);
|
||||
|
||||
if (o1 > o2) {
|
||||
freeexp(fs, e1);
|
||||
freeexp(fs, e2);
|
||||
} else {
|
||||
freeexp(fs, e2);
|
||||
freeexp(fs, e1);
|
||||
}
|
||||
e1->u.info = codegen_codeABC(fs, op, 0, o1, o2);
|
||||
e1->k = VRELOCABLE;
|
||||
codegen_fixline(fs, line);
|
||||
}
|
||||
}
|
||||
|
||||
static void codecomp(ktap_funcstate *fs, OpCode op, int cond, ktap_expdesc *e1,
|
||||
ktap_expdesc *e2)
|
||||
{
|
||||
int o1 = codegen_exp2RK(fs, e1);
|
||||
int o2 = codegen_exp2RK(fs, e2);
|
||||
|
||||
freeexp(fs, e2);
|
||||
freeexp(fs, e1);
|
||||
if (cond == 0 && op != OP_EQ) {
|
||||
int temp; /* exchange args to replace by `<' or `<=' */
|
||||
temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */
|
||||
cond = 1;
|
||||
}
|
||||
e1->u.info = condjump(fs, op, cond, o1, o2);
|
||||
e1->k = VJMP;
|
||||
}
|
||||
|
||||
void codegen_prefix(ktap_funcstate *fs, UnOpr op, ktap_expdesc *e, int line)
|
||||
{
|
||||
ktap_expdesc e2;
|
||||
|
||||
e2.t = e2.f = NO_JUMP;
|
||||
e2.k = VKNUM;
|
||||
e2.u.nval = 0;
|
||||
|
||||
switch (op) {
|
||||
case OPR_MINUS: {
|
||||
if (isnumeral(e)) /* minus constant? */
|
||||
e->u.nval = ktap_numunm(e->u.nval); /* fold it */
|
||||
else {
|
||||
codegen_exp2anyreg(fs, e);
|
||||
codearith(fs, OP_UNM, e, &e2, line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OPR_NOT:
|
||||
codenot(fs, e);
|
||||
break;
|
||||
case OPR_LEN: {
|
||||
codegen_exp2anyreg(fs, e); /* cannot operate on constants */
|
||||
codearith(fs, OP_LEN, e, &e2, line);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ktap_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_infix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *v)
|
||||
{
|
||||
switch (op) {
|
||||
case OPR_AND: {
|
||||
codegen_goiftrue(fs, v);
|
||||
break;
|
||||
}
|
||||
case OPR_OR: {
|
||||
codegen_goiffalse(fs, v);
|
||||
break;
|
||||
}
|
||||
case OPR_CONCAT: {
|
||||
codegen_exp2nextreg(fs, v); /* operand must be on the `stack' */
|
||||
break;
|
||||
}
|
||||
case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
|
||||
case OPR_MOD: case OPR_POW: {
|
||||
if (!isnumeral(v)) codegen_exp2RK(fs, v);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
codegen_exp2RK(fs, v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_posfix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *e1, ktap_expdesc *e2, int line)
|
||||
{
|
||||
switch (op) {
|
||||
case OPR_AND: {
|
||||
ktap_assert(e1->t == NO_JUMP); /* list must be closed */
|
||||
codegen_dischargevars(fs, e2);
|
||||
codegen_concat(fs, &e2->f, e1->f);
|
||||
*e1 = *e2;
|
||||
break;
|
||||
}
|
||||
case OPR_OR: {
|
||||
ktap_assert(e1->f == NO_JUMP); /* list must be closed */
|
||||
codegen_dischargevars(fs, e2);
|
||||
codegen_concat(fs, &e2->t, e1->t);
|
||||
*e1 = *e2;
|
||||
break;
|
||||
}
|
||||
case OPR_CONCAT: {
|
||||
codegen_exp2val(fs, e2);
|
||||
if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) {
|
||||
ktap_assert(e1->u.info == GETARG_B(getcode(fs, e2))-1);
|
||||
freeexp(fs, e1);
|
||||
SETARG_B(getcode(fs, e2), e1->u.info);
|
||||
e1->k = VRELOCABLE; e1->u.info = e2->u.info;
|
||||
} else {
|
||||
codegen_exp2nextreg(fs, e2); /* operand must be on the 'stack' */
|
||||
codearith(fs, OP_CONCAT, e1, e2, line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
|
||||
case OPR_MOD: case OPR_POW: {
|
||||
codearith(fs, (OpCode)(op - OPR_ADD + OP_ADD), e1, e2, line);
|
||||
break;
|
||||
}
|
||||
case OPR_EQ: case OPR_LT: case OPR_LE: {
|
||||
codecomp(fs, (OpCode)(op - OPR_EQ + OP_EQ), 1, e1, e2);
|
||||
break;
|
||||
}
|
||||
case OPR_NE: case OPR_GT: case OPR_GE: {
|
||||
codecomp(fs, (OpCode)(op - OPR_NE + OP_EQ), 0, e1, e2);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ktap_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_fixline(ktap_funcstate *fs, int line)
|
||||
{
|
||||
fs->f->lineinfo[fs->pc - 1] = line;
|
||||
}
|
||||
|
||||
void codegen_setlist(ktap_funcstate *fs, int base, int nelems, int tostore)
|
||||
{
|
||||
int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1;
|
||||
int b = (tostore == KTAP_MULTRET) ? 0 : tostore;
|
||||
|
||||
ktap_assert(tostore != 0);
|
||||
if (c <= MAXARG_C)
|
||||
codegen_codeABC(fs, OP_SETLIST, base, b, c);
|
||||
else if (c <= MAXARG_Ax) {
|
||||
codegen_codeABC(fs, OP_SETLIST, base, b, 0);
|
||||
codeextraarg(fs, c);
|
||||
} else
|
||||
lex_syntaxerror(fs->ls, "constructor too long");
|
||||
fs->freereg = base + 1; /* free registers with list values */
|
||||
}
|
||||
|
@ -1,187 +0,0 @@
|
||||
/*
|
||||
* dump.c - save precompiled ktap chunks
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../include/ktap_types.h"
|
||||
#include "../include/ktap_opcodes.h"
|
||||
#include "ktapc.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
ktap_writer writer;
|
||||
void *data;
|
||||
int strip;
|
||||
int status;
|
||||
} DumpState;
|
||||
|
||||
#define DumpMem(b, n, size, D) DumpBlock(b, (n)*(size), D)
|
||||
#define DumpVar(x, D) DumpMem(&x, 1, sizeof(x), D)
|
||||
|
||||
static void DumpBlock(const void *b, size_t size, DumpState *D)
|
||||
{
|
||||
if (D->status == 0)
|
||||
D->status = ((D->writer))(b, size, D->data);
|
||||
}
|
||||
|
||||
static void DumpChar(int y, DumpState *D)
|
||||
{
|
||||
char x = (char)y;
|
||||
DumpVar(x, D);
|
||||
}
|
||||
|
||||
static void DumpInt(int x, DumpState *D)
|
||||
{
|
||||
DumpVar(x, D);
|
||||
}
|
||||
|
||||
static void DumpNumber(ktap_number x, DumpState *D)
|
||||
{
|
||||
DumpVar(x,D);
|
||||
}
|
||||
|
||||
static void DumpVector(const void *b, int n, size_t size, DumpState *D)
|
||||
{
|
||||
DumpInt(n, D);
|
||||
DumpMem(b, n, size, D);
|
||||
}
|
||||
|
||||
static void DumpString(const ktap_string *s, DumpState *D)
|
||||
{
|
||||
if (s == NULL) {
|
||||
int size = 0;
|
||||
DumpVar(size, D);
|
||||
} else {
|
||||
int size = s->tsv.len + 1; /* include trailing '\0' */
|
||||
DumpVar(size, D);
|
||||
DumpBlock(getstr(s), size * sizeof(char), D);
|
||||
}
|
||||
}
|
||||
|
||||
#define DumpCode(f, D) DumpVector(f->code, f->sizecode, sizeof(ktap_instruction), D)
|
||||
|
||||
static void DumpFunction(const ktap_proto *f, DumpState *D);
|
||||
|
||||
static void DumpConstants(const ktap_proto *f, DumpState *D)
|
||||
{
|
||||
int i, n = f->sizek;
|
||||
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++) {
|
||||
const ktap_value* o=&f->k[i];
|
||||
DumpChar(ttypenv(o), D);
|
||||
switch (ttypenv(o)) {
|
||||
case KTAP_TNIL:
|
||||
break;
|
||||
case KTAP_TBOOLEAN:
|
||||
DumpChar(bvalue(o), D);
|
||||
break;
|
||||
case KTAP_TNUMBER:
|
||||
DumpNumber(nvalue(o), D);
|
||||
break;
|
||||
case KTAP_TSTRING:
|
||||
DumpString(rawtsvalue(o), D);
|
||||
break;
|
||||
default:
|
||||
printf("ktap: DumpConstants with unknown vaule type %d\n", ttypenv(o));
|
||||
ktap_assert(0);
|
||||
}
|
||||
}
|
||||
n = f->sizep;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++)
|
||||
DumpFunction(f->p[i], D);
|
||||
}
|
||||
|
||||
static void DumpUpvalues(const ktap_proto *f, DumpState *D)
|
||||
{
|
||||
int i, n = f->sizeupvalues;
|
||||
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++) {
|
||||
DumpChar(f->upvalues[i].instack, D);
|
||||
DumpChar(f->upvalues[i].idx, D);
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpDebug(const ktap_proto *f, DumpState *D)
|
||||
{
|
||||
int i,n;
|
||||
|
||||
DumpString((D->strip) ? NULL : f->source, D);
|
||||
n= (D->strip) ? 0 : f->sizelineinfo;
|
||||
DumpVector(f->lineinfo, n, sizeof(int), D);
|
||||
n = (D->strip) ? 0 : f->sizelocvars;
|
||||
DumpInt(n, D);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
DumpString(f->locvars[i].varname, D);
|
||||
DumpInt(f->locvars[i].startpc, D);
|
||||
DumpInt(f->locvars[i].endpc, D);
|
||||
}
|
||||
n = (D->strip) ? 0 : f->sizeupvalues;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++)
|
||||
DumpString(f->upvalues[i].name, D);
|
||||
}
|
||||
|
||||
static void DumpFunction(const ktap_proto *f, DumpState *D)
|
||||
{
|
||||
DumpInt(f->linedefined, D);
|
||||
DumpInt(f->lastlinedefined, D);
|
||||
DumpChar(f->numparams, D);
|
||||
DumpChar(f->is_vararg, D);
|
||||
DumpChar(f->maxstacksize, D);
|
||||
DumpCode(f, D);
|
||||
DumpConstants(f, D);
|
||||
DumpUpvalues(f, D);
|
||||
DumpDebug(f, D);
|
||||
}
|
||||
|
||||
static void DumpHeader(DumpState *D)
|
||||
{
|
||||
u8 h[KTAPC_HEADERSIZE];
|
||||
|
||||
kp_header(h);
|
||||
DumpBlock(h, KTAPC_HEADERSIZE, D);
|
||||
}
|
||||
|
||||
/*
|
||||
* dump ktap function as precompiled chunk
|
||||
*/
|
||||
int ktapc_dump(const ktap_proto *f, ktap_writer w, void *data, int strip)
|
||||
{
|
||||
DumpState D;
|
||||
|
||||
D.writer = w;
|
||||
D.data = data;
|
||||
D.strip = strip;
|
||||
D.status = 0;
|
||||
DumpHeader(&D);
|
||||
DumpFunction(f, &D);
|
||||
return D.status;
|
||||
}
|
@ -1,588 +0,0 @@
|
||||
/*
|
||||
* eventdef.c - ktap eventdef parser
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../include/ktap_types.h"
|
||||
#include "../include/ktap_opcodes.h"
|
||||
#include "ktapc.h"
|
||||
|
||||
static char tracing_events_path[] = "/sys/kernel/debug/tracing/events";
|
||||
|
||||
#define IDS_ARRAY_SIZE 4096
|
||||
static u8 *ids_array;
|
||||
|
||||
#define set_id(id) \
|
||||
do { \
|
||||
ids_array[id/8] = ids_array[id/8] | (1 << (id%8)); \
|
||||
} while(0)
|
||||
|
||||
#define clear_id(id) \
|
||||
do { \
|
||||
ids_array[id/8] = ids_array[id/8] & ~ (1 << (id%8)); \
|
||||
} while(0)
|
||||
|
||||
|
||||
static int get_digit_len(int id)
|
||||
{
|
||||
int len = -1;
|
||||
|
||||
if (id < 10)
|
||||
len = 1;
|
||||
else if (id < 100)
|
||||
len = 2;
|
||||
else if (id < 1000)
|
||||
len = 3;
|
||||
else if (id < 10000)
|
||||
len = 4;
|
||||
else if (id < 100000)
|
||||
len = 5;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static char *get_idstr(char *filter)
|
||||
{
|
||||
char *idstr, *ptr;
|
||||
int total_len = 0;
|
||||
int filter_len;
|
||||
int i;
|
||||
|
||||
filter_len = filter ? strlen(filter) : 0;
|
||||
|
||||
for (i = 0; i < IDS_ARRAY_SIZE*8; i++) {
|
||||
if (ids_array[i/8] & (1 << (i%8)))
|
||||
total_len += get_digit_len(i) + 1;
|
||||
}
|
||||
|
||||
if (!total_len)
|
||||
return NULL;
|
||||
|
||||
idstr = malloc(total_len + filter_len + 1);
|
||||
if (!idstr)
|
||||
return NULL;
|
||||
|
||||
memset(idstr, 0, total_len + filter_len + 1);
|
||||
ptr = idstr;
|
||||
for (i = 0; i < IDS_ARRAY_SIZE*8; i++) {
|
||||
if (ids_array[i/8] & (1 << (i%8))) {
|
||||
char digits[32] = {0};
|
||||
int len;
|
||||
|
||||
sprintf(digits, "%d ", i);
|
||||
len = strlen(digits);
|
||||
strncpy(ptr, digits, len);
|
||||
ptr += len;
|
||||
}
|
||||
}
|
||||
|
||||
if (filter)
|
||||
memcpy(ptr, filter, strlen(filter));
|
||||
|
||||
return idstr;
|
||||
}
|
||||
|
||||
static int add_event(char *evtid_path)
|
||||
{
|
||||
char id_buf[24];
|
||||
int id, fd;
|
||||
|
||||
fd = open(evtid_path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
/*
|
||||
* some tracepoint doesn't have id file, like ftrace,
|
||||
* return success in here, and don't print error.
|
||||
*/
|
||||
verbose_printf("warning: cannot open file %s\n", evtid_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (read(fd, id_buf, sizeof(id_buf)) < 0) {
|
||||
fprintf(stderr, "read file error %s\n", evtid_path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
id = atoll(id_buf);
|
||||
|
||||
if (id >= IDS_ARRAY_SIZE * 8) {
|
||||
fprintf(stderr, "tracepoint id(%d) is bigger than %d\n", id,
|
||||
IDS_ARRAY_SIZE * 8);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
set_id(id);
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_tracepoint(char *sys_name, char *evt_name)
|
||||
{
|
||||
char evtid_path[PATH_MAX] = {0};
|
||||
|
||||
|
||||
snprintf(evtid_path, PATH_MAX, "%s/%s/%s/id", tracing_events_path,
|
||||
sys_name, evt_name);
|
||||
return add_event(evtid_path);
|
||||
}
|
||||
|
||||
static int add_tracepoint_multi_event(char *sys_name, char *evt_name)
|
||||
{
|
||||
char evt_path[PATH_MAX];
|
||||
struct dirent *evt_ent;
|
||||
DIR *evt_dir;
|
||||
int ret = 0;
|
||||
|
||||
snprintf(evt_path, PATH_MAX, "%s/%s", tracing_events_path, sys_name);
|
||||
evt_dir = opendir(evt_path);
|
||||
if (!evt_dir) {
|
||||
perror("Can't open event dir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!ret && (evt_ent = readdir(evt_dir))) {
|
||||
if (!strcmp(evt_ent->d_name, ".")
|
||||
|| !strcmp(evt_ent->d_name, "..")
|
||||
|| !strcmp(evt_ent->d_name, "enable")
|
||||
|| !strcmp(evt_ent->d_name, "filter"))
|
||||
continue;
|
||||
|
||||
if (!strglobmatch(evt_ent->d_name, evt_name))
|
||||
continue;
|
||||
|
||||
ret = add_tracepoint(sys_name, evt_ent->d_name);
|
||||
}
|
||||
|
||||
closedir(evt_dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int add_tracepoint_event(char *sys_name, char *evt_name)
|
||||
{
|
||||
return strpbrk(evt_name, "*?") ?
|
||||
add_tracepoint_multi_event(sys_name, evt_name) :
|
||||
add_tracepoint(sys_name, evt_name);
|
||||
}
|
||||
|
||||
static int add_tracepoint_multi_sys(char *sys_name, char *evt_name)
|
||||
{
|
||||
struct dirent *events_ent;
|
||||
DIR *events_dir;
|
||||
int ret = 0;
|
||||
|
||||
events_dir = opendir(tracing_events_path);
|
||||
if (!events_dir) {
|
||||
perror("Can't open event dir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!ret && (events_ent = readdir(events_dir))) {
|
||||
if (!strcmp(events_ent->d_name, ".")
|
||||
|| !strcmp(events_ent->d_name, "..")
|
||||
|| !strcmp(events_ent->d_name, "enable")
|
||||
|| !strcmp(events_ent->d_name, "header_event")
|
||||
|| !strcmp(events_ent->d_name, "header_page"))
|
||||
continue;
|
||||
|
||||
if (!strglobmatch(events_ent->d_name, sys_name))
|
||||
continue;
|
||||
|
||||
ret = add_tracepoint_event(events_ent->d_name,
|
||||
evt_name);
|
||||
}
|
||||
|
||||
closedir(events_dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_events_add_tracepoint(char *sys, char *event)
|
||||
{
|
||||
if (strpbrk(sys, "*?"))
|
||||
return add_tracepoint_multi_sys(sys, event);
|
||||
else
|
||||
return add_tracepoint_event(sys, event);
|
||||
}
|
||||
|
||||
enum {
|
||||
KPROBE_EVENT,
|
||||
UPROBE_EVENT,
|
||||
};
|
||||
|
||||
struct probe_list {
|
||||
struct probe_list *next;
|
||||
int type;
|
||||
int kp_seq;
|
||||
char *probe_event;
|
||||
};
|
||||
|
||||
static struct probe_list *probe_list_head;
|
||||
|
||||
#define KPROBE_EVENTS_PATH "/sys/kernel/debug/tracing/kprobe_events"
|
||||
|
||||
static int parse_events_add_kprobe(char *old_event)
|
||||
{
|
||||
static int event_seq = 0;
|
||||
struct probe_list *pl;
|
||||
char probe_event[128] = {0};
|
||||
char event_id_path[128] = {0};
|
||||
char *event;
|
||||
char *r;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
fd = open(KPROBE_EVENTS_PATH, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Cannot open %s\n", KPROBE_EVENTS_PATH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
event = strdup(old_event);
|
||||
r = strstr(event, "%return");
|
||||
if (r) {
|
||||
memset(r, ' ', 7);
|
||||
snprintf(probe_event, 128, "r:kprobes/kp%d %s",
|
||||
event_seq, event);
|
||||
} else
|
||||
snprintf(probe_event, 128, "p:kprobes/kp%d %s",
|
||||
event_seq, event);
|
||||
|
||||
free(event);
|
||||
|
||||
verbose_printf("kprobe event %s\n", probe_event);
|
||||
ret = write(fd, probe_event, strlen(probe_event));
|
||||
if (ret <= 0) {
|
||||
fprintf(stderr, "Cannot write %s to %s\n", probe_event,
|
||||
KPROBE_EVENTS_PATH);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
pl = malloc(sizeof(struct probe_list));
|
||||
if (!pl)
|
||||
return -1;
|
||||
|
||||
pl->type = KPROBE_EVENT;
|
||||
pl->kp_seq = event_seq;
|
||||
pl->next = probe_list_head;
|
||||
probe_list_head = pl;
|
||||
|
||||
sprintf(event_id_path, "/sys/kernel/debug/tracing/events/kprobes/kp%d/id",
|
||||
event_seq);
|
||||
ret = add_event(event_id_path);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
event_seq++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define UPROBE_EVENTS_PATH "/sys/kernel/debug/tracing/uprobe_events"
|
||||
|
||||
static int parse_events_add_uprobe(char *old_event)
|
||||
{
|
||||
static int event_seq = 0;
|
||||
struct probe_list *pl;
|
||||
char probe_event[128] = {0};
|
||||
char event_id_path[128] = {0};
|
||||
char *event;
|
||||
char *r;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
fd = open(UPROBE_EVENTS_PATH, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Cannot open %s\n", UPROBE_EVENTS_PATH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
event = strdup(old_event);
|
||||
r = strstr(event, "%return");
|
||||
if (r) {
|
||||
memset(r, ' ', 7);
|
||||
snprintf(probe_event, 128, "r:uprobes/kp%d %s",
|
||||
event_seq, event);
|
||||
} else
|
||||
snprintf(probe_event, 128, "p:uprobes/kp%d %s",
|
||||
event_seq, event);
|
||||
|
||||
free(event);
|
||||
|
||||
verbose_printf("uprobe event %s\n", probe_event);
|
||||
ret = write(fd, probe_event, strlen(probe_event));
|
||||
if (ret <= 0) {
|
||||
fprintf(stderr, "Cannot write %s to %s\n", probe_event,
|
||||
UPROBE_EVENTS_PATH);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
pl = malloc(sizeof(struct probe_list));
|
||||
if (!pl)
|
||||
return -1;
|
||||
|
||||
pl->type = UPROBE_EVENT;
|
||||
pl->kp_seq = event_seq;
|
||||
pl->next = probe_list_head;
|
||||
probe_list_head = pl;
|
||||
|
||||
sprintf(event_id_path, "/sys/kernel/debug/tracing/events/uprobes/kp%d/id",
|
||||
event_seq);
|
||||
ret = add_event(event_id_path);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
event_seq++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_events_add_probe(char *old_event)
|
||||
{
|
||||
char *separator;
|
||||
|
||||
separator = strchr(old_event, ':');
|
||||
if (!separator || (separator == old_event))
|
||||
return parse_events_add_kprobe(old_event);
|
||||
else
|
||||
return parse_events_add_uprobe(old_event);
|
||||
}
|
||||
|
||||
static int parse_events_add_stapsdt(char *old_event)
|
||||
{
|
||||
printf("Currently ktap don't support stapsdt, please waiting\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void strim(char *s)
|
||||
{
|
||||
size_t size;
|
||||
char *end;
|
||||
|
||||
size = strlen(s);
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
end = s + size -1;
|
||||
while (end >= s && isspace(*end))
|
||||
end--;
|
||||
|
||||
*(end + 1) = '\0';
|
||||
}
|
||||
|
||||
static int get_sys_event_filter_str(char *start,
|
||||
char **sys, char **event, char **filter)
|
||||
{
|
||||
char *separator, *separator2, *ptr, *end;
|
||||
|
||||
while (*start == ' ')
|
||||
start++;
|
||||
|
||||
/* find sys */
|
||||
separator = strchr(start, ':');
|
||||
if (!separator || (separator == start)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ptr = malloc(separator - start + 1);
|
||||
if (!ptr)
|
||||
return -1;
|
||||
|
||||
strncpy(ptr, start, separator - start);
|
||||
ptr[separator - start] = '\0';
|
||||
|
||||
strim(ptr);
|
||||
*sys = ptr;
|
||||
|
||||
if (!strcmp(*sys, "probe") && (*(separator + 1) == '/')) {
|
||||
/* it's uprobe event */
|
||||
separator2 = strchr(separator + 1, ':');
|
||||
if (!separator2)
|
||||
return -1;
|
||||
} else
|
||||
separator2 = separator;
|
||||
|
||||
/* find filter */
|
||||
end = start + strlen(start);
|
||||
while (*--end == ' ') {
|
||||
}
|
||||
|
||||
if (*end == '/') {
|
||||
char *filter_start;
|
||||
|
||||
filter_start = strchr(separator2, '/');
|
||||
if (filter_start == end)
|
||||
return -1;
|
||||
|
||||
ptr = malloc(end - filter_start + 2);
|
||||
if (!ptr)
|
||||
return -1;
|
||||
|
||||
memcpy(ptr, filter_start, end - filter_start + 1);
|
||||
ptr[end - filter_start + 1] = '\0';
|
||||
|
||||
*filter = ptr;
|
||||
|
||||
end = filter_start;
|
||||
} else {
|
||||
*filter = NULL;
|
||||
end++;
|
||||
}
|
||||
|
||||
/* find event */
|
||||
ptr = malloc(end - separator);
|
||||
if (!ptr)
|
||||
return -1;
|
||||
|
||||
memcpy(ptr, separator + 1, end - separator - 1);
|
||||
ptr[end - separator - 1] = '\0';
|
||||
|
||||
strim(ptr);
|
||||
*event = ptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *get_next_eventdef(char *str)
|
||||
{
|
||||
char *separator;
|
||||
|
||||
separator = strchr(str, ',');
|
||||
if (!separator)
|
||||
return str + strlen(str);
|
||||
|
||||
*separator = '\0';
|
||||
return separator + 1;
|
||||
}
|
||||
|
||||
ktap_string *ktapc_parse_eventdef(ktap_string *eventdef)
|
||||
{
|
||||
const char *def_str = getstr(eventdef);
|
||||
char *str = strdup(def_str);
|
||||
char *sys, *event, *filter, *idstr, *g_idstr, *next;
|
||||
ktap_string *ts;
|
||||
int ret;
|
||||
|
||||
if (!ids_array) {
|
||||
ids_array = malloc(IDS_ARRAY_SIZE);
|
||||
if (!ids_array)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_idstr = malloc(4096);
|
||||
if (!g_idstr)
|
||||
return NULL;
|
||||
|
||||
memset(g_idstr, 0, 4096);
|
||||
|
||||
parse_next_eventdef:
|
||||
memset(ids_array, 0, IDS_ARRAY_SIZE);
|
||||
|
||||
next = get_next_eventdef(str);
|
||||
|
||||
if (get_sys_event_filter_str(str, &sys, &event, &filter))
|
||||
goto error;
|
||||
|
||||
verbose_printf("parse_eventdef: sys[%s], event[%s], filter[%s]\n",
|
||||
sys, event, filter);
|
||||
|
||||
if (!strcmp(sys, "probe"))
|
||||
ret = parse_events_add_probe(event);
|
||||
else if (!strcmp(sys, "stapsdt"))
|
||||
ret = parse_events_add_stapsdt(event);
|
||||
else
|
||||
ret = parse_events_add_tracepoint(sys, event);
|
||||
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
/* don't trace ftrace:function when all tracepoints enabled */
|
||||
if (!strcmp(sys, "*"))
|
||||
clear_id(1);
|
||||
|
||||
idstr = get_idstr(filter);
|
||||
if (!idstr)
|
||||
goto error;
|
||||
|
||||
str = next;
|
||||
|
||||
g_idstr = strcat(g_idstr, idstr);
|
||||
g_idstr = strcat(g_idstr, ",");
|
||||
|
||||
if (*next != '\0')
|
||||
goto parse_next_eventdef;
|
||||
|
||||
ts = ktapc_ts_new(g_idstr);
|
||||
free(g_idstr);
|
||||
|
||||
return ts;
|
||||
error:
|
||||
cleanup_event_resources();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cleanup_event_resources(void)
|
||||
{
|
||||
struct probe_list *pl;
|
||||
const char *path;
|
||||
char probe_event[32] = {0};
|
||||
int fd, ret;
|
||||
|
||||
for (pl = probe_list_head; pl; pl = pl->next) {
|
||||
if (pl->type == KPROBE_EVENT) {
|
||||
path = KPROBE_EVENTS_PATH;
|
||||
snprintf(probe_event, 32, "-:kprobes/kp%d", pl->kp_seq);
|
||||
} else if (pl->type == UPROBE_EVENT) {
|
||||
path = UPROBE_EVENTS_PATH;
|
||||
snprintf(probe_event, 32, "-:uprobes/kp%d", pl->kp_seq);
|
||||
} else {
|
||||
fprintf(stderr, "Cannot cleanup event type %d\n", pl->type);
|
||||
continue;
|
||||
}
|
||||
|
||||
fd = open(path, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Cannot open %s\n", UPROBE_EVENTS_PATH);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = write(fd, probe_event, strlen(probe_event));
|
||||
if (ret <= 0) {
|
||||
fprintf(stderr, "Cannot write %s to %s\n", probe_event,
|
||||
path);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
@ -1,376 +0,0 @@
|
||||
/*
|
||||
* ktapc.h
|
||||
* only can be included by userspace compiler
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
typedef int bool;
|
||||
#define false 0
|
||||
#define true 1
|
||||
|
||||
#define MAX_INT ((int)(~0U>>1))
|
||||
#define UCHAR_MAX 255
|
||||
|
||||
#define MAX_SIZET ((size_t)(~(size_t)0)-2)
|
||||
|
||||
#define KTAP_ERRSYNTAX 3
|
||||
|
||||
/*
|
||||
* KTAP_IDSIZE gives the maximum size for the description of the source
|
||||
* of a function in debug information.
|
||||
* CHANGE it if you want a different size.
|
||||
*/
|
||||
#define KTAP_IDSIZE 60
|
||||
|
||||
|
||||
#define FIRST_RESERVED 257
|
||||
|
||||
/*
|
||||
* maximum depth for nested C calls and syntactical nested non-terminals
|
||||
* in a program. (Value must fit in an unsigned short int.)
|
||||
*/
|
||||
#define KTAP_MAXCCALLS 200
|
||||
|
||||
#define KTAP_MULTRET (-1)
|
||||
|
||||
|
||||
#define SHRT_MAX UCHAR_MAX
|
||||
|
||||
#define MAXUPVAL UCHAR_MAX
|
||||
|
||||
|
||||
/* maximum stack for a ktap function */
|
||||
#define MAXSTACK 250
|
||||
|
||||
#define islalpha(c) (isalpha(c) || (c) == '_')
|
||||
#define islalnum(c) (isalnum(c) || (c) == '_')
|
||||
|
||||
#define isreserved(s) ((s)->tsv.tt == KTAP_TSHRSTR && (s)->tsv.extra > 0)
|
||||
|
||||
#define ktap_numeq(a,b) ((a)==(b))
|
||||
#define ktap_numisnan(L,a) (!ktap_numeq((a), (a)))
|
||||
|
||||
#define ktap_numunm(a) (-(a))
|
||||
|
||||
/*
|
||||
* ** Comparison and arithmetic functions
|
||||
* */
|
||||
|
||||
#define KTAP_OPADD 0 /* ORDER TM */
|
||||
#define KTAP_OPSUB 1
|
||||
#define KTAP_OPMUL 2
|
||||
#define KTAP_OPDIV 3
|
||||
#define KTAP_OPMOD 4
|
||||
#define KTAP_OPPOW 5
|
||||
#define KTAP_OPUNM 6
|
||||
|
||||
#define KTAP_OPEQ 0
|
||||
#define KTAP_OPLT 1
|
||||
#define KTAP_OPLE 2
|
||||
|
||||
|
||||
/*
|
||||
* WARNING: if you change the order of this enumeration,
|
||||
* grep "ORDER RESERVED"
|
||||
*/
|
||||
enum RESERVED {
|
||||
/* terminal symbols denoted by reserved words */
|
||||
TK_TRACE = FIRST_RESERVED, TK_TRACE_END,
|
||||
TK_ARGEVENT, TK_ARGNAME,
|
||||
TK_ARG1, TK_ARG2, TK_ARG3, TK_ARG4, TK_ARG5, TK_ARG6, TK_ARG7, TK_ARG8,
|
||||
TK_ARG9, TK_PROFILE, TK_TICK,
|
||||
TK_AND, TK_BREAK,
|
||||
TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
|
||||
TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
|
||||
TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
|
||||
/* other terminal symbols */
|
||||
TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_INCR, TK_DBCOLON,
|
||||
TK_EOS, TK_NUMBER, TK_NAME, TK_STRING
|
||||
};
|
||||
|
||||
/* number of reserved words */
|
||||
#define NUM_RESERVED ((int)(TK_WHILE-FIRST_RESERVED + 1))
|
||||
|
||||
#define EOZ (0) /* end of stream */
|
||||
|
||||
typedef union {
|
||||
ktap_number r;
|
||||
ktap_string *ts;
|
||||
} ktap_seminfo; /* semantics information */
|
||||
|
||||
|
||||
typedef struct ktap_token {
|
||||
int token;
|
||||
ktap_seminfo seminfo;
|
||||
} ktap_token;
|
||||
|
||||
typedef struct ktap_mbuffer {
|
||||
char *buffer;
|
||||
size_t n;
|
||||
size_t buffsize;
|
||||
} ktap_mbuffer;
|
||||
|
||||
#define mbuff_init(buff) ((buff)->buffer = NULL, (buff)->buffsize = 0)
|
||||
#define mbuff(buff) ((buff)->buffer)
|
||||
#define mbuff_reset(buff) ((buff)->n = 0, memset((buff)->buffer, 0, (buff)->buffsize))
|
||||
#define mbuff_len(buff) ((buff)->n)
|
||||
#define mbuff_size(buff) ((buff)->buffsize)
|
||||
|
||||
#define mbuff_resize(buff, size) \
|
||||
(ktapc_realloc((buff)->buffer, (buff)->buffsize, size, char), \
|
||||
(buff)->buffsize = size)
|
||||
|
||||
#define mbuff_free(buff) mbuff_resize(buff, 0)
|
||||
|
||||
|
||||
/*
|
||||
* state of the lexer plus state of the parser when shared by all
|
||||
* functions
|
||||
*/
|
||||
typedef struct ktap_lexstate {
|
||||
char *ptr; /* source file reading position */
|
||||
int current; /* current character (charint) */
|
||||
int linenumber; /* input line counter */
|
||||
int lastline; /* line of last token `consumed' */
|
||||
ktap_token t; /* current token */
|
||||
ktap_token lookahead; /* look ahead token */
|
||||
struct ktap_funcstate *fs; /* current function (parser) */
|
||||
ktap_mbuffer *buff; /* buffer for tokens */
|
||||
struct ktap_dyndata *dyd; /* dynamic structures used by the parser */
|
||||
ktap_string *source; /* current source name */
|
||||
ktap_string *envn; /* environment variable name */
|
||||
char decpoint; /* locale decimal point */
|
||||
int nCcalls;
|
||||
} ktap_lexstate;
|
||||
|
||||
|
||||
/*
|
||||
* Expression descriptor
|
||||
*/
|
||||
typedef enum {
|
||||
VVOID, /* no value */
|
||||
VNIL,
|
||||
VTRUE,
|
||||
VFALSE,
|
||||
VK, /* info = index of constant in `k' */
|
||||
VKNUM, /* nval = numerical value */
|
||||
VNONRELOC, /* info = result register */
|
||||
VLOCAL, /* info = local register */
|
||||
VUPVAL, /* info = index of upvalue in 'upvalues' */
|
||||
VINDEXED, /* t = table register/upvalue; idx = index R/K */
|
||||
VJMP, /* info = instruction pc */
|
||||
VRELOCABLE, /* info = instruction pc */
|
||||
VCALL, /* info = instruction pc */
|
||||
VVARARG, /* info = instruction pc */
|
||||
VEVENT,
|
||||
VEVENTNAME,
|
||||
VEVENTARG,
|
||||
} expkind;
|
||||
|
||||
|
||||
#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXED)
|
||||
#define vkisinreg(k) ((k) == VNONRELOC || (k) == VLOCAL)
|
||||
|
||||
typedef struct ktap_expdesc {
|
||||
expkind k;
|
||||
union {
|
||||
struct { /* for indexed variables (VINDEXED) */
|
||||
short idx; /* index (R/K) */
|
||||
u8 t; /* table (register or upvalue) */
|
||||
u8 vt; /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */
|
||||
} ind;
|
||||
int info; /* for generic use */
|
||||
ktap_number nval; /* for VKNUM */
|
||||
} u;
|
||||
int t; /* patch list of `exit when true' */
|
||||
int f; /* patch list of `exit when false' */
|
||||
} ktap_expdesc;
|
||||
|
||||
|
||||
typedef struct ktap_vardesc {
|
||||
short idx; /* variable index in stack */
|
||||
} ktap_vardesc;
|
||||
|
||||
|
||||
/* description of pending goto statements and label statements */
|
||||
typedef struct ktap_labeldesc {
|
||||
ktap_string *name; /* label identifier */
|
||||
int pc; /* position in code */
|
||||
int line; /* line where it appeared */
|
||||
u8 nactvar; /* local level where it appears in current block */
|
||||
} ktap_labeldesc;
|
||||
|
||||
|
||||
/* list of labels or gotos */
|
||||
typedef struct ktap_labellist {
|
||||
ktap_labeldesc *arr; /* array */
|
||||
int n; /* number of entries in use */
|
||||
int size; /* array size */
|
||||
} ktap_labellist;
|
||||
|
||||
|
||||
/* dynamic structures used by the parser */
|
||||
typedef struct ktap_dyndata {
|
||||
struct { /* list of active local variables */
|
||||
ktap_vardesc *arr;
|
||||
int n;
|
||||
int size;
|
||||
} actvar;
|
||||
ktap_labellist gt; /* list of pending gotos */
|
||||
ktap_labellist label; /* list of active labels */
|
||||
} ktap_dyndata;
|
||||
|
||||
|
||||
/* control of blocks */
|
||||
struct ktap_blockcnt; /* defined in lparser.c */
|
||||
|
||||
|
||||
/* state needed to generate code for a given function */
|
||||
typedef struct ktap_funcstate {
|
||||
ktap_proto *f; /* current function header */
|
||||
ktap_table *h; /* table to find (and reuse) elements in `k' */
|
||||
struct ktap_funcstate *prev; /* enclosing function */
|
||||
struct ktap_lexstate *ls; /* lexical state */
|
||||
struct ktap_blockcnt *bl; /* chain of current blocks */
|
||||
int pc; /* next position to code (equivalent to `ncode') */
|
||||
int lasttarget; /* 'label' of last 'jump label' */
|
||||
int jpc; /* list of pending jumps to `pc' */
|
||||
int nk; /* number of elements in `k' */
|
||||
int np; /* number of elements in `p' */
|
||||
int firstlocal; /* index of first local var (in ktap_dyndata array) */
|
||||
short nlocvars; /* number of elements in 'f->locvars' */
|
||||
u8 nactvar; /* number of active local variables */
|
||||
u8 nups; /* number of upvalues */
|
||||
u8 freereg; /* first free register */
|
||||
} ktap_funcstate;
|
||||
|
||||
|
||||
/*
|
||||
* Marks the end of a patch list. It is an invalid value both as an absolute
|
||||
* address, and as a list link (would link an element to itself).
|
||||
*/
|
||||
#define NO_JUMP (-1)
|
||||
|
||||
|
||||
/*
|
||||
* grep "ORDER OPR" if you change these enums (ORDER OP)
|
||||
*/
|
||||
typedef enum BinOpr {
|
||||
OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW,
|
||||
OPR_CONCAT,
|
||||
OPR_EQ, OPR_LT, OPR_LE,
|
||||
OPR_NE, OPR_GT, OPR_GE,
|
||||
OPR_AND, OPR_OR,
|
||||
OPR_NOBINOPR
|
||||
} BinOpr;
|
||||
|
||||
|
||||
typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
|
||||
|
||||
|
||||
#define getcode(fs,e) ((fs)->f->code[(e)->u.info])
|
||||
|
||||
#define codegen_codeAsBx(fs,o,A,sBx) codegen_codeABx(fs,o,A,(sBx)+MAXARG_sBx)
|
||||
|
||||
#define codegen_setmultret(fs,e) codegen_setreturns(fs, e, KTAP_MULTRET)
|
||||
|
||||
#define codegen_jumpto(fs,t) codegen_patchlist(fs, codegen_jump(fs), t)
|
||||
|
||||
|
||||
#define ktapc_realloc(v, osize, nsize, t) \
|
||||
((v) = (t *)ktapc_reallocv(v, osize * sizeof(t), nsize * sizeof(t)))
|
||||
|
||||
#define ktapc_reallocvector(v,oldn,n,t) ktapc_realloc(v,oldn,n,t)
|
||||
|
||||
|
||||
#define ktapc_growvector(v,nelems,size,t,limit,e) \
|
||||
if ((nelems)+1 > (size)) \
|
||||
((v)=(t *)ktapc_growaux(v,&(size),sizeof(t),limit,e))
|
||||
|
||||
|
||||
void lex_init();
|
||||
ktap_string *lex_newstring(ktap_lexstate *ls, const char *str, size_t l);
|
||||
const char *lex_token2str(ktap_lexstate *ls, int token);
|
||||
void lex_syntaxerror(ktap_lexstate *ls, const char *msg);
|
||||
void lex_setinput(ktap_lexstate *ls, char *ptr, ktap_string *source, int firstchar);
|
||||
void lex_next(ktap_lexstate *ls);
|
||||
int lex_lookahead(ktap_lexstate *ls);
|
||||
void lex_read_string_until(ktap_lexstate *ls, int c);
|
||||
ktap_closure *ktapc_parser(char *pos, const char *name);
|
||||
ktap_string *ktapc_ts_new(const char *str);
|
||||
int ktapc_ts_eqstr(ktap_string *a, ktap_string *b);
|
||||
ktap_string *ktapc_ts_newlstr(const char *str, size_t l);
|
||||
ktap_proto *ktapc_newproto();
|
||||
ktap_table *ktapc_table_new();
|
||||
const ktap_value *ktapc_table_get(ktap_table *t, const ktap_value *key);
|
||||
void ktapc_table_setvalue(ktap_table *t, const ktap_value *key, ktap_value *val);
|
||||
ktap_closure *ktapc_newlclosure(int n);
|
||||
char *ktapc_sprintf(const char *fmt, ...);
|
||||
|
||||
void *ktapc_reallocv(void *block, size_t osize, size_t nsize);
|
||||
void *ktapc_growaux(void *block, int *size, size_t size_elems, int limit,
|
||||
const char *what);
|
||||
|
||||
void ktapio_exit(void);
|
||||
int ktapio_create(const char *output_filename);
|
||||
|
||||
ktap_string *ktapc_parse_eventdef(ktap_string *eventdef);
|
||||
void cleanup_event_resources(void);
|
||||
|
||||
extern int verbose;
|
||||
#define verbose_printf(...) \
|
||||
if (verbose) \
|
||||
printf("[verbose] " __VA_ARGS__);
|
||||
|
||||
#define ktapc_equalobj(t1, t2) kp_equalobjv(NULL, t1, t2)
|
||||
|
||||
|
||||
#include "../include/ktap_opcodes.h"
|
||||
|
||||
int codegen_stringK(ktap_funcstate *fs, ktap_string *s);
|
||||
void codegen_indexed(ktap_funcstate *fs, ktap_expdesc *t, ktap_expdesc *k);
|
||||
void codegen_setreturns(ktap_funcstate *fs, ktap_expdesc *e, int nresults);
|
||||
void codegen_reserveregs(ktap_funcstate *fs, int n);
|
||||
void codegen_exp2nextreg(ktap_funcstate *fs, ktap_expdesc *e);
|
||||
void codegen_nil(ktap_funcstate *fs, int from, int n);
|
||||
void codegen_patchlist(ktap_funcstate *fs, int list, int target);
|
||||
void codegen_patchclose(ktap_funcstate *fs, int list, int level);
|
||||
int codegen_jump(ktap_funcstate *fs);
|
||||
void codegen_patchtohere(ktap_funcstate *fs, int list);
|
||||
int codegen_codeABx(ktap_funcstate *fs, OpCode o, int a, unsigned int bc);
|
||||
void codegen_ret(ktap_funcstate *fs, int first, int nret);
|
||||
void codegen_exp2anyregup(ktap_funcstate *fs, ktap_expdesc *e);
|
||||
void codegen_exp2val(ktap_funcstate *fs, ktap_expdesc *e);
|
||||
int codegen_exp2RK(ktap_funcstate *fs, ktap_expdesc *e);
|
||||
int codegen_codeABC(ktap_funcstate *fs, OpCode o, int a, int b, int c);
|
||||
void codegen_setlist(ktap_funcstate *fs, int base, int nelems, int tostore);
|
||||
void codegen_fixline (ktap_funcstate *fs, int line);
|
||||
void codegen_dischargevars(ktap_funcstate *fs, ktap_expdesc *e);
|
||||
void codegen_self(ktap_funcstate *fs, ktap_expdesc *e, ktap_expdesc *key);
|
||||
void codegen_prefix(ktap_funcstate *fs, UnOpr op, ktap_expdesc *e, int line);
|
||||
void codegen_infix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *v);
|
||||
void codegen_posfix(ktap_funcstate *fs, BinOpr op, ktap_expdesc *e1, ktap_expdesc *e2, int line);
|
||||
void codegen_setoneret(ktap_funcstate *fs, ktap_expdesc *e);
|
||||
void codegen_storevar(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex);
|
||||
void codegen_storeincr(ktap_funcstate *fs, ktap_expdesc *var, ktap_expdesc *ex);
|
||||
void codegen_goiftrue(ktap_funcstate *fs, ktap_expdesc *e);
|
||||
int codegen_getlabel(ktap_funcstate *fs);
|
||||
int codegen_codek(ktap_funcstate *fs, int reg, int k);
|
||||
int codegen_numberK(ktap_funcstate *fs, ktap_number r);
|
||||
void codegen_checkstack(ktap_funcstate *fs, int n);
|
||||
void codegen_goiffalse(ktap_funcstate *fs, ktap_expdesc *e);
|
||||
void codegen_concat(ktap_funcstate *fs, int *l1, int l2);
|
||||
int codegen_exp2anyreg(ktap_funcstate *fs, ktap_expdesc *e);
|
||||
|
||||
typedef int (*ktap_writer)(const void* p, size_t sz, void* ud);
|
||||
int ktapc_dump(const ktap_proto *f, ktap_writer w, void *data, int strip);
|
||||
|
||||
void ktapc_chunkid(char *out, const char *source, size_t bufflen);
|
||||
int ktapc_str2d(const char *s, size_t len, ktap_number *result);
|
||||
int ktapc_hexavalue(int c);
|
||||
ktap_number ktapc_arith(int op, ktap_number v1, ktap_number v2);
|
||||
int ktapc_int2fb(unsigned int x);
|
||||
|
||||
bool strglobmatch(const char *str, const char *pat);
|
||||
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* ktapio.c - ring buffer transport in userspace
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define MAX_BUFLEN 131072
|
||||
#define PATH_MAX 128
|
||||
|
||||
#define handle_error(str) do { perror(str); exit(-1); } while(0)
|
||||
|
||||
extern pid_t ktap_pid;
|
||||
|
||||
void sigfunc(int signo)
|
||||
{
|
||||
/* should not not reach here */
|
||||
}
|
||||
|
||||
static void block_sigint()
|
||||
{
|
||||
sigset_t mask;
|
||||
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGINT);
|
||||
|
||||
pthread_sigmask(SIG_BLOCK, &mask, NULL);
|
||||
}
|
||||
|
||||
static void *reader_thread(void *data)
|
||||
{
|
||||
char buf[MAX_BUFLEN];
|
||||
char filename[PATH_MAX];
|
||||
const char *output = data;
|
||||
int failed = 0, fd, out_fd, len;
|
||||
|
||||
block_sigint();
|
||||
|
||||
if (output) {
|
||||
out_fd = open(output, O_CREAT | O_WRONLY | O_TRUNC,
|
||||
S_IRUSR|S_IWUSR);
|
||||
if (out_fd < 0) {
|
||||
fprintf(stderr, "Cannot open output file %s\n", output);
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
out_fd = 2;
|
||||
|
||||
sprintf(filename, "/sys/kernel/debug/ktap/trace_pipe_%d", ktap_pid);
|
||||
|
||||
open_again:
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
usleep(10000);
|
||||
|
||||
if (failed++ == 10) {
|
||||
fprintf(stderr, "Cannot open file %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
goto open_again;
|
||||
}
|
||||
|
||||
while ((len = read(fd, buf, sizeof(buf))) > 0)
|
||||
write(out_fd, buf, len);
|
||||
|
||||
close(fd);
|
||||
close(out_fd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ktapio_create(const char *output)
|
||||
{
|
||||
pthread_t reader;
|
||||
|
||||
signal(SIGINT, sigfunc);
|
||||
|
||||
if (pthread_create(&reader, NULL, reader_thread, (void *)output) < 0)
|
||||
handle_error("pthread_create reader_thread failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,622 +0,0 @@
|
||||
/*
|
||||
* lex.c - ktap lexical analyzer
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <locale.h>
|
||||
#include "../include/ktap_types.h"
|
||||
#include "ktapc.h"
|
||||
|
||||
#define next(ls) (ls->current = *ls->ptr++)
|
||||
|
||||
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
|
||||
|
||||
#define KTAP_MINBUFFER 32
|
||||
|
||||
/* ORDER RESERVED */
|
||||
static const char *const ktap_tokens [] = {
|
||||
"trace", "trace_end", "argevent", "argname",
|
||||
"arg1", "arg2", "arg3", "arg4", "arg5", "arg6", "arg7", "arg9", "arg9",
|
||||
"profile", "tick",
|
||||
"and", "break", "do", "else", "elseif",
|
||||
"end", "false", "for", "function", "goto", "if",
|
||||
"in", "local", "nil", "not", "or", "repeat",
|
||||
"return", "then", "true", "until", "while",
|
||||
"..", "...", "==", ">=", "<=", "!=", "+=", "::", "<eof>",
|
||||
"<number>", "<name>", "<string>"
|
||||
};
|
||||
|
||||
#define save_and_next(ls) (save(ls, ls->current), next(ls))
|
||||
|
||||
static void lexerror(ktap_lexstate *ls, const char *msg, int token);
|
||||
|
||||
static void save(ktap_lexstate *ls, int c)
|
||||
{
|
||||
ktap_mbuffer *b = ls->buff;
|
||||
if (mbuff_len(b) + 1 > mbuff_size(b)) {
|
||||
size_t newsize;
|
||||
if (mbuff_size(b) >= MAX_SIZET / 2)
|
||||
lexerror(ls, "lexical element too long", 0);
|
||||
newsize = mbuff_size(b) * 2;
|
||||
mbuff_resize(b, newsize);
|
||||
}
|
||||
b->buffer[mbuff_len(b)++] = (char)c;
|
||||
}
|
||||
|
||||
void lex_init()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < NUM_RESERVED; i++) {
|
||||
ktap_string *ts = ktapc_ts_new(ktap_tokens[i]);
|
||||
ts->tsv.extra = (u8)(i+1); /* reserved word */
|
||||
}
|
||||
}
|
||||
|
||||
const char *lex_token2str(ktap_lexstate *ls, int token)
|
||||
{
|
||||
if (token < FIRST_RESERVED) {
|
||||
ktap_assert(token == (unsigned char)token);
|
||||
return (isprint(token)) ? ktapc_sprintf(KTAP_QL("%c"), token) :
|
||||
ktapc_sprintf("char(%d)", token);
|
||||
} else {
|
||||
const char *s = ktap_tokens[token - FIRST_RESERVED];
|
||||
if (token < TK_EOS)
|
||||
return ktapc_sprintf(KTAP_QS, s);
|
||||
else
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *txtToken(ktap_lexstate *ls, int token)
|
||||
{
|
||||
switch (token) {
|
||||
case TK_NAME:
|
||||
case TK_STRING:
|
||||
case TK_NUMBER:
|
||||
save(ls, '\0');
|
||||
return ktapc_sprintf(KTAP_QS, mbuff(ls->buff));
|
||||
default:
|
||||
return lex_token2str(ls, token);
|
||||
}
|
||||
}
|
||||
|
||||
static void lexerror(ktap_lexstate *ls, const char *msg, int token)
|
||||
{
|
||||
char buff[KTAP_IDSIZE];
|
||||
char *newmsg;
|
||||
|
||||
ktapc_chunkid(buff, getstr(ls->source), KTAP_IDSIZE);
|
||||
newmsg = ktapc_sprintf("%s:%d: %s", buff, ls->linenumber, msg);
|
||||
if (token)
|
||||
newmsg = ktapc_sprintf("%s near %s", newmsg, txtToken(ls, token));
|
||||
printf("lexerror: %s\n", newmsg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void lex_syntaxerror(ktap_lexstate *ls, const char *msg)
|
||||
{
|
||||
lexerror(ls, msg, ls->t.token);
|
||||
}
|
||||
|
||||
/*
|
||||
* creates a new string and anchors it in function's table so that
|
||||
* it will not be collected until the end of the function's compilation
|
||||
* (by that time it should be anchored in function's prototype)
|
||||
*/
|
||||
ktap_string *lex_newstring(ktap_lexstate *ls, const char *str, size_t l)
|
||||
{
|
||||
const ktap_value *o; /* entry for `str' */
|
||||
ktap_value val; /* entry for `str' */
|
||||
ktap_value tsv;
|
||||
ktap_string *ts = ktapc_ts_newlstr(str, l); /* create new string */
|
||||
setsvalue(&tsv, ts);
|
||||
o = ktapc_table_get(ls->fs->h, &tsv);
|
||||
if (ttisnil(o)) { /* not in use yet? (see 'addK') */
|
||||
/* boolean value does not need GC barrier;
|
||||
table has no metatable, so it does not need to invalidate cache */
|
||||
setbvalue(&val, 1); /* t[string] = true */
|
||||
ktapc_table_setvalue(ls->fs->h, &tsv, &val);
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
|
||||
/*
|
||||
* increment line number and skips newline sequence (any of
|
||||
* \n, \r, \n\r, or \r\n)
|
||||
*/
|
||||
static void inclinenumber(ktap_lexstate *ls)
|
||||
{
|
||||
int old = ls->current;
|
||||
ktap_assert(currIsNewline(ls));
|
||||
next(ls); /* skip `\n' or `\r' */
|
||||
if (currIsNewline(ls) && ls->current != old)
|
||||
next(ls); /* skip `\n\r' or `\r\n' */
|
||||
if (++ls->linenumber >= MAX_INT)
|
||||
lex_syntaxerror(ls, "chunk has too many lines");
|
||||
}
|
||||
|
||||
void lex_setinput(ktap_lexstate *ls, char *ptr, ktap_string *source, int firstchar)
|
||||
{
|
||||
ls->decpoint = '.';
|
||||
ls->current = firstchar;
|
||||
ls->lookahead.token = TK_EOS; /* no look-ahead token */
|
||||
ls->ptr = ptr;
|
||||
ls->fs = NULL;
|
||||
ls->linenumber = 1;
|
||||
ls->lastline = 1;
|
||||
ls->source = source;
|
||||
ls->envn = ktapc_ts_new(KTAP_ENV); /* create env name */
|
||||
mbuff_resize(ls->buff, KTAP_MINBUFFER); /* initialize buffer */
|
||||
}
|
||||
|
||||
/*
|
||||
* =======================================================
|
||||
* LEXICAL ANALYZER
|
||||
* =======================================================
|
||||
*/
|
||||
static int check_next(ktap_lexstate *ls, const char *set)
|
||||
{
|
||||
if (ls->current == '\0' || !strchr(set, ls->current))
|
||||
return 0;
|
||||
save_and_next(ls);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* change all characters 'from' in buffer to 'to'
|
||||
*/
|
||||
static void buffreplace(ktap_lexstate *ls, char from, char to)
|
||||
{
|
||||
size_t n = mbuff_len(ls->buff);
|
||||
char *p = mbuff(ls->buff);
|
||||
while (n--)
|
||||
if (p[n] == from) p[n] = to;
|
||||
}
|
||||
|
||||
#if !defined(getlocaledecpoint)
|
||||
#define getlocaledecpoint() (localeconv()->decimal_point[0])
|
||||
#endif
|
||||
|
||||
#define mbuff2d(b,e) ktapc_str2d(mbuff(b), mbuff_len(b) - 1, e)
|
||||
|
||||
/*
|
||||
* in case of format error, try to change decimal point separator to
|
||||
* the one defined in the current locale and check again
|
||||
*/
|
||||
static void trydecpoint(ktap_lexstate *ls, ktap_seminfo *seminfo)
|
||||
{
|
||||
char old = ls->decpoint;
|
||||
ls->decpoint = getlocaledecpoint();
|
||||
buffreplace(ls, old, ls->decpoint); /* try new decimal separator */
|
||||
if (!mbuff2d(ls->buff, &seminfo->r)) {
|
||||
/* format error with correct decimal point: no more options */
|
||||
buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */
|
||||
lexerror(ls, "malformed number", TK_NUMBER);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* this function is quite liberal in what it accepts, as 'ktapc_str2d'
|
||||
* will reject ill-formed numerals.
|
||||
*/
|
||||
static void read_numeral(ktap_lexstate *ls, ktap_seminfo *seminfo)
|
||||
{
|
||||
const char *expo = "Ee";
|
||||
int first = ls->current;
|
||||
|
||||
ktap_assert(isdigit(ls->current));
|
||||
save_and_next(ls);
|
||||
if (first == '0' && check_next(ls, "Xx")) /* hexadecimal? */
|
||||
expo = "Pp";
|
||||
for (;;) {
|
||||
if (check_next(ls, expo)) /* exponent part? */
|
||||
check_next(ls, "+-"); /* optional exponent sign */
|
||||
if (isxdigit(ls->current) || ls->current == '.')
|
||||
save_and_next(ls);
|
||||
else
|
||||
break;
|
||||
}
|
||||
save(ls, '\0');
|
||||
buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */
|
||||
if (!mbuff2d(ls->buff, &seminfo->r)) /* format error? */
|
||||
trydecpoint(ls, seminfo); /* try to update decimal point separator */
|
||||
}
|
||||
|
||||
/*
|
||||
* skip a sequence '[=*[' or ']=*]' and return its number of '='s or
|
||||
* -1 if sequence is malformed
|
||||
*/
|
||||
static int skip_sep(ktap_lexstate *ls)
|
||||
{
|
||||
int count = 0;
|
||||
int s = ls->current;
|
||||
|
||||
ktap_assert(s == '[' || s == ']');
|
||||
save_and_next(ls);
|
||||
while (ls->current == '=') {
|
||||
save_and_next(ls);
|
||||
count++;
|
||||
}
|
||||
return (ls->current == s) ? count : (-count) - 1;
|
||||
}
|
||||
|
||||
static void read_long_string(ktap_lexstate *ls, ktap_seminfo *seminfo, int sep)
|
||||
{
|
||||
save_and_next(ls); /* skip 2nd `[' */
|
||||
if (currIsNewline(ls)) /* string starts with a newline? */
|
||||
inclinenumber(ls); /* skip it */
|
||||
for (;;) {
|
||||
switch (ls->current) {
|
||||
case EOZ:
|
||||
lexerror(ls, (seminfo) ? "unfinished long string" :
|
||||
"unfinished long comment", TK_EOS);
|
||||
break; /* to avoid warnings */
|
||||
case ']': {
|
||||
if (skip_sep(ls) == sep) {
|
||||
save_and_next(ls); /* skip 2nd `]' */
|
||||
goto endloop;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '\n':
|
||||
case '\r': {
|
||||
save(ls, '\n');
|
||||
inclinenumber(ls);
|
||||
/* avoid wasting space */
|
||||
if (!seminfo)
|
||||
mbuff_reset(ls->buff);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (seminfo)
|
||||
save_and_next(ls);
|
||||
else
|
||||
next(ls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
endloop:
|
||||
if (seminfo)
|
||||
seminfo->ts = lex_newstring(ls, mbuff(ls->buff) + (2 + sep),
|
||||
mbuff_len(ls->buff) - 2*(2 + sep));
|
||||
}
|
||||
|
||||
static void escerror(ktap_lexstate *ls, int *c, int n, const char *msg)
|
||||
{
|
||||
int i;
|
||||
mbuff_reset(ls->buff); /* prepare error message */
|
||||
save(ls, '\\');
|
||||
for (i = 0; i < n && c[i] != EOZ; i++)
|
||||
save(ls, c[i]);
|
||||
lexerror(ls, msg, TK_STRING);
|
||||
}
|
||||
|
||||
static int readhexaesc(ktap_lexstate *ls)
|
||||
{
|
||||
int c[3], i; /* keep input for error message */
|
||||
int r = 0; /* result accumulator */
|
||||
c[0] = 'x'; /* for error message */
|
||||
for (i = 1; i < 3; i++) { /* read two hexa digits */
|
||||
c[i] = next(ls);
|
||||
if (!isxdigit(c[i]))
|
||||
escerror(ls, c, i + 1, "hexadecimal digit expected");
|
||||
r = (r << 4) + ktapc_hexavalue(c[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int readdecesc(ktap_lexstate *ls)
|
||||
{
|
||||
int c[3], i;
|
||||
int r = 0; /* result accumulator */
|
||||
for (i = 0; i < 3 && isdigit(ls->current); i++) { /* read up to 3 digits */
|
||||
c[i] = ls->current;
|
||||
r = 10*r + c[i] - '0';
|
||||
next(ls);
|
||||
}
|
||||
if (r > UCHAR_MAX)
|
||||
escerror(ls, c, i, "decimal escape too large");
|
||||
return r;
|
||||
}
|
||||
|
||||
static void read_string(ktap_lexstate *ls, int del, ktap_seminfo *seminfo)
|
||||
{
|
||||
save_and_next(ls); /* keep delimiter (for error messages) */
|
||||
while (ls->current != del) {
|
||||
switch (ls->current) {
|
||||
case EOZ:
|
||||
lexerror(ls, "unfinished string", TK_EOS);
|
||||
break; /* to avoid warnings */
|
||||
case '\n':
|
||||
case '\r':
|
||||
lexerror(ls, "unfinished string", TK_STRING);
|
||||
break; /* to avoid warnings */
|
||||
case '\\': { /* escape sequences */
|
||||
int c; /* final character to be saved */
|
||||
next(ls); /* do not save the `\' */
|
||||
switch (ls->current) {
|
||||
case 'a': c = '\a'; goto read_save;
|
||||
case 'b': c = '\b'; goto read_save;
|
||||
case 'f': c = '\f'; goto read_save;
|
||||
case 'n': c = '\n'; goto read_save;
|
||||
case 'r': c = '\r'; goto read_save;
|
||||
case 't': c = '\t'; goto read_save;
|
||||
case 'v': c = '\v'; goto read_save;
|
||||
case 'x': c = readhexaesc(ls); goto read_save;
|
||||
case '\n': case '\r':
|
||||
inclinenumber(ls); c = '\n'; goto only_save;
|
||||
case '\\': case '\"': case '\'':
|
||||
c = ls->current; goto read_save;
|
||||
case EOZ: goto no_save; /* will raise an error next loop */
|
||||
case 'z': { /* zap following span of spaces */
|
||||
next(ls); /* skip the 'z' */
|
||||
while (isspace(ls->current)) {
|
||||
if (currIsNewline(ls))
|
||||
inclinenumber(ls);
|
||||
else
|
||||
next(ls);
|
||||
}
|
||||
goto no_save;
|
||||
}
|
||||
default: {
|
||||
if (!isdigit(ls->current))
|
||||
escerror(ls, &ls->current, 1, "invalid escape sequence");
|
||||
/* digital escape \ddd */
|
||||
c = readdecesc(ls);
|
||||
goto only_save;
|
||||
}
|
||||
}
|
||||
read_save:
|
||||
next(ls); /* read next character */
|
||||
only_save:
|
||||
save(ls, c); /* save 'c' */
|
||||
no_save:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
save_and_next(ls);
|
||||
}
|
||||
}
|
||||
save_and_next(ls); /* skip delimiter */
|
||||
seminfo->ts = lex_newstring(ls, mbuff(ls->buff) + 1, mbuff_len(ls->buff) - 2);
|
||||
}
|
||||
|
||||
static int llex(ktap_lexstate *ls, ktap_seminfo *seminfo)
|
||||
{
|
||||
mbuff_reset(ls->buff);
|
||||
|
||||
for (;;) {
|
||||
switch (ls->current) {
|
||||
case '\n': case '\r': { /* line breaks */
|
||||
inclinenumber(ls);
|
||||
break;
|
||||
}
|
||||
case ' ': case '\f': case '\t': case '\v': { /* spaces */
|
||||
next(ls);
|
||||
break;
|
||||
}
|
||||
case '#': {
|
||||
while (!currIsNewline(ls) && ls->current != EOZ)
|
||||
next(ls); /* skip until end of line (or end of file) */
|
||||
break;
|
||||
}
|
||||
#if 0
|
||||
case '-': { /* '-' or '--' (comment) */
|
||||
next(ls);
|
||||
if (ls->current != '-')
|
||||
return '-';
|
||||
/* else is a comment */
|
||||
next(ls);
|
||||
if (ls->current == '[') { /* long comment? */
|
||||
int sep = skip_sep(ls);
|
||||
mbuff_reset(ls->buff); /* `skip_sep' may dirty the buffer */
|
||||
if (sep >= 0) {
|
||||
read_long_string(ls, NULL, sep); /* skip long comment */
|
||||
mbuff_reset(ls->buff); /* previous call may dirty the buff. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* else short comment */
|
||||
while (!currIsNewline(ls) && ls->current != EOZ)
|
||||
next(ls); /* skip until end of line (or end of file) */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case '[': { /* long string or simply '[' */
|
||||
int sep = skip_sep(ls);
|
||||
if (sep >= 0) {
|
||||
read_long_string(ls, seminfo, sep);
|
||||
return TK_STRING;
|
||||
}
|
||||
else if (sep == -1)
|
||||
return '[';
|
||||
else
|
||||
lexerror(ls, "invalid long string delimiter", TK_STRING);
|
||||
}
|
||||
case '+': {
|
||||
next(ls);
|
||||
if (ls->current != '=')
|
||||
return '+';
|
||||
else {
|
||||
next(ls);
|
||||
return TK_INCR;
|
||||
}
|
||||
}
|
||||
case '=': {
|
||||
next(ls);
|
||||
if (ls->current != '=')
|
||||
return '=';
|
||||
else {
|
||||
next(ls);
|
||||
return TK_EQ;
|
||||
}
|
||||
}
|
||||
case '<': {
|
||||
next(ls);
|
||||
if (ls->current != '=')
|
||||
return '<';
|
||||
else {
|
||||
next(ls);
|
||||
return TK_LE;
|
||||
}
|
||||
}
|
||||
case '>': {
|
||||
next(ls);
|
||||
if (ls->current != '=')
|
||||
return '>';
|
||||
else {
|
||||
next(ls);
|
||||
return TK_GE;
|
||||
}
|
||||
}
|
||||
case '!': {
|
||||
next(ls);
|
||||
if (ls->current != '=')
|
||||
return TK_NOT;
|
||||
else {
|
||||
next(ls);
|
||||
return TK_NE;
|
||||
}
|
||||
}
|
||||
case ':': {
|
||||
next(ls);
|
||||
if (ls->current != ':')
|
||||
return ':';
|
||||
else {
|
||||
next(ls);
|
||||
return TK_DBCOLON;
|
||||
}
|
||||
}
|
||||
case '"': case '\'': { /* short literal strings */
|
||||
read_string(ls, ls->current, seminfo);
|
||||
return TK_STRING;
|
||||
}
|
||||
case '.': { /* '.', '..', '...', or number */
|
||||
save_and_next(ls);
|
||||
if (check_next(ls, ".")) {
|
||||
if (check_next(ls, "."))
|
||||
return TK_DOTS; /* '...' */
|
||||
else
|
||||
return TK_CONCAT; /* '..' */
|
||||
}
|
||||
else if (!isdigit(ls->current))
|
||||
return '.';
|
||||
/* else go through */
|
||||
}
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9': {
|
||||
read_numeral(ls, seminfo);
|
||||
return TK_NUMBER;
|
||||
}
|
||||
case EOZ: {
|
||||
return TK_EOS;
|
||||
}
|
||||
case '&': {
|
||||
next(ls);
|
||||
if (ls->current != '&')
|
||||
return '&';
|
||||
else {
|
||||
next(ls);
|
||||
return TK_AND;
|
||||
}
|
||||
}
|
||||
case '|': {
|
||||
next(ls);
|
||||
if (ls->current != '|')
|
||||
return '|';
|
||||
else {
|
||||
next(ls);
|
||||
return TK_OR;
|
||||
}
|
||||
}
|
||||
default: {
|
||||
if (islalpha(ls->current)) {
|
||||
/* identifier or reserved word? */
|
||||
ktap_string *ts;
|
||||
do {
|
||||
save_and_next(ls);
|
||||
} while (islalnum(ls->current));
|
||||
ts = lex_newstring(ls, mbuff(ls->buff),
|
||||
mbuff_len(ls->buff));
|
||||
seminfo->ts = ts;
|
||||
if (isreserved(ts)) /* reserved word? */
|
||||
return ts->tsv.extra - 1 +
|
||||
FIRST_RESERVED;
|
||||
else {
|
||||
return TK_NAME;
|
||||
}
|
||||
} else { /* single-char tokens (+ - / ...) */
|
||||
int c = ls->current;
|
||||
next(ls);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lex_read_string_until(ktap_lexstate *ls, int c)
|
||||
{
|
||||
ktap_string *ts;
|
||||
char errmsg[32];
|
||||
|
||||
mbuff_reset(ls->buff);
|
||||
|
||||
while (ls->current == ' ')
|
||||
next(ls);
|
||||
|
||||
do {
|
||||
save_and_next(ls);
|
||||
} while (ls->current != c && ls->current != EOZ);
|
||||
|
||||
if (ls->current != c) {
|
||||
sprintf(errmsg, "expect %c", c);
|
||||
lexerror(ls, errmsg, 0);
|
||||
}
|
||||
|
||||
ts = lex_newstring(ls, mbuff(ls->buff), mbuff_len(ls->buff));
|
||||
ls->t.seminfo.ts = ts;
|
||||
ls->t.token = TK_STRING;
|
||||
}
|
||||
|
||||
void lex_next(ktap_lexstate *ls)
|
||||
{
|
||||
ls->lastline = ls->linenumber;
|
||||
if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
|
||||
ls->t = ls->lookahead; /* use this one */
|
||||
ls->lookahead.token = TK_EOS; /* and discharge it */
|
||||
} else
|
||||
ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
|
||||
}
|
||||
|
||||
int lex_lookahead(ktap_lexstate *ls)
|
||||
{
|
||||
ktap_assert(ls->lookahead.token == TK_EOS);
|
||||
ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
|
||||
return ls->lookahead.token;
|
||||
}
|
||||
|
@ -1,609 +0,0 @@
|
||||
/*
|
||||
* main.c - ktap compiler and loader entry
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sched.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "../include/ktap_types.h"
|
||||
#include "../include/ktap_opcodes.h"
|
||||
#include "ktapc.h"
|
||||
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
void *ktapc_reallocv(void *block, size_t osize, size_t nsize)
|
||||
{
|
||||
return kp_reallocv(NULL, block, osize, nsize);
|
||||
}
|
||||
|
||||
ktap_closure *ktapc_newlclosure(int n)
|
||||
{
|
||||
return kp_newlclosure(NULL, n);
|
||||
}
|
||||
|
||||
ktap_proto *ktapc_newproto()
|
||||
{
|
||||
return kp_newproto(NULL);
|
||||
}
|
||||
|
||||
const ktap_value *ktapc_table_get(ktap_table *t, const ktap_value *key)
|
||||
{
|
||||
return kp_table_get(t, key);
|
||||
}
|
||||
|
||||
void ktapc_table_setvalue(ktap_table *t, const ktap_value *key, ktap_value *val)
|
||||
{
|
||||
kp_table_setvalue(NULL, t, key, val);
|
||||
}
|
||||
|
||||
ktap_table *ktapc_table_new()
|
||||
{
|
||||
return kp_table_new(NULL);
|
||||
}
|
||||
|
||||
ktap_string *ktapc_ts_newlstr(const char *str, size_t l)
|
||||
{
|
||||
return kp_tstring_newlstr(NULL, str, l);
|
||||
}
|
||||
|
||||
ktap_string *ktapc_ts_new(const char *str)
|
||||
{
|
||||
return kp_tstring_new(NULL, str);
|
||||
}
|
||||
|
||||
int ktapc_ts_eqstr(ktap_string *a, ktap_string *b)
|
||||
{
|
||||
return kp_tstring_eqstr(a, b);
|
||||
}
|
||||
|
||||
static void ktapc_runerror(const char *err_msg_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf(stderr, "ktapc_runerror\n");
|
||||
|
||||
va_start(ap, err_msg_fmt);
|
||||
vfprintf(stderr, err_msg_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* todo: memory leak here
|
||||
*/
|
||||
char *ktapc_sprintf(const char *fmt, ...)
|
||||
{
|
||||
char *msg = malloc(128);
|
||||
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
vsprintf(msg, fmt, argp);
|
||||
va_end(argp);
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
#define MINSIZEARRAY 4
|
||||
|
||||
void *ktapc_growaux(void *block, int *size, size_t size_elems, int limit,
|
||||
const char *what)
|
||||
{
|
||||
void *newblock;
|
||||
int newsize;
|
||||
|
||||
if (*size >= limit/2) { /* cannot double it? */
|
||||
if (*size >= limit) /* cannot grow even a little? */
|
||||
ktapc_runerror("too many %s (limit is %d)\n",
|
||||
what, limit);
|
||||
newsize = limit; /* still have at least one free place */
|
||||
} else {
|
||||
newsize = (*size) * 2;
|
||||
if (newsize < MINSIZEARRAY)
|
||||
newsize = MINSIZEARRAY; /* minimum size */
|
||||
}
|
||||
|
||||
newblock = ktapc_reallocv(block, (*size) * size_elems, newsize * size_elems);
|
||||
*size = newsize; /* update only when everything else is OK */
|
||||
return newblock;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
#define print_base(i) \
|
||||
do { \
|
||||
if (i < f->sizelocvars) /* it's a localvars */ \
|
||||
printf("%s", getstr(f->locvars[i].varname)); \
|
||||
else \
|
||||
printf("base + %d", i); \
|
||||
} while (0)
|
||||
|
||||
#define print_RKC(instr) \
|
||||
do { \
|
||||
if (ISK(GETARG_C(instr))) \
|
||||
kp_showobj(NULL, k + INDEXK(GETARG_C(instr))); \
|
||||
else \
|
||||
print_base(GETARG_C(instr)); \
|
||||
} while (0)
|
||||
|
||||
static void decode_instruction(ktap_proto *f, int instr)
|
||||
{
|
||||
int opcode = GET_OPCODE(instr);
|
||||
ktap_value *k;
|
||||
|
||||
k = f->k;
|
||||
|
||||
printf("%.8x\t", instr);
|
||||
printf("%s\t", ktap_opnames[opcode]);
|
||||
|
||||
switch (opcode) {
|
||||
case OP_GETTABUP:
|
||||
print_base(GETARG_A(instr));
|
||||
printf(" <- ");
|
||||
|
||||
if (GETARG_B(instr) == 0)
|
||||
printf("global");
|
||||
else
|
||||
printf("upvalues[%d]", GETARG_B(instr));
|
||||
|
||||
printf("{"); print_RKC(instr); printf("}");
|
||||
|
||||
break;
|
||||
case OP_GETTABLE:
|
||||
print_base(GETARG_A(instr));
|
||||
printf(" <- ");
|
||||
|
||||
print_base(GETARG_B(instr));
|
||||
|
||||
printf("{");
|
||||
print_RKC(instr);
|
||||
printf("}");
|
||||
break;
|
||||
case OP_LOADK:
|
||||
printf("\t");
|
||||
print_base(GETARG_A(instr));
|
||||
printf(" <- ");
|
||||
|
||||
kp_showobj(NULL, k + GETARG_Bx(instr));
|
||||
break;
|
||||
case OP_CALL:
|
||||
printf("\t");
|
||||
print_base(GETARG_A(instr));
|
||||
break;
|
||||
case OP_JMP:
|
||||
printf("\t%d", GETARG_sBx(instr));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int function_nr = 0;
|
||||
|
||||
/* this is a debug function used for check bytecode chunk file */
|
||||
static void dump_function(int level, ktap_proto *f)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("\n----------------------------------------------------\n");
|
||||
printf("function %d [level %d]:\n", function_nr++, level);
|
||||
printf("linedefined: %d\n", f->linedefined);
|
||||
printf("lastlinedefined: %d\n", f->lastlinedefined);
|
||||
printf("numparams: %d\n", f->numparams);
|
||||
printf("is_vararg: %d\n", f->is_vararg);
|
||||
printf("maxstacksize: %d\n", f->maxstacksize);
|
||||
printf("source: %s\n", getstr(f->source));
|
||||
printf("sizelineinfo: %d \t", f->sizelineinfo);
|
||||
for (i = 0; i < f->sizelineinfo; i++)
|
||||
printf("%d ", f->lineinfo[i]);
|
||||
printf("\n");
|
||||
|
||||
printf("sizek: %d\n", f->sizek);
|
||||
for (i = 0; i < f->sizek; i++) {
|
||||
switch(f->k[i].type) {
|
||||
case KTAP_TNIL:
|
||||
printf("\tNIL\n");
|
||||
break;
|
||||
case KTAP_TBOOLEAN:
|
||||
printf("\tBOOLEAN: ");
|
||||
printf("%d\n", f->k[i].val.b);
|
||||
break;
|
||||
case KTAP_TNUMBER:
|
||||
printf("\tTNUMBER: ");
|
||||
printf("%ld\n", f->k[i].val.n);
|
||||
break;
|
||||
case KTAP_TSTRING:
|
||||
printf("\tTSTRING: ");
|
||||
printf("%s\n", svalue(&(f->k[i])));
|
||||
|
||||
break;
|
||||
default:
|
||||
printf("\terror: unknow constant type\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("sizelocvars: %d\n", f->sizelocvars);
|
||||
for (i = 0; i < f->sizelocvars; i++) {
|
||||
printf("\tlocvars: %s startpc: %d endpc: %d\n",
|
||||
getstr(f->locvars[i].varname), f->locvars[i].startpc,
|
||||
f->locvars[i].endpc);
|
||||
}
|
||||
|
||||
printf("sizeupvalues: %d\n", f->sizeupvalues);
|
||||
for (i = 0; i < f->sizeupvalues; i++) {
|
||||
printf("\tname: %s instack: %d idx: %d\n",
|
||||
getstr(f->upvalues[i].name), f->upvalues[i].instack,
|
||||
f->upvalues[i].idx);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
printf("sizecode: %d\n", f->sizecode);
|
||||
for (i = 0; i < f->sizecode; i++)
|
||||
decode_instruction(f, f->code[i]);
|
||||
|
||||
printf("sizep: %d\n", f->sizep);
|
||||
for (i = 0; i < f->sizep; i++)
|
||||
dump_function(level + 1, f->p[i]);
|
||||
|
||||
}
|
||||
|
||||
static void usage(const char *msg_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, msg_fmt);
|
||||
fprintf(stderr, msg_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: ktap [options] file [script args] -- cmd [args]\n"
|
||||
" or: ktap [options] -e one-liner -- cmd [args]\n"
|
||||
"\n"
|
||||
"Options and arguments:\n"
|
||||
" -o file : send script output to file, instead of stderr\n"
|
||||
" -p pid : specific tracing pid\n"
|
||||
" -C cpu : cpu to monitor in system-wide\n"
|
||||
" -T : show timestamp for event\n"
|
||||
" -V : show version\n"
|
||||
" -v : enable verbose mode\n"
|
||||
" -s : simple event tracing\n"
|
||||
" -b : list byte codes\n"
|
||||
" file : program read from script file\n"
|
||||
" -- cmd [args] : workload to tracing\n");
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ktap_global_state dummy_global_state;
|
||||
|
||||
static void init_dummy_global_state()
|
||||
{
|
||||
memset(&dummy_global_state, 0, sizeof(ktap_global_state));
|
||||
dummy_global_state.seed = 201236;
|
||||
|
||||
kp_tstring_resize(NULL, 32); /* set inital string hashtable size */
|
||||
}
|
||||
|
||||
#define handle_error(str) do { perror(str); exit(-1); } while(0)
|
||||
|
||||
static struct ktap_parm uparm;
|
||||
static int ktap_trunk_mem_size = 1024;
|
||||
|
||||
static int ktapc_writer(const void* p, size_t sz, void* ud)
|
||||
{
|
||||
if (uparm.trunk_len + sz > ktap_trunk_mem_size) {
|
||||
int new_size = (uparm.trunk_len + sz) * 2;
|
||||
uparm.trunk = realloc(uparm.trunk, new_size);
|
||||
ktap_trunk_mem_size = new_size;
|
||||
}
|
||||
|
||||
memcpy(uparm.trunk + uparm.trunk_len, p, sz);
|
||||
uparm.trunk_len += sz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int forks;
|
||||
static char **workload_argv;
|
||||
|
||||
static int fork_workload(int ktap_fd)
|
||||
{
|
||||
int pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
handle_error("failed to fork");
|
||||
|
||||
if (pid > 0)
|
||||
return pid;
|
||||
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
|
||||
execvp("", workload_argv);
|
||||
|
||||
/*
|
||||
* waiting ktapvm prepare all tracing event
|
||||
* make it more robust in future.
|
||||
*/
|
||||
pause();
|
||||
|
||||
execvp(workload_argv[0], workload_argv);
|
||||
|
||||
perror(workload_argv[0]);
|
||||
exit(-1);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define KTAPVM_PATH "/sys/kernel/debug/ktap/ktapvm"
|
||||
|
||||
static char *output_filename;
|
||||
pid_t ktap_pid;
|
||||
|
||||
static int run_ktapvm()
|
||||
{
|
||||
int ktapvm_fd, ktap_fd;
|
||||
int ret;
|
||||
|
||||
ktap_pid = getpid();
|
||||
|
||||
ktapvm_fd = open(KTAPVM_PATH, O_RDONLY);
|
||||
if (ktapvm_fd < 0)
|
||||
handle_error("open " KTAPVM_PATH " failed");
|
||||
|
||||
ktap_fd = ioctl(ktapvm_fd, 0, NULL);
|
||||
if (ktap_fd < 0)
|
||||
handle_error("ioctl ktapvm failed");
|
||||
|
||||
ktapio_create(output_filename);
|
||||
|
||||
if (forks) {
|
||||
uparm.trace_pid = fork_workload(ktap_fd);
|
||||
uparm.workload = 1;
|
||||
}
|
||||
|
||||
ret = ioctl(ktap_fd, KTAP_CMD_IOC_RUN, &uparm);
|
||||
|
||||
close(ktap_fd);
|
||||
close(ktapvm_fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int verbose;
|
||||
static int dump_bytecode;
|
||||
static char oneline_src[1024];
|
||||
static int trace_pid = -1;
|
||||
static int trace_cpu = -1;
|
||||
static int print_timestamp;
|
||||
|
||||
#define SIMPLE_ONE_LINER_FMT \
|
||||
"trace %s { print(cpu(), tid(), execname(), argevent) }"
|
||||
|
||||
static const char *script_file;
|
||||
static int script_args_start;
|
||||
static int script_args_end;
|
||||
|
||||
static void parse_option(int argc, char **argv)
|
||||
{
|
||||
char pid[32] = {0};
|
||||
char cpu_str[32] = {0};
|
||||
char *next_arg;
|
||||
int i, j;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (argv[i][0] != '-') {
|
||||
script_file = argv[i];
|
||||
if (!script_file)
|
||||
usage("");
|
||||
|
||||
script_args_start = i + 1;
|
||||
script_args_end = argc;
|
||||
|
||||
for (j = i + 1; j < argc; j++) {
|
||||
if (argv[j][0] == '-' && argv[j][1] == '-')
|
||||
goto found_cmd;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (argv[i][0] == '-' && argv[i][1] == '-') {
|
||||
j = i;
|
||||
goto found_cmd;
|
||||
}
|
||||
|
||||
next_arg = argv[i + 1];
|
||||
|
||||
switch (argv[i][1]) {
|
||||
case 'o':
|
||||
output_filename = malloc(strlen(next_arg) + 1);
|
||||
if (!output_filename)
|
||||
return;
|
||||
|
||||
strncpy(output_filename, next_arg, strlen(next_arg));
|
||||
i++;
|
||||
break;
|
||||
case 'e':
|
||||
strncpy(oneline_src, next_arg, strlen(next_arg));
|
||||
i++;
|
||||
break;
|
||||
case 'p':
|
||||
strncpy(pid, next_arg, strlen(next_arg));
|
||||
trace_pid = atoi(pid);
|
||||
i++;
|
||||
break;
|
||||
case 'C':
|
||||
strncpy(cpu_str, next_arg, strlen(next_arg));
|
||||
trace_cpu = atoi(cpu_str);
|
||||
i++;
|
||||
break;
|
||||
case 'T':
|
||||
print_timestamp = 1;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 's':
|
||||
sprintf(oneline_src, SIMPLE_ONE_LINER_FMT, next_arg);
|
||||
i++;
|
||||
break;
|
||||
case 'b':
|
||||
dump_bytecode = 1;
|
||||
break;
|
||||
case 'V':
|
||||
case '?':
|
||||
case 'h':
|
||||
usage("");
|
||||
break;
|
||||
default:
|
||||
usage("wrong argument\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
found_cmd:
|
||||
script_args_end = j;
|
||||
forks = 1;
|
||||
workload_argv = &argv[j + 1];
|
||||
}
|
||||
|
||||
static void compile(const char *input)
|
||||
{
|
||||
ktap_closure *cl;
|
||||
char *buff;
|
||||
struct stat sb;
|
||||
int fdin;
|
||||
|
||||
if (oneline_src[0] != '\0') {
|
||||
init_dummy_global_state();
|
||||
cl = ktapc_parser(oneline_src, input);
|
||||
goto dump;
|
||||
}
|
||||
|
||||
fdin = open(input, O_RDONLY);
|
||||
if (fdin < 0) {
|
||||
fprintf(stderr, "open file %s failed\n", input);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (fstat(fdin, &sb) == -1)
|
||||
handle_error("fstat failed");
|
||||
|
||||
buff = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fdin, 0);
|
||||
if (buff == MAP_FAILED)
|
||||
handle_error("mmap failed");
|
||||
|
||||
init_dummy_global_state();
|
||||
cl = ktapc_parser(buff, input);
|
||||
|
||||
munmap(buff, sb.st_size);
|
||||
close(fdin);
|
||||
|
||||
dump:
|
||||
if (dump_bytecode) {
|
||||
dump_function(1, cl->l.p);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* ktapc output */
|
||||
uparm.trunk = malloc(ktap_trunk_mem_size);
|
||||
if (!uparm.trunk)
|
||||
handle_error("malloc failed");
|
||||
|
||||
ktapc_dump(cl->l.p, ktapc_writer, NULL, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char **ktapvm_argv;
|
||||
int new_index, i;
|
||||
int ret;
|
||||
|
||||
if (argc == 1)
|
||||
usage("");
|
||||
|
||||
parse_option(argc, argv);
|
||||
|
||||
if (oneline_src[0] != '\0')
|
||||
script_file = "one-liner";
|
||||
|
||||
compile(script_file);
|
||||
|
||||
ktapvm_argv = (char **)malloc(sizeof(char *)*(script_args_end -
|
||||
script_args_start + 1));
|
||||
if (!ktapvm_argv) {
|
||||
fprintf(stderr, "canno allocate ktapvm_argv\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ktapvm_argv[0] = malloc(strlen(script_file) + 1);
|
||||
if (!ktapvm_argv[0]) {
|
||||
fprintf(stderr, "canno allocate memory\n");
|
||||
return -1;
|
||||
}
|
||||
strcpy(ktapvm_argv[0], script_file);
|
||||
ktapvm_argv[0][strlen(script_file)] = '\0';
|
||||
|
||||
/* pass rest argv into ktapvm */
|
||||
new_index = 1;
|
||||
for (i = script_args_start; i < script_args_end; i++) {
|
||||
ktapvm_argv[new_index] = malloc(strlen(argv[i]) + 1);
|
||||
if (!ktapvm_argv[new_index]) {
|
||||
fprintf(stderr, "canno allocate memory\n");
|
||||
return -1;
|
||||
}
|
||||
strcpy(ktapvm_argv[new_index], argv[i]);
|
||||
ktapvm_argv[new_index][strlen(argv[i])] = '\0';
|
||||
new_index++;
|
||||
}
|
||||
|
||||
uparm.argv = ktapvm_argv;
|
||||
uparm.argc = new_index;
|
||||
uparm.verbose = verbose;
|
||||
uparm.trace_pid = trace_pid;
|
||||
uparm.trace_cpu = trace_cpu;
|
||||
uparm.print_timestamp = print_timestamp;
|
||||
|
||||
/* start running into kernel ktapvm */
|
||||
ret = run_ktapvm();
|
||||
|
||||
cleanup_event_resources();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,346 +0,0 @@
|
||||
/*
|
||||
* util.c
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "../include/ktap_types.h"
|
||||
#include "ktapc.h"
|
||||
|
||||
/*
|
||||
* converts an integer to a "floating point byte", represented as
|
||||
* (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
|
||||
* eeeee != 0 and (xxx) otherwise.
|
||||
*/
|
||||
int ktapc_int2fb(unsigned int x)
|
||||
{
|
||||
int e = 0; /* exponent */
|
||||
|
||||
if (x < 8)
|
||||
return x;
|
||||
while (x >= 0x10) {
|
||||
x = (x+1) >> 1;
|
||||
e++;
|
||||
}
|
||||
return ((e+1) << 3) | ((int)x - 8);
|
||||
}
|
||||
|
||||
/* converts back */
|
||||
int ktapc_fb2int(int x)
|
||||
{
|
||||
int e = (x >> 3) & 0x1f;
|
||||
|
||||
if (e == 0)
|
||||
return x;
|
||||
else
|
||||
return ((x & 7) + 8) << (e - 1);
|
||||
}
|
||||
|
||||
int ktapc_ceillog2(unsigned int x)
|
||||
{
|
||||
static const u8 log_2[256] = {
|
||||
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
|
||||
};
|
||||
int l = 0;
|
||||
|
||||
x--;
|
||||
while (x >= 256) {
|
||||
l += 8;
|
||||
x >>= 8;
|
||||
}
|
||||
return l + log_2[x];
|
||||
}
|
||||
|
||||
ktap_number ktapc_arith(int op, ktap_number v1, ktap_number v2)
|
||||
{
|
||||
switch (op) {
|
||||
case KTAP_OPADD: return NUMADD(v1, v2);
|
||||
case KTAP_OPSUB: return NUMSUB(v1, v2);
|
||||
case KTAP_OPMUL: return NUMMUL(v1, v2);
|
||||
case KTAP_OPDIV: return NUMDIV(v1, v2);
|
||||
case KTAP_OPMOD: return NUMMOD(v1, v2);
|
||||
//case KTAP_OPPOW: return NUMPOW(v1, v2);
|
||||
case KTAP_OPUNM: return NUMUNM(v1);
|
||||
default: ktap_assert(0); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ktapc_hexavalue(int c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
else
|
||||
return tolower(c) - 'a' + 10;
|
||||
}
|
||||
|
||||
static int isneg(const char **s)
|
||||
{
|
||||
if (**s == '-') {
|
||||
(*s)++;
|
||||
return 1;
|
||||
} else if (**s == '+')
|
||||
(*s)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ktap_number readhexa(const char **s, ktap_number r, int *count)
|
||||
{
|
||||
for (; isxdigit((unsigned char)(**s)); (*s)++) { /* read integer part */
|
||||
r = (r * 16.0) + (ktap_number)(ktapc_hexavalue((unsigned char)(**s)));
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* convert an hexadecimal numeric string to a number, following
|
||||
* C99 specification for 'strtod'
|
||||
*/
|
||||
static ktap_number strx2number(const char *s, char **endptr)
|
||||
{
|
||||
ktap_number r = 0.0;
|
||||
int e = 0, i = 0;
|
||||
int neg = 0; /* 1 if number is negative */
|
||||
|
||||
*endptr = (char *)s; /* nothing is valid yet */
|
||||
while (isspace((unsigned char)(*s)))
|
||||
s++; /* skip initial spaces */
|
||||
|
||||
neg = isneg(&s); /* check signal */
|
||||
if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */
|
||||
return 0.0; /* invalid format (no '0x') */
|
||||
|
||||
s += 2; /* skip '0x' */
|
||||
r = readhexa(&s, r, &i); /* read integer part */
|
||||
if (*s == '.') {
|
||||
s++; /* skip dot */
|
||||
r = readhexa(&s, r, &e); /* read fractional part */
|
||||
}
|
||||
|
||||
if (i == 0 && e == 0)
|
||||
return 0.0; /* invalid format (no digit) */
|
||||
e *= -4; /* each fractional digit divides value by 2^-4 */
|
||||
*endptr = (char *)s; /* valid up to here */
|
||||
|
||||
if (*s == 'p' || *s == 'P') { /* exponent part? */
|
||||
int exp1 = 0;
|
||||
int neg1;
|
||||
|
||||
s++; /* skip 'p' */
|
||||
neg1 = isneg(&s); /* signal */
|
||||
if (!isdigit((unsigned char)(*s)))
|
||||
goto ret; /* must have at least one digit */
|
||||
while (isdigit((unsigned char)(*s))) /* read exponent */
|
||||
exp1 = exp1 * 10 + *(s++) - '0';
|
||||
if (neg1) exp1 = -exp1;
|
||||
e += exp1;
|
||||
}
|
||||
|
||||
*endptr = (char *)s; /* valid up to here */
|
||||
ret:
|
||||
if (neg)
|
||||
r = -r;
|
||||
|
||||
return ldexp(r, e);
|
||||
}
|
||||
|
||||
int ktapc_str2d(const char *s, size_t len, ktap_number *result)
|
||||
{
|
||||
char *endptr;
|
||||
|
||||
if (strpbrk(s, "nN")) /* reject 'inf' and 'nan' */
|
||||
return 0;
|
||||
else if (strpbrk(s, "xX")) /* hexa? */
|
||||
*result = strx2number(s, &endptr);
|
||||
else
|
||||
*result = strtod(s, &endptr);
|
||||
|
||||
if (endptr == s)
|
||||
return 0; /* nothing recognized */
|
||||
while (isspace((unsigned char)(*endptr)))
|
||||
endptr++;
|
||||
return (endptr == s + len); /* OK if no trailing characters */
|
||||
}
|
||||
|
||||
/* number of chars of a literal string without the ending \0 */
|
||||
#define LL(x) (sizeof(x)/sizeof(char) - 1)
|
||||
|
||||
#define RETS "..."
|
||||
#define PRE "[string \""
|
||||
#define POS "\"]"
|
||||
|
||||
#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) )
|
||||
|
||||
void ktapc_chunkid(char *out, const char *source, size_t bufflen)
|
||||
{
|
||||
size_t l = strlen(source);
|
||||
|
||||
if (*source == '=') { /* 'literal' source */
|
||||
if (l <= bufflen) /* small enough? */
|
||||
memcpy(out, source + 1, l * sizeof(char));
|
||||
else { /* truncate it */
|
||||
addstr(out, source + 1, bufflen - 1);
|
||||
*out = '\0';
|
||||
}
|
||||
} else if (*source == '@') { /* file name */
|
||||
if (l <= bufflen) /* small enough? */
|
||||
memcpy(out, source + 1, l * sizeof(char));
|
||||
else { /* add '...' before rest of name */
|
||||
addstr(out, RETS, LL(RETS));
|
||||
bufflen -= LL(RETS);
|
||||
memcpy(out, source + 1 + l - bufflen, bufflen * sizeof(char));
|
||||
}
|
||||
} else { /* string; format as [string "source"] */
|
||||
const char *nl = strchr(source, '\n'); /* find first new line (if any) */
|
||||
addstr(out, PRE, LL(PRE)); /* add prefix */
|
||||
bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */
|
||||
if (l < bufflen && nl == NULL) { /* small one-line source? */
|
||||
addstr(out, source, l); /* keep it */
|
||||
} else {
|
||||
if (nl != NULL)
|
||||
l = nl - source; /* stop at first newline */
|
||||
if (l > bufflen)
|
||||
l = bufflen;
|
||||
addstr(out, source, l);
|
||||
addstr(out, RETS, LL(RETS));
|
||||
}
|
||||
memcpy(out, POS, (LL(POS) + 1) * sizeof(char));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* strglobmatch is copyed from perf(linux/tools/perf/util/string.c)
|
||||
*/
|
||||
|
||||
/* Character class matching */
|
||||
static bool __match_charclass(const char *pat, char c, const char **npat)
|
||||
{
|
||||
bool complement = false, ret = true;
|
||||
|
||||
if (*pat == '!') {
|
||||
complement = true;
|
||||
pat++;
|
||||
}
|
||||
if (*pat++ == c) /* First character is special */
|
||||
goto end;
|
||||
|
||||
while (*pat && *pat != ']') { /* Matching */
|
||||
if (*pat == '-' && *(pat + 1) != ']') { /* Range */
|
||||
if (*(pat - 1) <= c && c <= *(pat + 1))
|
||||
goto end;
|
||||
if (*(pat - 1) > *(pat + 1))
|
||||
goto error;
|
||||
pat += 2;
|
||||
} else if (*pat++ == c)
|
||||
goto end;
|
||||
}
|
||||
if (!*pat)
|
||||
goto error;
|
||||
ret = false;
|
||||
|
||||
end:
|
||||
while (*pat && *pat != ']') /* Searching closing */
|
||||
pat++;
|
||||
if (!*pat)
|
||||
goto error;
|
||||
*npat = pat + 1;
|
||||
return complement ? !ret : ret;
|
||||
|
||||
error:
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Glob/lazy pattern matching */
|
||||
static bool __match_glob(const char *str, const char *pat, bool ignore_space)
|
||||
{
|
||||
while (*str && *pat && *pat != '*') {
|
||||
if (ignore_space) {
|
||||
/* Ignore spaces for lazy matching */
|
||||
if (isspace(*str)) {
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
if (isspace(*pat)) {
|
||||
pat++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (*pat == '?') { /* Matches any single character */
|
||||
str++;
|
||||
pat++;
|
||||
continue;
|
||||
} else if (*pat == '[') /* Character classes/Ranges */
|
||||
if (__match_charclass(pat + 1, *str, &pat)) {
|
||||
str++;
|
||||
continue;
|
||||
} else
|
||||
return false;
|
||||
else if (*pat == '\\') /* Escaped char match as normal char */
|
||||
pat++;
|
||||
if (*str++ != *pat++)
|
||||
return false;
|
||||
}
|
||||
/* Check wild card */
|
||||
if (*pat == '*') {
|
||||
while (*pat == '*')
|
||||
pat++;
|
||||
if (!*pat) /* Tail wild card matches all */
|
||||
return true;
|
||||
while (*str)
|
||||
if (__match_glob(str++, pat, ignore_space))
|
||||
return true;
|
||||
}
|
||||
return !*str && !*pat;
|
||||
}
|
||||
|
||||
/**
|
||||
* strglobmatch - glob expression pattern matching
|
||||
* @str: the target string to match
|
||||
* @pat: the pattern string to match
|
||||
*
|
||||
* This returns true if the @str matches @pat. @pat can includes wildcards
|
||||
* ('*','?') and character classes ([CHARS], complementation and ranges are
|
||||
* also supported). Also, this supports escape character ('\') to use special
|
||||
* characters as normal character.
|
||||
*
|
||||
* Note: if @pat syntax is broken, this always returns false.
|
||||
*/
|
||||
bool strglobmatch(const char *str, const char *pat)
|
||||
{
|
||||
return __match_glob(str, pat, false);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user