提交 c63a1642 编写于 作者: J Jovi Zhangwei 提交者: Greg Kroah-Hartman

staging: ktap: add to the kernel tree

This patch introduces ktap to staging tree.

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

The merit of putting this software in staging tree is
to make it more possible to get feedback from users
and thus polish the code.
Signed-off-by: NJovi Zhangwei <jovi.zhangwei@gmail.com>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
上级 67aa4acb
......@@ -4748,6 +4748,13 @@ S: Maintained
F: Documentation/hwmon/k8temp
F: drivers/hwmon/k8temp.c
KTAP
M: zhangwei(Jovi) <jovi.zhangwei@gmail.com>
W: http://www.ktap.org
L: ktap@freelists.org
S: Maintained
F: drivers/staging/ktap/
KCONFIG
M: Michal Marek <mmarek@suse.cz>
L: linux-kbuild@vger.kernel.org
......
......@@ -150,4 +150,6 @@ source "drivers/staging/dgnc/Kconfig"
source "drivers/staging/dgap/Kconfig"
source "drivers/staging/ktap/Kconfig"
endif # STAGING
......@@ -67,3 +67,4 @@ obj-$(CONFIG_XILLYBUS) += xillybus/
obj-$(CONFIG_DGNC) += dgnc/
obj-$(CONFIG_DGAP) += dgap/
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
obj-$(CONFIG_KTAP) += ktap/
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
# 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:
# 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
% 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 ::= '-'
#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__ */
#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__ */
#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__ */
/*
* 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");
/*
* 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);
}
/*
* 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);
}
/*
* kdebug.c - ktap probing core implementation
*
* This file is part of ktap by Jovi Zhangwei.
*
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
*
* ktap is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* ktap is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/version.h>
#include <linux/ftrace_event.h>
#include <asm/syscall.h> //syscall_set_return_value defined here
#include "../../include/ktap.h"
static void ktap_call_probe_closure(ktap_state *mainthread, ktap_closure *cl,
struct ktap_event *e)
{
ktap_state *ks;
ktap_value *func;
ks = kp_newthread(mainthread);
setcllvalue(ks->top, cl);
func = ks->top;
incr_top(ks);
ks->current_event = e;
kp_call(ks, func, 0);
ks->current_event = NULL;
kp_exitthread(ks);
}
void kp_event_tostring(ktap_state *ks, struct trace_seq *seq)
{
struct ktap_event *e = ks->current_event;
struct trace_iterator *iter;
struct trace_event *ev;
enum print_line_t ret = TRACE_TYPE_NO_CONSUME;
/* Simulate the iterator */
/*
* use temp percpu buffer as trace_iterator
* we cannot use same temp buffer as printf.
*/
iter = kp_percpu_data(KTAP_PERCPU_DATA_BUFFER2);
trace_seq_init(&iter->seq);
iter->ent = e->entry;
ev = &(e->call->event);
if (ev)
ret = ev->funcs->trace(iter, 0, ev);
if (ret != TRACE_TYPE_NO_CONSUME) {
struct trace_seq *s = &iter->seq;
int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len;
s->buffer[len] = '\0';
trace_seq_puts(seq, s->buffer);
}
}
#if 0
/* check pt_regs defintion in linux/arch/x86/include/asm/ptrace.h */
/* support other architecture pt_regs showing */
static void event_regstr(ktap_state *ks, struct ktap_event *e, StkId ra)
{
struct pt_regs *regs = e->regs;
char str[256] = {0};
#if defined(CONFIG_X86_32)
snprintf(str, sizeof(str),
"{ax: 0x%lx, orig_ax: 0x%lx, bx: 0x%lx, cx: 0x%lx, dx: 0x%lx, "
"si: 0x%lx, di: 0x%lx, bp: 0x%lx, ds: 0x%lx, es: 0x%lx, fs: 0x%lx, "
"gs: 0x%lx, ip: 0x%lx, cs: 0x%lx, flags: 0x%lx, sp: 0x%lx, ss: 0x%lx}\n",
regs->ax, regs->orig_ax, regs->bx, regs->cx, regs->dx,
regs->si, regs->di, regs->bp, regs->ds, regs->es, regs->fs,
regs->gs, regs->ip, regs->cs, regs->flags, regs->sp, regs->ss);
#elif defined(CONFIG_X86_64)
/* x86_64 pt_regs doesn't have ds, es, fs or gs. */
snprintf(str, sizeof(str),
"{ax: 0x%lx, orig_ax: 0x%lx, bx: 0x%lx, cx: 0x%lx, dx: 0x%lx, "
"si: 0x%lx, di: 0x%lx, r8: 0x%lx, r9: 0x%lx, r10: 0x%lx, r11: 0x%lx, "
"r12: 0x%lx, r13: 0x%lx, r14: 0x%lx, r15: 0x%lx, bp: 0x%lx, ip: 0x%lx, "
"cs: 0x%lx, flags: 0x%lx, sp: 0x%lx, ss: 0x%lx}\n",
regs->ax, regs->orig_ax, regs->bx, regs->cx, regs->dx,
regs->si, regs->di, regs->r8, regs->r9, regs->r10, regs->r11,
regs->r12, regs->r13, regs->r14, regs->r15, regs->bp, regs->ip,
regs->cs, regs->flags, regs->sp, regs->ss);
#endif
setsvalue(ra, kp_tstring_new_local(ks, str));
}
#endif
/***************************/
/* This definition should keep update with kernel/trace/trace.h */
struct ftrace_event_field {
struct list_head link;
const char *name;
const char *type;
int filter_type;
int offset;
int size;
int is_signed;
};
static struct list_head *ktap_get_fields(struct ftrace_event_call *event_call)
{
if (!event_call->class->get_fields)
return &event_call->class->fields;
return event_call->class->get_fields(event_call);
}
static void get_field_value(ktap_state *ks, struct ktap_event *e,
struct ftrace_event_field *field, ktap_value *ra)
{
void *value = (unsigned char *)e->entry + field->offset;
if (field->size == 4) {
int n = *(int *)value;
setnvalue(ra, n);
return;
} else if (field->size == 8) {
long n = *(long *)value;
setnvalue(ra, n);
return;
}
if (!strncmp(field->type, "char", 4)) {
setsvalue(ra, kp_tstring_new(ks, (char *)value));
return;
}
}
void kp_event_getarg(ktap_state *ks, ktap_value *ra, int n)
{
struct ktap_event *e = ks->current_event;
int index = n;
struct ftrace_event_field *field;
struct list_head *head;
/* this is very slow and not safe, fix it in future */
head = ktap_get_fields(e->call);
list_for_each_entry_reverse(field, head, link) {
if (--index == 0) {
get_field_value(ks, e, field, ra);
return;
}
}
setnilvalue(ra);
return;
}
/* Callback function for perf event subsystem
* make ktap reentrant, don't disable irq in callback function,
* same as perf and ftrace. to make reentrant, we need some
* percpu data to be context isolation(irq/sirq/nmi/process)
*
* perf callback already consider on the recursion issue,
* so ktap don't need to check again in here.
*
* Note tracepoint handler is calling with rcu_read_lock.
*/
static void ktap_overflow_callback(struct perf_event *event,
struct perf_sample_data *data,
struct pt_regs *regs)
{
struct ktap_probe_event *ktap_pevent;
struct ktap_event e;
ktap_state *ks;
int rctx;
ktap_pevent = event->overflow_handler_context;
ks = ktap_pevent->ks;
if (unlikely(ks->stop))
return;
rctx = get_recursion_context();
if (rctx < 0)
return;
/* profile perf event don't have valid associated tp_event */
if (event->tp_event) {
e.call = event->tp_event;
e.entry = data->raw->data;
e.entry_size = data->raw->size;
}
e.pevent = ktap_pevent;
e.regs = regs;
ktap_call_probe_closure(ks, ktap_pevent->cl, &e);
put_recursion_context(rctx);
}
static void perf_destructor(struct ktap_probe_event *ktap_pevent)
{
perf_event_release_kernel(ktap_pevent->perf);
}
static int (*kp_ftrace_profile_set_filter)(struct perf_event *event,
int event_id, char *filter_str);
/*
* Generic perf event register function
* used by tracepoints/kprobe/uprobe/profile-timer/hw_breakpoint.
*/
void kp_perf_event_register(ktap_state *ks, struct perf_event_attr *attr,
struct task_struct *task, char *filter,
ktap_closure *cl)
{
struct ktap_probe_event *ktap_pevent;
struct perf_event *event;
int cpu, ret;
kp_verbose_printf(ks, "enable perf event id: %d, filter: %s "
"pid: %d\n", attr->config, filter,
task ? task_tgid_vnr(task) : -1);
/*
* don't tracing until ktap_wait, the reason is:
* 1). some event may hit before apply filter
* 2). more simple to manage tracing thread
* 3). avoid race with mainthread.
*
* Another way to do this is make attr.disabled as 1, then use
* perf_event_enable after filter apply, however, perf_event_enable
* was not exported in kernel older than 3.3, so we drop this method.
*/
ks->stop = 1;
for_each_cpu(cpu, G(ks)->cpumask) {
ktap_pevent = kp_zalloc(ks, sizeof(*ktap_pevent));
ktap_pevent->ks = ks;
ktap_pevent->cl = cl;
event = perf_event_create_kernel_counter(attr, cpu, task,
ktap_overflow_callback,
ktap_pevent);
if (IS_ERR(event)) {
int err = PTR_ERR(event);
kp_error(ks, "unable register perf event %d on cpu %d, "
"err: %d\n", attr->config, cpu, err);
kp_free(ks, ktap_pevent);
return;
}
ktap_pevent->perf = event;
INIT_LIST_HEAD(&ktap_pevent->list);
list_add_tail(&ktap_pevent->list, &G(ks)->probe_events_head);
if (!filter)
continue;
ret = kp_ftrace_profile_set_filter(event, attr->config, filter);
if (ret) {
kp_error(ks, "unable set filter %s for event id %d, "
"ret: %d\n", filter, attr->config, ret);
perf_destructor(ktap_pevent);
list_del(&ktap_pevent->list);
kp_free(ks, ktap_pevent);
return;
}
}
}
static void end_probes(struct ktap_state *ks)
{
struct ktap_probe_event *ktap_pevent;
struct list_head *tmp, *pos;
struct list_head *head = &G(ks)->probe_events_head;
list_for_each(pos, head) {
ktap_pevent = container_of(pos, struct ktap_probe_event,
list);
perf_destructor(ktap_pevent);
}
/*
* Ensure our callback won't be called anymore. The buffers
* will be freed after that.
*/
tracepoint_synchronize_unregister();
list_for_each_safe(pos, tmp, head) {
ktap_pevent = container_of(pos, struct ktap_probe_event,
list);
list_del(&ktap_pevent->list);
kp_free(ks, ktap_pevent);
}
}
static int ktap_lib_probe_by_id(ktap_state *ks)
{
const char *ids_str;
char *start;
ktap_closure *cl;
struct task_struct *task = G(ks)->trace_task;
char filter_str[128] = {0};
char *filter, *ptr1, *sep, *ptr;
kp_arg_check(ks, 1, KTAP_TSTRING);
kp_arg_check(ks, 2, KTAP_TFUNCTION);
ids_str = svalue(kp_arg(ks, 1));
cl = clvalue(kp_arg(ks, 2));
start = (char *)ids_str;
again:
filter = NULL;
sep = strchr(start, ',');
if (!sep)
ptr1 = strchr(start, '/');
else
ptr1 = strnchr(start, sep - start, '/');
if (ptr1) {
char *ptr2 = strrchr(ptr1 + 1, '/');
if (ptr2) {
memset(filter_str, 0, sizeof(filter_str));
strncpy(filter_str, ptr1 + 1, ptr2 - ptr1 - 1);
filter = &filter_str[0];
} else {
kp_printf(ks, "cannot parse ids_str: %s\n", ids_str);
return -1;
}
}
for (ptr = start; *ptr != ',' && *ptr != '\0' && *ptr != '/'; ptr++) {
char token[32] = {0};
int id;
int i = 0;
if (*ptr == ' ')
continue;
while (isdigit(*ptr)) {
token[i++] = *ptr++;
}
if (!kstrtoint(token, 10, &id)) {
struct perf_event_attr attr;
memset(&attr, 0, sizeof(attr));
attr.type = PERF_TYPE_TRACEPOINT;
attr.config = id;
attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD;
attr.sample_period = 1;
attr.size = sizeof(attr);
attr.disabled = 0;
kp_perf_event_register(ks, &attr, task, filter, cl);
}
}
if (sep && (*(sep + 1) != '\0')) {
start = sep + 1;
goto again;
}
return 0;
}
static int ktap_lib_probe_end(ktap_state *ks)
{
kp_arg_check(ks, 1, KTAP_TFUNCTION);
G(ks)->trace_end_closure = clvalue(kp_arg(ks, 1));
return 0;
}
static int ktap_lib_traceoff(ktap_state *ks)
{
end_probes(ks);
/* call trace_end_closure after probed end */
if (G(ks)->trace_end_closure) {
setcllvalue(ks->top, G(ks)->trace_end_closure);
incr_top(ks);
kp_call(ks, ks->top - 1, 0);
G(ks)->trace_end_closure = NULL;
}
return 0;
}
void kp_probe_exit(ktap_state *ks)
{
if (!G(ks)->trace_enabled)
return;
end_probes(ks);
/* call trace_end_closure after probed end */
if (!G(ks)->error && G(ks)->trace_end_closure) {
setcllvalue(ks->top, G(ks)->trace_end_closure);
incr_top(ks);
kp_call(ks, ks->top - 1, 0);
G(ks)->trace_end_closure = NULL;
}
G(ks)->trace_enabled = 0;
}
int kp_probe_init(ktap_state *ks)
{
G(ks)->trace_enabled = 1;
return 0;
}
static const ktap_Reg kdebuglib_funcs[] = {
{"probe_by_id", ktap_lib_probe_by_id},
{"probe_end", ktap_lib_probe_end},
{"traceoff", ktap_lib_traceoff},
{NULL}
};
void kp_init_kdebuglib(ktap_state *ks)
{
kp_ftrace_profile_set_filter =
(void *)kallsyms_lookup_name("ftrace_profile_set_filter");
if (!kp_ftrace_profile_set_filter) {
printk("ktap: cannot lookup ftrace_profile_set_filter "
"in kallsyms\n");
return;
}
kp_register_lib(ks, "kdebug", kdebuglib_funcs);
}
/*
* 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);
}
/*
* 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;
}
/*
* 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));
}
/*
* 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 */
};
/*
* 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;
}
此差异已折叠。
此差异已折叠。
/*
* 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);
}
}
}
此差异已折叠。
#!/usr/bin/env ktap
trace sched:sched_switch {
print_backtrace()
}
#!/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()
}
}
#!/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()
}
}
#!/usr/bin/env ktap
trace ftrace:function /ip==mutex*/ {
print(cpu(), pid(), execname(), argevent)
}
#!/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)
}
#!/usr/bin/env ktap
trace probe:vfs_read%return fd=$retval {
print(execname(), argevent);
}
此差异已折叠。
#!/usr/bin/env ktap
print("Hello World! I am ktap")
#!/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)
}
#!/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)
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
#!/usr/bin/env ktap
trace sched:sched_switch {
printf("%s ... ", arg1)
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册