Commit c63a1642 authored by Jovi Zhangwei's avatar Jovi Zhangwei Committed by 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: default avatarJovi Zhangwei <jovi.zhangwei@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 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
This diff is collapsed.
#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__ */
This diff is collapsed.
/*
* 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);
}
This diff is collapsed.
/*
* 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;
}
This diff is collapsed.
/*
* 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;
}
This diff is collapsed.
This diff is collapsed.
/*
* 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);
}
}
}
This diff is collapsed.
#!/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);
}
This diff is collapsed.
#!/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)
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#!/usr/bin/env ktap
trace sched:sched_switch {
printf("%s ... ", arg1)
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment