Messages in this thread Patch in this message | | | From | Namhyung Kim <> | Subject | [PATCH/RFC 17/17] tracing/uprobes: Add @+file_offset fetch method | Date | Wed, 27 Nov 2013 15:20:03 +0900 |
| |
From: Namhyung Kim <namhyung.kim@lge.com>
Enable to fetch data from a file offset. Currently it only supports fetching from same binary uprobe set. It'll translate the file offset to a proper virtual address in the process.
The syntax is "@+OFFSET" as it does similar to normal memory fetching (@ADDR) which does no address translation.
To do it, change fifth argument to receive 'void *priv' and make it set only for uprobes so that we can determine the arg is for kprobes or uprobes easily.
Suggested-by: Oleg Nesterov <oleg@redhat.com> Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: zhangwei(Jovi) <jovi.zhangwei@huawei.com> Cc: Arnaldo Carvalho de Melo <acme@ghostprotocols.net> Signed-off-by: Namhyung Kim <namhyung@kernel.org> --- Documentation/trace/uprobetracer.txt | 1 + kernel/trace/trace_kprobe.c | 10 +++++++++- kernel/trace/trace_probe.c | 32 +++++++++++++++++++++++++++----- kernel/trace/trace_probe.h | 9 ++++++++- kernel/trace/trace_uprobe.c | 36 +++++++++++++++++++++++++++++++++++- 5 files changed, 80 insertions(+), 8 deletions(-)
diff --git a/Documentation/trace/uprobetracer.txt b/Documentation/trace/uprobetracer.txt index 6e5cff263e2b..f1cf9a34ad9d 100644 --- a/Documentation/trace/uprobetracer.txt +++ b/Documentation/trace/uprobetracer.txt @@ -32,6 +32,7 @@ Synopsis of uprobe_tracer FETCHARGS : Arguments. Each probe can have up to 128 args. %REG : Fetch register REG @ADDR : Fetch memory at ADDR (ADDR should be in userspace) + @+OFFSET : Fetch memory at OFFSET (OFFSET from same file as PATH) $stackN : Fetch Nth entry of stack (N >= 0) $stack : Fetch stack address. $retval : Fetch return value.(*) diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 5c4f72d45b97..300fd0f8b2a9 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -239,6 +239,14 @@ DEFINE_BASIC_FETCH_FUNCS(symbol) DEFINE_FETCH_symbol(string) DEFINE_FETCH_symbol(string_size) +/* kprobes don't support file_offset fetch methods */ +#define fetch_file_offset_u8 NULL +#define fetch_file_offset_u16 NULL +#define fetch_file_offset_u32 NULL +#define fetch_file_offset_u64 NULL +#define fetch_file_offset_string NULL +#define fetch_file_offset_string_size NULL + /* Fetch type information table */ const struct fetch_type kprobes_fetch_type_table[] = { /* Special types */ @@ -758,7 +766,7 @@ static int create_trace_kprobe(int argc, char **argv) /* Parse fetch argument */ ret = traceprobe_parse_probe_arg(arg, &tp->p.size, parg, - is_return, true); + is_return, NULL); if (ret) { pr_info("Parse error at argument[%d]. (%d)\n", i, ret); goto error; diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 1be603d45811..bd16aed615b2 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -348,13 +348,14 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t, /* Recursive argument parser */ static int parse_probe_arg(char *arg, const struct fetch_type *t, - struct fetch_param *f, bool is_return, bool is_kprobe) + struct fetch_param *f, bool is_return, void *priv) { const struct fetch_type *ftbl; unsigned long param; long offset; char *tmp; int ret = 0; + bool is_kprobe = (priv == NULL); ftbl = is_kprobe ? kprobes_fetch_type_table : uprobes_fetch_type_table; BUG_ON(ftbl == NULL); @@ -373,7 +374,7 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t, } break; - case '@': /* memory or symbol */ + case '@': /* memory, file-offset or symbol */ if (isdigit(arg[1])) { ret = kstrtoul(arg + 1, 0, ¶m); if (ret) @@ -381,6 +382,26 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t, f->fn = t->fetch[FETCH_MTD_memory]; f->data = (void *)param; + } else if (arg[1] == '+') { + struct file_offset_fetch_param *foprm; + + /* kprobes don't support file offsets */ + if (is_kprobe) + return -EINVAL; + + ret = kstrtol(arg + 2, 0, &offset); + if (ret) + break; + + foprm = kzalloc(sizeof(*foprm), GFP_KERNEL); + if (!foprm) + return -ENOMEM; + + foprm->tu = priv; + foprm->offset = offset; + + f->fn = t->fetch[FETCH_MTD_file_offset]; + f->data = foprm; } else { /* uprobes don't support symbols */ if (!is_kprobe) @@ -428,7 +449,7 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t, dprm->fetch_size = get_fetch_size_function(t, dprm->fetch, ftbl); ret = parse_probe_arg(arg, t2, &dprm->orig, is_return, - is_kprobe); + priv); if (ret) kfree(dprm); else { @@ -486,11 +507,12 @@ static int __parse_bitfield_probe_arg(const char *bf, /* String length checking wrapper */ int traceprobe_parse_probe_arg(char *arg, ssize_t *size, - struct probe_arg *parg, bool is_return, bool is_kprobe) + struct probe_arg *parg, bool is_return, void *priv) { const struct fetch_type *ftbl; const char *t; int ret; + bool is_kprobe = (priv == NULL); ftbl = is_kprobe ? kprobes_fetch_type_table : uprobes_fetch_type_table; BUG_ON(ftbl == NULL); @@ -516,7 +538,7 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size, } parg->offset = *size; *size += parg->type->size; - ret = parse_probe_arg(arg, parg->type, &parg->fetch, is_return, is_kprobe); + ret = parse_probe_arg(arg, parg->type, &parg->fetch, is_return, priv); if (ret >= 0 && t != NULL) ret = __parse_bitfield_probe_arg(t, parg->type, &parg->fetch); diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index c2f9adbfab3f..ea0c80d37c31 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -106,6 +106,7 @@ enum { FETCH_MTD_symbol, FETCH_MTD_deref, FETCH_MTD_bitfield, + FETCH_MTD_file_offset, FETCH_MTD_END, }; @@ -217,6 +218,7 @@ ASSIGN_FETCH_FUNC(memory, ftype), \ ASSIGN_FETCH_FUNC(symbol, ftype), \ ASSIGN_FETCH_FUNC(deref, ftype), \ ASSIGN_FETCH_FUNC(bitfield, ftype), \ +ASSIGN_FETCH_FUNC(file_offset, ftype), \ } \ } @@ -291,6 +293,11 @@ static inline __kprobes void call_fetch(struct fetch_param *fprm, return fprm->fn(regs, fprm->data, dest); } +struct file_offset_fetch_param { + unsigned long offset; + struct trace_uprobe *tu; +}; + /* Check the name is good for event/group/fields */ static inline int is_good_name(const char *name) { @@ -304,7 +311,7 @@ static inline int is_good_name(const char *name) } extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size, - struct probe_arg *parg, bool is_return, bool is_kprobe); + struct probe_arg *parg, bool is_return, void *priv); extern int traceprobe_conflict_field_name(const char *name, struct probe_arg *args, int narg); diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index c037477d1c1a..fb1b81e2603a 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -175,6 +175,26 @@ static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs, #define fetch_symbol_string NULL #define fetch_symbol_string_size NULL +static unsigned long translate_user_vaddr(struct file_offset_fetch_param *foprm) +{ + unsigned long base_addr; + + base_addr = current->utask->vaddr - foprm->tu->offset; + return base_addr + foprm->offset; +} + +#define DEFINE_FETCH_file_offset(type) \ +static __kprobes void FETCH_FUNC_NAME(file_offset, type)(struct pt_regs *regs,\ + void *data, void *dest) \ +{ \ + void *vaddr = (void *)translate_user_vaddr(data); \ + \ + FETCH_FUNC_NAME(memory, type)(regs, vaddr, dest); \ +} +DEFINE_BASIC_FETCH_FUNCS(file_offset) +DEFINE_FETCH_file_offset(string) +DEFINE_FETCH_file_offset(string_size) + /* Fetch type information table */ const struct fetch_type uprobes_fetch_type_table[] = { /* Special types */ @@ -512,7 +532,7 @@ static int create_trace_uprobe(int argc, char **argv) /* Parse fetch argument */ ret = traceprobe_parse_probe_arg(arg, &tu->p.size, parg, - is_return, false); + is_return, tu); if (ret) { pr_info("Parse error at argument[%d]. (%d)\n", i, ret); goto error; @@ -1104,11 +1124,18 @@ int trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type, static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs) { struct trace_uprobe *tu; + struct uprobe_task *utask; int ret = 0; tu = container_of(con, struct trace_uprobe, consumer); tu->nhit++; + utask = current->utask; + if (utask == NULL) + return UPROBE_HANDLER_REMOVE; + + utask->vaddr = instruction_pointer(regs); + if (tu->p.flags & TP_FLAG_TRACE) ret |= uprobe_trace_func(tu, regs); @@ -1123,9 +1150,16 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con, unsigned long func, struct pt_regs *regs) { struct trace_uprobe *tu; + struct uprobe_task *utask; tu = container_of(con, struct trace_uprobe, consumer); + utask = current->utask; + if (utask == NULL) + return UPROBE_HANDLER_REMOVE; + + utask->vaddr = func; + if (tu->p.flags & TP_FLAG_TRACE) uretprobe_trace_func(tu, func, regs); -- 1.7.11.7
| |