/* * 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 "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_NARGS "__probe_nargs"#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_NARGS, FIELD_STRING_RETIP, FIELD_STRING_FUNC,}; struct fetch_func { unsigned long (*func)(struct pt_regs *, void *); void *data;}; static __kprobes unsigned long call_fetch(struct fetch_func *f, struct pt_regs *regs){ return f->func(regs, f->data);} /* fetch handlers */static __kprobes unsigned long fetch_register(struct pt_regs *regs, void *offset){ return regs_get_register(regs, (unsigned int)((unsigned long)offset));} static __kprobes unsigned long fetch_stack(struct pt_regs *regs, void *num){ return regs_get_kernel_stack_nth(regs, (unsigned int)((unsigned long)num));} static __kprobes unsigned long fetch_memory(struct pt_regs *regs, void *addr){ unsigned long retval; if (probe_kernel_address(addr, retval)) return 0; return retval;} static __kprobes unsigned long fetch_retvalue(struct pt_regs *regs, void *dummy){ return regs_return_value(regs);} static __kprobes unsigned long fetch_stack_address(struct pt_regs *regs, void *dummy){ return kernel_stack_pointer(regs);} /* 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;} static __kprobes unsigned long fetch_symbol(struct pt_regs *regs, void *data){ struct symbol_cache *sc = data; if (sc->addr) return fetch_memory(regs, (void *)sc->addr); else return 0;} /* Special indirect memory access interface */struct indirect_fetch_data { struct fetch_func orig; long offset;}; static __kprobes unsigned long fetch_indirect(struct pt_regs *regs, void *data){ struct indirect_fetch_data *ind = data; unsigned long addr; addr = call_fetch(&ind->orig, regs); if (addr) { addr += ind->offset; return fetch_memory(regs, (void *)addr); } else return 0;} static __kprobes void free_indirect_fetch_data(struct indirect_fetch_data *data){ if (data->orig.func == fetch_indirect) free_indirect_fetch_data(data->orig.data); else if (data->orig.func == fetch_symbol) free_symbol_cache(data->orig.data); kfree(data);} /** * Kprobe event core functions */ struct probe_arg { struct fetch_func fetch; const char *name;}; /* 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_call call; struct trace_event event; 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 probe_arg_string(char *buf, size_t n, struct fetch_func *ff){ int ret = -EINVAL; if (ff->func == fetch_register) { const char *name; name = regs_query_register_name((unsigned int)((long)ff->data)); ret = snprintf(buf, n, "%%%s", name); } else if (ff->func == fetch_stack) ret = snprintf(buf, n, "$stack%lu", (unsigned long)ff->data); else if (ff->func == fetch_memory) ret = snprintf(buf, n, "@0x%p", ff->data); else if (ff->func == fetch_symbol) { struct symbol_cache *sc = ff->data; if (sc->offset) ret = snprintf(buf, n, "@%s%+ld", sc->symbol, sc->offset); else ret = snprintf(buf, n, "@%s", sc->symbol); } else if (ff->func == fetch_retvalue) ret = snprintf(buf, n, "$retval"); else if (ff->func == fetch_stack_address) ret = snprintf(buf, n, "$stack"); else if (ff->func == fetch_indirect) { struct indirect_fetch_data *id = ff->data; size_t l = 0; ret = snprintf(buf, n, "%+ld(", id->offset); if (ret >= n) goto end; l += ret; ret = probe_arg_string(buf + l, n - l, &id->orig); if (ret < 0) goto end; l += ret; ret = snprintf(buf + l, n - l, ")"); ret += l; }end: if (ret >= n) return -ENOSPC; return ret;} 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.name = kstrdup(event, GFP_KERNEL); if (!tp->call.name) goto error; if (!group || !check_event_name(group)) { ret = -EINVAL; goto error; } tp->call.system = kstrdup(group, GFP_KERNEL); if (!tp->call.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 (arg->fetch.func == fetch_symbol) free_symbol_cache(arg->fetch.data); else if (arg->fetch.func == fetch_indirect) free_indirect_fetch_data(arg->fetch.data); kfree(arg->name);} 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.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.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.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 ret = register_kprobe(&tp->rp.kp); if (ret) { pr_warning("Could not insert probe(%d)\n", ret); if (ret == -EILSEQ) { pr_warning("Probing address(0x%p) is not an " "instruction boundary.\n", tp->rp.kp.addr); ret = -EINVAL; } unregister_probe_event(tp); } else list_add_tail(&tp->list, &probe_list);end: mutex_unlock(&probe_lock); return ret;} /* Split symbol and offset. */static int split_symbol_offset(char *symbol, unsigned long *offset){ char *tmp; int ret; if (!offset) return -EINVAL; tmp = strchr(symbol, '+'); if (tmp) { /* skip sign because strict_strtol doesn't accept '+' */ ret = strict_strtoul(tmp + 1, 0, offset); if (ret) return ret; *tmp = '\0'; } else *offset = 0; return 0;} #define PARAM_MAX_ARGS 16#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return){ int ret = 0; unsigned long param; if (strcmp(arg, "retval") == 0) { if (is_return) { ff->func = fetch_retvalue; ff->data = NULL; } else ret = -EINVAL; } else if (strncmp(arg, "stack", 5) == 0) { if (arg[5] == '\0') { ff->func = fetch_stack_address; ff->data = NULL; } else if (isdigit(arg[5])) { ret = strict_strtoul(arg + 5, 10, ¶m); if (ret || param > PARAM_MAX_STACK) ret = -EINVAL; else { ff->func = fetch_stack; ff->data = (void *)param; } } else ret = -EINVAL; } else ret = -EINVAL; return ret;} /* Recursive argument parser */static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return){ int ret = 0; unsigned long param; long offset; char *tmp; switch (arg[0]) { case '$': ret = parse_probe_vars(arg + 1, ff, is_return);Lines 1–500 of 1488. 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 "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_NARGS "__probe_nargs" #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_NARGS, FIELD_STRING_RETIP, FIELD_STRING_FUNC, }; struct fetch_func { unsigned long (*func)(struct pt_regs *, void *); void *data; }; static __kprobes unsigned long call_fetch(struct fetch_func *f, struct pt_regs *regs) { return f->func(regs, f->data); } /* fetch handlers */ static __kprobes unsigned long fetch_register(struct pt_regs *regs, void *offset) { return regs_get_register(regs, (unsigned int)((unsigned long)offset)); } static __kprobes unsigned long fetch_stack(struct pt_regs *regs, void *num) { return regs_get_kernel_stack_nth(regs, (unsigned int)((unsigned long)num)); } static __kprobes unsigned long fetch_memory(struct pt_regs *regs, void *addr) { unsigned long retval; if (probe_kernel_address(addr, retval)) return 0; return retval; } static __kprobes unsigned long fetch_retvalue(struct pt_regs *regs, void *dummy) { return regs_return_value(regs); } static __kprobes unsigned long fetch_stack_address(struct pt_regs *regs, void *dummy) { return kernel_stack_pointer(regs); } /* 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; } static __kprobes unsigned long fetch_symbol(struct pt_regs *regs, void *data) { struct symbol_cache *sc = data; if (sc->addr) return fetch_memory(regs, (void *)sc->addr); else return 0; } /* Special indirect memory access interface */ struct indirect_fetch_data { struct fetch_func orig; long offset; }; static __kprobes unsigned long fetch_indirect(struct pt_regs *regs, void *data) { struct indirect_fetch_data *ind = data; unsigned long addr; addr = call_fetch(&ind->orig, regs); if (addr) { addr += ind->offset; return fetch_memory(regs, (void *)addr); } else return 0; } static __kprobes void free_indirect_fetch_data(struct indirect_fetch_data *data) { if (data->orig.func == fetch_indirect) free_indirect_fetch_data(data->orig.data); else if (data->orig.func == fetch_symbol) free_symbol_cache(data->orig.data); kfree(data); } /** * Kprobe event core functions */ struct probe_arg { struct fetch_func fetch; const char *name; }; /* 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_call call; struct trace_event event; 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 probe_arg_string(char *buf, size_t n, struct fetch_func *ff) { int ret = -EINVAL; if (ff->func == fetch_register) { const char *name; name = regs_query_register_name((unsigned int)((long)ff->data)); ret = snprintf(buf, n, "%%%s", name); } else if (ff->func == fetch_stack) ret = snprintf(buf, n, "$stack%lu", (unsigned long)ff->data); else if (ff->func == fetch_memory) ret = snprintf(buf, n, "@0x%p", ff->data); else if (ff->func == fetch_symbol) { struct symbol_cache *sc = ff->data; if (sc->offset) ret = snprintf(buf, n, "@%s%+ld", sc->symbol, sc->offset); else ret = snprintf(buf, n, "@%s", sc->symbol); } else if (ff->func == fetch_retvalue) ret = snprintf(buf, n, "$retval"); else if (ff->func == fetch_stack_address) ret = snprintf(buf, n, "$stack"); else if (ff->func == fetch_indirect) { struct indirect_fetch_data *id = ff->data; size_t l = 0; ret = snprintf(buf, n, "%+ld(", id->offset); if (ret >= n) goto end; l += ret; ret = probe_arg_string(buf + l, n - l, &id->orig); if (ret < 0) goto end; l += ret; ret = snprintf(buf + l, n - l, ")"); ret += l; } end: if (ret >= n) return -ENOSPC; return ret; } 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.name = kstrdup(event, GFP_KERNEL); if (!tp->call.name) goto error; if (!group || !check_event_name(group)) { ret = -EINVAL; goto error; } tp->call.system = kstrdup(group, GFP_KERNEL); if (!tp->call.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 (arg->fetch.func == fetch_symbol) free_symbol_cache(arg->fetch.data); else if (arg->fetch.func == fetch_indirect) free_indirect_fetch_data(arg->fetch.data); kfree(arg->name); } 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.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.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.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 ret = register_kprobe(&tp->rp.kp); if (ret) { pr_warning("Could not insert probe(%d)\n", ret); if (ret == -EILSEQ) { pr_warning("Probing address(0x%p) is not an " "instruction boundary.\n", tp->rp.kp.addr); ret = -EINVAL; } unregister_probe_event(tp); } else list_add_tail(&tp->list, &probe_list); end: mutex_unlock(&probe_lock); return ret; } /* Split symbol and offset. */ static int split_symbol_offset(char *symbol, unsigned long *offset) { char *tmp; int ret; if (!offset) return -EINVAL; tmp = strchr(symbol, '+'); if (tmp) { /* skip sign because strict_strtol doesn't accept '+' */ ret = strict_strtoul(tmp + 1, 0, offset); if (ret) return ret; *tmp = '\0'; } else *offset = 0; return 0; } #define PARAM_MAX_ARGS 16 #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return) { int ret = 0; unsigned long param; if (strcmp(arg, "retval") == 0) { if (is_return) { ff->func = fetch_retvalue; ff->data = NULL; } else ret = -EINVAL; } else if (strncmp(arg, "stack", 5) == 0) { if (arg[5] == '\0') { ff->func = fetch_stack_address; ff->data = NULL; } else if (isdigit(arg[5])) { ret = strict_strtoul(arg + 5, 10, ¶m); if (ret || param > PARAM_MAX_STACK) ret = -EINVAL; else { ff->func = fetch_stack; ff->data = (void *)param; } } else ret = -EINVAL; } else ret = -EINVAL; return ret; } /* Recursive argument parser */ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) { int ret = 0; unsigned long param; long offset; char *tmp; switch (arg[0]) { case '$': ret = parse_probe_vars(arg + 1, ff, is_return);