ktap register built-in functions and library into table. 1). Built-in functions(lib_base,c):
print, printf, print_hist, pairs, len, delete, stack, print_trace_clock, num_cpus, arch, kernel_v, kernel_string, user_string, stringof, ipof, gettimeofday_ns, gettimeofday_us, gettimeofday_ms, gettimeofday_s, curr_taskinfo, in_iowait, in_interrupt, exit. 2). Ansi library(lib_ansi.c): ansi.clear_screen ansi.set_color ansi.set_color2 ansi.set_color3 ansi.reset_color ansi.new_line 3). kdebug library(lib_kdebug.c): kdebug.trace_by_id kdebug.trace_end kdebug.tracepoint kdebug.kprobe 4). net library(lib_net.c): net.ip_sock_saddr net.ip_sock_daddr net.format_ip_addr 5). table library(lib_table.c): table.new 6). timer library(lib_timer.c): timer.profile timer.tick Signed-off-by: Jovi Zhangwei <jovi.zhang...@gmail.com> --- tools/ktap/runtime/lib_ansi.c | 142 ++++++++++++++ tools/ktap/runtime/lib_base.c | 409 ++++++++++++++++++++++++++++++++++++++++ tools/ktap/runtime/lib_kdebug.c | 198 +++++++++++++++++++ tools/ktap/runtime/lib_net.c | 107 +++++++++++ tools/ktap/runtime/lib_table.c | 58 ++++++ tools/ktap/runtime/lib_timer.c | 210 +++++++++++++++++++++ 6 files changed, 1124 insertions(+) create mode 100644 tools/ktap/runtime/lib_ansi.c create mode 100644 tools/ktap/runtime/lib_base.c create mode 100644 tools/ktap/runtime/lib_kdebug.c create mode 100644 tools/ktap/runtime/lib_net.c create mode 100644 tools/ktap/runtime/lib_table.c create mode 100644 tools/ktap/runtime/lib_timer.c diff --git a/tools/ktap/runtime/lib_ansi.c b/tools/ktap/runtime/lib_ansi.c new file mode 100644 index 0000000..e690b2b --- /dev/null +++ b/tools/ktap/runtime/lib_ansi.c @@ -0,0 +1,142 @@ +/* + * lib_ansi.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-2014 Jovi Zhangwei <jovi.zhang...@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_types.h" +#include "ktap.h" +#include "kp_vm.h" + +/** + * function ansi.clear_screen - Move cursor to top left and clear screen. + * + * Description: Sends ansi code for moving cursor to top left and then the + * ansi code for clearing the screen from the cursor position to the end. + */ + +static int kplib_ansi_clear_screen(ktap_state_t *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 kplib_ansi_set_color(ktap_state_t *ks) +{ + int fg = kp_arg_checknumber(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 kplib_ansi_set_color2(ktap_state_t *ks) +{ + int fg = kp_arg_checknumber(ks, 1); + int bg = kp_arg_checknumber(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 kplib_ansi_set_color3(ktap_state_t *ks) +{ + int fg = kp_arg_checknumber(ks, 1); + int bg = kp_arg_checknumber(ks, 2); + int attr = kp_arg_checknumber(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 kplib_ansi_reset_color(ktap_state_t *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 kplib_ansi_new_line (ktap_state_t *ks) +{ + kp_printf(ks, "\12"); + return 0; +} + +static const ktap_libfunc_t ansi_lib_funcs[] = { + {"clear_screen", kplib_ansi_clear_screen}, + {"set_color", kplib_ansi_set_color}, + {"set_color2", kplib_ansi_set_color2}, + {"set_color3", kplib_ansi_set_color3}, + {"reset_color", kplib_ansi_reset_color}, + {"new_line", kplib_ansi_new_line}, + {NULL} +}; + +int kp_lib_init_ansi(ktap_state_t *ks) +{ + return kp_vm_register_lib(ks, "ansi", ansi_lib_funcs); +} diff --git a/tools/ktap/runtime/lib_base.c b/tools/ktap/runtime/lib_base.c new file mode 100644 index 0000000..0294134 --- /dev/null +++ b/tools/ktap/runtime/lib_base.c @@ -0,0 +1,409 @@ +/* + * lib_base.c - base library + * + * Caveat: all kernel funtion called by ktap library have to be lock free, + * otherwise system will deadlock. + * + * This file is part of ktap by Jovi Zhangwei. + * + * Copyright (C) 2012-2014 Jovi Zhangwei <jovi.zhang...@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/version.h> +#include <linux/hardirq.h> +#include <linux/module.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 <linux/cred.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) +#include <linux/uidgid.h> +#endif +#include "../include/ktap_types.h" +#include "ktap.h" +#include "kp_obj.h" +#include "kp_str.h" +#include "kp_tab.h" +#include "kp_transport.h" +#include "kp_events.h" +#include "kp_vm.h" + +static int kplib_print(ktap_state_t *ks) +{ + int i; + int n = kp_arg_nr(ks); + + for (i = 1; i <= n; i++) { + ktap_val_t *arg = kp_arg(ks, i); + if (i > 1) + kp_puts(ks, "\t"); + kp_obj_show(ks, arg); + } + + kp_puts(ks, "\n"); + return 0; +} + +/* don't engage with intern string in printf, use buffer directly */ +static int kplib_printf(ktap_state_t *ks) +{ + struct trace_seq *seq; + + preempt_disable_notrace(); + + seq = kp_this_cpu_print_buffer(ks); + trace_seq_init(seq); + + if (kp_str_fmt(ks, seq)) + goto out; + + seq->buffer[seq->len] = '\0'; + kp_transport_write(ks, seq->buffer, seq->len + 1); + + out: + preempt_enable_notrace(); + return 0; +} + +#define HISTOGRAM_DEFAULT_TOP_NUM 20 + +static int kplib_print_hist(ktap_state_t *ks) +{ + int n ; + + kp_arg_check(ks, 1, KTAP_TTAB); + n = kp_arg_checkoptnumber(ks, 2, HISTOGRAM_DEFAULT_TOP_NUM); + + n = min(n, 1000); + n = max(n, HISTOGRAM_DEFAULT_TOP_NUM); + + kp_tab_print_hist(ks, hvalue(kp_arg(ks, 1)), n); + + return 0; +} + +static int kplib_pairs(ktap_state_t *ks) +{ + kp_arg_check(ks, 1, KTAP_TTAB); + + set_cfunc(ks->top++, (ktap_cfunction)kp_tab_next); + set_table(ks->top++, hvalue(kp_arg(ks, 1))); + set_nil(ks->top++); + return 3; +} + +static int kplib_len(ktap_state_t *ks) +{ + int len = kp_obj_len(ks, kp_arg(ks, 1)); + + if (len < 0) + return -1; + + set_number(ks->top, len); + incr_top(ks); + return 1; +} + +static int kplib_delete(ktap_state_t *ks) +{ + kp_arg_check(ks, 1, KTAP_TTAB); + kp_tab_clear(hvalue(kp_arg(ks, 1))); + return 0; +} + +#ifdef CONFIG_STACKTRACE +static int kplib_stack(ktap_state_t *ks) +{ + uint16_t skip, depth = 10; + + depth = kp_arg_checkoptnumber(ks, 1, 10); /* default as 10 */ + depth = min_t(uint16_t, depth, KP_MAX_STACK_DEPTH); + skip = kp_arg_checkoptnumber(ks, 2, 10); /* default as 10 */ + + set_kstack(ks->top, depth, skip); + incr_top(ks); + return 1; +} +#else +static int kplib_stack(ktap_state_t *ks) +{ + kp_error(ks, "Please enable CONFIG_STACKTRACE before call stack()\n"); + return -1; +} +#endif + + +extern unsigned long long ns2usecs(cycle_t nsec); +static int kplib_print_trace_clock(ktap_state_t *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 kplib_num_cpus(ktap_state_t *ks) +{ + set_number(ks->top, num_online_cpus()); + incr_top(ks); + return 1; +} + +/* TODO: intern string firstly */ +static int kplib_arch(ktap_state_t *ks) +{ + ktap_str_t *ts = kp_str_newz(ks, utsname()->machine); + if (unlikely(!ts)) + return -1; + + set_string(ks->top, ts); + incr_top(ks); + return 1; +} + +/* TODO: intern string firstly */ +static int kplib_kernel_v(ktap_state_t *ks) +{ + ktap_str_t *ts = kp_str_newz(ks, utsname()->release); + if (unlikely(!ts)) + return -1; + + set_string(ks->top, ts); + incr_top(ks); + return 1; +} + +static int kplib_kernel_string(ktap_state_t *ks) +{ + unsigned long addr = kp_arg_checknumber(ks, 1); + char str[256] = {0}; + ktap_str_t *ts; + char *ret; + + ret = strncpy((void *)str, (const void *)addr, 256); + (void) &ret; /* Silence compiler warning. */ + str[255] = '\0'; + + ts = kp_str_newz(ks, str); + if (unlikely(!ts)) + return -1; + + set_string(ks->top, ts); + incr_top(ks); + return 1; +} + +static int kplib_user_string(ktap_state_t *ks) +{ + unsigned long addr = kp_arg_checknumber(ks, 1); + char str[256] = {0}; + ktap_str_t *ts; + int ret; + + pagefault_disable(); + ret = __copy_from_user_inatomic((void *)str, (const void *)addr, 256); + (void) &ret; /* Silence compiler warning. */ + pagefault_enable(); + str[255] = '\0'; + + ts = kp_str_newz(ks, str); + if (unlikely(!ts)) + return -1; + + set_string(ks->top, ts); + incr_top(ks); + return 1; +} + +static int kplib_stringof(ktap_state_t *ks) +{ + ktap_val_t *v = kp_arg(ks, 1); + const ktap_str_t *ts = NULL; + + if (itype(v) == KTAP_TEVENTSTR) { + ts = kp_event_stringify(ks); + } else if (itype(v) == KTAP_TKIP) { + char str[KSYM_SYMBOL_LEN]; + + SPRINT_SYMBOL(str, nvalue(v)); + ts = kp_str_newz(ks, str); + } + + if (unlikely(!ts)) + return -1; + + set_string(ks->top++, ts); + return 1; +} + +static int kplib_ipof(ktap_state_t *ks) +{ + unsigned long addr = kp_arg_checknumber(ks, 1); + + set_ip(ks->top++, addr); + return 1; +} + +static int kplib_gettimeofday_ns(ktap_state_t *ks) +{ + set_number(ks->top, gettimeofday_ns()); + incr_top(ks); + + return 1; +} + +static int kplib_gettimeofday_us(ktap_state_t *ks) +{ + set_number(ks->top, gettimeofday_ns() / NSEC_PER_USEC); + incr_top(ks); + + return 1; +} + +static int kplib_gettimeofday_ms(ktap_state_t *ks) +{ + set_number(ks->top, gettimeofday_ns() / NSEC_PER_MSEC); + incr_top(ks); + + return 1; +} + +static int kplib_gettimeofday_s(ktap_state_t *ks) +{ + set_number(ks->top, gettimeofday_ns() / NSEC_PER_SEC); + 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 kplib_curr_taskinfo(ktap_state_t *ks) +{ + int offset = kp_arg_checknumber(ks, 1); + int fetch_bytes = kp_arg_checkoptnumber(ks, 2, 4); /* fetch 4 bytes */ + + if (offset >= sizeof(struct task_struct)) { + set_nil(ks->top++); + kp_error(ks, "access out of bound value of task_struct\n"); + return 1; + } + +#define RET_VALUE ((unsigned long)current + offset) + + switch (fetch_bytes) { + case 4: + set_number(ks->top, *(unsigned int *)RET_VALUE); + break; + case 8: + set_number(ks->top, *(unsigned long *)RET_VALUE); + break; + default: + kp_error(ks, "unsupported fetch bytes in curr_task_info\n"); + set_nil(ks->top); + break; + } + +#undef RET_VALUE + + incr_top(ks); + return 1; +} + +/* + * This built-in function mainly purpose scripts/schedule/schedtimes.kp + */ +static int kplib_in_iowait(ktap_state_t *ks) +{ + set_number(ks->top, current->in_iowait); + incr_top(ks); + + return 1; +} + +static int kplib_in_interrupt(ktap_state_t *ks) +{ + int ret = in_interrupt(); + + set_number(ks->top, ret); + incr_top(ks); + return 1; +} + +static int kplib_exit(ktap_state_t *ks) +{ + kp_vm_try_to_exit(ks); + + /* do not execute bytecode any more in this thread */ + return -1; +} + +static const ktap_libfunc_t base_lib_funcs[] = { + {"print", kplib_print}, + {"printf", kplib_printf}, + {"print_hist", kplib_print_hist}, + + {"pairs", kplib_pairs}, + {"len", kplib_len}, + {"delete", kplib_delete}, + + {"stack", kplib_stack}, + {"print_trace_clock", kplib_print_trace_clock}, + + {"num_cpus", kplib_num_cpus}, + {"arch", kplib_arch}, + {"kernel_v", kplib_kernel_v}, + {"kernel_string", kplib_kernel_string}, + {"user_string", kplib_user_string}, + {"stringof", kplib_stringof}, + {"ipof", kplib_ipof}, + + {"gettimeofday_ns", kplib_gettimeofday_ns}, + {"gettimeofday_us", kplib_gettimeofday_us}, + {"gettimeofday_ms", kplib_gettimeofday_ms}, + {"gettimeofday_s", kplib_gettimeofday_s}, + + {"curr_taskinfo", kplib_curr_taskinfo}, + + {"in_iowait", kplib_in_iowait}, + {"in_interrupt", kplib_in_interrupt}, + + {"exit", kplib_exit}, + {NULL} +}; + +int kp_lib_init_base(ktap_state_t *ks) +{ + return kp_vm_register_lib(ks, NULL, base_lib_funcs); +} diff --git a/tools/ktap/runtime/lib_kdebug.c b/tools/ktap/runtime/lib_kdebug.c new file mode 100644 index 0000000..430537e --- /dev/null +++ b/tools/ktap/runtime/lib_kdebug.c @@ -0,0 +1,198 @@ +/* + * lib_kdebug.c - kdebug library support for ktap + * + * This file is part of ktap by Jovi Zhangwei. + * + * Copyright (C) 2012-2014 Jovi Zhangwei <jovi.zhang...@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/slab.h> +#include <linux/version.h> +#include <linux/ftrace_event.h> +#include "../include/ktap_types.h" +#include "ktap.h" +#include "kp_obj.h" +#include "kp_str.h" +#include "kp_transport.h" +#include "kp_vm.h" +#include "kp_events.h" + +/** + * function kdebug.trace_by_id + * + * @uaddr: userspace address refer to ktap_eventdesc_t + * @closure + */ +static int kplib_kdebug_trace_by_id(ktap_state_t *ks) +{ + unsigned long uaddr = kp_arg_checknumber(ks, 1); + ktap_func_t *fn = kp_arg_checkfunction(ks, 2); + struct task_struct *task = G(ks)->trace_task; + ktap_eventdesc_t eventsdesc; + char *filter = NULL; + int *id_arr; + int ret, i; + + if (G(ks)->mainthread != ks) { + kp_error(ks, + "kdebug.trace_by_id only can be called in mainthread\n"); + return -1; + } + + /* kdebug.trace_by_id cannot be called in trace_end state */ + if (G(ks)->state != KTAP_RUNNING) { + kp_error(ks, + "kdebug.trace_by_id only can be called in RUNNING state\n"); + return -1; + } + + /* copy ktap_eventdesc_t from userspace */ + ret = copy_from_user(&eventsdesc, (void *)uaddr, + sizeof(ktap_eventdesc_t)); + if (ret < 0) + return -1; + + if (eventsdesc.filter) { + int len; + + len = strlen_user(eventsdesc.filter); + if (len > 0x1000) + return -1; + + filter = kmalloc(len + 1, GFP_KERNEL); + if (!filter) + return -1; + + /* copy filter string from userspace */ + if (strncpy_from_user(filter, eventsdesc.filter, len) < 0) { + kfree(filter); + return -1; + } + } + + id_arr = kmalloc(eventsdesc.nr * sizeof(int), GFP_KERNEL); + if (!id_arr) { + kfree(filter); + return -1; + } + + /* copy all event id from userspace */ + ret = copy_from_user(id_arr, eventsdesc.id_arr, + eventsdesc.nr * sizeof(int)); + if (ret < 0) { + kfree(filter); + kfree(id_arr); + return -1; + } + + fn = clvalue(kp_arg(ks, 2)); + + for (i = 0; i < eventsdesc.nr; i++) { + struct perf_event_attr attr; + + cond_resched(); + + if (signal_pending(current)) { + flush_signals(current); + kfree(filter); + kfree(id_arr); + return -1; + } + + memset(&attr, 0, sizeof(attr)); + attr.type = PERF_TYPE_TRACEPOINT; + attr.config = id_arr[i]; + attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | + PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD; + attr.sample_period = 1; + attr.size = sizeof(attr); + attr.disabled = 0; + + /* register event one by one */ + ret = kp_event_create(ks, &attr, task, filter, fn); + if (ret < 0) + break; + } + + kfree(filter); + kfree(id_arr); + return 0; +} + +static int kplib_kdebug_trace_end(ktap_state_t *ks) +{ + /* trace_end_closure will be called when ktap main thread exit */ + G(ks)->trace_end_closure = kp_arg_checkfunction(ks, 1); + return 0; +} + +static int kplib_kdebug_tracepoint(ktap_state_t *ks) +{ + const char *event_name = kp_arg_checkstring(ks, 1); + ktap_func_t *fn = kp_arg_checkfunction(ks, 2); + + if (G(ks)->mainthread != ks) { + kp_error(ks, + "kdebug.tracepoint only can be called in mainthread\n"); + return -1; + } + + /* kdebug.tracepoint cannot be called in trace_end state */ + if (G(ks)->state != KTAP_RUNNING) { + kp_error(ks, + "kdebug.tracepoint only can be called in RUNNING state\n"); + return -1; + } + + return kp_event_create_tracepoint(ks, event_name, fn); +} + +static int kplib_kdebug_kprobe(ktap_state_t *ks) +{ + const char *event_name = kp_arg_checkstring(ks, 1); + ktap_func_t *fn = kp_arg_checkfunction(ks, 2); + + if (G(ks)->mainthread != ks) { + kp_error(ks, + "kdebug.kprobe only can be called in mainthread\n"); + return -1; + } + + /* kdebug.kprobe cannot be called in trace_end state */ + if (G(ks)->state != KTAP_RUNNING) { + kp_error(ks, + "kdebug.kprobe only can be called in RUNNING state\n"); + return -1; + } + + return kp_event_create_kprobe(ks, event_name, fn); +} +static const ktap_libfunc_t kdebug_lib_funcs[] = { + {"trace_by_id", kplib_kdebug_trace_by_id}, + {"trace_end", kplib_kdebug_trace_end}, + + {"tracepoint", kplib_kdebug_tracepoint}, + {"kprobe", kplib_kdebug_kprobe}, + {NULL} +}; + +int kp_lib_init_kdebug(ktap_state_t *ks) +{ + return kp_vm_register_lib(ks, "kdebug", kdebug_lib_funcs); +} + diff --git a/tools/ktap/runtime/lib_net.c b/tools/ktap/runtime/lib_net.c new file mode 100644 index 0000000..0103d14 --- /dev/null +++ b/tools/ktap/runtime/lib_net.c @@ -0,0 +1,107 @@ +/* + * lib_base.c - base library + * + * This file is part of ktap by Jovi Zhangwei. + * + * Copyright (C) 2012-2014 Jovi Zhangwei <jovi.zhang...@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 <net/inet_sock.h> +#include "../include/ktap_types.h" +#include "ktap.h" +#include "kp_obj.h" +#include "kp_str.h" +#include "kp_vm.h" + +/** + * Return the source IP address for a given sock + */ +static int kplib_net_ip_sock_saddr(ktap_state_t *ks) +{ + struct inet_sock *isk; + int family; + + /* TODO: need to validate the address firstly */ + + isk = (struct inet_sock *)kp_arg_checknumber(ks, 1); + family = isk->sk.__sk_common.skc_family; + + if (family == AF_INET) { + set_number(ks->top, isk->inet_rcv_saddr); + } else { + kp_error(ks, "ip_sock_saddr only support ipv4 now\n"); + set_nil(ks->top); + } + + incr_top(ks); + return 1; +} + +/** + * Return the destination IP address for a given sock + */ +static int kplib_net_ip_sock_daddr(ktap_state_t *ks) +{ + struct inet_sock *isk; + int family; + + /* TODO: need to validate the address firstly */ + + isk = (struct inet_sock *)kp_arg_checknumber(ks, 1); + family = isk->sk.__sk_common.skc_family; + + if (family == AF_INET) { + set_number(ks->top, isk->inet_daddr); + } else { + kp_error(ks, "ip_sock_daddr only support ipv4 now\n"); + set_nil(ks->top); + } + + incr_top(ks); + return 1; + +} + +/** + * Returns a string representation for an IP address + */ +static int kplib_net_format_ip_addr(ktap_state_t *ks) +{ + __be32 ip = (__be32)kp_arg_checknumber(ks, 1); + ktap_str_t *ts; + char ipstr[32]; + + snprintf(ipstr, 32, "%pI4", &ip); + ts = kp_str_newz(ks, ipstr); + if (ts) { + set_string(ks->top, kp_str_newz(ks, ipstr)); + incr_top(ks); + return 1; + } else + return -1; +} + +static const ktap_libfunc_t net_lib_funcs[] = { + {"ip_sock_saddr", kplib_net_ip_sock_saddr}, + {"ip_sock_daddr", kplib_net_ip_sock_daddr}, + {"format_ip_addr", kplib_net_format_ip_addr}, + {NULL} +}; + +int kp_lib_init_net(ktap_state_t *ks) +{ + return kp_vm_register_lib(ks, "net", net_lib_funcs); +} diff --git a/tools/ktap/runtime/lib_table.c b/tools/ktap/runtime/lib_table.c new file mode 100644 index 0000000..5c00f83 --- /dev/null +++ b/tools/ktap/runtime/lib_table.c @@ -0,0 +1,58 @@ +/* + * lib_table.c - Table library + * + * This file is part of ktap by Jovi Zhangwei. + * + * Copyright (C) 2012-2014 Jovi Zhangwei <jovi.zhang...@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_types.h" +#include "ktap.h" +#include "kp_obj.h" +#include "kp_vm.h" +#include "kp_tab.h" + +static int kplib_table_new(ktap_state_t *ks) +{ + int narr = kp_arg_checkoptnumber(ks, 1, 0); + int nrec = kp_arg_checkoptnumber(ks, 2, 0); + ktap_tab_t *h; + + h = kp_tab_new_ah(ks, narr, nrec); + if (!h) { + set_nil(ks->top); + } else { + set_table(ks->top, h); + } + + incr_top(ks); + return 1; +} + +static const ktap_libfunc_t table_lib_funcs[] = { + {"new", kplib_table_new}, + {NULL} +}; + +int kp_lib_init_table(ktap_state_t *ks) +{ + return kp_vm_register_lib(ks, "table", table_lib_funcs); +} + diff --git a/tools/ktap/runtime/lib_timer.c b/tools/ktap/runtime/lib_timer.c new file mode 100644 index 0000000..3f160ef --- /dev/null +++ b/tools/ktap/runtime/lib_timer.c @@ -0,0 +1,210 @@ +/* + * lib_timer.c - timer library support for ktap + * + * This file is part of ktap by Jovi Zhangwei. + * + * Copyright (C) 2012-2014 Jovi Zhangwei <jovi.zhang...@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_types.h" +#include "ktap.h" +#include "kp_obj.h" +#include "kp_vm.h" +#include "kp_events.h" + +struct ktap_hrtimer { + struct hrtimer timer; + ktap_state_t *ks; + ktap_func_t *fn; + u64 ns; + struct list_head list; +}; + +/* + * Currently ktap disallow tracing event in timer callback closure, + * that will corrupt ktap_state_t and ktap stack, because timer closure + * and event closure use same irq percpu ktap_state_t and stack. + * We can use a different percpu ktap_state_t and stack for timer purpuse, + * but that's don't bring any big value with cost on memory consuming. + * + * So just simply disable tracing in timer closure, + * get_recursion_context()/put_recursion_context() is used for this purpose. + */ +static enum hrtimer_restart hrtimer_ktap_fn(struct hrtimer *timer) +{ + struct ktap_hrtimer *t; + ktap_state_t *ks; + int rctx; + + rcu_read_lock_sched_notrace(); + + t = container_of(timer, struct ktap_hrtimer, timer); + rctx = get_recursion_context(t->ks); + + ks = kp_vm_new_thread(t->ks, rctx); + set_func(ks->top, t->fn); + incr_top(ks); + kp_vm_call(ks, ks->top - 1, 0); + kp_vm_exit_thread(ks); + + hrtimer_add_expires_ns(timer, t->ns); + + put_recursion_context(ks, rctx); + rcu_read_unlock_sched_notrace(); + + return HRTIMER_RESTART; +} + +static int set_tick_timer(ktap_state_t *ks, u64 period, ktap_func_t *fn) +{ + struct ktap_hrtimer *t; + + t = kp_malloc(ks, sizeof(*t)); + if (unlikely(!t)) + return -ENOMEM; + t->ks = ks; + t->fn = fn; + 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); + + return 0; +} + +static int set_profile_timer(ktap_state_t *ks, u64 period, ktap_func_t *fn) +{ + 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; + + return kp_event_create(ks, &attr, NULL, NULL, fn); +} + +static int do_tick_profile(ktap_state_t *ks, int is_tick) +{ + const char *str = kp_arg_checkstring(ks, 1); + ktap_func_t *fn = kp_arg_checkfunction(ks, 2); + const char *tmp; + char interval_str[32] = {0}; + char suffix[10] = {0}; + int i = 0, ret, n; + int factor; + + 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) + ret = set_tick_timer(ks, (u64)factor * n, fn); + else + ret = set_profile_timer(ks, (u64)factor * n, fn); + + return ret; + + 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 kplib_timer_tick(ktap_state_t *ks) +{ + /* timer.tick cannot be called in trace_end state */ + if (G(ks)->state != KTAP_RUNNING) { + kp_error(ks, + "timer.tick only can be called in RUNNING state\n"); + return -1; + } + + 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 kplib_timer_profile(ktap_state_t *ks) +{ + /* timer.profile cannot be called in trace_end state */ + if (G(ks)->state != KTAP_RUNNING) { + kp_error(ks, + "timer.profile only can be called in RUNNING state\n"); + return -1; + } + + return do_tick_profile(ks, 0); +} + +void kp_exit_timers(ktap_state_t *ks) +{ + struct ktap_hrtimer *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_libfunc_t timer_lib_funcs[] = { + {"profile", kplib_timer_profile}, + {"tick", kplib_timer_tick}, + {NULL} +}; + +int kp_lib_init_timer(ktap_state_t *ks) +{ + return kp_vm_register_lib(ks, "timer", timer_lib_funcs); +} + -- 1.8.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/