uprobes tracer: Add stack/memory/retval access support
PATCH v2 1/1 uprobes tracer: Add stack/memory/retval access support
Event arguments except @SYM are supported for uprobes tracer. They are @ADDR, $stack, $stackN, $retval, and off(arguments).
uprobes tracer and kprobes tracer have their own fetch type information tables and fetch type functions. But they share printing type functions and some fetch type functions for register, retval, and memory dereference. Common functions are defined in trace_probe.c.
Signed-off-by Hyeoncheol Lee <[email protected]>
---
v2 - separate fetch type functions for uprobes and kprobes tracer
kernel/trace/trace_kprobe.c | 147 +++++++++++++++++++++- kernel/trace/trace_probe.c | 292 ++++++++++++------------------------------- kernel/trace/trace_probe.h | 79 +++++++++++- kernel/trace/trace_uprobe.c | 158 ++++++++++++++++++++++- 4 files changed, 460 insertions(+), 216 deletions(-)
kernel/trace/trace_kprobe.c +145 -2
@@ -24,10 +24,152 @@#define KPROBE_EVENT_SYSTEM "kprobes" +/* +* 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 (method) \ + DEFINE_FETCH_##method(u8) \ + DEFINE_FETCH_##method(u16) \ + DEFINE_FETCH_##method(u32) \ + DEFINE_FETCH_##method(u64) + +/* Stack fetch function */ +#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 fetch_stack_string NULL +#define fetch_stack_string_size NULL + +/* Memory fetch function */ +#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) + +/* +* Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max +* length and relative data location. +*/ +static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, +void *addr,void *dest) +{ +long ret; +int maxlen =get_rloc_len (*(u32 *)dest); +u8 *dst =get_rloc_data (dest); +u8 *src = addr; +mm_segment_t old_fs =get_fs (); + +if (!maxlen) +return ; + +/* +* Try to get string again, since the string can be changed while +* probing. +*/ +set_fs (KERNEL_DS ); +pagefault_disable (); + +do + ret =__copy_from_user_inatomic (dst++, src++,1 ); +while (dst[-1 ] && ret ==0 && src - (u8 *)addr < maxlen); + + dst[-1 ] ='\0' ; +pagefault_enable (); +set_fs (old_fs); + +if (ret <0 ) {/* Failed to fetch string */ + ((u8 *)get_rloc_data (dest))[0 ] ='\0' ; + *(u32 *)dest =make_data_rloc (0 ,get_rloc_offs (*(u32 *)dest)); + }else { + *(u32 *)dest =make_data_rloc (src - (u8 *)addr, +get_rloc_offs (*(u32 *)dest)); + } +} + +/* Return the length of string -- including null terminal byte */ +static __kprobes void FETCH_FUNC_NAME (memory ,string_size )(struct pt_regs *regs, +void *addr,void *dest) +{ +mm_segment_t old_fs; +int ret, len =0 ; +u8 c; + + old_fs =get_fs (); +set_fs (KERNEL_DS ); +pagefault_disable (); + +do { + ret =__copy_from_user_inatomic (&c, (u8 *)addr + len,1 ); + len++; + }while (c && ret ==0 && len <MAX_STRING_SIZE ); + +pagefault_enable (); +set_fs (old_fs); + +if (ret <0 )/* Failed to check the length */ + *(u32 *)dest =0 ; +else + *(u32 *)dest = len; +} + +#define ASSIGN_FETCH_FUNC (method, type) \ + [FETCH_MTD_##method] = FETCH_FUNC_NAME(method, type) + +#define __ASSIGN_FETCH_TYPE (_name, ptype, ftype, _size, sign, _fmttype) \ + {.name = _name, \ + .size = _size, \ + .is_signed = sign, \ + .print = PRINT_TYPE_FUNC_NAME(ptype), \ + .fmt = PRINT_TYPE_FMT_NAME(ptype), \ + .fmttype = _fmttype, \ + .fetch = { \ +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), \ +ASSIGN_FETCH_FUNC(bitfield, ftype), \ + } \ + } + +#define ASSIGN_FETCH_TYPE (ptype, ftype, sign) \ + __ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #ptype) + +/* Fetch type information table */ +static const struct fetch_type fetch_type_table[NUM_FETCH_TYPES ] = { +/* Special types */ + [FETCH_TYPE_STRING ] =__ASSIGN_FETCH_TYPE ("string" , string, string, +sizeof (u32),1 ,"__data_loc char[]" ), + [FETCH_TYPE_STRSIZE ] =__ASSIGN_FETCH_TYPE ("string_size" , u32, + string_size,sizeof (u32),0 ,"u32" ), +/* Basic types */ +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 ), +}; +/** * Kprobe event core functions */ -struct trace_probe {struct list_head list;struct kretprobe rp;/* Use rp.kp for kprobe use */
@@ -525,7 +667,8 @@ static int create_trace_probe(int argc, char **argv)/* Parse fetch argument */ ret =traceprobe_parse_probe_arg (arg, &tp->size, &tp->args[i], - is_return, true); + is_return, true, + fetch_type_table);if (ret) {pr_info ("Parse error at argument[%d]. (%d)\n" , i, ret); goto error;
kernel/trace/trace_probe.c +83 -209
542 lines not on this page · raw message
kernel/trace/trace_probe.h +75 -4
100 lines not on this page · raw message
kernel/trace/trace_uprobe.c +157 -1
170 lines not on this page · raw message
Signature · 1 line
1.7.10.4