/* * Kprobes-based tracing events * * Created by Masami Hiramatsu <mhiramat@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/module.h>#include <linux/uaccess.h>#include <linux/kprobes.h>#include <linux/seq_file.h>#include <linux/slab.h>#include <linux/smp.h>#include <linux/debugfs.h>#include <linux/types.h>#include <linux/string.h>#include <linux/ctype.h>#include <linux/ptrace.h>#include <linux/perf_event.h>#include <linux/stringify.h>#include <asm/bitsperlong.h> #include "trace.h"#include "trace_output.h" #define MAX_TRACE_ARGS 128#define MAX_ARGSTR_LEN 63#define MAX_EVENT_NAME_LEN 64#define KPROBE_EVENT_SYSTEM "kprobes" /* Reserved field names */#define FIELD_STRING_IP "__probe_ip"#define FIELD_STRING_RETIP "__probe_ret_ip"#define FIELD_STRING_FUNC "__probe_func" const char *reserved_field_names[] = { "common_type", "common_flags", "common_preempt_count", "common_pid", "common_tgid", "common_lock_depth", FIELD_STRING_IP, FIELD_STRING_RETIP, FIELD_STRING_FUNC,}; /* Printing function type */typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *);#define PRINT_TYPE_FUNC_NAME(type) print_type_##type#define PRINT_TYPE_FMT_NAME(type) print_type_format_##type /* Printing in basic type function template */#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \ const char *name, void *data)\{ \ return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\} \static const char PRINT_TYPE_FMT_NAME(type)[] = fmt; DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int)DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int)DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long)DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long)DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int)DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int)DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long)DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long) /* Data fetch function type */typedef void (*fetch_func_t)(struct pt_regs *, void *, void *); struct fetch_param { fetch_func_t fn; void *data;}; static __kprobes void call_fetch(struct fetch_param *fprm, struct pt_regs *regs, void *dest){ return fprm->fn(regs, fprm->data, dest);} #define FETCH_FUNC_NAME(kind, type) fetch_##kind##_##type/* * Define macro for basic types - we don't need to define s* types, because * we have to care only about bitwidth at recording time. */#define DEFINE_BASIC_FETCH_FUNCS(kind) \DEFINE_FETCH_##kind(u8) \DEFINE_FETCH_##kind(u16) \DEFINE_FETCH_##kind(u32) \DEFINE_FETCH_##kind(u64) #define CHECK_BASIC_FETCH_FUNCS(kind, fn) \ ((FETCH_FUNC_NAME(kind, u8) == fn) || \ (FETCH_FUNC_NAME(kind, u16) == fn) || \ (FETCH_FUNC_NAME(kind, u32) == fn) || \ (FETCH_FUNC_NAME(kind, u64) == fn)) /* Data fetch function templates */#define DEFINE_FETCH_reg(type) \static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \ void *offset, void *dest) \{ \ *(type *)dest = (type)regs_get_register(regs, \ (unsigned int)((unsigned long)offset)); \}DEFINE_BASIC_FETCH_FUNCS(reg) #define DEFINE_FETCH_stack(type) \static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\ void *offset, void *dest) \{ \ *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \ (unsigned int)((unsigned long)offset)); \}DEFINE_BASIC_FETCH_FUNCS(stack) #define DEFINE_FETCH_retval(type) \static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\ void *dummy, void *dest) \{ \ *(type *)dest = (type)regs_return_value(regs); \}DEFINE_BASIC_FETCH_FUNCS(retval) #define DEFINE_FETCH_memory(type) \static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\ void *addr, void *dest) \{ \ type retval; \ if (probe_kernel_address(addr, retval)) \ *(type *)dest = 0; \ else \ *(type *)dest = retval; \}DEFINE_BASIC_FETCH_FUNCS(memory) /* Memory fetching by symbol */struct symbol_cache { char *symbol; long offset; unsigned long addr;}; static unsigned long update_symbol_cache(struct symbol_cache *sc){ sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol); if (sc->addr) sc->addr += sc->offset; return sc->addr;} static void free_symbol_cache(struct symbol_cache *sc){ kfree(sc->symbol); kfree(sc);} static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset){ struct symbol_cache *sc; if (!sym || strlen(sym) == 0) return NULL; sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL); if (!sc) return NULL; sc->symbol = kstrdup(sym, GFP_KERNEL); if (!sc->symbol) { kfree(sc); return NULL; } sc->offset = offset; update_symbol_cache(sc); return sc;} #define DEFINE_FETCH_symbol(type) \static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\ void *data, void *dest) \{ \ struct symbol_cache *sc = data; \ if (sc->addr) \ fetch_memory_##type(regs, (void *)sc->addr, dest); \ else \ *(type *)dest = 0; \}DEFINE_BASIC_FETCH_FUNCS(symbol) /* Dereference memory access function */struct deref_fetch_param { struct fetch_param orig; long offset;}; #define DEFINE_FETCH_deref(type) \static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\ void *data, void *dest) \{ \ struct deref_fetch_param *dprm = data; \ unsigned long addr; \ call_fetch(&dprm->orig, regs, &addr); \ if (addr) { \ addr += dprm->offset; \ fetch_memory_##type(regs, (void *)addr, dest); \ } else \ *(type *)dest = 0; \}DEFINE_BASIC_FETCH_FUNCS(deref) static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data){ if (CHECK_BASIC_FETCH_FUNCS(deref, data->orig.fn)) free_deref_fetch_param(data->orig.data); else if (CHECK_BASIC_FETCH_FUNCS(symbol, data->orig.fn)) free_symbol_cache(data->orig.data); kfree(data);} /* Default (unsigned long) fetch type */#define __DEFAULT_FETCH_TYPE(t) u##t#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG)#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) #define ASSIGN_FETCH_FUNC(kind, type) \ .kind = FETCH_FUNC_NAME(kind, type) #define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \ {.name = #ptype, \ .size = sizeof(ftype), \ .is_signed = sign, \ .print = PRINT_TYPE_FUNC_NAME(ptype), \ .fmt = PRINT_TYPE_FMT_NAME(ptype), \ASSIGN_FETCH_FUNC(reg, ftype), \ASSIGN_FETCH_FUNC(stack, ftype), \ASSIGN_FETCH_FUNC(retval, ftype), \ASSIGN_FETCH_FUNC(memory, ftype), \ASSIGN_FETCH_FUNC(symbol, ftype), \ASSIGN_FETCH_FUNC(deref, ftype), \ } /* Fetch type information table */static const struct fetch_type { const char *name; /* Name of type */ size_t size; /* Byte size of type */ int is_signed; /* Signed flag */ print_type_func_t print; /* Print functions */ const char *fmt; /* Fromat string */ /* Fetch functions */ fetch_func_t reg; fetch_func_t stack; fetch_func_t retval; fetch_func_t memory; fetch_func_t symbol; fetch_func_t deref;} fetch_type_table[] = { ASSIGN_FETCH_TYPE(u8, u8, 0), ASSIGN_FETCH_TYPE(u16, u16, 0), ASSIGN_FETCH_TYPE(u32, u32, 0), ASSIGN_FETCH_TYPE(u64, u64, 0), ASSIGN_FETCH_TYPE(s8, u8, 1), ASSIGN_FETCH_TYPE(s16, u16, 1), ASSIGN_FETCH_TYPE(s32, u32, 1), ASSIGN_FETCH_TYPE(s64, u64, 1),}; static const struct fetch_type *find_fetch_type(const char *type){ int i; if (!type) type = DEFAULT_FETCH_TYPE_STR; for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++) if (strcmp(type, fetch_type_table[i].name) == 0) return &fetch_type_table[i]; return NULL;} /* Special function : only accept unsigned long */static __kprobes void fetch_stack_address(struct pt_regs *regs, void *dummy, void *dest){ *(unsigned long *)dest = kernel_stack_pointer(regs);} /** * Kprobe event core functions */ struct probe_arg { struct fetch_param fetch; unsigned int offset; /* Offset from argument entry */ const char *name; /* Name of this argument */ const char *comm; /* Command of this argument */ const struct fetch_type *type; /* Type of this argument */}; /* Flags for trace_probe */#define TP_FLAG_TRACE 1#define TP_FLAG_PROFILE 2 struct trace_probe { struct list_head list; struct kretprobe rp; /* Use rp.kp for kprobe use */ unsigned long nhit; unsigned int flags; /* For TP_FLAG_* */ const char *symbol; /* symbol name */ struct ftrace_event_class class; struct ftrace_event_call call; ssize_t size; /* trace entry size */ unsigned int nr_args; struct probe_arg args[];}; #define SIZEOF_TRACE_PROBE(n) \ (offsetof(struct trace_probe, args) + \ (sizeof(struct probe_arg) * (n))) static __kprobes int probe_is_return(struct trace_probe *tp){ return tp->rp.handler != NULL;} static __kprobes const char *probe_symbol(struct trace_probe *tp){ return tp->symbol ? tp->symbol : "unknown";} static int register_probe_event(struct trace_probe *tp);static void unregister_probe_event(struct trace_probe *tp); static DEFINE_MUTEX(probe_lock);static LIST_HEAD(probe_list); static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs);static int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs); /* Check the name is good for event/group */static int check_event_name(const char *name){ if (!isalpha(*name) && *name != '_') return 0; while (*++name != '\0') { if (!isalpha(*name) && !isdigit(*name) && *name != '_') return 0; } return 1;} /* * Allocate new trace_probe and initialize it (including kprobes). */static struct trace_probe *alloc_trace_probe(const char *group, const char *event, void *addr, const char *symbol, unsigned long offs, int nargs, int is_return){ struct trace_probe *tp; int ret = -ENOMEM; tp = kzalloc(SIZEOF_TRACE_PROBE(nargs), GFP_KERNEL); if (!tp) return ERR_PTR(ret); if (symbol) { tp->symbol = kstrdup(symbol, GFP_KERNEL); if (!tp->symbol) goto error; tp->rp.kp.symbol_name = tp->symbol; tp->rp.kp.offset = offs; } else tp->rp.kp.addr = addr; if (is_return) tp->rp.handler = kretprobe_dispatcher; else tp->rp.kp.pre_handler = kprobe_dispatcher; if (!event || !check_event_name(event)) { ret = -EINVAL; goto error; } tp->call.class = &tp->class; tp->call.name = kstrdup(event, GFP_KERNEL); if (!tp->call.name) goto error; if (!group || !check_event_name(group)) { ret = -EINVAL; goto error; } tp->class.system = kstrdup(group, GFP_KERNEL); if (!tp->class.system) goto error; INIT_LIST_HEAD(&tp->list); return tp;error: kfree(tp->call.name); kfree(tp->symbol); kfree(tp); return ERR_PTR(ret);} static void free_probe_arg(struct probe_arg *arg){ if (CHECK_BASIC_FETCH_FUNCS(deref, arg->fetch.fn)) free_deref_fetch_param(arg->fetch.data); else if (CHECK_BASIC_FETCH_FUNCS(symbol, arg->fetch.fn)) free_symbol_cache(arg->fetch.data); kfree(arg->name); kfree(arg->comm);} static void free_trace_probe(struct trace_probe *tp){ int i; for (i = 0; i < tp->nr_args; i++) free_probe_arg(&tp->args[i]); kfree(tp->call.class->system); kfree(tp->call.name); kfree(tp->symbol); kfree(tp);} static struct trace_probe *find_probe_event(const char *event, const char *group){ struct trace_probe *tp; list_for_each_entry(tp, &probe_list, list) if (strcmp(tp->call.name, event) == 0 && strcmp(tp->call.class->system, group) == 0) return tp; return NULL;} /* Unregister a trace_probe and probe_event: call with locking probe_lock */static void unregister_trace_probe(struct trace_probe *tp){ if (probe_is_return(tp)) unregister_kretprobe(&tp->rp); else unregister_kprobe(&tp->rp.kp); list_del(&tp->list); unregister_probe_event(tp);} /* Register a trace_probe and probe_event */static int register_trace_probe(struct trace_probe *tp){ struct trace_probe *old_tp; int ret; mutex_lock(&probe_lock); /* register as an event */ old_tp = find_probe_event(tp->call.name, tp->call.class->system); if (old_tp) { /* delete old event */ unregister_trace_probe(old_tp); free_trace_probe(old_tp); } ret = register_probe_event(tp); if (ret) { pr_warning("Faild to register probe event(%d)\n", ret); goto end; } tp->rp.kp.flags |= KPROBE_FLAG_DISABLED; if (probe_is_return(tp)) ret = register_kretprobe(&tp->rp); elseLines 1–500 of 1632. Load more
/* * Kprobes-based tracing events * * Created by Masami Hiramatsu <mhiramat@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/module.h> #include <linux/uaccess.h> #include <linux/kprobes.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/smp.h> #include <linux/debugfs.h> #include <linux/types.h> #include <linux/string.h> #include <linux/ctype.h> #include <linux/ptrace.h> #include <linux/perf_event.h> #include <linux/stringify.h> #include <asm/bitsperlong.h> #include "trace.h" #include "trace_output.h" #define MAX_TRACE_ARGS 128 #define MAX_ARGSTR_LEN 63 #define MAX_EVENT_NAME_LEN 64 #define KPROBE_EVENT_SYSTEM "kprobes" /* Reserved field names */ #define FIELD_STRING_IP "__probe_ip" #define FIELD_STRING_RETIP "__probe_ret_ip" #define FIELD_STRING_FUNC "__probe_func" const char *reserved_field_names[] = { "common_type", "common_flags", "common_preempt_count", "common_pid", "common_tgid", "common_lock_depth", FIELD_STRING_IP, FIELD_STRING_RETIP, FIELD_STRING_FUNC, }; /* Printing function type */ typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *); #define PRINT_TYPE_FUNC_NAME(type) print_type_##type #define PRINT_TYPE_FMT_NAME(type) print_type_format_##type /* Printing in basic type function template */ #define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \ static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \ const char *name, void *data)\ { \ return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\ } \ static const char PRINT_TYPE_FMT_NAME(type)[] = fmt; DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int) DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int) DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long) DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long) DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int) DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int) DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long) DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long) /* Data fetch function type */ typedef void (*fetch_func_t)(struct pt_regs *, void *, void *); struct fetch_param { fetch_func_t fn; void *data; }; static __kprobes void call_fetch(struct fetch_param *fprm, struct pt_regs *regs, void *dest) { return fprm->fn(regs, fprm->data, dest); } #define FETCH_FUNC_NAME(kind, type) fetch_##kind##_##type /* * Define macro for basic types - we don't need to define s* types, because * we have to care only about bitwidth at recording time. */ #define DEFINE_BASIC_FETCH_FUNCS(kind) \ DEFINE_FETCH_##kind(u8) \ DEFINE_FETCH_##kind(u16) \ DEFINE_FETCH_##kind(u32) \ DEFINE_FETCH_##kind(u64) #define CHECK_BASIC_FETCH_FUNCS(kind, fn) \ ((FETCH_FUNC_NAME(kind, u8) == fn) || \ (FETCH_FUNC_NAME(kind, u16) == fn) || \ (FETCH_FUNC_NAME(kind, u32) == fn) || \ (FETCH_FUNC_NAME(kind, u64) == fn)) /* Data fetch function templates */ #define DEFINE_FETCH_reg(type) \ static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \ void *offset, void *dest) \ { \ *(type *)dest = (type)regs_get_register(regs, \ (unsigned int)((unsigned long)offset)); \ } DEFINE_BASIC_FETCH_FUNCS(reg) #define DEFINE_FETCH_stack(type) \ static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\ void *offset, void *dest) \ { \ *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \ (unsigned int)((unsigned long)offset)); \ } DEFINE_BASIC_FETCH_FUNCS(stack) #define DEFINE_FETCH_retval(type) \ static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\ void *dummy, void *dest) \ { \ *(type *)dest = (type)regs_return_value(regs); \ } DEFINE_BASIC_FETCH_FUNCS(retval) #define DEFINE_FETCH_memory(type) \ static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\ void *addr, void *dest) \ { \ type retval; \ if (probe_kernel_address(addr, retval)) \ *(type *)dest = 0; \ else \ *(type *)dest = retval; \ } DEFINE_BASIC_FETCH_FUNCS(memory) /* Memory fetching by symbol */ struct symbol_cache { char *symbol; long offset; unsigned long addr; }; static unsigned long update_symbol_cache(struct symbol_cache *sc) { sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol); if (sc->addr) sc->addr += sc->offset; return sc->addr; } static void free_symbol_cache(struct symbol_cache *sc) { kfree(sc->symbol); kfree(sc); } static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset) { struct symbol_cache *sc; if (!sym || strlen(sym) == 0) return NULL; sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL); if (!sc) return NULL; sc->symbol = kstrdup(sym, GFP_KERNEL); if (!sc->symbol) { kfree(sc); return NULL; } sc->offset = offset; update_symbol_cache(sc); return sc; } #define DEFINE_FETCH_symbol(type) \ static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\ void *data, void *dest) \ { \ struct symbol_cache *sc = data; \ if (sc->addr) \ fetch_memory_##type(regs, (void *)sc->addr, dest); \ else \ *(type *)dest = 0; \ } DEFINE_BASIC_FETCH_FUNCS(symbol) /* Dereference memory access function */ struct deref_fetch_param { struct fetch_param orig; long offset; }; #define DEFINE_FETCH_deref(type) \ static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\ void *data, void *dest) \ { \ struct deref_fetch_param *dprm = data; \ unsigned long addr; \ call_fetch(&dprm->orig, regs, &addr); \ if (addr) { \ addr += dprm->offset; \ fetch_memory_##type(regs, (void *)addr, dest); \ } else \ *(type *)dest = 0; \ } DEFINE_BASIC_FETCH_FUNCS(deref) static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data) { if (CHECK_BASIC_FETCH_FUNCS(deref, data->orig.fn)) free_deref_fetch_param(data->orig.data); else if (CHECK_BASIC_FETCH_FUNCS(symbol, data->orig.fn)) free_symbol_cache(data->orig.data); kfree(data); } /* Default (unsigned long) fetch type */ #define __DEFAULT_FETCH_TYPE(t) u##t #define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t) #define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG) #define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) #define ASSIGN_FETCH_FUNC(kind, type) \ .kind = FETCH_FUNC_NAME(kind, type) #define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \ {.name = #ptype, \ .size = sizeof(ftype), \ .is_signed = sign, \ .print = PRINT_TYPE_FUNC_NAME(ptype), \ .fmt = PRINT_TYPE_FMT_NAME(ptype), \ ASSIGN_FETCH_FUNC(reg, ftype), \ ASSIGN_FETCH_FUNC(stack, ftype), \ ASSIGN_FETCH_FUNC(retval, ftype), \ ASSIGN_FETCH_FUNC(memory, ftype), \ ASSIGN_FETCH_FUNC(symbol, ftype), \ ASSIGN_FETCH_FUNC(deref, ftype), \ } /* Fetch type information table */ static const struct fetch_type { const char *name; /* Name of type */ size_t size; /* Byte size of type */ int is_signed; /* Signed flag */ print_type_func_t print; /* Print functions */ const char *fmt; /* Fromat string */ /* Fetch functions */ fetch_func_t reg; fetch_func_t stack; fetch_func_t retval; fetch_func_t memory; fetch_func_t symbol; fetch_func_t deref; } fetch_type_table[] = { ASSIGN_FETCH_TYPE(u8, u8, 0), ASSIGN_FETCH_TYPE(u16, u16, 0), ASSIGN_FETCH_TYPE(u32, u32, 0), ASSIGN_FETCH_TYPE(u64, u64, 0), ASSIGN_FETCH_TYPE(s8, u8, 1), ASSIGN_FETCH_TYPE(s16, u16, 1), ASSIGN_FETCH_TYPE(s32, u32, 1), ASSIGN_FETCH_TYPE(s64, u64, 1), }; static const struct fetch_type *find_fetch_type(const char *type) { int i; if (!type) type = DEFAULT_FETCH_TYPE_STR; for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++) if (strcmp(type, fetch_type_table[i].name) == 0) return &fetch_type_table[i]; return NULL; } /* Special function : only accept unsigned long */ static __kprobes void fetch_stack_address(struct pt_regs *regs, void *dummy, void *dest) { *(unsigned long *)dest = kernel_stack_pointer(regs); } /** * Kprobe event core functions */ struct probe_arg { struct fetch_param fetch; unsigned int offset; /* Offset from argument entry */ const char *name; /* Name of this argument */ const char *comm; /* Command of this argument */ const struct fetch_type *type; /* Type of this argument */ }; /* Flags for trace_probe */ #define TP_FLAG_TRACE 1 #define TP_FLAG_PROFILE 2 struct trace_probe { struct list_head list; struct kretprobe rp; /* Use rp.kp for kprobe use */ unsigned long nhit; unsigned int flags; /* For TP_FLAG_* */ const char *symbol; /* symbol name */ struct ftrace_event_class class; struct ftrace_event_call call; ssize_t size; /* trace entry size */ unsigned int nr_args; struct probe_arg args[]; }; #define SIZEOF_TRACE_PROBE(n) \ (offsetof(struct trace_probe, args) + \ (sizeof(struct probe_arg) * (n))) static __kprobes int probe_is_return(struct trace_probe *tp) { return tp->rp.handler != NULL; } static __kprobes const char *probe_symbol(struct trace_probe *tp) { return tp->symbol ? tp->symbol : "unknown"; } static int register_probe_event(struct trace_probe *tp); static void unregister_probe_event(struct trace_probe *tp); static DEFINE_MUTEX(probe_lock); static LIST_HEAD(probe_list); static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs); static int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs); /* Check the name is good for event/group */ static int check_event_name(const char *name) { if (!isalpha(*name) && *name != '_') return 0; while (*++name != '\0') { if (!isalpha(*name) && !isdigit(*name) && *name != '_') return 0; } return 1; } /* * Allocate new trace_probe and initialize it (including kprobes). */ static struct trace_probe *alloc_trace_probe(const char *group, const char *event, void *addr, const char *symbol, unsigned long offs, int nargs, int is_return) { struct trace_probe *tp; int ret = -ENOMEM; tp = kzalloc(SIZEOF_TRACE_PROBE(nargs), GFP_KERNEL); if (!tp) return ERR_PTR(ret); if (symbol) { tp->symbol = kstrdup(symbol, GFP_KERNEL); if (!tp->symbol) goto error; tp->rp.kp.symbol_name = tp->symbol; tp->rp.kp.offset = offs; } else tp->rp.kp.addr = addr; if (is_return) tp->rp.handler = kretprobe_dispatcher; else tp->rp.kp.pre_handler = kprobe_dispatcher; if (!event || !check_event_name(event)) { ret = -EINVAL; goto error; } tp->call.class = &tp->class; tp->call.name = kstrdup(event, GFP_KERNEL); if (!tp->call.name) goto error; if (!group || !check_event_name(group)) { ret = -EINVAL; goto error; } tp->class.system = kstrdup(group, GFP_KERNEL); if (!tp->class.system) goto error; INIT_LIST_HEAD(&tp->list); return tp; error: kfree(tp->call.name); kfree(tp->symbol); kfree(tp); return ERR_PTR(ret); } static void free_probe_arg(struct probe_arg *arg) { if (CHECK_BASIC_FETCH_FUNCS(deref, arg->fetch.fn)) free_deref_fetch_param(arg->fetch.data); else if (CHECK_BASIC_FETCH_FUNCS(symbol, arg->fetch.fn)) free_symbol_cache(arg->fetch.data); kfree(arg->name); kfree(arg->comm); } static void free_trace_probe(struct trace_probe *tp) { int i; for (i = 0; i < tp->nr_args; i++) free_probe_arg(&tp->args[i]); kfree(tp->call.class->system); kfree(tp->call.name); kfree(tp->symbol); kfree(tp); } static struct trace_probe *find_probe_event(const char *event, const char *group) { struct trace_probe *tp; list_for_each_entry(tp, &probe_list, list) if (strcmp(tp->call.name, event) == 0 && strcmp(tp->call.class->system, group) == 0) return tp; return NULL; } /* Unregister a trace_probe and probe_event: call with locking probe_lock */ static void unregister_trace_probe(struct trace_probe *tp) { if (probe_is_return(tp)) unregister_kretprobe(&tp->rp); else unregister_kprobe(&tp->rp.kp); list_del(&tp->list); unregister_probe_event(tp); } /* Register a trace_probe and probe_event */ static int register_trace_probe(struct trace_probe *tp) { struct trace_probe *old_tp; int ret; mutex_lock(&probe_lock); /* register as an event */ old_tp = find_probe_event(tp->call.name, tp->call.class->system); if (old_tp) { /* delete old event */ unregister_trace_probe(old_tp); free_trace_probe(old_tp); } ret = register_probe_event(tp); if (ret) { pr_warning("Faild to register probe event(%d)\n", ret); goto end; } tp->rp.kp.flags |= KPROBE_FLAG_DISABLED; if (probe_is_return(tp)) ret = register_kretprobe(&tp->rp); else