You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
staging: ktap: remove code from tree
ktap should be merged through the "proper" place in the kernel tree, in the perf tool, not as a stand-alone kernel module in staging. So remove it from here for now so that it can be merged correctly later. Reported-by: Ingo Molnar <mingo@kernel.org> Cc: Jovi Zhangwei <jovi.zhangwei@gmail.com> Cc: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
@@ -150,6 +150,4 @@ source "drivers/staging/dgnc/Kconfig"
|
||||
|
||||
source "drivers/staging/dgap/Kconfig"
|
||||
|
||||
source "drivers/staging/ktap/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
||||
@@ -67,4 +67,3 @@ obj-$(CONFIG_XILLYBUS) += xillybus/
|
||||
obj-$(CONFIG_DGNC) += dgnc/
|
||||
obj-$(CONFIG_DGAP) += dgap/
|
||||
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
|
||||
obj-$(CONFIG_KTAP) += ktap/
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
config KTAP
|
||||
tristate "a programable dynamic tracing tool for Linux"
|
||||
depends on PERF_EVENTS && EVENT_TRACING
|
||||
default n
|
||||
help
|
||||
ktap is a new script-based dynamic tracing tool for Linux,
|
||||
it uses a scripting language and lets users trace the
|
||||
Linux kernel dynamically. ktap is designed to give
|
||||
operational insights with interoperability that allow
|
||||
users to tune, troubleshoot and extend kernel and application.
|
||||
It's similar with Linux Systemtap and Solaris Dtrace.
|
||||
|
||||
ktap have different design principles from Linux mainstream
|
||||
dynamic tracing language in that it's based on bytecode,
|
||||
so it doesn't depend upon GCC, doesn't require compiling
|
||||
kernel module for each script, safe to use in production
|
||||
environment, fulfilling the embedded ecosystem's tracing needs.
|
||||
|
||||
See ktap tutorial for more information:
|
||||
http://www.ktap.org/doc/tutorial.html
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
|
||||
# Do not instrument the tracer itself:
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
ORIG_CFLAGS := $(KBUILD_CFLAGS)
|
||||
KBUILD_CFLAGS = $(subst -pg,,$(ORIG_CFLAGS))
|
||||
endif
|
||||
|
||||
all: mod ktap
|
||||
|
||||
INTP = interpreter
|
||||
|
||||
LIBDIR = $(INTP)/library
|
||||
|
||||
LIB_OBJS += $(LIBDIR)/baselib.o $(LIBDIR)/kdebug.o $(LIBDIR)/timer.o \
|
||||
$(LIBDIR)/ansilib.o
|
||||
|
||||
INTP_OBJS += $(INTP)/ktap.o $(INTP)/loader.o $(INTP)/object.o \
|
||||
$(INTP)/tstring.o $(INTP)/table.o $(INTP)/vm.o \
|
||||
$(INTP)/opcode.o $(INTP)/strfmt.o $(INTP)/transport.o \
|
||||
$(LIB_OBJS)
|
||||
|
||||
obj-m += ktapvm.o
|
||||
ktapvm-y := $(INTP_OBJS)
|
||||
|
||||
KVERSION ?= $(shell uname -r)
|
||||
KERNEL_SRC ?= /lib/modules/$(KVERSION)/build
|
||||
mod:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules
|
||||
|
||||
modules_install:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules_install
|
||||
|
||||
KTAPC_CFLAGS = -Wall -O2
|
||||
|
||||
UDIR = userspace
|
||||
|
||||
$(UDIR)/lex.o: $(UDIR)/lex.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/parser.o: $(UDIR)/parser.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/code.o: $(UDIR)/code.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/dump.o: $(UDIR)/dump.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/main.o: $(UDIR)/main.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/util.o: $(UDIR)/util.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/ktapio.o: $(UDIR)/ktapio.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/eventdef.o: $(UDIR)/eventdef.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/opcode.o: $(INTP)/opcode.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/table.o: $(INTP)/table.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/tstring.o: $(INTP)/tstring.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
$(UDIR)/object.o: $(INTP)/object.c
|
||||
$(QUIET_CC)$(CC) $(DEBUGINFO_FLAG) $(KTAPC_CFLAGS) -o $@ -c $<
|
||||
|
||||
KTAPOBJS =
|
||||
KTAPOBJS += $(UDIR)/lex.o
|
||||
KTAPOBJS += $(UDIR)/parser.o
|
||||
KTAPOBJS += $(UDIR)/code.o
|
||||
KTAPOBJS += $(UDIR)/dump.o
|
||||
KTAPOBJS += $(UDIR)/main.o
|
||||
KTAPOBJS += $(UDIR)/util.o
|
||||
KTAPOBJS += $(UDIR)/ktapio.o
|
||||
KTAPOBJS += $(UDIR)/eventdef.o
|
||||
KTAPOBJS += $(UDIR)/opcode.o
|
||||
KTAPOBJS += $(UDIR)/table.o
|
||||
KTAPOBJS += $(UDIR)/tstring.o
|
||||
KTAPOBJS += $(UDIR)/object.o
|
||||
|
||||
ktap: $(KTAPOBJS)
|
||||
$(QUIET_LINK)$(CC) $(KTAPC_CFLAGS) -o $@ $(KTAPOBJS) -lpthread
|
||||
|
||||
KMISC := /lib/modules/$(KVERSION)/ktapvm/
|
||||
|
||||
install: mod ktap
|
||||
install -d $(KMISC)
|
||||
install -m 644 -c *.ko /lib/modules/$(KVERSION)/ktapvm/
|
||||
/sbin/depmod -a
|
||||
|
||||
load:
|
||||
insmod ktapvm.ko
|
||||
|
||||
unload:
|
||||
rmmod ktapvm
|
||||
|
||||
test: FORCE
|
||||
cd test; sh ./run_test.sh; cd -
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
|
||||
$(RM) ktap
|
||||
|
||||
PHONY += FORCE
|
||||
FORCE:
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
# ktap
|
||||
|
||||
A New Scripting Dynamic Tracing Tool For Linux
|
||||
[www.ktap.org][homepage]
|
||||
|
||||
ktap is a new scripting dynamic tracing tool for Linux,
|
||||
it uses a scripting language and lets users trace the Linux kernel dynamically.
|
||||
ktap is designed to give operational insights with interoperability
|
||||
that allows users to tune, troubleshoot and extend kernel and application.
|
||||
It's similar with Linux Systemtap and Solaris Dtrace.
|
||||
|
||||
ktap have different design principles from Linux mainstream dynamic tracing
|
||||
language in that it's based on bytecode, so it doesn't depend upon GCC,
|
||||
doesn't require compiling kernel module for each script, safe to use in
|
||||
production environment, fulfilling the embedded ecosystem's tracing needs.
|
||||
|
||||
More information can be found at [ktap homepage][homepage].
|
||||
|
||||
[homepage]: http://www.ktap.org
|
||||
|
||||
## Highlights
|
||||
|
||||
* simple but powerful scripting language
|
||||
* register based interpreter (heavily optimized) in Linux kernel
|
||||
* small and lightweight (6KLOC of interpreter)
|
||||
* not depend on gcc for each script running
|
||||
* easy to use in embedded environment without debugging info
|
||||
* support for tracepoint, kprobe, uprobe, function trace, timer, and more
|
||||
* supported in x86, arm, ppc, mips
|
||||
* safety in sandbox
|
||||
|
||||
## Building & Running
|
||||
|
||||
1. Clone ktap from github
|
||||
|
||||
$ git clone http://github.com/ktap/ktap.git
|
||||
|
||||
2. Compiling ktap
|
||||
|
||||
$ cd ktap
|
||||
$ make #generate ktapvm kernel module and ktap binary
|
||||
|
||||
3. Load ktapvm kernel module(make sure debugfs mounted)
|
||||
|
||||
$ make load #need to be root or have sudo access
|
||||
|
||||
4. Running ktap
|
||||
|
||||
$ ./ktap scripts/helloworld.kp
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
1. simplest one-liner command to enable all tracepoints
|
||||
|
||||
ktap -e "trace *:* { print(argevent) }"
|
||||
|
||||
2. syscall tracing on target process
|
||||
|
||||
ktap -e "trace syscalls:* { print(argevent) }" -- ls
|
||||
|
||||
3. function tracing
|
||||
|
||||
ktap -e "trace ftrace:function { print(argevent) }"
|
||||
|
||||
ktap -e "trace ftrace:function /ip==mutex*/ { print(argevent) }"
|
||||
|
||||
4. simple syscall tracing
|
||||
|
||||
trace syscalls:* {
|
||||
print(cpu(), pid(), execname(), argevent)
|
||||
}
|
||||
|
||||
5. syscall tracing in histogram style
|
||||
|
||||
s = {}
|
||||
|
||||
trace syscalls:sys_enter_* {
|
||||
s[argname] += 1
|
||||
}
|
||||
|
||||
trace_end {
|
||||
histogram(s)
|
||||
}
|
||||
|
||||
6. kprobe tracing
|
||||
|
||||
trace probe:do_sys_open dfd=%di fname=%dx flags=%cx mode=+4($stack) {
|
||||
print("entry:", execname(), argevent)
|
||||
}
|
||||
|
||||
trace probe:do_sys_open%return fd=$retval {
|
||||
print("exit:", execname(), argevent)
|
||||
}
|
||||
|
||||
7. uprobe tracing
|
||||
|
||||
trace probe:/lib/libc.so.6:0x000773c0 {
|
||||
print("entry:", execname(), argevent)
|
||||
}
|
||||
|
||||
trace probe:/lib/libc.so.6:0x000773c0%return {
|
||||
print("exit:", execname(), argevent)
|
||||
}
|
||||
|
||||
8. timer
|
||||
|
||||
tick-1ms {
|
||||
printf("time fired on one cpu\n");
|
||||
}
|
||||
|
||||
profile-2s {
|
||||
printf("time fired on every cpu\n");
|
||||
}
|
||||
|
||||
More sample scripts can be found at scripts/ directory.
|
||||
|
||||
## Mailing list
|
||||
|
||||
ktap@freelists.org
|
||||
You can subscribe to ktap mailing list at link (subscribe before posting):
|
||||
http://www.freelists.org/list/ktap
|
||||
|
||||
|
||||
## Copyright and License
|
||||
|
||||
ktap is licensed under GPL v2
|
||||
|
||||
Copyright (C) 2012-2013, Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
All rights reserved.
|
||||
|
||||
|
||||
## Contribution
|
||||
|
||||
ktap is still under active development, so contributions are welcome.
|
||||
You are encouraged to report bugs, provide feedback, send feature request,
|
||||
or hack on it.
|
||||
|
||||
|
||||
## See More
|
||||
|
||||
More info can be found at [documentation][tutorial]
|
||||
[tutorial]: http://www.ktap.org/doc/tutorial.html
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,169 +0,0 @@
|
||||
#ifndef __KTAP_H__
|
||||
#define __KTAP_H__
|
||||
|
||||
#include "ktap_types.h"
|
||||
#include "ktap_opcodes.h"
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/trace_seq.h>
|
||||
|
||||
typedef struct ktap_Reg {
|
||||
const char *name;
|
||||
ktap_cfunction func;
|
||||
} ktap_Reg;
|
||||
|
||||
struct ktap_probe_event {
|
||||
struct list_head list;
|
||||
struct perf_event *perf;
|
||||
ktap_state *ks;
|
||||
ktap_closure *cl;
|
||||
};
|
||||
|
||||
/* this structure allocate on stack */
|
||||
struct ktap_event {
|
||||
struct ktap_probe_event *pevent;
|
||||
struct ftrace_event_call *call;
|
||||
struct trace_entry *entry;
|
||||
int entry_size;
|
||||
struct pt_regs *regs;
|
||||
};
|
||||
|
||||
enum {
|
||||
KTAP_PERCPU_DATA_STATE,
|
||||
KTAP_PERCPU_DATA_STACK,
|
||||
KTAP_PERCPU_DATA_BUFFER,
|
||||
KTAP_PERCPU_DATA_BUFFER2,
|
||||
KTAP_PERCPU_DATA_BTRACE,
|
||||
|
||||
KTAP_PERCPU_DATA_MAX
|
||||
};
|
||||
|
||||
#define KTAP_PERCPU_BUFFER_SIZE (3 * PAGE_SIZE)
|
||||
|
||||
int gettimeofday_us(void);
|
||||
ktap_state *kp_newstate(struct ktap_parm *parm, struct dentry *dir);
|
||||
void kp_exit(ktap_state *ks);
|
||||
void kp_final_exit(ktap_state *ks);
|
||||
ktap_state *kp_newthread(ktap_state *mainthread);
|
||||
void kp_exitthread(ktap_state *ks);
|
||||
ktap_closure *kp_load(ktap_state *ks, unsigned char *buff);
|
||||
void kp_call(ktap_state *ks, StkId func, int nresults);
|
||||
void kp_optimize_code(ktap_state *ks, int level, ktap_proto *f);
|
||||
void kp_register_lib(ktap_state *ks, const char *libname, const ktap_Reg *funcs);
|
||||
void *kp_percpu_data(int type);
|
||||
|
||||
void kp_init_baselib(ktap_state *ks);
|
||||
void kp_init_oslib(ktap_state *ks);
|
||||
void kp_init_kdebuglib(ktap_state *ks);
|
||||
void kp_init_timerlib(ktap_state *ks);
|
||||
void kp_init_ansilib(ktap_state *ks);
|
||||
|
||||
int kp_probe_init(ktap_state *ks);
|
||||
void kp_probe_exit(ktap_state *ks);
|
||||
|
||||
void kp_perf_event_register(ktap_state *ks, struct perf_event_attr *attr,
|
||||
struct task_struct *task, char *filter,
|
||||
ktap_closure *cl);
|
||||
|
||||
void kp_event_getarg(ktap_state *ks, ktap_value *ra, int n);
|
||||
void kp_event_tostring(ktap_state *ks, struct trace_seq *seq);
|
||||
|
||||
int kp_strfmt(ktap_state *ks, struct trace_seq *seq);
|
||||
|
||||
void kp_transport_write(ktap_state *ks, const void *data, size_t length);
|
||||
void kp_transport_event_write(ktap_state *ks, struct ktap_event *e);
|
||||
void kp_transport_print_backtrace(ktap_state *ks);
|
||||
void *kp_transport_reserve(ktap_state *ks, size_t length);
|
||||
void kp_transport_exit(ktap_state *ks);
|
||||
int kp_transport_init(ktap_state *ks, struct dentry *dir);
|
||||
|
||||
void kp_exit_timers(ktap_state *ks);
|
||||
|
||||
extern int kp_max_exec_count;
|
||||
|
||||
/* get from kernel/trace/trace.h */
|
||||
static __always_inline int trace_get_context_bit(void)
|
||||
{
|
||||
int bit;
|
||||
|
||||
if (in_interrupt()) {
|
||||
if (in_nmi())
|
||||
bit = 0;
|
||||
else if (in_irq())
|
||||
bit = 1;
|
||||
else
|
||||
bit = 2;
|
||||
} else
|
||||
bit = 3;
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
/* use a special timer context kp_state instead use this recursion approach? */
|
||||
DECLARE_PER_CPU(int, kp_recursion_context[PERF_NR_CONTEXTS]);
|
||||
|
||||
static __always_inline int get_recursion_context(void)
|
||||
{
|
||||
int rctx = trace_get_context_bit();
|
||||
|
||||
if (__this_cpu_read(kp_recursion_context[rctx]))
|
||||
return -1;
|
||||
|
||||
__this_cpu_write(kp_recursion_context[rctx], true);
|
||||
barrier();
|
||||
|
||||
return rctx;
|
||||
}
|
||||
|
||||
static inline void put_recursion_context(int rctx)
|
||||
{
|
||||
barrier();
|
||||
__this_cpu_write(kp_recursion_context[rctx], false);
|
||||
}
|
||||
|
||||
|
||||
extern unsigned int kp_stub_exit_instr;
|
||||
|
||||
static inline void set_next_as_exit(ktap_state *ks)
|
||||
{
|
||||
ktap_callinfo *ci;
|
||||
|
||||
ci = ks->ci;
|
||||
if (!ci)
|
||||
return;
|
||||
|
||||
ci->u.l.savedpc = &kp_stub_exit_instr;
|
||||
|
||||
/* See precall, ci changed to ci->prev after invoke C function */
|
||||
if (ci->prev) {
|
||||
ci = ci->prev;
|
||||
ci->u.l.savedpc = &kp_stub_exit_instr;
|
||||
}
|
||||
}
|
||||
|
||||
#define kp_verbose_printf(ks, ...) \
|
||||
if (G(ks)->parm->verbose) \
|
||||
kp_printf(ks, "[verbose] "__VA_ARGS__);
|
||||
|
||||
/* get argument operation macro */
|
||||
#define kp_arg(ks, n) ((ks)->ci->func + (n))
|
||||
#define kp_arg_nr(ks) ((int)(ks->top - (ks->ci->func + 1)))
|
||||
|
||||
#define kp_arg_check(ks, narg, type) \
|
||||
do { \
|
||||
if (unlikely(ttypenv(kp_arg(ks, narg)) != type)) { \
|
||||
kp_error(ks, "wrong type of argument %d\n", narg);\
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 5, 0)
|
||||
#define SPRINT_SYMBOL sprint_symbol_no_offset
|
||||
#else
|
||||
#define SPRINT_SYMBOL sprint_symbol
|
||||
#endif
|
||||
|
||||
#endif /* __KTAP_H__ */
|
||||
@@ -1,240 +0,0 @@
|
||||
#ifndef __KTAP_BYTECODE_H__
|
||||
#define __KTAP_BYTECODE_H__
|
||||
|
||||
|
||||
/* opcode is copied from lua initially */
|
||||
|
||||
typedef enum {
|
||||
/*----------------------------------------------------------------------
|
||||
* name args description
|
||||
* ------------------------------------------------------------------------*/
|
||||
OP_MOVE,/* A B R(A) := R(B) */
|
||||
OP_LOADK,/* A Bx R(A) := Kst(Bx) */
|
||||
OP_LOADKX,/* A R(A) := Kst(extra arg) */
|
||||
OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
|
||||
OP_LOADNIL,/* A B R(A), R(A+1), ..., R(A+B) := nil */
|
||||
OP_GETUPVAL,/* A B R(A) := UpValue[B] */
|
||||
|
||||
OP_GETTABUP,/* A B C R(A) := UpValue[B][RK(C)] */
|
||||
OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
|
||||
|
||||
OP_SETTABUP,/* A B C UpValue[A][RK(B)] := RK(C) */
|
||||
OP_SETTABUP_INCR,/* A B C UpValue[A][RK(B)] += RK(C) */
|
||||
OP_SETUPVAL,/* A B UpValue[B] := R(A) */
|
||||
OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
|
||||
OP_SETTABLE_INCR,/* A B C R(A)[RK(B)] += RK(C) */
|
||||
|
||||
OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
|
||||
|
||||
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
|
||||
|
||||
OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
|
||||
OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
|
||||
OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
|
||||
OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
|
||||
OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
|
||||
OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
|
||||
OP_UNM,/* A B R(A) := -R(B) */
|
||||
OP_NOT,/* A B R(A) := not R(B) */
|
||||
OP_LEN,/* A B R(A) := length of R(B) */
|
||||
|
||||
OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
|
||||
|
||||
OP_JMP,/* A sBx pc+=sBx; if (A) close all upvalues >= R(A) + 1 */
|
||||
OP_EQ,/* A B C if ((RK(B) == RK(C)) != A) then pc++ */
|
||||
OP_LT,/* A B C if ((RK(B) < RK(C)) != A) then pc++ */
|
||||
OP_LE,/* A B C if ((RK(B) <= RK(C)) != A) then pc++ */
|
||||
|
||||
OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
|
||||
OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
|
||||
|
||||
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
|
||||
|
||||
OP_FORLOOP,/* A sBx R(A)+=R(A+2);
|
||||
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
|
||||
OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
|
||||
|
||||
OP_TFORCALL,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
|
||||
OP_TFORLOOP,/* A sBx if R(A+1) != nil then { R(A)=R(A+1); pc += sBx }*/
|
||||
|
||||
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
||||
|
||||
OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx]) */
|
||||
|
||||
OP_VARARG,/* A B R(A), R(A+1), ..., R(A+B-2) = vararg */
|
||||
|
||||
OP_EXTRAARG,/* Ax extra (larger) argument for previous opcode */
|
||||
|
||||
OP_EVENT,/* A B C R(A) := R(B)[C] */
|
||||
|
||||
OP_EVENTNAME, /* A R(A) = event_name() */
|
||||
|
||||
OP_EVENTARG,/* A B R(A) := event_arg(B)*/
|
||||
|
||||
OP_LOAD_GLOBAL,/* A B C R(A) := R(B)[C] */
|
||||
|
||||
OP_EXIT,
|
||||
|
||||
} OpCode;
|
||||
|
||||
|
||||
#define NUM_OPCODES ((int)OP_LOAD_GLOBAL + 1)
|
||||
|
||||
|
||||
enum OpMode {iABC, iABx, iAsBx, iAx}; /* basic instruction format */
|
||||
|
||||
|
||||
/*
|
||||
* ** size and position of opcode arguments.
|
||||
* */
|
||||
#define SIZE_C 9
|
||||
#define SIZE_B 9
|
||||
#define SIZE_Bx (SIZE_C + SIZE_B)
|
||||
#define SIZE_A 8
|
||||
#define SIZE_Ax (SIZE_C + SIZE_B + SIZE_A)
|
||||
|
||||
#define SIZE_OP 6
|
||||
|
||||
#define POS_OP 0
|
||||
#define POS_A (POS_OP + SIZE_OP)
|
||||
#define POS_C (POS_A + SIZE_A)
|
||||
#define POS_B (POS_C + SIZE_C)
|
||||
#define POS_Bx POS_C
|
||||
#define POS_Ax POS_A
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ** limits for opcode arguments.
|
||||
* ** we use (signed) int to manipulate most arguments,
|
||||
* ** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
|
||||
* */
|
||||
#define MAXARG_Bx ((1<<SIZE_Bx)-1)
|
||||
#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */
|
||||
|
||||
#define MAXARG_Ax ((1<<SIZE_Ax)-1)
|
||||
|
||||
#define MAXARG_A ((1<<SIZE_A)-1)
|
||||
#define MAXARG_B ((1<<SIZE_B)-1)
|
||||
#define MAXARG_C ((1<<SIZE_C)-1)
|
||||
|
||||
|
||||
/* creates a mask with `n' 1 bits at position `p' */
|
||||
#define MASK1(n,p) ((~((~(ktap_instruction)0)<<(n)))<<(p))
|
||||
|
||||
/* creates a mask with `n' 0 bits at position `p' */
|
||||
#define MASK0(n,p) (~MASK1(n,p))
|
||||
|
||||
/*
|
||||
* ** the following macros help to manipulate instructions
|
||||
* */
|
||||
|
||||
#define GET_OPCODE(i) ((OpCode)((i)>>POS_OP) & MASK1(SIZE_OP,0))
|
||||
#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
|
||||
((((ktap_instruction)o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
|
||||
|
||||
#define getarg(i,pos,size) ((int)((i)>>pos) & MASK1(size,0))
|
||||
#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \
|
||||
((((ktap_instruction)v)<<pos)&MASK1(size,pos))))
|
||||
|
||||
#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
|
||||
#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
|
||||
|
||||
#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
|
||||
#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
|
||||
|
||||
#define GETARG_B(i) getarg(i, POS_B, SIZE_B)
|
||||
#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B)
|
||||
|
||||
#define GETARG_C(i) getarg(i, POS_C, SIZE_C)
|
||||
#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C)
|
||||
|
||||
#define GETARG_Bx(i) getarg(i, POS_Bx, SIZE_Bx)
|
||||
#define SETARG_Bx(i,v) setarg(i, v, POS_Bx, SIZE_Bx)
|
||||
|
||||
#define GETARG_Ax(i) getarg(i, POS_Ax, SIZE_Ax)
|
||||
#define SETARG_Ax(i,v) setarg(i, v, POS_Ax, SIZE_Ax)
|
||||
|
||||
#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx)
|
||||
#define SETARG_sBx(i,b) SETARG_Bx((i), (unsigned int)(b)+MAXARG_sBx)
|
||||
|
||||
#define CREATE_ABC(o,a,b,c) (((ktap_instruction)(o))<<POS_OP) \
|
||||
| (((ktap_instruction)(a))<<POS_A) \
|
||||
| (((ktap_instruction)(b))<<POS_B) \
|
||||
| (((ktap_instruction)(c))<<POS_C)
|
||||
|
||||
#define CREATE_ABx(o,a,bc) (((ktap_instruction)(o))<<POS_OP) \
|
||||
| (((ktap_instruction)(a))<<POS_A) \
|
||||
| (((ktap_instruction)(bc))<<POS_Bx)
|
||||
|
||||
#define CREATE_Ax(o,a) (((ktap_instruction)(o))<<POS_OP) \
|
||||
| (((ktap_instruction)(a))<<POS_Ax)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ** Macros to operate RK indices
|
||||
* */
|
||||
|
||||
/* this bit 1 means constant (0 means register) */
|
||||
#define BITRK (1 << (SIZE_B - 1))
|
||||
|
||||
/* test whether value is a constant */
|
||||
#define ISK(x) ((x) & BITRK)
|
||||
|
||||
/* gets the index of the constant */
|
||||
#define INDEXK(r) ((int)(r) & ~BITRK)
|
||||
|
||||
#define MAXINDEXRK (BITRK - 1)
|
||||
|
||||
/* code a constant index as a RK value */
|
||||
#define RKASK(x) ((x) | BITRK)
|
||||
|
||||
|
||||
/*
|
||||
* ** invalid register that fits in 8 bits
|
||||
* */
|
||||
#define NO_REG MAXARG_A
|
||||
|
||||
|
||||
/*
|
||||
* ** R(x) - register
|
||||
* ** Kst(x) - constant (in constant table)
|
||||
* ** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
|
||||
* */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ** masks for instruction properties. The format is:
|
||||
* ** bits 0-1: op mode
|
||||
* ** bits 2-3: C arg mode
|
||||
* ** bits 4-5: B arg mode
|
||||
* ** bit 6: instruction set register A
|
||||
* ** bit 7: operator is a test (next instruction must be a jump)
|
||||
* */
|
||||
|
||||
enum OpArgMask {
|
||||
OpArgN, /* argument is not used */
|
||||
OpArgU, /* argument is used */
|
||||
OpArgR, /* argument is a register or a jump offset */
|
||||
OpArgK /* argument is a constant or register/constant */
|
||||
};
|
||||
|
||||
extern const u8 ktap_opmodes[NUM_OPCODES];
|
||||
|
||||
#define getOpMode(m) ((enum OpMode)ktap_opmodes[m] & 3)
|
||||
#define getBMode(m) ((enum OpArgMask)(ktap_opmodes[m] >> 4) & 3)
|
||||
#define getCMode(m) ((enum OpArgMask)(ktap_opmodes[m] >> 2) & 3)
|
||||
#define testAMode(m) (ktap_opmodes[m] & (1 << 6))
|
||||
#define testTMode(m) (ktap_opmodes[m] & (1 << 7))
|
||||
|
||||
|
||||
/* number of list items to accumulate before a SETLIST instruction */
|
||||
#define LFIELDS_PER_FLUSH 50
|
||||
|
||||
extern const char *const ktap_opnames[NUM_OPCODES + 1];
|
||||
|
||||
#endif /* __KTAP_BYTECODE_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,235 +0,0 @@
|
||||
/*
|
||||
* ktap.c - ktapvm kernel module main entry
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* this file is the first file to be compile, add CONFIG_ checking in here.
|
||||
* See Requirements in doc/introduction.txt
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
|
||||
#error "Currently ktap don't support kernel older than 3.1"
|
||||
#endif
|
||||
|
||||
#if !CONFIG_EVENT_TRACING
|
||||
#error "Please enable CONFIG_EVENT_TRACING before compile ktap"
|
||||
#endif
|
||||
|
||||
#if !CONFIG_PERF_EVENTS
|
||||
#error "Please enable CONFIG_PERF_EVENTS before compile ktap"
|
||||
#endif
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include "../include/ktap.h"
|
||||
|
||||
static int load_trunk(struct ktap_parm *parm, unsigned long **buff)
|
||||
{
|
||||
int ret;
|
||||
unsigned long *vmstart;
|
||||
|
||||
vmstart = vmalloc(parm->trunk_len);
|
||||
if (!vmstart)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = copy_from_user(vmstart, (void __user *)parm->trunk,
|
||||
parm->trunk_len);
|
||||
if (ret < 0) {
|
||||
vfree(vmstart);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*buff = vmstart;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gettimeofday_us(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
do_gettimeofday(&tv);
|
||||
return tv.tv_sec * USEC_PER_SEC + tv.tv_usec;
|
||||
}
|
||||
|
||||
struct dentry *kp_dir_dentry;
|
||||
static atomic_t kp_is_running = ATOMIC_INIT(0);
|
||||
|
||||
/* Ktap Main Entry */
|
||||
static int ktap_main(struct file *file, ktap_parm *parm)
|
||||
{
|
||||
unsigned long *buff = NULL;
|
||||
ktap_state *ks;
|
||||
ktap_closure *cl;
|
||||
int start_time, delta_time;
|
||||
int ret;
|
||||
|
||||
if (atomic_inc_return(&kp_is_running) != 1) {
|
||||
atomic_dec(&kp_is_running);
|
||||
pr_info("only one ktap thread allow to run\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
start_time = gettimeofday_us();
|
||||
|
||||
ks = kp_newstate(parm, kp_dir_dentry);
|
||||
if (unlikely(!ks)) {
|
||||
ret = -ENOEXEC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
file->private_data = ks;
|
||||
|
||||
ret = load_trunk(parm, &buff);
|
||||
if (ret) {
|
||||
pr_err("cannot load file\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
cl = kp_load(ks, (unsigned char *)buff);
|
||||
|
||||
vfree(buff);
|
||||
|
||||
if (cl) {
|
||||
/* optimize bytecode before excuting */
|
||||
kp_optimize_code(ks, 0, cl->l.p);
|
||||
|
||||
delta_time = gettimeofday_us() - start_time;
|
||||
kp_verbose_printf(ks, "booting time: %d (us)\n", delta_time);
|
||||
kp_call(ks, ks->top - 1, 0);
|
||||
}
|
||||
|
||||
kp_final_exit(ks);
|
||||
|
||||
out:
|
||||
atomic_dec(&kp_is_running);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void print_version(void)
|
||||
{
|
||||
}
|
||||
|
||||
static long ktap_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
ktap_parm parm;
|
||||
int ret;
|
||||
|
||||
switch (cmd) {
|
||||
case KTAP_CMD_IOC_VERSION:
|
||||
print_version();
|
||||
return 0;
|
||||
case KTAP_CMD_IOC_RUN:
|
||||
ret = copy_from_user(&parm, (void __user *)arg,
|
||||
sizeof(ktap_parm));
|
||||
if (ret < 0)
|
||||
return -EFAULT;
|
||||
|
||||
return ktap_main(file, &parm);
|
||||
default:
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations ktap_fops = {
|
||||
.llseek = no_llseek,
|
||||
.unlocked_ioctl = ktap_ioctl,
|
||||
};
|
||||
|
||||
static long ktapvm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int new_fd, err;
|
||||
struct file *new_file;
|
||||
|
||||
new_fd = get_unused_fd();
|
||||
if (new_fd < 0)
|
||||
return new_fd;
|
||||
|
||||
new_file = anon_inode_getfile("[ktap]", &ktap_fops, NULL, O_RDWR);
|
||||
if (IS_ERR(new_file)) {
|
||||
err = PTR_ERR(new_file);
|
||||
put_unused_fd(new_fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
file->private_data = NULL;
|
||||
fd_install(new_fd, new_file);
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
static const struct file_operations ktapvm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = ktapvm_ioctl,
|
||||
};
|
||||
|
||||
unsigned int kp_stub_exit_instr;
|
||||
|
||||
static int __init init_ktap(void)
|
||||
{
|
||||
struct dentry *ktapvm_dentry;
|
||||
|
||||
kp_dir_dentry = debugfs_create_dir("ktap", NULL);
|
||||
if (!kp_dir_dentry) {
|
||||
pr_err("ktap: debugfs_create_dir failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ktapvm_dentry = debugfs_create_file("ktapvm", 0444, kp_dir_dentry, NULL,
|
||||
&ktapvm_fops);
|
||||
|
||||
if (!ktapvm_dentry) {
|
||||
pr_err("ktapvm: cannot create ktapvm file\n");
|
||||
debugfs_remove_recursive(kp_dir_dentry);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SET_OPCODE(kp_stub_exit_instr, OP_EXIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit exit_ktap(void)
|
||||
{
|
||||
debugfs_remove_recursive(kp_dir_dentry);
|
||||
}
|
||||
|
||||
module_init(init_ktap);
|
||||
module_exit(exit_ktap);
|
||||
|
||||
MODULE_AUTHOR("Jovi Zhangwei <jovi.zhangwei@gmail.com>");
|
||||
MODULE_DESCRIPTION("ktap");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
int kp_max_exec_count = 10000;
|
||||
module_param_named(max_exec_count, kp_max_exec_count, int, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(max_exec_count, "non-mainthread max instruction execution count");
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* ansilib.c - ANSI escape sequences library
|
||||
*
|
||||
* http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "../../include/ktap.h"
|
||||
|
||||
/**
|
||||
* function ansi.clear_screen - Move cursor to top left and clear screen.
|
||||
*
|
||||
* Description: Sends ansi code for moving cursor to top left and then the
|
||||
* ansi code for clearing the screen from the cursor position to the end.
|
||||
*/
|
||||
|
||||
static int ktap_lib_clear_screen(ktap_state *ks)
|
||||
{
|
||||
kp_printf(ks, "\033[1;1H\033[J");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* function ansi.set_color - Set the ansi Select Graphic Rendition mode.
|
||||
* @fg: Foreground color to set.
|
||||
*
|
||||
* Description: Sends ansi code for Select Graphic Rendition mode for the
|
||||
* given forground color. Black (30), Blue (34), Green (32), Cyan (36),
|
||||
* Red (31), Purple (35), Brown (33), Light Gray (37).
|
||||
*/
|
||||
|
||||
static int ktap_lib_set_color(ktap_state *ks)
|
||||
{
|
||||
int fg;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
fg = nvalue(kp_arg(ks, 1));
|
||||
kp_printf(ks, "\033[%dm", fg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* function ansi.set_color2 - Set the ansi Select Graphic Rendition mode.
|
||||
* @fg: Foreground color to set.
|
||||
* @bg: Background color to set.
|
||||
*
|
||||
* Description: Sends ansi code for Select Graphic Rendition mode for the
|
||||
* given forground color, Black (30), Blue (34), Green (32), Cyan (36),
|
||||
* Red (31), Purple (35), Brown (33), Light Gray (37) and the given
|
||||
* background color, Black (40), Red (41), Green (42), Yellow (43),
|
||||
* Blue (44), Magenta (45), Cyan (46), White (47).
|
||||
*/
|
||||
static int ktap_lib_set_color2(ktap_state *ks)
|
||||
{
|
||||
int fg, bg;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
kp_arg_check(ks, 2, KTAP_TNUMBER);
|
||||
|
||||
fg = nvalue(kp_arg(ks, 1));
|
||||
bg = nvalue(kp_arg(ks, 2));
|
||||
kp_printf(ks, "\033[%d;%dm", fg, bg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* function ansi.set_color3 - Set the ansi Select Graphic Rendition mode.
|
||||
* @fg: Foreground color to set.
|
||||
* @bg: Background color to set.
|
||||
* @attr: Color attribute to set.
|
||||
*
|
||||
* Description: Sends ansi code for Select Graphic Rendition mode for the
|
||||
* given forground color, Black (30), Blue (34), Green (32), Cyan (36),
|
||||
* Red (31), Purple (35), Brown (33), Light Gray (37), the given
|
||||
* background color, Black (40), Red (41), Green (42), Yellow (43),
|
||||
* Blue (44), Magenta (45), Cyan (46), White (47) and the color attribute
|
||||
* All attributes off (0), Intensity Bold (1), Underline Single (4),
|
||||
* Blink Slow (5), Blink Rapid (6), Image Negative (7).
|
||||
*/
|
||||
static int ktap_lib_set_color3(ktap_state *ks)
|
||||
{
|
||||
int fg, bg, attr;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
kp_arg_check(ks, 2, KTAP_TNUMBER);
|
||||
kp_arg_check(ks, 3, KTAP_TNUMBER);
|
||||
|
||||
fg = nvalue(kp_arg(ks, 1));
|
||||
bg = nvalue(kp_arg(ks, 2));
|
||||
attr = nvalue(kp_arg(ks, 3));
|
||||
|
||||
if (attr)
|
||||
kp_printf(ks, "\033[%d;%d;%dm", fg, bg, attr);
|
||||
else
|
||||
kp_printf(ks, "\033[%d;%dm", fg, bg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* function ansi.reset_color - Resets Select Graphic Rendition mode.
|
||||
*
|
||||
* Description: Sends ansi code to reset foreground, background and color
|
||||
* attribute to default values.
|
||||
*/
|
||||
static int ktap_lib_reset_color(ktap_state *ks)
|
||||
{
|
||||
kp_printf(ks, "\033[0;0m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* function ansi.new_line - Move cursor to new line.
|
||||
*
|
||||
* Description: Sends ansi code new line.
|
||||
*/
|
||||
static int ktap_lib_new_line (ktap_state *ks)
|
||||
{
|
||||
kp_printf(ks, "\12");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const ktap_Reg ansi_funcs[] = {
|
||||
{"clear_screen", ktap_lib_clear_screen},
|
||||
{"set_color", ktap_lib_set_color},
|
||||
{"set_color2", ktap_lib_set_color2},
|
||||
{"set_color3", ktap_lib_set_color3},
|
||||
{"reset_color", ktap_lib_reset_color},
|
||||
{"new_line", ktap_lib_new_line},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
void kp_init_ansilib(ktap_state *ks)
|
||||
{
|
||||
kp_register_lib(ks, "ansi", ansi_funcs);
|
||||
}
|
||||
@@ -1,455 +0,0 @@
|
||||
/*
|
||||
* baselib.c - ktapvm kernel module base library
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/ring_buffer.h>
|
||||
#include <linux/stacktrace.h>
|
||||
#include "../../include/ktap.h"
|
||||
|
||||
static int ktap_lib_next(ktap_state *ks)
|
||||
{
|
||||
ktap_table *t = hvalue(ks->top - 2);
|
||||
|
||||
if (kp_table_next(ks, t, ks->top-1)) {
|
||||
ks->top += 1;
|
||||
return 2;
|
||||
} else {
|
||||
ks->top -= 1;
|
||||
setnilvalue(ks->top++);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int ktap_lib_pairs(ktap_state *ks)
|
||||
{
|
||||
ktap_value *v = kp_arg(ks, 1);
|
||||
ktap_table *t;
|
||||
|
||||
if (G(ks)->mainthread != ks) {
|
||||
kp_error(ks, "only mainthread can call table pairs\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ttistable(v)) {
|
||||
t = hvalue(v);
|
||||
} else if (ttisaggrtable(v)) {
|
||||
t = kp_aggrtable_synthesis(ks, ahvalue(v));
|
||||
} else if (isnil(v)) {
|
||||
kp_error(ks, "table is nil in pairs\n");
|
||||
return 0;
|
||||
} else {
|
||||
kp_error(ks, "wrong argument for pairs\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
setfvalue(ks->top++, ktap_lib_next);
|
||||
sethvalue(ks->top++, t);
|
||||
setnilvalue(ks->top++);
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int ktap_lib_len(ktap_state *ks)
|
||||
{
|
||||
int len = kp_objlen(ks, kp_arg(ks, 1));
|
||||
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
setnvalue(ks->top, len);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_print(ktap_state *ks)
|
||||
{
|
||||
int i;
|
||||
int n = kp_arg_nr(ks);
|
||||
|
||||
for (i = 1; i <= n; i++) {
|
||||
ktap_value *arg = kp_arg(ks, i);
|
||||
if (i > 1)
|
||||
kp_puts(ks, "\t");
|
||||
kp_showobj(ks, arg);
|
||||
}
|
||||
|
||||
kp_puts(ks, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* don't engage with tstring when printf, use buffer directly */
|
||||
static int ktap_lib_printf(ktap_state *ks)
|
||||
{
|
||||
struct trace_seq *seq;
|
||||
|
||||
preempt_disable_notrace();
|
||||
|
||||
seq = kp_percpu_data(KTAP_PERCPU_DATA_BUFFER);
|
||||
trace_seq_init(seq);
|
||||
|
||||
if (kp_strfmt(ks, seq))
|
||||
return 0;
|
||||
|
||||
seq->buffer[seq->len] = '\0';
|
||||
kp_transport_write(ks, seq->buffer, seq->len + 1);
|
||||
|
||||
preempt_enable_notrace();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STACKTRACE
|
||||
static int ktap_lib_print_backtrace(ktap_state *ks)
|
||||
{
|
||||
kp_transport_print_backtrace(ks);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int ktap_lib_print_backtrace(ktap_state *ks)
|
||||
{
|
||||
kp_error(ks, "Please enable CONFIG_STACKTRACE before use "
|
||||
"ktap print_backtrace\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ktap_lib_backtrace(ktap_state *ks)
|
||||
{
|
||||
struct stack_trace trace;
|
||||
ktap_btrace *bt;
|
||||
|
||||
bt = kp_percpu_data(KTAP_PERCPU_DATA_BTRACE);
|
||||
|
||||
trace.nr_entries = 0;
|
||||
trace.skip = 10;
|
||||
trace.max_entries = KTAP_STACK_MAX_ENTRIES;
|
||||
trace.entries = &bt->entries[0];
|
||||
save_stack_trace(&trace);
|
||||
|
||||
bt->nr_entries = trace.nr_entries;
|
||||
setbtvalue(ks->top, bt);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern unsigned long long ns2usecs(cycle_t nsec);
|
||||
static int ktap_lib_print_trace_clock(ktap_state *ks)
|
||||
{
|
||||
unsigned long long t;
|
||||
unsigned long secs, usec_rem;
|
||||
u64 timestamp;
|
||||
|
||||
/* use ring buffer's timestamp */
|
||||
timestamp = ring_buffer_time_stamp(G(ks)->buffer, smp_processor_id());
|
||||
|
||||
t = ns2usecs(timestamp);
|
||||
usec_rem = do_div(t, USEC_PER_SEC);
|
||||
secs = (unsigned long)t;
|
||||
|
||||
kp_printf(ks, "%5lu.%06lu\n", secs, usec_rem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ktap_lib_exit(ktap_state *ks)
|
||||
{
|
||||
kp_exit(ks);
|
||||
|
||||
/* do not execute bytecode any more in this thread */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ktap_lib_pid(ktap_state *ks)
|
||||
{
|
||||
pid_t pid = task_tgid_vnr(current);
|
||||
|
||||
setnvalue(ks->top, (int)pid);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_tid(ktap_state *ks)
|
||||
{
|
||||
pid_t pid = task_pid_vnr(current);
|
||||
|
||||
setnvalue(ks->top, (int)pid);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_execname(ktap_state *ks)
|
||||
{
|
||||
ktap_string *ts = kp_tstring_new(ks, current->comm);
|
||||
setsvalue(ks->top, ts);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_cpu(ktap_state *ks)
|
||||
{
|
||||
setnvalue(ks->top, smp_processor_id());
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_num_cpus(ktap_state *ks)
|
||||
{
|
||||
setnvalue(ks->top, num_online_cpus());
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_in_interrupt(ktap_state *ks)
|
||||
{
|
||||
int ret = in_interrupt();
|
||||
|
||||
setnvalue(ks->top, ret);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_arch(ktap_state *ks)
|
||||
{
|
||||
setsvalue(ks->top, kp_tstring_new(ks, utsname()->machine));
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_kernel_v(ktap_state *ks)
|
||||
{
|
||||
setsvalue(ks->top, kp_tstring_new(ks, utsname()->release));
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_user_string(ktap_state *ks)
|
||||
{
|
||||
unsigned long addr;
|
||||
char str[256] = {0};
|
||||
int ret;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
addr = nvalue(kp_arg(ks, 1));
|
||||
|
||||
pagefault_disable();
|
||||
ret = __copy_from_user_inatomic((void *)str, (const void *)addr, 256);
|
||||
(void) &ret; /* Silence compiler warning. */
|
||||
pagefault_enable();
|
||||
str[255] = '\0';
|
||||
setsvalue(ks->top, kp_tstring_new(ks, str));
|
||||
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_histogram(ktap_state *ks)
|
||||
{
|
||||
ktap_value *v = kp_arg(ks, 1);
|
||||
|
||||
if (G(ks)->mainthread != ks) {
|
||||
kp_error(ks, "only mainthread can call table historgram\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ttistable(v))
|
||||
kp_table_histogram(ks, hvalue(v));
|
||||
else if (ttisaggrtable(v))
|
||||
kp_aggrtable_histogram(ks, ahvalue(v));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_table(ktap_state *ks)
|
||||
{
|
||||
ktap_aggrtable *ah;
|
||||
|
||||
ah = kp_aggrtable_new(ks);
|
||||
setahvalue(ks->top, ah);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_count(ktap_state *ks)
|
||||
{
|
||||
setaggrvalue(ks->top, AGGREGATION_TYPE_COUNT);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_max(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
ks->aggr_accval = nvalue(kp_arg(ks, 1));
|
||||
setaggrvalue(ks->top, AGGREGATION_TYPE_MAX);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_min(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
ks->aggr_accval = nvalue(kp_arg(ks, 1));
|
||||
setaggrvalue(ks->top, AGGREGATION_TYPE_MIN);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_sum(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
ks->aggr_accval = nvalue(kp_arg(ks, 1));
|
||||
setaggrvalue(ks->top, AGGREGATION_TYPE_SUM);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_aggr_avg(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
ks->aggr_accval = nvalue(kp_arg(ks, 1));
|
||||
setaggrvalue(ks->top, AGGREGATION_TYPE_AVG);
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ktap_lib_delete(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TTABLE);
|
||||
|
||||
kp_table_clear(ks, hvalue(kp_arg(ks, 1)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ktap_lib_gettimeofday_us(ktap_state *ks)
|
||||
{
|
||||
setnvalue(ks->top, gettimeofday_us());
|
||||
incr_top(ks);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* use gdb to get field offset of struct task_struct, for example:
|
||||
*
|
||||
* gdb vmlinux
|
||||
* (gdb)p &(((struct task_struct *)0).prio)
|
||||
*/
|
||||
static int ktap_lib_curr_task_info(ktap_state *ks)
|
||||
{
|
||||
int offset;
|
||||
int fetch_bytes;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TNUMBER);
|
||||
|
||||
offset = nvalue(kp_arg(ks, 1));
|
||||
|
||||
if (kp_arg_nr(ks) == 1)
|
||||
fetch_bytes = 4; /* default fetch 4 bytes*/
|
||||
else {
|
||||
kp_arg_check(ks, 2, KTAP_TNUMBER);
|
||||
fetch_bytes = nvalue(kp_arg(ks, 2));
|
||||
}
|
||||
|
||||
if (offset >= sizeof(struct task_struct)) {
|
||||
setnilvalue(ks->top++);
|
||||
kp_error(ks, "access out of bound value of task_struct\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define RET_VALUE ((unsigned long)current + offset)
|
||||
|
||||
switch (fetch_bytes) {
|
||||
case 4:
|
||||
setnvalue(ks->top, *(unsigned int *)RET_VALUE);
|
||||
break;
|
||||
case 8:
|
||||
setnvalue(ks->top, *(unsigned long *)RET_VALUE);
|
||||
break;
|
||||
default:
|
||||
kp_error(ks, "unsupported fetch bytes in curr_task_info\n");
|
||||
setnilvalue(ks->top);
|
||||
break;
|
||||
}
|
||||
|
||||
#undef RET_VALUE
|
||||
|
||||
incr_top(ks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This built-in function mainly purpose scripts/schedule/schedtimes.kp
|
||||
*/
|
||||
static int ktap_lib_in_iowait(ktap_state *ks)
|
||||
{
|
||||
setnvalue(ks->top, current->in_iowait);
|
||||
incr_top(ks);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const ktap_Reg base_funcs[] = {
|
||||
{"pairs", ktap_lib_pairs},
|
||||
{"len", ktap_lib_len},
|
||||
{"print", ktap_lib_print},
|
||||
{"printf", ktap_lib_printf},
|
||||
{"print_backtrace", ktap_lib_print_backtrace},
|
||||
{"backtrace", ktap_lib_backtrace},
|
||||
{"print_trace_clock", ktap_lib_print_trace_clock},
|
||||
{"in_interrupt", ktap_lib_in_interrupt},
|
||||
{"exit", ktap_lib_exit},
|
||||
{"pid", ktap_lib_pid},
|
||||
{"tid", ktap_lib_tid},
|
||||
{"execname", ktap_lib_execname},
|
||||
{"cpu", ktap_lib_cpu},
|
||||
{"num_cpus", ktap_lib_num_cpus},
|
||||
{"arch", ktap_lib_arch},
|
||||
{"kernel_v", ktap_lib_kernel_v},
|
||||
{"user_string", ktap_lib_user_string},
|
||||
{"histogram", ktap_lib_histogram},
|
||||
{"aggr_table", ktap_lib_aggr_table},
|
||||
{"count", ktap_lib_aggr_count},
|
||||
{"max", ktap_lib_aggr_max},
|
||||
{"min", ktap_lib_aggr_min},
|
||||
{"sum", ktap_lib_aggr_sum},
|
||||
{"avg", ktap_lib_aggr_avg},
|
||||
|
||||
{"delete", ktap_lib_delete},
|
||||
{"gettimeofday_us", ktap_lib_gettimeofday_us},
|
||||
{"curr_taskinfo", ktap_lib_curr_task_info},
|
||||
{"in_iowait", ktap_lib_in_iowait},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
void kp_init_baselib(ktap_state *ks)
|
||||
{
|
||||
kp_register_lib(ks, NULL, base_funcs);
|
||||
}
|
||||
@@ -1,449 +0,0 @@
|
||||
/*
|
||||
* kdebug.c - ktap probing core implementation
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/ftrace_event.h>
|
||||
#include "../../include/ktap.h"
|
||||
|
||||
static void ktap_call_probe_closure(ktap_state *mainthread, ktap_closure *cl,
|
||||
struct ktap_event *e)
|
||||
{
|
||||
ktap_state *ks;
|
||||
ktap_value *func;
|
||||
|
||||
ks = kp_newthread(mainthread);
|
||||
setcllvalue(ks->top, cl);
|
||||
func = ks->top;
|
||||
incr_top(ks);
|
||||
|
||||
ks->current_event = e;
|
||||
|
||||
kp_call(ks, func, 0);
|
||||
|
||||
ks->current_event = NULL;
|
||||
kp_exitthread(ks);
|
||||
}
|
||||
|
||||
void kp_event_tostring(ktap_state *ks, struct trace_seq *seq)
|
||||
{
|
||||
struct ktap_event *e = ks->current_event;
|
||||
struct trace_iterator *iter;
|
||||
struct trace_event *ev;
|
||||
enum print_line_t ret = TRACE_TYPE_NO_CONSUME;
|
||||
|
||||
/* Simulate the iterator */
|
||||
|
||||
/*
|
||||
* use temp percpu buffer as trace_iterator
|
||||
* we cannot use same temp buffer as printf.
|
||||
*/
|
||||
iter = kp_percpu_data(KTAP_PERCPU_DATA_BUFFER2);
|
||||
|
||||
trace_seq_init(&iter->seq);
|
||||
iter->ent = e->entry;
|
||||
|
||||
ev = &(e->call->event);
|
||||
if (ev)
|
||||
ret = ev->funcs->trace(iter, 0, ev);
|
||||
|
||||
if (ret != TRACE_TYPE_NO_CONSUME) {
|
||||
struct trace_seq *s = &iter->seq;
|
||||
int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len;
|
||||
|
||||
s->buffer[len] = '\0';
|
||||
trace_seq_puts(seq, s->buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* check pt_regs defintion in linux/arch/x86/include/asm/ptrace.h */
|
||||
/* support other architecture pt_regs showing */
|
||||
static void event_regstr(ktap_state *ks, struct ktap_event *e, StkId ra)
|
||||
{
|
||||
struct pt_regs *regs = e->regs;
|
||||
char str[256] = {0};
|
||||
|
||||
#if defined(CONFIG_X86_32)
|
||||
snprintf(str, sizeof(str),
|
||||
"{ax: 0x%lx, orig_ax: 0x%lx, bx: 0x%lx, cx: 0x%lx, dx: 0x%lx, "
|
||||
"si: 0x%lx, di: 0x%lx, bp: 0x%lx, ds: 0x%lx, es: 0x%lx, fs: 0x%lx, "
|
||||
"gs: 0x%lx, ip: 0x%lx, cs: 0x%lx, flags: 0x%lx, sp: 0x%lx, ss: 0x%lx}\n",
|
||||
regs->ax, regs->orig_ax, regs->bx, regs->cx, regs->dx,
|
||||
regs->si, regs->di, regs->bp, regs->ds, regs->es, regs->fs,
|
||||
regs->gs, regs->ip, regs->cs, regs->flags, regs->sp, regs->ss);
|
||||
#elif defined(CONFIG_X86_64)
|
||||
/* x86_64 pt_regs doesn't have ds, es, fs or gs. */
|
||||
snprintf(str, sizeof(str),
|
||||
"{ax: 0x%lx, orig_ax: 0x%lx, bx: 0x%lx, cx: 0x%lx, dx: 0x%lx, "
|
||||
"si: 0x%lx, di: 0x%lx, r8: 0x%lx, r9: 0x%lx, r10: 0x%lx, r11: 0x%lx, "
|
||||
"r12: 0x%lx, r13: 0x%lx, r14: 0x%lx, r15: 0x%lx, bp: 0x%lx, ip: 0x%lx, "
|
||||
"cs: 0x%lx, flags: 0x%lx, sp: 0x%lx, ss: 0x%lx}\n",
|
||||
regs->ax, regs->orig_ax, regs->bx, regs->cx, regs->dx,
|
||||
regs->si, regs->di, regs->r8, regs->r9, regs->r10, regs->r11,
|
||||
regs->r12, regs->r13, regs->r14, regs->r15, regs->bp, regs->ip,
|
||||
regs->cs, regs->flags, regs->sp, regs->ss);
|
||||
#endif
|
||||
setsvalue(ra, kp_tstring_new_local(ks, str));
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************/
|
||||
/* This definition should keep update with kernel/trace/trace.h */
|
||||
struct ftrace_event_field {
|
||||
struct list_head link;
|
||||
const char *name;
|
||||
const char *type;
|
||||
int filter_type;
|
||||
int offset;
|
||||
int size;
|
||||
int is_signed;
|
||||
};
|
||||
|
||||
static struct list_head *ktap_get_fields(struct ftrace_event_call *event_call)
|
||||
{
|
||||
if (!event_call->class->get_fields)
|
||||
return &event_call->class->fields;
|
||||
return event_call->class->get_fields(event_call);
|
||||
}
|
||||
|
||||
static void get_field_value(ktap_state *ks, struct ktap_event *e,
|
||||
struct ftrace_event_field *field, ktap_value *ra)
|
||||
{
|
||||
void *value = (unsigned char *)e->entry + field->offset;
|
||||
|
||||
if (field->size == 4) {
|
||||
int n = *(int *)value;
|
||||
setnvalue(ra, n);
|
||||
return;
|
||||
} else if (field->size == 8) {
|
||||
long n = *(long *)value;
|
||||
setnvalue(ra, n);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp(field->type, "char", 4)) {
|
||||
setsvalue(ra, kp_tstring_new(ks, (char *)value));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void kp_event_getarg(ktap_state *ks, ktap_value *ra, int n)
|
||||
{
|
||||
struct ktap_event *e = ks->current_event;
|
||||
int index = n;
|
||||
struct ftrace_event_field *field;
|
||||
struct list_head *head;
|
||||
|
||||
/* this is very slow and not safe, fix it in future */
|
||||
head = ktap_get_fields(e->call);
|
||||
list_for_each_entry_reverse(field, head, link) {
|
||||
if (--index == 0) {
|
||||
get_field_value(ks, e, field, ra);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setnilvalue(ra);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Callback function for perf event subsystem
|
||||
* make ktap reentrant, don't disable irq in callback function,
|
||||
* same as perf and ftrace. to make reentrant, we need some
|
||||
* percpu data to be context isolation(irq/sirq/nmi/process)
|
||||
*
|
||||
* perf callback already consider on the recursion issue,
|
||||
* so ktap don't need to check again in here.
|
||||
*
|
||||
* Note tracepoint handler is calling with rcu_read_lock.
|
||||
*/
|
||||
static void ktap_overflow_callback(struct perf_event *event,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct ktap_probe_event *ktap_pevent;
|
||||
struct ktap_event e;
|
||||
ktap_state *ks;
|
||||
int rctx;
|
||||
|
||||
ktap_pevent = event->overflow_handler_context;
|
||||
ks = ktap_pevent->ks;
|
||||
|
||||
if (unlikely(ks->stop))
|
||||
return;
|
||||
|
||||
rctx = get_recursion_context();
|
||||
if (rctx < 0)
|
||||
return;
|
||||
|
||||
/* profile perf event don't have valid associated tp_event */
|
||||
if (event->tp_event) {
|
||||
e.call = event->tp_event;
|
||||
e.entry = data->raw->data;
|
||||
e.entry_size = data->raw->size;
|
||||
}
|
||||
e.pevent = ktap_pevent;
|
||||
e.regs = regs;
|
||||
|
||||
ktap_call_probe_closure(ks, ktap_pevent->cl, &e);
|
||||
|
||||
put_recursion_context(rctx);
|
||||
}
|
||||
|
||||
static void perf_destructor(struct ktap_probe_event *ktap_pevent)
|
||||
{
|
||||
perf_event_release_kernel(ktap_pevent->perf);
|
||||
}
|
||||
|
||||
static int (*kp_ftrace_profile_set_filter)(struct perf_event *event,
|
||||
int event_id, char *filter_str);
|
||||
|
||||
/*
|
||||
* Generic perf event register function
|
||||
* used by tracepoints/kprobe/uprobe/profile-timer/hw_breakpoint.
|
||||
*/
|
||||
void kp_perf_event_register(ktap_state *ks, struct perf_event_attr *attr,
|
||||
struct task_struct *task, char *filter,
|
||||
ktap_closure *cl)
|
||||
{
|
||||
struct ktap_probe_event *ktap_pevent;
|
||||
struct perf_event *event;
|
||||
int cpu, ret;
|
||||
|
||||
kp_verbose_printf(ks, "enable perf event id: %d, filter: %s "
|
||||
"pid: %d\n", attr->config, filter,
|
||||
task ? task_tgid_vnr(task) : -1);
|
||||
|
||||
/*
|
||||
* don't tracing until ktap_wait, the reason is:
|
||||
* 1). some event may hit before apply filter
|
||||
* 2). more simple to manage tracing thread
|
||||
* 3). avoid race with mainthread.
|
||||
*
|
||||
* Another way to do this is make attr.disabled as 1, then use
|
||||
* perf_event_enable after filter apply, however, perf_event_enable
|
||||
* was not exported in kernel older than 3.3, so we drop this method.
|
||||
*/
|
||||
ks->stop = 1;
|
||||
|
||||
for_each_cpu(cpu, G(ks)->cpumask) {
|
||||
ktap_pevent = kp_zalloc(ks, sizeof(*ktap_pevent));
|
||||
ktap_pevent->ks = ks;
|
||||
ktap_pevent->cl = cl;
|
||||
event = perf_event_create_kernel_counter(attr, cpu, task,
|
||||
ktap_overflow_callback,
|
||||
ktap_pevent);
|
||||
if (IS_ERR(event)) {
|
||||
int err = PTR_ERR(event);
|
||||
kp_error(ks, "unable register perf event %d on cpu %d, "
|
||||
"err: %d\n", attr->config, cpu, err);
|
||||
kp_free(ks, ktap_pevent);
|
||||
return;
|
||||
}
|
||||
|
||||
ktap_pevent->perf = event;
|
||||
INIT_LIST_HEAD(&ktap_pevent->list);
|
||||
list_add_tail(&ktap_pevent->list, &G(ks)->probe_events_head);
|
||||
|
||||
if (!filter)
|
||||
continue;
|
||||
|
||||
ret = kp_ftrace_profile_set_filter(event, attr->config, filter);
|
||||
if (ret) {
|
||||
kp_error(ks, "unable set filter %s for event id %d, "
|
||||
"ret: %d\n", filter, attr->config, ret);
|
||||
perf_destructor(ktap_pevent);
|
||||
list_del(&ktap_pevent->list);
|
||||
kp_free(ks, ktap_pevent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void end_probes(struct ktap_state *ks)
|
||||
{
|
||||
struct ktap_probe_event *ktap_pevent;
|
||||
struct list_head *tmp, *pos;
|
||||
struct list_head *head = &G(ks)->probe_events_head;
|
||||
|
||||
list_for_each(pos, head) {
|
||||
ktap_pevent = container_of(pos, struct ktap_probe_event,
|
||||
list);
|
||||
perf_destructor(ktap_pevent);
|
||||
}
|
||||
/*
|
||||
* Ensure our callback won't be called anymore. The buffers
|
||||
* will be freed after that.
|
||||
*/
|
||||
tracepoint_synchronize_unregister();
|
||||
|
||||
list_for_each_safe(pos, tmp, head) {
|
||||
ktap_pevent = container_of(pos, struct ktap_probe_event,
|
||||
list);
|
||||
list_del(&ktap_pevent->list);
|
||||
kp_free(ks, ktap_pevent);
|
||||
}
|
||||
}
|
||||
|
||||
static int ktap_lib_probe_by_id(ktap_state *ks)
|
||||
{
|
||||
const char *ids_str;
|
||||
char *start;
|
||||
ktap_closure *cl;
|
||||
struct task_struct *task = G(ks)->trace_task;
|
||||
char filter_str[128] = {0};
|
||||
char *filter, *ptr1, *sep, *ptr;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TSTRING);
|
||||
kp_arg_check(ks, 2, KTAP_TFUNCTION);
|
||||
|
||||
ids_str = svalue(kp_arg(ks, 1));
|
||||
cl = clvalue(kp_arg(ks, 2));
|
||||
|
||||
start = (char *)ids_str;
|
||||
|
||||
again:
|
||||
filter = NULL;
|
||||
|
||||
sep = strchr(start, ',');
|
||||
if (!sep)
|
||||
ptr1 = strchr(start, '/');
|
||||
else
|
||||
ptr1 = strnchr(start, sep - start, '/');
|
||||
|
||||
if (ptr1) {
|
||||
char *ptr2 = strrchr(ptr1 + 1, '/');
|
||||
|
||||
if (ptr2) {
|
||||
memset(filter_str, 0, sizeof(filter_str));
|
||||
strncpy(filter_str, ptr1 + 1, ptr2 - ptr1 - 1);
|
||||
filter = &filter_str[0];
|
||||
} else {
|
||||
kp_printf(ks, "cannot parse ids_str: %s\n", ids_str);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (ptr = start; *ptr != ',' && *ptr != '\0' && *ptr != '/'; ptr++) {
|
||||
char token[32] = {0};
|
||||
int id;
|
||||
int i = 0;
|
||||
|
||||
if (*ptr == ' ')
|
||||
continue;
|
||||
|
||||
while (isdigit(*ptr)) {
|
||||
token[i++] = *ptr++;
|
||||
}
|
||||
|
||||
if (!kstrtoint(token, 10, &id)) {
|
||||
struct perf_event_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.type = PERF_TYPE_TRACEPOINT;
|
||||
attr.config = id;
|
||||
attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
|
||||
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD;
|
||||
attr.sample_period = 1;
|
||||
attr.size = sizeof(attr);
|
||||
attr.disabled = 0;
|
||||
|
||||
kp_perf_event_register(ks, &attr, task, filter, cl);
|
||||
}
|
||||
}
|
||||
|
||||
if (sep && (*(sep + 1) != '\0')) {
|
||||
start = sep + 1;
|
||||
goto again;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ktap_lib_probe_end(ktap_state *ks)
|
||||
{
|
||||
kp_arg_check(ks, 1, KTAP_TFUNCTION);
|
||||
|
||||
G(ks)->trace_end_closure = clvalue(kp_arg(ks, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ktap_lib_traceoff(ktap_state *ks)
|
||||
{
|
||||
end_probes(ks);
|
||||
|
||||
/* call trace_end_closure after probed end */
|
||||
if (G(ks)->trace_end_closure) {
|
||||
setcllvalue(ks->top, G(ks)->trace_end_closure);
|
||||
incr_top(ks);
|
||||
kp_call(ks, ks->top - 1, 0);
|
||||
G(ks)->trace_end_closure = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kp_probe_exit(ktap_state *ks)
|
||||
{
|
||||
if (!G(ks)->trace_enabled)
|
||||
return;
|
||||
|
||||
end_probes(ks);
|
||||
|
||||
/* call trace_end_closure after probed end */
|
||||
if (!G(ks)->error && G(ks)->trace_end_closure) {
|
||||
setcllvalue(ks->top, G(ks)->trace_end_closure);
|
||||
incr_top(ks);
|
||||
kp_call(ks, ks->top - 1, 0);
|
||||
G(ks)->trace_end_closure = NULL;
|
||||
}
|
||||
|
||||
G(ks)->trace_enabled = 0;
|
||||
}
|
||||
|
||||
int kp_probe_init(ktap_state *ks)
|
||||
{
|
||||
G(ks)->trace_enabled = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const ktap_Reg kdebuglib_funcs[] = {
|
||||
{"probe_by_id", ktap_lib_probe_by_id},
|
||||
{"probe_end", ktap_lib_probe_end},
|
||||
{"traceoff", ktap_lib_traceoff},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
void kp_init_kdebuglib(ktap_state *ks)
|
||||
{
|
||||
kp_ftrace_profile_set_filter =
|
||||
(void *)kallsyms_lookup_name("ftrace_profile_set_filter");
|
||||
if (!kp_ftrace_profile_set_filter) {
|
||||
printk("ktap: cannot lookup ftrace_profile_set_filter "
|
||||
"in kallsyms\n");
|
||||
return;
|
||||
}
|
||||
|
||||
kp_register_lib(ks, "kdebug", kdebuglib_funcs);
|
||||
}
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* timer.c - timer library support for ktap
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include "../../include/ktap.h"
|
||||
|
||||
struct hrtimer_ktap {
|
||||
struct hrtimer timer;
|
||||
ktap_state *ks;
|
||||
ktap_closure *cl;
|
||||
u64 ns;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/*
|
||||
* Currently ktap disallow tracing event in timer callback closure,
|
||||
* that will corrupt ktap_state and ktap stack, because timer closure
|
||||
* and event closure use same irq percpu ktap_state and stack.
|
||||
* We can use a different percpu ktap_state and stack for timer purpuse,
|
||||
* but that's don't bring any big value with cost on memory consuming.
|
||||
*
|
||||
* So just simply disable tracing in timer closure,
|
||||
* get_recursion_context()/put_recursion_context() is used for this purpose.
|
||||
*
|
||||
* option: export perf_swevent_put_recursion_context to slove this issue.
|
||||
*/
|
||||
static enum hrtimer_restart hrtimer_ktap_fn(struct hrtimer *timer)
|
||||
{
|
||||
struct hrtimer_ktap *t;
|
||||
ktap_state *ks;
|
||||
int rctx;
|
||||
|
||||
rcu_read_lock_sched_notrace();
|
||||
rctx = get_recursion_context();
|
||||
|
||||
t = container_of(timer, struct hrtimer_ktap, timer);
|
||||
|
||||
ks = kp_newthread(t->ks);
|
||||
setcllvalue(ks->top, t->cl);
|
||||
incr_top(ks);
|
||||
kp_call(ks, ks->top - 1, 0);
|
||||
kp_exitthread(ks);
|
||||
|
||||
hrtimer_add_expires_ns(timer, t->ns);
|
||||
|
||||
put_recursion_context(rctx);
|
||||
rcu_read_unlock_sched_notrace();
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
static void set_tick_timer(ktap_state *ks, u64 period, ktap_closure *cl)
|
||||
{
|
||||
struct hrtimer_ktap *t;
|
||||
|
||||
t = kp_malloc(ks, sizeof(*t));
|
||||
t->ks = ks;
|
||||
t->cl = cl;
|
||||
t->ns = period;
|
||||
|
||||
INIT_LIST_HEAD(&t->list);
|
||||
list_add(&t->list, &(G(ks)->timers));
|
||||
|
||||
hrtimer_init(&t->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
t->timer.function = hrtimer_ktap_fn;
|
||||
hrtimer_start(&t->timer, ns_to_ktime(period), HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
static void set_profile_timer(ktap_state *ks, u64 period, ktap_closure *cl)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.type = PERF_TYPE_SOFTWARE;
|
||||
attr.config = PERF_COUNT_SW_CPU_CLOCK;
|
||||
attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
|
||||
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD;
|
||||
attr.sample_period = period;
|
||||
attr.size = sizeof(attr);
|
||||
attr.disabled = 0;
|
||||
|
||||
kp_perf_event_register(ks, &attr, NULL, NULL, cl);
|
||||
}
|
||||
|
||||
static int do_tick_profile(ktap_state *ks, int is_tick)
|
||||
{
|
||||
const char *str, *tmp;
|
||||
char interval_str[32] = {0};
|
||||
char suffix[10] = {0};
|
||||
int n, i = 0;
|
||||
int factor;
|
||||
|
||||
kp_arg_check(ks, 1, KTAP_TSTRING);
|
||||
kp_arg_check(ks, 2, KTAP_TFUNCTION);
|
||||
|
||||
str = svalue(kp_arg(ks, 1));
|
||||
tmp = str;
|
||||
while (isdigit(*tmp))
|
||||
tmp++;
|
||||
|
||||
strncpy(interval_str, str, tmp - str);
|
||||
if (kstrtoint(interval_str, 10, &n))
|
||||
goto error;
|
||||
|
||||
strncpy(suffix, tmp, 9);
|
||||
while (suffix[i] != ' ' && suffix[i] != '\0')
|
||||
i++;
|
||||
|
||||
suffix[i] = '\0';
|
||||
|
||||
if (!strcmp(suffix, "s") || !strcmp(suffix, "sec"))
|
||||
factor = NSEC_PER_SEC;
|
||||
else if (!strcmp(suffix, "ms") || !strcmp(suffix, "msec"))
|
||||
factor = NSEC_PER_MSEC;
|
||||
else if (!strcmp(suffix, "us") || !strcmp(suffix, "usec"))
|
||||
factor = NSEC_PER_USEC;
|
||||
else
|
||||
goto error;
|
||||
|
||||
if (is_tick)
|
||||
set_tick_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2)));
|
||||
else
|
||||
set_profile_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2)));
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kp_error(ks, "cannot parse timer interval: %s\n", str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* tick-n probes fire on only one CPU per interval.
|
||||
* valid time suffixes: sec/s, msec/ms, usec/us
|
||||
*/
|
||||
static int ktap_lib_tick(ktap_state *ks)
|
||||
{
|
||||
return do_tick_profile(ks, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* A profile-n probe fires every fixed interval on every CPU
|
||||
* valid time suffixes: sec/s, msec/ms, usec/us
|
||||
*/
|
||||
static int ktap_lib_profile(ktap_state *ks)
|
||||
{
|
||||
return do_tick_profile(ks, 0);
|
||||
}
|
||||
|
||||
void kp_exit_timers(ktap_state *ks)
|
||||
{
|
||||
struct hrtimer_ktap *t, *tmp;
|
||||
struct list_head *timers_list = &(G(ks)->timers);
|
||||
|
||||
list_for_each_entry_safe(t, tmp, timers_list, list) {
|
||||
hrtimer_cancel(&t->timer);
|
||||
kp_free(ks, t);
|
||||
}
|
||||
}
|
||||
|
||||
static const ktap_Reg timerlib_funcs[] = {
|
||||
{"profile", ktap_lib_profile},
|
||||
{"tick", ktap_lib_tick},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
void kp_init_timerlib(ktap_state *ks)
|
||||
{
|
||||
kp_register_lib(ks, "timer", timerlib_funcs);
|
||||
}
|
||||
|
||||
@@ -1,310 +0,0 @@
|
||||
/*
|
||||
* loader.c - loader for ktap bytecode chunk file
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include "../include/ktap.h"
|
||||
|
||||
#define KTAPC_TAIL "\x19\x93\r\n\x1a\n"
|
||||
|
||||
struct load_state {
|
||||
unsigned char *buff;
|
||||
int pos;
|
||||
ktap_state *ks;
|
||||
};
|
||||
|
||||
#define READ_CHAR(S) (S->buff[S->pos++])
|
||||
#define READ_BYTE(S) READ_CHAR(S)
|
||||
#define READ_INT(S) load_int(S)
|
||||
#define READ_NUMBER(S) load_number(S)
|
||||
#define READ_STRING(S) load_string(S)
|
||||
#define READ_VECTOR(S, dst, size) \
|
||||
do { \
|
||||
memcpy(dst, &S->buff[S->pos], size); \
|
||||
S->pos += size; \
|
||||
} while(0)
|
||||
|
||||
#define NEW_VECTOR(S, size) kp_malloc(S->ks, size)
|
||||
#define GET_CURRENT(S) &S->buff[S->pos]
|
||||
#define ADD_POS(S, size) S->pos += size
|
||||
|
||||
|
||||
static int load_function(struct load_state *S, ktap_proto *f);
|
||||
|
||||
|
||||
static int load_int(struct load_state *S)
|
||||
{
|
||||
int x;
|
||||
|
||||
READ_VECTOR(S, &x, sizeof(int));
|
||||
return x;
|
||||
}
|
||||
|
||||
static int load_number(struct load_state *S)
|
||||
{
|
||||
int x;
|
||||
|
||||
READ_VECTOR(S, &x, sizeof(ktap_number));
|
||||
return x;
|
||||
}
|
||||
|
||||
static ktap_string *load_string(struct load_state *S)
|
||||
{
|
||||
ktap_string *ts;
|
||||
size_t size;
|
||||
|
||||
size = READ_INT(S);
|
||||
|
||||
if (!size)
|
||||
return NULL;
|
||||
else {
|
||||
char *s = GET_CURRENT(S);
|
||||
ADD_POS(S, size);
|
||||
/* remove trailing '\0' */
|
||||
ts = kp_tstring_newlstr(S->ks, s, size - 1);
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int load_code(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
int n = READ_INT(S);
|
||||
|
||||
f->sizecode = n;
|
||||
f->code = NEW_VECTOR(S, n * sizeof(ktap_instruction));
|
||||
READ_VECTOR(S, f->code, n * sizeof(ktap_instruction));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_constants(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
int i,n;
|
||||
|
||||
n = READ_INT(S);
|
||||
|
||||
f->sizek = n;
|
||||
f->k = NEW_VECTOR(S, n * sizeof(ktap_value));
|
||||
for (i = 0; i < n; i++)
|
||||
setnilvalue(&f->k[i]);
|
||||
|
||||
for (i=0; i < n; i++) {
|
||||
ktap_value *o = &f->k[i];
|
||||
|
||||
int t = READ_CHAR(S);
|
||||
switch (t) {
|
||||
case KTAP_TNIL:
|
||||
setnilvalue(o);
|
||||
break;
|
||||
case KTAP_TBOOLEAN:
|
||||
setbvalue(o, READ_CHAR(S));
|
||||
break;
|
||||
case KTAP_TNUMBER:
|
||||
/*
|
||||
* todo: kernel not support fp, check double when
|
||||
* loading
|
||||
*/
|
||||
setnvalue(o, READ_NUMBER(S));
|
||||
break;
|
||||
case KTAP_TSTRING:
|
||||
setsvalue(o, READ_STRING(S));
|
||||
break;
|
||||
default:
|
||||
kp_error(S->ks, "ktap: load_constants: "
|
||||
"unknow ktap_value\n");
|
||||
return -1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
n = READ_INT(S);
|
||||
f->p = NEW_VECTOR(S, n * sizeof(ktap_proto));
|
||||
f->sizep = n;
|
||||
for (i = 0; i < n; i++)
|
||||
f->p[i] = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->p[i] = kp_newproto(S->ks);
|
||||
if (load_function(S, f->p[i]))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int load_upvalues(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
int i,n;
|
||||
|
||||
n = READ_INT(S);
|
||||
f->upvalues = NEW_VECTOR(S, n * sizeof(ktap_upvaldesc));
|
||||
f->sizeupvalues = n;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
f->upvalues[i].name = NULL;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
f->upvalues[i].instack = READ_BYTE(S);
|
||||
f->upvalues[i].idx = READ_BYTE(S);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_debuginfo(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
int i,n;
|
||||
|
||||
f->source = READ_STRING(S);
|
||||
n = READ_INT(S);
|
||||
f->sizelineinfo = n;
|
||||
f->lineinfo = NEW_VECTOR(S, n * sizeof(int));
|
||||
READ_VECTOR(S, f->lineinfo, n * sizeof(int));
|
||||
n = READ_INT(S);
|
||||
f->locvars = NEW_VECTOR(S, n * sizeof(struct ktap_locvar));
|
||||
f->sizelocvars = n;
|
||||
for (i = 0; i < n; i++)
|
||||
f->locvars[i].varname = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->locvars[i].varname = READ_STRING(S);
|
||||
f->locvars[i].startpc = READ_INT(S);
|
||||
f->locvars[i].endpc = READ_INT(S);
|
||||
}
|
||||
n = READ_INT(S);
|
||||
for (i = 0; i < n; i++)
|
||||
f->upvalues[i].name = READ_STRING(S);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_function(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
f->linedefined = READ_INT(S);
|
||||
f->lastlinedefined = READ_INT(S);
|
||||
f->numparams = READ_BYTE(S);
|
||||
f->is_vararg = READ_BYTE(S);
|
||||
f->maxstacksize = READ_BYTE(S);
|
||||
if (load_code(S, f))
|
||||
return -1;
|
||||
if (load_constants(S, f))
|
||||
return -1;
|
||||
if (load_upvalues(S, f))
|
||||
return -1;
|
||||
if (load_debuginfo(S, f))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define error(S, why) \
|
||||
kp_error(S->ks, "load failed: %s precompiled chunk\n", why)
|
||||
|
||||
#define N0 KTAPC_HEADERSIZE
|
||||
#define N1 (sizeof(KTAP_SIGNATURE) - sizeof(char))
|
||||
#define N2 N1 + 2
|
||||
#define N3 N2 + 6
|
||||
|
||||
static int load_header(struct load_state *S)
|
||||
{
|
||||
u8 h[KTAPC_HEADERSIZE];
|
||||
u8 s[KTAPC_HEADERSIZE];
|
||||
|
||||
kp_header(h);
|
||||
READ_VECTOR(S, s, KTAPC_HEADERSIZE);
|
||||
|
||||
if (memcmp(h, s, N0) == 0)
|
||||
return 0;
|
||||
if (memcmp(h, s, N1) != 0)
|
||||
error(S, "not a");
|
||||
else if (memcmp(h, s, N2) != 0)
|
||||
error(S, "version mismatch in");
|
||||
else if (memcmp(h, s, N3) != 0)
|
||||
error(S, "incompatible");
|
||||
else
|
||||
error(S,"corrupted");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int verify_code(struct load_state *S, ktap_proto *f)
|
||||
{
|
||||
/* not support now */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
ktap_closure *kp_load(ktap_state *ks, unsigned char *buff)
|
||||
{
|
||||
struct load_state S;
|
||||
ktap_closure *cl;
|
||||
ktap_lclosure *f;
|
||||
int ret, i;
|
||||
|
||||
S.ks = ks;
|
||||
S.buff = buff;
|
||||
S.pos = 0;
|
||||
|
||||
ret = load_header(&S);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
cl = kp_newlclosure(ks, 1);
|
||||
if (!cl)
|
||||
return cl;
|
||||
|
||||
/* put closure on the top, prepare to run with this closure */
|
||||
setcllvalue(ks->top, cl);
|
||||
incr_top(ks);
|
||||
|
||||
cl->l.p = kp_newproto(ks);
|
||||
if (load_function(&S, cl->l.p))
|
||||
return NULL;
|
||||
|
||||
if (cl->l.p->sizeupvalues != 1) {
|
||||
ktap_proto *p = cl->l.p;
|
||||
cl = kp_newlclosure(ks, cl->l.p->sizeupvalues);
|
||||
cl->l.p = p;
|
||||
setcllvalue(ks->top - 1, cl);
|
||||
}
|
||||
|
||||
f = &cl->l;
|
||||
for (i = 0; i < f->nupvalues; i++) { /* initialize upvalues */
|
||||
ktap_upval *up = kp_newupval(ks);
|
||||
f->upvals[i] = up;
|
||||
}
|
||||
|
||||
/* set global table as 1st upvalue of 'f' */
|
||||
if (f->nupvalues == 1) {
|
||||
ktap_table *reg = hvalue(&G(ks)->registry);
|
||||
const ktap_value *gt = kp_table_getint(reg, KTAP_RIDX_GLOBALS);
|
||||
setobj(f->upvals[0]->v, gt);
|
||||
}
|
||||
|
||||
verify_code(&S, cl->l.p);
|
||||
return cl;
|
||||
}
|
||||
|
||||
@@ -1,483 +0,0 @@
|
||||
/*
|
||||
* object.c - ktap object generic operation
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include "../include/ktap.h"
|
||||
#else
|
||||
#include "../include/ktap_types.h"
|
||||
#endif
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#define KTAP_ALLOC_FLAGS ((GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN) \
|
||||
& ~__GFP_WAIT)
|
||||
|
||||
void *kp_malloc(ktap_state *ks, int size)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
/*
|
||||
* Normally we don't want to trace under memory pressure,
|
||||
* so we use a simple rule to handle memory allocation failure:
|
||||
*
|
||||
* retry until allocation success, this will make caller don't need
|
||||
* to handle the unlikely failure case, then ktap exit.
|
||||
*
|
||||
* In this approach, if user find there have memory allocation failure,
|
||||
* user should re-run the ktap script, or fix the memory pressure
|
||||
* issue, or figure out why the script need so many memory.
|
||||
*
|
||||
* Perhaps return pre-allocated stub memory trunk when allocate failed
|
||||
* is a better approch?
|
||||
*/
|
||||
addr = kmalloc(size, KTAP_ALLOC_FLAGS);
|
||||
if (unlikely(!addr)) {
|
||||
kp_error(ks, "kmalloc size %d failed, retry again\n", size);
|
||||
printk("ktap kmalloc size %d failed, retry again\n", size);
|
||||
dump_stack();
|
||||
while (1) {
|
||||
addr = kmalloc(size, KTAP_ALLOC_FLAGS);
|
||||
if (addr)
|
||||
break;
|
||||
}
|
||||
kp_printf(ks, "kmalloc retry success after failed, exit\n");
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
void kp_free(ktap_state *ks, void *addr)
|
||||
{
|
||||
kfree(addr);
|
||||
}
|
||||
|
||||
void *kp_reallocv(ktap_state *ks, void *addr, int oldsize, int newsize)
|
||||
{
|
||||
void *new_addr;
|
||||
|
||||
new_addr = krealloc(addr, newsize, KTAP_ALLOC_FLAGS);
|
||||
if (unlikely(!new_addr)) {
|
||||
kp_error(ks, "krealloc size %d failed, retry again\n", newsize);
|
||||
printk("ktap krealloc size %d failed, retry again\n", newsize);
|
||||
dump_stack();
|
||||
while (1) {
|
||||
new_addr = krealloc(addr, newsize, KTAP_ALLOC_FLAGS);
|
||||
if (new_addr)
|
||||
break;
|
||||
}
|
||||
kp_printf(ks, "krealloc retry success after failed, exit\n");
|
||||
}
|
||||
|
||||
return new_addr;
|
||||
}
|
||||
|
||||
void *kp_zalloc(ktap_state *ks, int size)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
addr = kzalloc(size, KTAP_ALLOC_FLAGS);
|
||||
if (unlikely(!addr)) {
|
||||
kp_error(ks, "kzalloc size %d failed, retry again\n", size);
|
||||
printk("ktap kzalloc size %d failed, retry again\n", size);
|
||||
dump_stack();
|
||||
while (1) {
|
||||
addr = kzalloc(size, KTAP_ALLOC_FLAGS);
|
||||
if (addr)
|
||||
break;
|
||||
}
|
||||
kp_printf(ks, "kzalloc retry success after failed, exit\n");
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
void kp_obj_dump(ktap_state *ks, const ktap_value *v)
|
||||
{
|
||||
switch (ttype(v)) {
|
||||
case KTAP_TNIL:
|
||||
kp_puts(ks, "NIL");
|
||||
break;
|
||||
case KTAP_TNUMBER:
|
||||
kp_printf(ks, "NUMBER %ld", nvalue(v));
|
||||
break;
|
||||
case KTAP_TBOOLEAN:
|
||||
kp_printf(ks, "BOOLEAN %d", bvalue(v));
|
||||
break;
|
||||
case KTAP_TLIGHTUSERDATA:
|
||||
kp_printf(ks, "LIGHTUSERDATA 0x%lx", (unsigned long)pvalue(v));
|
||||
break;
|
||||
case KTAP_TLCF:
|
||||
kp_printf(ks, "LIGHTCFCUNTION 0x%lx", (unsigned long)fvalue(v));
|
||||
break;
|
||||
case KTAP_TSHRSTR:
|
||||
case KTAP_TLNGSTR:
|
||||
kp_printf(ks, "SHRSTR #%s", svalue(v));
|
||||
break;
|
||||
case KTAP_TUSERDATA:
|
||||
kp_printf(ks, "USERDATA 0x%lx", (unsigned long)uvalue(v));
|
||||
break;
|
||||
case KTAP_TTABLE:
|
||||
kp_printf(ks, "TABLE 0x%lx", (unsigned long)hvalue(v));
|
||||
break;
|
||||
default:
|
||||
kp_printf(ks, "GCVALUE 0x%lx", (unsigned long)gcvalue(v));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/stacktrace.h>
|
||||
#include <linux/kallsyms.h>
|
||||
|
||||
static void kp_btrace_dump(ktap_state *ks, ktap_btrace *bt)
|
||||
{
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bt->nr_entries; i++) {
|
||||
unsigned long p = bt->entries[i];
|
||||
|
||||
if (p == ULONG_MAX)
|
||||
break;
|
||||
|
||||
SPRINT_SYMBOL(str, p);
|
||||
kp_printf(ks, "%s\n", str);
|
||||
}
|
||||
}
|
||||
|
||||
static int kp_btrace_equal(ktap_btrace *bt1, ktap_btrace *bt2)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (bt1->nr_entries != bt2->nr_entries)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < bt1->nr_entries; i++) {
|
||||
if (bt1->entries[i] != bt2->entries[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void kp_showobj(ktap_state *ks, const ktap_value *v)
|
||||
{
|
||||
switch (ttype(v)) {
|
||||
case KTAP_TNIL:
|
||||
kp_puts(ks, "nil");
|
||||
break;
|
||||
case KTAP_TNUMBER:
|
||||
kp_printf(ks, "%ld", nvalue(v));
|
||||
break;
|
||||
case KTAP_TBOOLEAN:
|
||||
kp_puts(ks, (bvalue(v) == 1) ? "true" : "false");
|
||||
break;
|
||||
case KTAP_TLIGHTUSERDATA:
|
||||
kp_printf(ks, "0x%lx", (unsigned long)pvalue(v));
|
||||
break;
|
||||
case KTAP_TLCF:
|
||||
kp_printf(ks, "0x%lx", (unsigned long)fvalue(v));
|
||||
break;
|
||||
case KTAP_TSHRSTR:
|
||||
case KTAP_TLNGSTR:
|
||||
kp_puts(ks, svalue(v));
|
||||
break;
|
||||
case KTAP_TUSERDATA:
|
||||
kp_printf(ks, "0x%lx", (unsigned long)uvalue(v));
|
||||
break;
|
||||
case KTAP_TTABLE:
|
||||
kp_table_dump(ks, hvalue(v));
|
||||
break;
|
||||
#ifdef __KERNEL__
|
||||
case KTAP_TEVENT:
|
||||
kp_transport_event_write(ks, evalue(v));
|
||||
break;
|
||||
case KTAP_TBTRACE:
|
||||
kp_btrace_dump(ks, btvalue(v));
|
||||
break;
|
||||
case KTAP_TAGGRTABLE:
|
||||
kp_aggrtable_dump(ks, ahvalue(v));
|
||||
break;
|
||||
case KTAP_TAGGRACCVAL:
|
||||
kp_aggraccval_dump(ks, aggraccvalue(v));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
kp_error(ks, "print unknown value type: %d\n", ttype(v));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* equality of ktap values. ks == NULL means raw equality
|
||||
*/
|
||||
int kp_equalobjv(ktap_state *ks, const ktap_value *t1, const ktap_value *t2)
|
||||
{
|
||||
switch (ttype(t1)) {
|
||||
case KTAP_TNIL:
|
||||
return 1;
|
||||
case KTAP_TNUMBER:
|
||||
return nvalue(t1) == nvalue(t2);
|
||||
case KTAP_TBOOLEAN:
|
||||
return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
|
||||
case KTAP_TLIGHTUSERDATA:
|
||||
return pvalue(t1) == pvalue(t2);
|
||||
case KTAP_TLCF:
|
||||
return fvalue(t1) == fvalue(t2);
|
||||
case KTAP_TSHRSTR:
|
||||
return eqshrstr(rawtsvalue(t1), rawtsvalue(t2));
|
||||
case KTAP_TLNGSTR:
|
||||
return kp_tstring_eqlngstr(rawtsvalue(t1), rawtsvalue(t2));
|
||||
case KTAP_TUSERDATA:
|
||||
if (uvalue(t1) == uvalue(t2))
|
||||
return 1;
|
||||
else if (ks == NULL)
|
||||
return 0;
|
||||
case KTAP_TTABLE:
|
||||
if (hvalue(t1) == hvalue(t2))
|
||||
return 1;
|
||||
else if (ks == NULL)
|
||||
return 0;
|
||||
#ifdef __KERNEL__
|
||||
case KTAP_TBTRACE:
|
||||
return kp_btrace_equal(btvalue(t1), btvalue(t2));
|
||||
#endif
|
||||
default:
|
||||
return gcvalue(t1) == gcvalue(t2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ktap will not use lua's length operator on table meaning,
|
||||
* also # is not for length operator any more in ktap.
|
||||
*
|
||||
* Quote from lua mannal:
|
||||
* 2.5.5 - The Length Operator
|
||||
*
|
||||
* The length operator is denoted by the unary operator #.
|
||||
* The length of a string is its number of bytes(that is,
|
||||
* the usual meaning of string length when each character is one byte).
|
||||
*
|
||||
* The length of a table t is defined to be any integer index n
|
||||
* such that t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil,
|
||||
* n can be zero. For a regular array, with non-nil values from 1 to a given n,
|
||||
* its length is exactly that n, the index of its last value. If the array has
|
||||
* "holes" (that is, nil values between other non-nil values), then #t can be
|
||||
* any of the indices that directly precedes a nil value
|
||||
* (that is, it may consider any such nil value as the end of the array).
|
||||
*/
|
||||
int kp_objlen(ktap_state *ks, const ktap_value *v)
|
||||
{
|
||||
switch(v->type) {
|
||||
case KTAP_TTABLE:
|
||||
return kp_table_length(ks, hvalue(v));
|
||||
case KTAP_TSTRING:
|
||||
return rawtsvalue(v)->tsv.len;
|
||||
default:
|
||||
kp_printf(ks, "cannot get length of type %d\n", v->type);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* need to protect allgc field? */
|
||||
ktap_gcobject *kp_newobject(ktap_state *ks, int type, size_t size,
|
||||
ktap_gcobject **list)
|
||||
{
|
||||
ktap_gcobject *o;
|
||||
|
||||
o = kp_malloc(ks, size);
|
||||
if (list == NULL)
|
||||
list = &G(ks)->allgc;
|
||||
|
||||
gch(o)->tt = type;
|
||||
gch(o)->next = *list;
|
||||
*list = o;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
ktap_upval *kp_newupval(ktap_state *ks)
|
||||
{
|
||||
ktap_upval *uv;
|
||||
|
||||
uv = &kp_newobject(ks, KTAP_TUPVAL, sizeof(ktap_upval), NULL)->uv;
|
||||
uv->v = &uv->u.value;
|
||||
setnilvalue(uv->v);
|
||||
return uv;
|
||||
}
|
||||
|
||||
static ktap_btrace *kp_newbacktrace(ktap_state *ks, ktap_gcobject **list)
|
||||
{
|
||||
ktap_btrace *bt;
|
||||
|
||||
bt = &kp_newobject(ks, KTAP_TBTRACE, sizeof(ktap_btrace), list)->bt;
|
||||
return bt;
|
||||
}
|
||||
|
||||
void kp_objclone(ktap_state *ks, const ktap_value *o, ktap_value *newo,
|
||||
ktap_gcobject **list)
|
||||
{
|
||||
if (ttisbtrace(o)) {
|
||||
ktap_btrace *bt;
|
||||
bt = kp_newbacktrace(ks, list);
|
||||
bt->nr_entries = btvalue(o)->nr_entries;
|
||||
memcpy(&bt->entries[0], &btvalue(o)->entries[0],
|
||||
sizeof(bt->entries));
|
||||
setbtvalue(newo, bt);
|
||||
} else {
|
||||
kp_error(ks, "cannot clone ktap value type %d\n", ttype(o));
|
||||
setnilvalue(newo);
|
||||
}
|
||||
}
|
||||
|
||||
ktap_closure *kp_newlclosure(ktap_state *ks, int n)
|
||||
{
|
||||
ktap_closure *cl;
|
||||
|
||||
cl = (ktap_closure *)kp_newobject(ks, KTAP_TLCL, sizeof(*cl), NULL);
|
||||
cl->l.p = NULL;
|
||||
cl->l.nupvalues = n;
|
||||
while (n--)
|
||||
cl->l.upvals[n] = NULL;
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
static void free_proto(ktap_state *ks, ktap_proto *f)
|
||||
{
|
||||
kp_free(ks, f->code);
|
||||
kp_free(ks, f->p);
|
||||
kp_free(ks, f->k);
|
||||
kp_free(ks, f->lineinfo);
|
||||
kp_free(ks, f->locvars);
|
||||
kp_free(ks, f->upvalues);
|
||||
kp_free(ks, f);
|
||||
}
|
||||
|
||||
ktap_proto *kp_newproto(ktap_state *ks)
|
||||
{
|
||||
ktap_proto *f;
|
||||
f = (ktap_proto *)kp_newobject(ks, KTAP_TPROTO, sizeof(*f), NULL);
|
||||
f->k = NULL;
|
||||
f->sizek = 0;
|
||||
f->p = NULL;
|
||||
f->sizep = 0;
|
||||
f->code = NULL;
|
||||
f->cache = NULL;
|
||||
f->sizecode = 0;
|
||||
f->lineinfo = NULL;
|
||||
f->sizelineinfo = 0;
|
||||
f->upvalues = NULL;
|
||||
f->sizeupvalues = 0;
|
||||
f->numparams = 0;
|
||||
f->is_vararg = 0;
|
||||
f->maxstacksize = 0;
|
||||
f->locvars = NULL;
|
||||
f->sizelocvars = 0;
|
||||
f->linedefined = 0;
|
||||
f->lastlinedefined = 0;
|
||||
f->source = NULL;
|
||||
return f;
|
||||
}
|
||||
|
||||
static ktap_udata *newudata(ktap_state *ks, size_t s)
|
||||
{
|
||||
ktap_udata *u;
|
||||
|
||||
u = &kp_newobject(ks, KTAP_TUSERDATA, sizeof(ktap_udata) + s, NULL)->u;
|
||||
u->uv.len = s;
|
||||
return u;
|
||||
}
|
||||
|
||||
void *kp_newuserdata(ktap_state *ks, size_t size)
|
||||
{
|
||||
ktap_udata *u;
|
||||
|
||||
u = newudata(ks, size);
|
||||
return u + 1;
|
||||
}
|
||||
|
||||
void kp_free_gclist(ktap_state *ks, ktap_gcobject *o)
|
||||
{
|
||||
while (o) {
|
||||
ktap_gcobject *next;
|
||||
|
||||
next = gch(o)->next;
|
||||
switch (gch(o)->tt) {
|
||||
case KTAP_TTABLE:
|
||||
kp_table_free(ks, (ktap_table *)o);
|
||||
break;
|
||||
case KTAP_TPROTO:
|
||||
free_proto(ks, (ktap_proto *)o);
|
||||
break;
|
||||
#ifdef __KERNEL__
|
||||
case KTAP_TAGGRTABLE:
|
||||
kp_aggrtable_free(ks, (ktap_aggrtable *)o);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
kp_free(ks, o);
|
||||
}
|
||||
o = next;
|
||||
}
|
||||
}
|
||||
|
||||
void kp_free_all_gcobject(ktap_state *ks)
|
||||
{
|
||||
kp_free_gclist(ks, G(ks)->allgc);
|
||||
G(ks)->allgc = NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/*
|
||||
* make header for precompiled chunks
|
||||
* if you change the code below be sure to update load_header and FORMAT above
|
||||
* and KTAPC_HEADERSIZE in ktap_types.h
|
||||
*/
|
||||
void kp_header(u8 *h)
|
||||
{
|
||||
int x = 1;
|
||||
|
||||
memcpy(h, KTAP_SIGNATURE, sizeof(KTAP_SIGNATURE) - sizeof(char));
|
||||
h += sizeof(KTAP_SIGNATURE) - sizeof(char);
|
||||
*h++ = (u8)VERSION;
|
||||
*h++ = (u8)FORMAT;
|
||||
*h++ = (u8)(*(char*)&x); /* endianness */
|
||||
*h++ = (u8)(sizeof(int));
|
||||
*h++ = (u8)(sizeof(size_t));
|
||||
*h++ = (u8)(sizeof(ktap_instruction));
|
||||
*h++ = (u8)(sizeof(ktap_number));
|
||||
*h++ = (u8)(((ktap_number)0.5) == 0); /* is ktap_number integral? */
|
||||
memcpy(h, KTAPC_TAIL, sizeof(KTAPC_TAIL) - sizeof(char));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* opcode.c
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "../include/ktap_types.h"
|
||||
#include "../include/ktap_opcodes.h"
|
||||
|
||||
const char *const ktap_opnames[NUM_OPCODES + 1] = {
|
||||
"MOVE",
|
||||
"LOADK",
|
||||
"LOADKX",
|
||||
"LOADBOOL",
|
||||
"LOADNIL",
|
||||
"GETUPVAL",
|
||||
"GETTABUP",
|
||||
"GETTABLE",
|
||||
"SETTABUP",
|
||||
"SETTABUP_INCR",
|
||||
"SETUPVAL",
|
||||
"SETTABLE",
|
||||
"SETTABLE_INCR",
|
||||
"NEWTABLE",
|
||||
"SELF",
|
||||
"ADD",
|
||||
"SUB",
|
||||
"MUL",
|
||||
"DIV",
|
||||
"MOD",
|
||||
"POW",
|
||||
"UNM",
|
||||
"NOT",
|
||||
"LEN",
|
||||
"CONCAT",
|
||||
"JMP",
|
||||
"EQ",
|
||||
"LT",
|
||||
"LE",
|
||||
"TEST",
|
||||
"TESTSET",
|
||||
"CALL",
|
||||
"TAILCALL",
|
||||
"RETURN",
|
||||
"FORLOOP",
|
||||
"FORPREP",
|
||||
"TFORCALL",
|
||||
"TFORLOOP",
|
||||
"SETLIST",
|
||||
"CLOSURE",
|
||||
"VARARG",
|
||||
"EXTRAARG",
|
||||
|
||||
"EVENT",
|
||||
"EVENT_NAME",
|
||||
"EVENT_ARG", /* arg1, arg2 .. arg9 */
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))
|
||||
|
||||
const u8 ktap_opmodes[NUM_OPCODES] = {
|
||||
/* T A B C mode opcode */
|
||||
opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */
|
||||
,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */
|
||||
,opmode(0, 1, OpArgN, OpArgN, iABx) /* OP_LOADKX */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */
|
||||
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_LOADNIL */
|
||||
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */
|
||||
,opmode(0, 1, OpArgU, OpArgK, iABC) /* OP_GETTABUP */
|
||||
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */
|
||||
,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABUP */
|
||||
,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */
|
||||
,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */
|
||||
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */
|
||||
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */
|
||||
,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */
|
||||
,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */
|
||||
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */
|
||||
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */
|
||||
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */
|
||||
,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TEST */
|
||||
,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */
|
||||
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */
|
||||
,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */
|
||||
,opmode(0, 0, OpArgN, OpArgU, iABC) /* OP_TFORCALL */
|
||||
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_TFORLOOP */
|
||||
,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */
|
||||
,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */
|
||||
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */
|
||||
,opmode(0, 0, OpArgU, OpArgU, iAx) /* OP_EXTRAARG */
|
||||
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_EVENT */
|
||||
};
|
||||
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* strfmt.c - printf implementation
|
||||
*
|
||||
* This file is part of ktap by Jovi Zhangwei.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
|
||||
*
|
||||
* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
|
||||
* - The part of code in this file is copied from lua initially.
|
||||
* - lua's MIT license is compatible with GPL.
|
||||
*
|
||||
* ktap is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* ktap is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include "../include/ktap.h"
|
||||
|
||||
/* macro to `unsign' a character */
|
||||
#define uchar(c) ((unsigned char)(c))
|
||||
|
||||
#define L_ESC '%'
|
||||
|
||||
/* valid flags in a format specification */
|
||||
#define FLAGS "-+ #0"
|
||||
|
||||
#define INTFRMLEN "ll"
|
||||
#define INTFRM_T long long
|
||||
|
||||
/*
|
||||
* maximum size of each format specification (such as '%-099.99d')
|
||||
* (+10 accounts for %99.99x plus margin of error)
|
||||
*/
|
||||
#define MAX_FORMAT (sizeof(FLAGS) + sizeof(INTFRMLEN) + 10)
|
||||
|
||||
static const char *scanformat(ktap_state *ks, const char *strfrmt, char *form)
|
||||
{
|
||||
const char *p = strfrmt;
|
||||
while (*p != '\0' && strchr(FLAGS, *p) != NULL)
|
||||
p++; /* skip flags */
|
||||
|
||||
if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char)) {
|
||||
kp_error(ks, "invalid format (repeated flags)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (isdigit(uchar(*p)))
|
||||
p++; /* skip width */
|
||||
|
||||
if (isdigit(uchar(*p)))
|
||||
p++; /* (2 digits at most) */
|
||||
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
if (isdigit(uchar(*p)))
|
||||
p++; /* skip precision */
|
||||
if (isdigit(uchar(*p)))
|
||||
p++; /* (2 digits at most) */
|
||||
}
|
||||
|
||||
if (isdigit(uchar(*p))) {
|
||||
kp_error(ks, "invalid format (width or precision too long)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*(form++) = '%';
|
||||
memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char));
|
||||
form += p - strfrmt + 1;
|
||||
*form = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* add length modifier into formats
|
||||
*/
|
||||
static void addlenmod(char *form, const char *lenmod)
|
||||
{
|
||||
size_t l = strlen(form);
|
||||
size_t lm = strlen(lenmod);
|
||||
char spec = form[l - 1];
|
||||
|
||||
strcpy(form + l - 1, lenmod);
|
||||
form[l + lm - 1] = spec;
|
||||
form[l + lm] = '\0';
|
||||
}
|
||||
|
||||
|
||||
static void ktap_argerror(ktap_state *ks, int narg, const char *extramsg)
|
||||
{
|
||||
kp_error(ks, "bad argument #%d: (%s)\n", narg, extramsg);
|
||||
}
|
||||
|
||||
int kp_strfmt(ktap_state *ks, struct trace_seq *seq)
|
||||
{
|
||||
int arg = 1;
|
||||
size_t sfl;
|
||||
ktap_value *arg_fmt = kp_arg(ks, 1);
|
||||
int argnum = kp_arg_nr(ks);
|
||||
const char *strfrmt, *strfrmt_end;
|
||||
|
||||
strfrmt = svalue(arg_fmt);
|
||||
sfl = rawtsvalue(arg_fmt)->tsv.len;
|
||||
strfrmt_end = strfrmt + sfl;
|
||||
|
||||
while (strfrmt < strfrmt_end) {
|
||||
if (*strfrmt != L_ESC)
|
||||
trace_seq_putc(seq, *strfrmt++);
|
||||
else if (*++strfrmt == L_ESC)
|
||||
trace_seq_putc(seq, *strfrmt++);
|
||||
else { /* format item */
|
||||
char form[MAX_FORMAT];
|
||||
|
||||
if (++arg > argnum) {
|
||||
ktap_argerror(ks, arg, "no value");
|
||||
return -1;
|
||||
}
|
||||
|
||||
strfrmt = scanformat(ks, strfrmt, form);
|
||||
switch (*strfrmt++) {
|
||||
case 'c':
|
||||
trace_seq_printf(seq, form,
|
||||
nvalue(kp_arg(ks, arg)));
|
||||
break;
|
||||
case 'd': case 'i': {
|
||||
ktap_number n = nvalue(kp_arg(ks, arg));
|
||||
INTFRM_T ni = (INTFRM_T)n;
|
||||
addlenmod(form, INTFRMLEN);
|
||||
trace_seq_printf(seq, form, ni);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
SPRINT_SYMBOL(str, nvalue(kp_arg(ks, arg)));
|
||||
trace_seq_puts(seq, str);
|
||||
break;
|
||||
}
|
||||
case 'o': case 'u': case 'x': case 'X': {
|
||||
ktap_number n = nvalue(kp_arg(ks, arg));
|
||||
unsigned INTFRM_T ni = (unsigned INTFRM_T)n;
|
||||
addlenmod(form, INTFRMLEN);
|
||||
trace_seq_printf(seq, form, ni);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
ktap_value *v = kp_arg(ks, arg);
|
||||
const char *s;
|
||||
size_t l;
|
||||
|
||||
if (isnil(v)) {
|
||||
trace_seq_puts(seq, "nil");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ttisevent(v)) {
|
||||
kp_event_tostring(ks, seq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s = svalue(v);
|
||||
l = rawtsvalue(v)->tsv.len;
|
||||
if (!strchr(form, '.') && l >= 100) {
|
||||
/*
|
||||
* no precision and string is too long
|
||||
* to be formatted;
|
||||
* keep original string
|
||||
*/
|
||||
trace_seq_puts(seq, s);
|
||||
break;
|
||||
} else {
|
||||
trace_seq_printf(seq, form, s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
default: /* also treat cases `pnLlh' */
|
||||
kp_error(ks, "invalid option " KTAP_QL("%%%c")
|
||||
" to " KTAP_QL("format"),
|
||||
*(strfrmt - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user